#ifndef SML3__REGEX_H_
#define SML3__REGEX_H_

/* ++++++++++++++++++++
 * +++ Beschreibung +++
 * ++++++++++++++++++++

  Suchen und Ersetzen mit regulaeren Ausdruecken.
  Suchen des naechsten oder letzten Ausdrucks geschieht mit:
   - SML3_rexsuche()
   - SML3_rexstr()
  Suchen und Ersetzen geschieht mit:
   - SML3_rexersetze()
  Ausserdem:
   - SML3_rexquot()        Quotet alle reg. Sonderzeichen in einem String
   - SML3_rexcomp()        Compiliert einen reg. Ausdruck (fuer haeufige Wiederholung)
   - SML3_rexcompfree()    dealloziert einen mit SML3_rexcomp() compilierten Ausdruck

  Bei einem wiederholten Suchen eines reg. Ausdrucks in einem Text beachte
  man das Verhindern einer eventuellen Endlosschleife wie im u.a. Beispiel
  dargestellt.

  Beim Suchen und Ersetzen koennen nach dem zu durchsuchenden Text beliebig
  viele Ersetzeparameter (struct SML3_rexrep) mithilfe SML3_REXREP() oder
  SML3_REXREPFUNK() angegeben werden, wobei diese nacheinander abgearbeitet
  werden, beendet sein muss die Liste mit SML3_REXEND.
  Der Originaltext wird nicht veraendert, sondern ein allozierter ersetzter
  Text zurueckgegeben. Falls keine Ersetzung stattfand, wird NULL mit
  SML3-errno-Wert = 0 zurueckgegeben.
  Zur Ersetzung kann ein String (SML3_REXREP()) oder eine Funktion
  (SML3_REXREPFUNK()) verwendet werden.
  Der String kann Klammerreferenzen enthalten (\<Ziffer>), die auf
  Unterausdruecke in der Suche referenzieren.
  Die Funktion muss einen allozierten Ersetzestring zurueckgeben.

  Sowohl die Suche mit SML3_rexsuche() als auch die Ersetzefunktion von
  SML3_rexersetze() verwenden ein Treffer-Struct (struct SML3_rexsub).
  Das Element "sptr" ist der Beginn des Originaltextes.
  Die Elemente "von" und "bis" geben den Treffer-Bereich ab "sptr" an.
  Das Element "anzsub" gibt die Anzahl der Unterausdruecke an, die
  im Element "sub[]" enthalten sind; die darin enthaltenen Elemente
  "von" und "bis" geben den Treffer-Bereich ab "sptr" fuer die jeweiligen
  Unterausdruecke an.
  "sub[]" geht von 0 bis "anzsub" - 1.
  "bis" ist immer exklusiv.

  +++ Regulaere Ausdruecke +++
  Portabel sind sie maximal 256 Zeichen lang.
  Extended reg. Ausdruecke:
    ^      Textbeginn; bei Compilierung mit SML3_REXFLAG_N auch Zeilenbeginn
    $      Textende; bei Compilierung mit SML3_REXFLAG_N auch Zeilenende
    .      beliebiges Zeichen; bei Compilierung mit SML3_REXFLAG_N nicht Newline
    []     ein Zeichen aus der Menge
    [^]    beliebiges Zeichen, das nicht in der Menge steht
           Bei der Menge verlieren die Sonderzeichen ihren Sonderstatus,
           allerdings wird "-" zu einem Bereichskenner (z.B. [A-Z]).
           Um "-" zu suchen, muss es am Anfang oder Ende stehen;
           um "]" zu suchen, muss es am Anfang stehen.
    *      voriges Zeichen nullmal, einmal oder mehrmals
    ?      voriges Zeichen nullmal oder einmal
    +      voriges Zeichen einmal oder mehrmals
    {m,n}  voriges Zeichen m- bis n-mal
    {m,}   voriges Zeichen mindestens m-mal
    {m}    voriges Zeichen genau m-mal
    |      Trenner fuer Alternativausdruck
    ()     Gruppierung und gleichzeitig Setzen von Unterausdruecken,
           dahinter kann auch * + {} gesetzt werden, wobei im
           Unterausdruck das letzte Vorkommen enthalten ist
    \      Quotung, macht Sonderzeichen zu normalem Zeichen
  Beispiel:
           A(.)|B(.)   --> findet A<Zeichen> oder B<Zeichen>
                           setzt \1=<Zeichen von A>
                                 \2=<Zeichen von B>
           A(B|C)D     --> findet ABD oder ACD
                           setzt \1=<B|C>
           (A(B|C))|D  --> findet AB oder AC oder D
                           setzt \1=<AB|AC|leer>
                           setzt \2=<B|C|leer>
+++ */


