#ifndef SML3__FEHLER_H_
#define SML3__FEHLER_H_

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

  Funktionen zum Setzen und Erhalten von Fehlermeldungen.
  Sie sind thread-sicher.

  Eine neue Fehlermeldungspur wird zuerst mit SML3_fehlernew() begonnen,
  hoehergelegene Funktionen setzen mit SML3_fehleradd() ihren Senf dazu.
  Mit SML3_fehlermsg() erhaelt man die gesamte Fehlermeldung.
  Mit SML3_fehlererrno() erhaelt man den Ursprungs-errno-Wert.

  Mit SML3_fehlerstop() deaktiviert man Fehlersetzung, z.B. weil eine
  Originalfehlermeldung nicht ueberschrieben werden soll, und mit
  SML3_fehlerstart() aktiviert man Fehlersetzung wieder.
  Beide Funktionen arbeiten als Stack, d.h. es muss ebensooft
  SML3_fehlerstart() aufgerufen werden wie SML3_fehlerstop() aufgerufen
  wurde.

  Threads muessen zusaetzlich zu Beginn SML3_fehlerattach() und am Ende
  SML3_fehlerdetach() aufrufen.
+++ */


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

  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/types.h>
  #include <unistd.h>
  #include <errno.h>
  #include <sammel3.h>

  int o1(void);
  int o2(void);
  int o3(void);

  int
  o1(void)
  {
    if (o2() < 0) { SML3_fehleradd(NULL); return -1; }
    return 0;
  }

  int
  o2(void)
  {
    if (o3() < 0) {
      SML3_fehleradd("o2 ist %d mal beleidigt", 7);
      return -1;
    }
    return 0;
  }

  int
  o3(void)
  {
    SML3_fehlernew(EINVAL, "o3 gibt Fehler zurueck");
    return -1;
  }

  int
  main(int argc, char **argv)
  {
    if (o1() < 0) {
      fprintf(stderr, "%s (errno=%d)\n", SML3_fehlermsg(), SML3_fehlererrno());
    }
    return 0;
  }
+++ */


/* ++++++++++++++++++++++++++++++
 * +++ Beispiel mit 2 Threads +++
 * ++++++++++++++++++++++++++++++

  #include <pthread.h>

  #define MAX_THREADS 2
  static void * run_thread(void *);

  int
  main(int argc, char **argv)
  {
    pthread_t th1[MAX_THREADS];
    int nr;

    // Threads erzeugen und aufrufen
    for (nr = 0; nr < MAX_THREADS; nr++) {
      if (pthread_create(&th1[nr], NULL, run_thread, NULL) != 0) {
        fprintf(stderr, "pthread_create[%d]: %s\n", nr, strerror(errno));
        exit(1);
      }
    }

    // auf Beendiung der Threads warten
    for (nr = 0; nr < MAX_THREADS; nr++) {
      pthread_join(th1[nr], NULL);
    }
    printf("\nFertig\n");
    exit(0);
  }


  // Threadfunktion
  static void *
  run_thread(void *arg)
  {
    char fbuf[512];

    SML3_fehlerattach();

    // ersten Fehler erzeugen und ausgeben
    printf("[Thread %p] Setze Fehler1 ...\n", (void *)pthread_self()); fflush(stdout);
    SML3_fehlernew(EINVAL, "Thread %p: Fehler1", (void *)pthread_self());
    SML3_fehleradd(NULL);
    printf("[Thread %p] Fehler1: SML3_fehlermsg()=<%s>\n\tSML3_fehlermsgtext()=<%s>\n",
          (void *)pthread_self(), SML3_fehlermsg(), SML3_fehlermsgtext(fbuf, sizeof(fbuf)));
    fflush(stdout);

    // Fehlersetzung deaktivieren
    printf("\n[Thread %p] Deaktiviere Fehler ...\n\n", (void *)pthread_self()); fflush(stdout);
    SML3_fehlerstop();

    // zweiten Fehler erzeugen und ausgeben -> gibt ersten Fehler aus, da deaktiviert
    printf("[Thread %p] Setze Fehler2 ...\n", (void *)pthread_self()); fflush(stdout);
    SML3_fehlernew(EINVAL, "Thread %p: Fehler2", (void *)pthread_self());
    SML3_fehleradd(NULL);
    printf("[Thread %p] Fehler2: SML3_fehlermsg()=<%s>\n\tSML3_fehlermsgtext()=<%s>\n",
          (void *)pthread_self(), SML3_fehlermsg(), SML3_fehlermsgtext(fbuf, sizeof(fbuf)));
    fflush(stdout);

    // Fehlersetzung aktivieren
    printf("\n[Thread %p] Aktiviere Fehler ...\n\n", (void *)pthread_self()); fflush(stdout);
    SML3_fehlerstart();

    // momentanen Fehler ausgeben -> weiterhin erster Fehler gesetzt
    printf("[Thread %p] Fehler: SML3_fehlermsg()=<%s>\n\tSML3_fehlermsgtext()=<%s>\n",
          (void *)pthread_self(), SML3_fehlermsg(), SML3_fehlermsgtext(fbuf, sizeof(fbuf)));
    fflush(stdout);

    SML3_fehlerdetach();
    return arg;
  } // Ende run_thread
+++ */


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

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


