/* sml3_escapeseq.c: Funktionen fuer Escape-Sequenzen */

/* Copyright 2012-2017 Kurt Nienhaus
 *
 * This file is part of libsammel3.
 * libsammel3 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 * libsammel3 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with libsammel3.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "config.h"
#include "sml3_fehler.h"
#include "sml3_util.h"
#include "sml3_gummi.h"
#include "sml3_escapeseq.h"

size_t SML3_escapeseq_convert(const char *, size_t, struct SML3_gummi *, size_t);
size_t SML3_escapeseq_next(const char *, size_t, char *);


/* SML3_escapeseq_convert [thread-sicher]:
 * verarbeitet Escape-Sequenzen
 * 1.Arg: zu bearbeitender String
 * 2.Arg: Anzahl zu bearbeitende Zeichen im 1.Arg
 * 3.Arg: Rueckgabe-Gummistring fuer bearbeiteten String (erhaelt Ende-0)
 * 4.Arg: Beginnposition Rueckgabe-Gummistring
 * Rueckgabe: Anzahl Zeichen im Gummistring ab 4.Arg
 */
size_t
SML3_escapeseq_convert(const char *string, size_t stringlen, struct SML3_gummi *rgm, size_t gmstart)
{
  const char *sptr;
  size_t eanz, gmpos, r1;
  char ezch;

  if (rgm == NULL) { SML3_fehlerexit("%s", SML3_strerror(EINVAL)); }
  if (string == NULL || stringlen == 0) { SML3_gumcpy(rgm, gmstart, ""); return 0; }

  gmpos = gmstart;
  for (sptr = string; stringlen > 0; sptr++, stringlen--) {
    if (*sptr != '\\') { continue; }  /* kein Escape-Sequenz-Beginn */

    eanz = SML3_escapeseq_next(sptr, stringlen, &ezch);
    if (eanz == 0) { break; }  /* unvollstaendige Escape-Sequenz */

    if (sptr > string) {  /* vorige Zeichen uebertragen */
      r1 = (size_t)(sptr - string);
      SML3_gummove(rgm, gmpos, string, r1);
      gmpos += r1;
      string = sptr;
    }

    /* Escape-Zeichen anhaengen und Stringposition aktualisieren */
    SML3_gumncpy(rgm, gmpos, &ezch, 1);
    gmpos++;
    sptr += (eanz - 1);
    stringlen -= (eanz - 1);
    string = sptr + 1;
  }

  if (sptr > string) {  /* restliche Zeichen uebertragen */
    r1 = (size_t)(sptr - string);
    SML3_gummove(rgm, gmpos, string, r1);
    gmpos += r1;
    string = sptr;
  }

  SML3_gumcpy(rgm, gmpos, "");
  return gmpos - gmstart;
} /* Ende SML3_escapeseq_convert */


/* SML3_escapeseq_next [thread-sicher]:
 * verarbeitet naechste Escape-Sequenz, falls 1.Arg mit Backslash beginnt
 * 1.Arg: String
 * 2.Arg: Anzahl Zeichen im 1.Arg
 * 3.Arg: Rueckgabe fuer verarbeitetes Escape-Zeichen
 * Rueckgabe: > 0: Laenge der Escape-Sequenz im 1.Arg
 *              0: keine oder unvollstaendige Escape-Sequenz im 1.Arg
 *
 * Escape-Sequenzen:
 *  - \a \b \f \n \r \t \v \' \" \\ \?
 *  - \0<bis 3 Stellen: Zahl>: Oktalwert
 *  - \[xX]<bis 2 Stellen: [0-9a-fA-F]>: Hexwert
 *  - bei nicht erkannten wird \ entfernt
 */
size_t
SML3_escapeseq_next(const char *string, size_t stringlen, char *rzch)
{
  char ezch;
  int i1;
  size_t olen;

  if (string == NULL || stringlen == 0 || string[0] != '\\') { return 0; }

  olen = stringlen;
  if (--stringlen == 0) {
    if (rzch != NULL) { *rzch = string[0]; }
    return (olen - stringlen);
  }
  string++;

  switch (*string) {

    /* Escape-Zeichen */
    case 'a':  ezch='\a'; break;
    case 'b':  ezch='\b'; break;
    case 'f':  ezch='\f'; break;
    case 'n':  ezch='\n'; break;
    case 'r':  ezch='\r'; break;
    case 't':  ezch='\t'; break;
    case 'v':  ezch='\v'; break;
    case '\'': ezch='\''; break;
    case '"':  ezch='"'; break;
    case '\\': ezch='\\'; break;
    case '?':  ezch='?'; break;

    /* oktal */
    case '0':
      ezch = 0;
      for (i1 = 0; i1 < 3; i1++) {
        if (--stringlen == 0) { stringlen++; break; }
        if (string[1] < '0' || string[1] > '7') { stringlen++; break; }
        string++;
        ezch <<= 3;
        ezch += (*string - '0');
      }
      break;

    /* hexadezimal */
    case 'x':
    case 'X':
      ezch = 0;
      for (i1 = 0; i1 < 2; i1++) {
        if (--stringlen == 0) {
          stringlen++;
          if (i1 == 0) { ezch = *string; }  /* nur Backslash enfernen */
          break;
        }
        if (string[1] >= '0' && string[1] <= '9') {
          string++;
          ezch <<= 4;
          ezch += (*string - '0');
        } else if (string[1] >= 'A' && string[1] <= 'F') {
          string++;
          ezch <<= 4;
          ezch += (*string - 'A' + 10);
        } else if (string[1] >= 'a' && string[1] <= 'f') {
          string++;
          ezch <<= 4;
          ezch += (*string - 'a' + 10);
        } else {
          stringlen++;
          if (i1 == 0) { ezch = *string; }  /* nur Backslash enfernen */
          break;
        }
      }
      break;

    /* Backslash entfernen, normales Zeichen */
    default:
      ezch = *string;
  }

  stringlen--;
  if (rzch != NULL) { *rzch = ezch; }
  return (olen - stringlen);
} /* Ende SML3_escapeseq_next */