/* ++++++++++++++++
 * +++ Beispiel +++
 * ++++++++++++++++

 * +++ 
 * +++ Durchsuchen eines ueber stdin eingegebenen Textes
 * +++ nach einem als Parameter eingegebenen regulaeren Ausdrucks
 * +++ und Anzeigen aller Treffer samt Unterausdruecken
 * +++
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/types.h>
  #include <unistd.h>
  #include <errno.h>
  #include <sammel3.h>

  int main(int argc, char **argv) {
    char *p0, *p1;
    struct SML3_rexpatt *rpatt;
    struct SML3_rexsub rsub;
    ssize_t s1;
    size_t slen;
    int i1;
    struct SML3_gummi gm = SML3_GUM_INITIALIZER;

    if (argc < 2) { exit(1); }

    // Einlesen des Textes von stdin
    slen = 0;
    while ((s1 = SML3_gumfget(&gm, slen, stdin)) > 0) { slen += s1; }

    // Vorcompilieren des reg. Ausdrucks
    rpatt = SML3_rexcomp(argv[1], SML3_REXFLAG_I);
    if (rpatt == NULL) { fprintf(stderr, "%s: %s\n", argv[0], SML3_fehlermsg()); exit(1); }

    // Suchen nach dem jeweils naechstem Treffer mit Ausgabe
    p0 = SML3_gumgetval(&gm);  // Startpointer Text
    printf("Textstart bei %p\n\n", p0);

  #define USE_REXSUCHE  // fuer Alternativauswahl: SML3_rexsuche() oder SML3_rexstr()
  #ifdef USE_REXSUCHE
    while ((p1 = SML3_rexsuche(p0, &p0, rpatt, SML3_REXFLAG_COMP, &rsub)) != NULL) {
      printf("Gefunden bei %p: <%.*s>\n", p1, (int)(rsub.bis - rsub.von), rsub.sptr + rsub.von);
      for (i1 = 1; i1 <= rsub.anzsub; i1++) {  // Unterausdruecke
        printf(" - SubExpr %d: <%.*s>\n", i1, (int)(rsub.sub[i1 - 1].bis - rsub.sub[i1 - 1].von), rsub.sptr + rsub.sub[i1 - 1].von);
      }
      printf("\n");
      if (p1 == p0) {  // Endlosschleife verhindern
        if (*p0 == '\0') { break; }  // Textende
        p0++;  // Endlosschleife ueberspringen
      }
    }
  #else  // !USE_REXSUCHE
    while ((p1 = SML3_rexstr(p0, &p0, rpatt, SML3_REXFLAG_COMP, &slen)) != NULL) {
      printf("Gefunden bei %p: <%.*s>\n", p1, (int)slen, p1);
      if (p1 == p0) {  // Endlosschleife verhindern
        if (*p0 == '\0') { break; }  // Textende
        p0++;  // Endlosschleife ueberspringen
      }
    }
  #endif  // !USE_REXSUCHE

    if (SML3_fehlererrno() != 0) { fprintf(stderr, "%s: %s\n", argv[0], SML3_fehlermsg()); exit(1); }

    SML3_rexcompfree(rpatt);
    SML3_gumdest(&gm);
    exit(0);
  }


 * +++
 * +++ Viermalige Ersetzung eines ueber stdin eingegebenen Textes
 * +++ - zweimal Vertauschen jeweils zweier Buchstaben (direkt)
 * +++ - zweimal Grossetzen von Kleinbuchstaben und umgekehrt (ueber Ersetzefunktion)
 * +++ ergibt zum Schluss den Originaltext
 * +++
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/types.h>
  #include <unistd.h>
  #include <errno.h>
  #include <sammel3.h>

  // Ersetzefunktion: macht Grossbuchstaben klein und Kleinbuchstaben gross
  static char *
  cbkrep(struct SML3_rexsub *rsub, void *vptr)
  {
    char *rets, c1;
    size_t i1;

    rets = malloc((size_t)(rsub->bis - rsub->von + 1));  // Rueckgabe allozieren
    if (rets == NULL) { SML3_fehlernew(ENOMEM, "malloc: %s", strerror(errno)); return NULL; }

    for (i1 = rsub->von; i1 < rsub->bis; i1++) {  // Gesamt-Treffer auswerten
      c1 = rsub->sptr[i1];
      if (c1 >= 'A' && c1 <= 'Z') {  // Grossbuchstaben klein machen
        rets[i1 - rsub->von] = c1 - 'A' + 'a';
      } else if (c1 >= 'a' && c1 <= 'z') {  // Kleinbuchstaben gross machen
        rets[i1 - rsub->von] = c1 - 'a' + 'A';
      } else {  // keine Aenderung
        rets[i1 - rsub->von] = c1;
      }
    }
    rets[i1 - rsub->von] = '\0';
    return rets;
  }

  int main(int argc, char **argv) {
    char *p0, *p1;
    struct SML3_rexpatt *rpt1, *rpt2;
    ssize_t s1;
    size_t slen;
    struct SML3_gummi gm = SML3_GUM_INITIALIZER;

    // Einlesen des Textes von stdin
    slen = 0;
    while ((s1 = SML3_gumfget(&gm, slen, stdin)) > 0) { slen += s1; }
    p0 = SML3_gumgetval(&gm);  // Startpointer Text

    // Vorcompilieren des 1. und 3. reg. Ausdrucks (nur zu Demonstrationszwecken)
    rpt1 = SML3_rexcomp("([a-z])([a-z])", SML3_REXFLAG_I);
    if (rpt1 == NULL) { fprintf(stderr, "%s: %s\n", argv[0], SML3_fehlermsg()); exit(1); }
    rpt2 = SML3_rexcomp("[A-Za-z]+", 0);
    if (rpt2 == NULL) { fprintf(stderr, "%s: %s\n", argv[0], SML3_fehlermsg()); exit(1); }

    // Ersetzen: jeweils zweimal ergibt wieder Originaltext
    p1 = SML3_rexersetze(p0,
      SML3_REXREP(rpt1, SML3_REXFLAG_COMP | SML3_REXFLAG_GLOBAL, "\\2\\1"),
      SML3_REXREP("([a-z])([a-z])", SML3_REXFLAG_I | SML3_REXFLAG_GLOBAL, "\\2\\1"),
      SML3_REXREPFUNK(rpt2, SML3_REXFLAG_COMP | SML3_REXFLAG_GLOBAL, cbkrep, NULL),
      SML3_REXREPFUNK("[A-Za-z]+", SML3_REXFLAG_GLOBAL, cbkrep, NULL),
      SML3_REXEND);

    if (p1 == NULL) {  // Fehler oder keine Ersetzung
      if (SML3_fehlererrno() != 0) {
        fprintf(stderr, "%s: %s\n", argv[0], SML3_fehlermsg());
        exit(1);
      }
    } else {  // ersetzten Text mit Originaltext vergleichen
      if (strcmp(p0, p1) != 0) { fprintf(stderr, "Fehler\n"); exit(1); }
      free(p1);  // ersetzten Text freigeben
    }
    printf("OK\n");

    SML3_rexcompfree(rpt1);
    SML3_rexcompfree(rpt2);
    SML3_gumdest(&gm);
    exit(0);
  }
+++ */