/** SML3_fehlerattach [thread-sicher]:
 * reserviert eigene Fehlervariable fuer aufrufenden Thread,
 * muss zu Beginn eines neuen Threads aufgerufen werden
 */
extern void SML3_fehlerattach(void);


/** SML3_fehlerdetach [thread-sicher]:
 * gibt eigene Fehlervariable fuer aufrufenden Thread frei,
 * muss vor Beendigung eines Threads aufgerufen werden
 */
extern void SML3_fehlerdetach(void);


/** SML3_fehlernew [thread-sicher]:
 * beginnt neue Fehlermeldungspur
 * 1.Arg: errno-Wert (oder 0) => wird gemerkt und setzt errno
 * 2.Arg: Format (oder NULL = Fehlervariable loeschen)
 * weitere Arg: Formatvariablen (falls 1.Arg != NULL)
 */
#define SML3_fehlernew(errnum, ...)  _SML3_fehlerput(__FILE__, SML3_funcname, __LINE__, 0, errnum, __VA_ARGS__)

/** SML3_fehleradd [thread-sicher]:
 * fuegt Fehlermeldung an
 * 1.Arg: Format (oder NULL = keine Zusatzinfos ausser Zeileninfo)
 * weitere Arg: Formatvariablen (falls 1.Arg != NULL)
 */
#define SML3_fehleradd(...)  _SML3_fehlerput(__FILE__, SML3_funcname, __LINE__, 1, 0, __VA_ARGS__)

/** SML3_fehlerexit [thread-sicher]:
 * beendet mit Fehlermeldung nach stderr
 * 1.Arg: Format (oder NULL = schon gesetzte Fehlermeldung verwenden)
 * weitere Arg: Formatvariablen (falls 1.Arg != NULL)
 */
#define SML3_fehlerexit(...)  _SML3_fehlerput(__FILE__, SML3_funcname, __LINE__, 2, 0, __VA_ARGS__)


/* wird nicht direkt aufgerufen */
extern void _SML3_fehlerput(const char *, const char *, int, int, int, ...);


/** SML3_fehlererrno [thread-sicher]:
 * gibt errno-Wert zurueck, der beim Aufruf von SML3_fehlernew() gemerkt wurde
 * Rueckgabe: SML3-errno-Wert
 */
extern int SML3_fehlererrno(void);


/** SML3_fehlermoderrno [thread-sicher]:
 * veraendert mit SML3_fehlernew() gesetzten errno-Wert
 * 1.Arg: SML3-errno-Wert
 */
extern void SML3_fehlermoderrno(int);


/** SML3_fehlermsg [thread-sicher]:
 * gibt Fehlermeldungspur zurueck
 * Rueckgabe: Fehlermeldungspur
 */
extern const char * SML3_fehlermsg(void);


/** SML3_fehlermsgtext [thread-sicher]:
 * gibt reinen Fehlermeldungstext im 2.Arg zurueck
 * 1.Arg: fuer Rueckgabe Fehlermeldungstext
 * 2.Arg: sizeof 1.Arg
 * Rueckgabe: Pointer auf 1.Arg (oder leer, aber nicht NULL)
 */
extern const char * SML3_fehlermsgtext(char *, size_t);


/** SML3_fehlerstart [thread-sicher]:
 * aktiviert Setzen von Fehlermeldungen.
 * Je Aufruf wird eine Stackvariable dekrementiert,
 * d.h. SML3_fehlerstart() muss ebenso oft aufgerufen werden,
 * wie SML3_fehlerstop() aufgerufen wurde, damit Fehler wieder gesetzt werden.
 */
extern void SML3_fehlerstart(void);


/** SML3_fehlerstop [thread-sicher]:
 * deaktiviert Setzen von Fehlermeldungen.
 * Je Aufruf wird eine Stackvariable inkrementiert,
 * d.h. es muss ebenso oft SML3_fehlerstart() aufgerufen werden,
 * damit Fehler wieder gesetzt werden.
 */
extern void SML3_fehlerstop(void);


/** SML3_strerror:
 * wie strerror(), aber threadsicher
 * 1.Arg: Fehlernummer
 * Rueckgabe: statischer String mit Fehlermeldung
 */
extern char * SML3_strerror(int);

#endif /* SML3__FEHLER_H_ */