/* ++++++++++++++++++ */
/* +++ Funktionen +++ */
/* ++++++++++++++++++ */

#include <sys/types.h>
#include <stdarg.h>

/* compiliertes Suchpattern */
struct SML3_rexpatt {
  int sflag;     /* Suchpatternflag geodert (SML3_REXFLAG_?) */
  void *cpatt;   /* compiliertes Suchpattern */
};

/* Suchpatternflags */
#define SML3_REXFLAG_Q       1  /* Suchausdruck vor Compilieren quoten */
#define SML3_REXFLAG_I       2  /* Suchausdruck case-insensitiv */
#define SML3_REXFLAG_N       4  /* Suchausdruck: Newline ist Trenner fuer ^$ und nicht in .
                                   Newline wird nicht gefunden in [^...], wenn nicht enthalten
                                 */
#define SML3_REXFLAG_GLOBAL  8  /* global: alle Treffer finden */
#define SML3_REXFLAG_COMP   16  /* Suchausdruck ist bereits compiliert */
#define SML3_REXFLAG_FUNK   32  /* Ersetzestring ist eine Funktion */

/* gefundener Ausdruck samt Unterausdruecken */
#define SML3_REXSUBMAX 19  /* max. Anzahl Unterausdruecke */
struct SML3_rexsub {  /* enthaelt Unterausdruecke */
  const char *sptr;    /* Startpointer String */
  size_t von, bis;     /* Position gefundener Ausdruck Von und Bis (exkl.) */
  int anzsub;          /* Anzahl Unterausdruecke (0 bis SML3_REXSUBMAX) */
  struct {             /* Unterausdruecke */
    size_t von, bis;     /* Position gefundener Unterausdruck Von und Bis (exkl.) */
  } sub[SML3_REXSUBMAX];
};

/* fuer wiederholtes Suchen + Ersetzen */
struct SML3_rexrep {  /* Struct zum Suchen + Ersetzen */
  const void *spatt;   /* Suchausdruck (evtl. schon compiliert) */
  int sflag;           /* Suchpatternflag geodert (SML3_REXFLAG_*) */
  union {
    void *replace;       /* Ersetzestring */
    char * (*replfunk)(struct SML3_rexsub *, void *);  /* Ersetzefunktion */
  } e;
  void *param;         /* 2.Parameter fuer Ersetzefunktion oder NULL */
};
#define SML3_REXREP(P1, P2, P3)          SML3_rexmakerep((P1), (P2), (P3), NULL, NULL)
#define SML3_REXREPFUNK(P1, P2, P3, P4)  SML3_rexmakerep((P1), SML3_REXFLAG_FUNK | (P2), NULL, (P3), (P4))
#define SML3_REXEND                      SML3_rexmakerep(NULL, 0, NULL, NULL, NULL)


/** SML3_rexquot [thread-sicher]:
 * quotet alle Metazeichen im Suchpattern
 * 1.Arg: Suchpattern
 * Rueckgabe: alloziertes gequotetes Suchpattern
 */
extern char * SML3_rexquot(const char *);


/** SML3_rexcomp [thread-sicher]:
 * compiliert Suchpattern
 * 1.Arg: Suchpattern
 * 2.Arg: Suchpatternflag geodert (SML3_REXFLAG_?)
 * Rueckgabe: alloziertes compiliertes Suchpattern
 *            oder NULL = Fehler
 * SML3-errno-Wert: EINVAL  = Fehler Uebergabeparameter
 *                  ENOMEM  = Allokationsfehler
 *                  ENOEXEC = Compilierungsfehler
 */
extern struct SML3_rexpatt * SML3_rexcomp(const char *, int);


/** SML3_rexcompfree [thread-sicher]:
 * dealloziert compiliertes Suchpattern
 * 1.Arg: compiliertes Suchpattern
 */
extern void SML3_rexcompfree(struct SML3_rexpatt *);


/** SML3_rexsuche [thread-sicher]:
 * sucht nach erstem bzw. letztem Vorkommen eines reg. Ausdrucks
 * 1.Arg: zu durchsuchender String
 * 2.Arg: fuer Rueckgabe Startpointer im 1.Arg fuer weitere Suche
 *        (darf Adresse auf 1.Arg sein)
 *        oder NULL
 * 3.Arg: Suchpattern (als String oder schon compiliert)
 * 4.Arg: Suchpatternflag geodert (SML3_REXFLAG_*)
 *         - als String: SML3_REXFLAG_Q, SML3_REXFLAG_I, SML3_REXFLAG_N
 *         - schon compiliert: SML3_REXFLAG_COMP
 *         - ausserdem: SML3_REXFLAG_GLOBAL = letzten Treffer finden
 * 5.Arg: Adresse fuer Rueckgabe Unterausdruecke oder NULL
 * Rueckgabe: Pointer auf Beginn gefundenen Treffer
 *            (Achtung: falls gleich 2.Arg -> Endlosschleife)
 *            oder NULL = kein Treffer (SML3-errno-Wert = 0)
 *                        bzw. Fehler (SML3-errno-Wert != 0)
 * SML3-errno-Wert: 0       = kein Treffer
 *                  EINVAL  = Fehler Uebergabeparameter
 *                  ENOMEM  = Allokationsfehler
 *                  ENOEXEC = Compilierungsfehler
 *
 * Der Rueckgabe-Pointer ist gleich ((5.Arg)->sptr + (5.Arg)->von)
 * Der Rueckgabe-Startpointer des 2.Arg ist gleich ((5.Arg)->sptr + (5.Arg)->bis)
 */
extern char * SML3_rexsuche(const char *, char **, const void *, int, struct SML3_rexsub *);


/** SML3_rexstr [thread-sicher]:
 * wie SML3_rexsuche(): sucht nach erstem bzw. letztem Vorkommen eines reg. Ausdrucks
 * 1.Arg: zu durchsuchender String
 * 2.Arg: fuer Rueckgabe Startpointer im 1.Arg fuer weitere Suche
 *        (darf Adresse auf 1.Arg sein)
 *        oder NULL
 * 3.Arg: Suchpattern (als String oder schon compiliert)
 * 4.Arg: Suchpatternflag geodert (SML3_REXFLAG_*)
 *         - als String: SML3_REXFLAG_Q, SML3_REXFLAG_I, SML3_REXFLAG_N
 *         - schon compiliert: SML3_REXFLAG_COMP
 *         - ausserdem: SML3_REXFLAG_GLOBAL = letzten Treffer finden
 * 5.Arg: Adresse fuer Rueckgabe Laenge gefundener Treffer oder NULL
 * Rueckgabe: Pointer auf Beginn gefundenen Treffer
 *            (Achtung: falls gleich 2.Arg -> Endlosschleife)
 *            oder NULL = kein Treffer (SML3-errno-Wert = 0)
 *                        bzw. Fehler (SML3-errno-Wert != 0)
 * SML3-errno-Wert: 0       = kein Treffer
 *                  EINVAL  = Fehler Uebergabeparameter
 *                  ENOMEM  = Allokationsfehler
 *                  ENOEXEC = Compilierungsfehler
 */
extern char * SML3_rexstr(const char *, char **, const void *, int, size_t *);


/* wird nicht direkt aufgerufen */
extern struct SML3_rexrep SML3_rexmakerep(const void *, int, void *, char * (*)(struct SML3_rexsub *, void *), void *);


/** SML3_rexersetze [ohne Ersetzefunktion: thread-sicher]:
 * ersetzt ersten oder alle Vorkommen eines oder mehrerer reg. Ausdruecke
 * 1.Arg: zu durchsuchender String
 * weitere Arg: Parameter fuer Suchen + Ersetzen (struct SML3_rexrep)
 *              uebergeben durch
 *              - SML3_REXREP(P1, P2, P3):
 *                  P1: Suchausdruck (evtl. schon compiliert)
 *                  P2: Suchpatternflag geodert (SML3_REXFLAG_*)
 *                  P3: Ersetzestring
 *              - SML3_REXREPFUNK(P1, P2, P3, P4)
 *                  P1: Suchausdruck (evtl. schon compiliert)
 *                  P2: Suchpatternflag geodert (SML3_REXFLAG_*)
 *                  P3: Ersetzefunktion: char * (*)(struct SML3_rexsub *, void *)
 *                  P4: 2.Parameter fuer Ersetzefunktion oder NULL
 *                (Die Ersetzefunktion erhaelt:
 *                 - den aktuellen struct SML3_rexsub als Pointer
 *                 - den uebergebenen 2.Parameter (P4)
 *                 Sie muss zurueckgeben:
 *                 - einen allozierten Ersetze-String oder NULL = Fehler
 *                )
 *              beendet mit
 *              - SML3_REXEND
 * Rueckgabe: allozierter ersetzter String
 *            oder NULL = keine Ersetzung (SML3-errno-Wert = 0)
 *                        bzw. Fehler (SML3-errno-Wert != 0)
 * SML3-errno-Wert: 0       = keine Ersetzung
 *                  EINVAL  = Fehler Uebergabeparameter
 *                  ENOMEM  = Allokationsfehler
 *                  ENOEXEC = Compilierungsfehler
 */
extern char * SML3_rexersetze(const char *, ...);

#endif /* SML3__REGEX_H_ */
