/* sml3_util.c: verschiedene Funktionen (braucht -lm) */

/* 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 <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <ctype.h>
#include <math.h>
#include "config.h"
#ifdef SML3_HAVE_PTHREAD
# include <pthread.h>
#endif
#ifdef SML3_HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif
#include "sml3_fehler.h"
#include "sml3_util.h"

void * SML3_malloc(size_t);
void * SML3_calloc(size_t, size_t);
void * SML3_realloc(void *, size_t);
char * SML3_strdup(const char *);
void * SML3_memdup(const char *, size_t);
int SML3_errno_is_eagain(int);
char * SML3_toupper(char *, size_t);
char * SML3_tolower(char *, size_t);
unsigned long SML3_primzahl(unsigned long, int);
unsigned short SML3_hton16(unsigned short);
unsigned short SML3_ntoh16(unsigned short);
unsigned int SML3_hton32(unsigned int);
unsigned int SML3_ntoh32(unsigned int);
unsigned SML3_int64 SML3_hton64(unsigned SML3_int64);
unsigned SML3_int64 SML3_ntoh64(unsigned SML3_int64);
int SML3_zufallzahl(int, int);
size_t SML3_schnipp(char *, const char *, int);
void * SML3_memmem(const void *, size_t, const void *, size_t, int);
SML3_int64 SML3_antoi(const char *, size_t);
unsigned SML3_int64 SML3_antoui(const char *, size_t);
double SML3_antof(const char *, size_t);
SML3_int64 SML3_strtoi(const char *, size_t, char **, int);
unsigned SML3_int64 SML3_strtoui(const char *, size_t, char **, int);
char * SML3_uitostr(unsigned SML3_int64, int, char *, size_t);
SML3_int64 SML3_zeitpunkt_diff(SML3_int64 *);
void SML3_sleep_msek(int);
int SML3_getlock(int, unsigned long, unsigned long, pid_t *, int);
FILE * SML3_tmpfile(const char *, const char *);

static unsigned SML3_int64 str_to_uint64(const char *, size_t, char **, int, int *, int *);


/* ermittelt Zahl aus String */
static unsigned SML3_int64
str_to_uint64(const char *zkt, size_t size, char **endptr, int base, int *minus, int *err)
{
  static const char blzch[] = "0123456789abcdefghijklmnopqrstuvwxyz";
  static const char buzch[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  unsigned SML3_int64 nwert, vwert, plwert;
  int obvorz, abstand, i1;
  char *ptr;

  if (zkt == NULL || minus == NULL || err == NULL) { return 0; }
  if (base < 2 || base > 36) { return 0; }

  abstand = 0;
  *err = 0;
  nwert = 0;
  *minus = 0;
  obvorz = 1;
  do {
    if (obvorz && (*zkt == '-' || *zkt == '+')) {
      if (*zkt == '-') { *minus = !(*minus); }
    } else {
      if (*zkt == '\0') { break; }
      if (obvorz) {
        if (base == 16 && (size == 0 || size >= 2) && zkt[0] == '0' && (zkt[1] == 'x' || zkt[1] == 'X')) {
          if (size > 0) { size -= 2; }
          zkt += 2;
        }
        obvorz = 0;
      }
      if ((ptr = strchr(blzch, (int)*zkt)) != NULL) { abstand = (int)(ptr - blzch); }
      if (ptr == NULL && (ptr = strchr(buzch, (int)*zkt)) != NULL) { abstand = (int)(ptr - buzch); }
      if (ptr == NULL || abstand >= base) { break; }
      plwert = nwert;
      for (i1 = 1; i1 < base; i1++) {
        vwert = nwert;
        nwert += plwert;
        if (nwert < vwert) { nwert = SMLINT64_UMAX; *err = 1; break; }
      }
      if (*err) { break; }
      vwert = nwert;
      nwert += abstand;
      if (nwert < vwert) { nwert = SMLINT64_UMAX; *err = 1; break; }
    }
    zkt++;
  } while (--size != 0);

  if (endptr != NULL) { *endptr = (char *)zkt; }

  return nwert;
} /* Ende str_to_uint64 */


/* SML3_malloc [thread-sicher]:
 * wie malloc(), beendet sich aber bei Fehler
 * Args: wie bei malloc()
 * Rueckgabe: Pointer auf Memory
 */
void *
SML3_malloc(size_t size)
{
  void *nptr;

  if (size == 0) { SML3_fehlerexit("%s", SML3_strerror(EINVAL)); }
  nptr = malloc(size);
  if (nptr == NULL) { SML3_fehlerexit("malloc: %s", SML3_strerror(errno)); }
  return nptr;
} /* Ende SML3_malloc */


/* SML3_calloc [thread-sicher]:
 * wie calloc(), beendet sich aber bei Fehler
 * Args: wie bei calloc()
 * Rueckgabe: Pointer auf Memory
 */
void *
SML3_calloc(size_t nmemb, size_t size)
{
  void *nptr;

  if (nmemb == 0 || size == 0) { SML3_fehlerexit("%s", SML3_strerror(EINVAL)); }
  nptr = calloc(nmemb, size);
  if (nptr == NULL) { SML3_fehlerexit("calloc: %s", SML3_strerror(errno)); }
  return nptr;
} /* Ende SML3_calloc */


/* SML3_realloc [thread-sicher]:
 * realloc(), beendet sich aber bei Fehler
 * Args: wie bei realloc()
 * Rueckgabe: Pointer auf Memory
 */
void *
SML3_realloc(void *ptr, size_t size)
{
  void *nptr;

  if (ptr == NULL || size == 0) { SML3_fehlerexit("%s", SML3_strerror(EINVAL)); }
  nptr = realloc(ptr, size);
  if (nptr == NULL) { SML3_fehlerexit("realloc: %s", SML3_strerror(errno)); }
  return nptr;
} /* Ende SML3_realloc */


/* SML3_strdup [thread-sicher]:
 * wie strdup(), beendet sich aber bei Fehler
 * Args: wie bei strdup()
 * Rueckgabe: Pointer auf Memory
 */
char *
SML3_strdup(const char *s)
{
  void *nptr;

  if (s == NULL) { SML3_fehlerexit("%s", SML3_strerror(EINVAL)); }
  nptr = strdup(s);
  if (nptr == NULL) { SML3_fehlerexit("strdup: %s", SML3_strerror(errno)); }
  return nptr;
} /* Ende SML3_strdup */


/* SML3_memdup [thread-sicher]:
 * gibt allozierten Bereich mit kopierten Daten zurueck
 * 1.Arg: zu kopierende Daten
 * 2.Arg: Anzahl zu kopierender Daten
 * Rueckgabe: Pointer auf Memory oder NULL, wenn Argumente ungueltig
 */
void *
SML3_memdup(const char *ptr, size_t size)
{
  void *nptr;

  if (ptr == NULL || size == 0) { return NULL; }
  nptr = SML3_malloc(size);
  memmove(nptr, ptr, size);
  return nptr;
} /* Ende SML3_memdup */


/* SML3_errno_is_eagain [reentrant]:
 * prueft, ob 1.Arg EAGAIN oder, falls definiert, EWOULDBLOCK ist
 * 1.Arg: errno-Wert
 * Rueckgabe: 1 = ja, 0 = nein
 */
int
SML3_errno_is_eagain(int errnr)
{
#ifdef EWOULDBLOCK
  if (errnr == EWOULDBLOCK) { return 1; }
#endif
  if (errnr == EAGAIN) { return 1; }
  return 0;
} /* Ende SML3_errno_is_eagain */


/* SML3_toupper [reentrant]:
 * wie toupper() fuer einen String in der C-Lokale
 * (siehe auch SML3_latin_toupper())
 * 1.Arg: String, wird veraendert
 * 2.Arg: Anzahl zu veraendernde Bytes im 1.Arg, oder 0 = bis Ende-0
 * Rueckgabe: 1.Arg
 */
char *
SML3_toupper(char *string, size_t anz)
{
  char *ptr;

  if (string == NULL) { return string; }
  for (ptr = string; *ptr != '\0'; ptr++) {
    if (*ptr >= 'a' && *ptr <= 'z') { *ptr = *ptr - 'a' + 'A'; }
    if (anz > 0 && --anz == 0) { break; }
  }

  return string;
} /* Ende SML3_toupper */


/* SML3_tolower [reentrant]:
 * wie tolower() fuer einen String in der C-Lokale
 * (siehe auch SML3_latin_tolower())
 * 1.Arg: String, wird veraendert
 * 2.Arg: Anzahl zu veraendernde Bytes im 1.Arg, oder 0 = bis Ende-0
 * Rueckgabe: 1.Arg
 */
char *
SML3_tolower(char *string, size_t anz)
{
  char *ptr;

  if (string == NULL) { return string; }
  for (ptr = string; *ptr != '\0'; ptr++) {
    if (*ptr >= 'A' && *ptr <= 'Z') { *ptr = *ptr - 'A' + 'a'; }
    if (anz > 0 && --anz == 0) { break; }
  }

  return string;
} /* Ende SML3_tolower */


/* SML3_primzahl [thread-sicher]:
 * Ermittelt naechstgelegene Primzahl von 1.Arg
 * 1.Arg: Zahl, ab der die Primzahl ermittelt wird
 * 2.Arg: -1=abwaerts ermitteln
 *         1=aufwaerts ermitteln
 * Rueckgabe: ermittelte Primzahl
 */
unsigned long
SML3_primzahl(unsigned long primz, int flag)
{
  unsigned long d1, wurz;
  long addw;

  if ( primz == 0UL) { return 1UL; }
  addw = (flag < 0 ? -1L : 1L);

  while (primz > 2UL) {
    if (primz % 2UL == 0UL) { primz += addw; continue; }
    wurz = (unsigned long)(sqrt((double)primz) + 1);
    d1=3UL;
    while (d1 <= wurz) {
      if (primz % d1 == 0UL) { break; }
      d1 += 2UL;
    }
    if (d1 > wurz) { break; }
    if ((primz += addw) == 0UL) { primz--; addw = -1L; }
  }

  return primz;
} /* Ende SML3_primzahl */


/* SML3_hton16 [reentrant]:
 * konvertiert unsigned short von Host-Byte-Order zu Netzwerk-Byte-Order
 * 1.Arg: Wert in Host-Byte-Order
 * Rueckgabe: Wert in Netzwerk-Byte-Order
 */
unsigned short
SML3_hton16(unsigned short zahl)
{
  unsigned short retw;
#ifdef SML3_ENDIAN_IS_LITTLE
  retw = ((zahl & 0xff) << 8) | ((zahl >> 8) & 0xff);
#else
  retw = zahl;
#endif
  return retw;
} /* Ende SML3_hton16 */


/* SML3_ntoh16 [reentrant]:
 * konvertiert unsigned short von Netzwerk-Byte-Order zu Host-Byte-Order
 * 1.Arg: Wert in Netzwerk-Byte-Order
 * Rueckgabe: Wert in Host-Byte-Order
 */
unsigned short
SML3_ntoh16(unsigned short zahl)
{
  return SML3_hton16(zahl);
} /* Ende SML3_ntoh16 */


/* SML3_hton32 [reentrant]:
 * konvertiert unsigned int von Host-Byte-Order zu Netzwerk-Byte-Order
 * 1.Arg: Wert in Host-Byte-Order
 * Rueckgabe: Wert in Netzwerk-Byte-Order
 */
unsigned int
SML3_hton32(unsigned int zahl)
{
  unsigned int retw;
#ifdef SML3_ENDIAN_IS_LITTLE
  retw = ((zahl & 0xff000000) >> 24) | ((zahl & 0x00ff0000) >>  8) | ((zahl & 0x0000ff00) <<  8) | ((zahl & 0x000000ff) << 24);
#else
  retw = zahl;
#endif
  return retw;
} /* Ende SML3_hton32 */


/* SML3_ntoh32 [reentrant]:
 * konvertiert unsigned int von Netzwerk-Byte-Order zu Host-Byte-Order
 * 1.Arg: Wert in Netzwerk-Byte-Order
 * Rueckgabe: Wert in Host-Byte-Order
 */
unsigned int
SML3_ntoh32(unsigned int zahl)
{
  return SML3_hton32(zahl);
} /* Ende SML3_ntoh32 */


/* SML3_hton64 [reentrant]:
 * konvertiert unsigned SML3_int64 von Host-Byte-Order zu Netzwerk-Byte-Order
 * 1.Arg: Wert in Host-Byte-Order
 * Rueckgabe: Wert in Netzwerk-Byte-Order
 */
unsigned SML3_int64
SML3_hton64(unsigned SML3_int64 zahl)
{
  unsigned SML3_int64 retw;
#ifdef SML3_ENDIAN_IS_LITTLE
  unsigned int w1, w2;
  w1 = ((zahl & 0xff000000) >> 24) | ((zahl & 0x00ff0000) >>  8) | ((zahl & 0x0000ff00) <<  8) | ((zahl & 0x000000ff) << 24);
  zahl >>= 32;
  w2 = ((zahl & 0xff000000) >> 24) | ((zahl & 0x00ff0000) >>  8) | ((zahl & 0x0000ff00) <<  8) | ((zahl & 0x000000ff) << 24);
  retw = ((unsigned SML3_int64)w1 << 32) | (unsigned SML3_int64)w2; 
#else
  retw = zahl;
#endif
  return retw;
} /* Ende SML3_hton64 */


/* SML3_ntoh64 [reentrant]:
 * konvertiert unsigned SML3_int64 von Netzwerk-Byte-Order zu Host-Byte-Order
 * 1.Arg: Wert in Netzwerk-Byte-Order
 * Rueckgabe: Wert in Host-Byte-Order
 */
unsigned SML3_int64
SML3_ntoh64(unsigned SML3_int64 zahl)
{
  return SML3_hton64(zahl);
} /* Ende SML3_ntoh64 */


/* SML3_zufallzahl [thread-sicher]:
 * gibt eine zufaellige Zahl zwischen 1.Arg und 2.Arg zurueck
 * 1.Arg: Startwert (inkl.)
 * 2.Arg: Endewert (inkl.)
 * Rueckgabe: zufaellige Zahl (oder 0, wenn 1.Arg > 2.Arg)
 */
int
SML3_zufallzahl(int von, int bis)
{
  int retw;
#ifdef SML3_HAVE_PTHREAD
  static pthread_mutex_t f_mtx = PTHREAD_MUTEX_INITIALIZER;
  pthread_mutex_lock(&f_mtx);
#endif

  retw = 0;
  if (von <= bis) {
    unsigned int wert;
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd >= 0 && read(fd, &wert, sizeof(unsigned int)) == sizeof(unsigned int)) {
      retw = von + (wert % (bis - von + 1));
    } else {
      static int neu = 1;
      if (neu) { srand((unsigned int)time(NULL)); neu = 0; }
      retw = von + (int)((SML3_int64)(bis - von + 1) * rand() / ((SML3_int64)RAND_MAX + 1));
    }
    if (fd >= 0) { close(fd); }
  }

#ifdef SML3_HAVE_PTHREAD
  pthread_mutex_unlock(&f_mtx);
#endif

  return retw;
} /* Ende SML3_zufallzahl */


/* SML3_schnipp [thread-sicher]:
 * schneidet im 1.Arg vorne oder/und hinten eine Auswahl von Zeichen ab
 * 1.Arg: zu stutzender String
 * 2.Arg: Auswahl von Zeichen
 * 3.Arg: 1 = vorne, 2 = hinten, 3 = vorne + hinten
 * Rueckgabe: Byteanzahl des gestutzten Strings
 */
size_t
SML3_schnipp(char *zkt, const char *zausw, int flag)
{
  char *pvon, *pbis;

  if (zkt == NULL) { return 0; }
  if (zausw == NULL || *zausw == '\0') { flag = 0; }

  pvon = zkt;
  pbis = pvon + strlen(zkt) - 1;

  if (flag & 1) {  /* vorne */
    for (; pvon <= pbis; pvon++) {
      if (strchr(zausw, (int)*pvon) == NULL) { break; }
    }
  }

  if (flag & 2) {  /* hinten */
    for (; pbis >= pvon; pbis--) {
      if (strchr(zausw, (int)*pbis) == NULL) { break; }
    }
  }

  pbis[1] = '\0';
  if (pvon > zkt) { memmove(zkt, pvon, (size_t)(pbis - pvon + 2)); }

  return (size_t)(pbis - pvon + 1);
} /* Ende SML3_schnipp */


/* SML3_memmem [thread-sicher]:
 * sucht 3.Arg im 1.Arg und gibt erstes Vorkommen zurueck
 * 1.+2.Arg: zu durchsuchender String mit Stringlaenge
 * 3.+4.Arg: zu suchender String mit Stringlaenge
 * 5.Arg: ob Gross-/Klein-Schreibung (nur ASCII) egal: 0 = nein, 1 = ja
 * Rueckgabe: Pointer auf erstes Vorkommen oder NULL = nicht gefunden
 *
 * Bsp fuer Ausgabe aller Vorkommen von needle in haystack:
 *   const char *p1 = haystack;
 *   const char *p2 = needle;
 *   char *rptr;
 *   while ((rptr = SML3_memmem(p1, strlen(p1), p2, strlen(p2), 0)) != NULL) {
 *     printf("- %s\n", rptr);
 *     p1 = rptr + strlen(p2);
 *   }
 */
void *
SML3_memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen, int icase)
{
  char *pret;
  const char *cneedle, *pv0, *pv1, *pv2;
  char *rneedle;
  size_t n1;

  if (haystack == NULL || haystacklen == 0 || needle == NULL || needlelen == 0) { return NULL; }

  cneedle = (const char *)needle;
  rneedle = NULL;
  if (icase) {
    rneedle = SML3_malloc(needlelen);
    for (n1 = 0; n1 < needlelen; n1++) {
      if (islower((unsigned char)cneedle[n1])) {
        rneedle[n1] = toupper((int)(unsigned char)cneedle[n1]);
      } else if (isupper((unsigned char)cneedle[n1])) {
        rneedle[n1] = tolower((int)(unsigned char)cneedle[n1]);
      } else {
        rneedle[n1] = cneedle[n1];
      }
    }
  }

  pret = NULL;
  pv1 = memchr(haystack, (int)(unsigned char)cneedle[0], haystacklen);
  if (pv1 != NULL && haystacklen - (size_t)(pv1 - (const char *)haystack) < needlelen) { pv1 = NULL; }
  if (icase) {
    pv2 = memchr(haystack, (int)(unsigned char)rneedle[0], haystacklen);
    if (pv2 != NULL && haystacklen - (size_t)(pv2 - (const char *)haystack) < needlelen) { pv2 = NULL; }
  } else {
    pv2 = NULL;
  }

  for (;;) {
    if (haystacklen < needlelen) { break; }

    if (pv1 != NULL && pv2 != NULL) {
      if (pv1 < pv2) { pv0 = pv1; } else { pv0 = pv2; }
    } else if (pv1 != NULL) {
      pv0 = pv1;
    } else if (pv2 != NULL) {
      pv0 = pv2;
    } else {
      break;
    }

    if (icase) {
      for (n1 = 1; n1 < needlelen; n1++) {
        if (pv0[n1] != cneedle[n1] && pv0[n1] != rneedle[n1]) { break; }
      }
      if (n1 == needlelen) { pret = (char *)pv0; break; }
    } else {
      if (memcmp(pv0, cneedle, needlelen) == 0) { pret = (char *)pv0; break; }
    }

    haystack = (char *)haystack + 1;
    haystacklen--;
    if (haystacklen == 0) { break; }

    if (pv0 == pv1) {
      pv1 = memchr(haystack, (int)(unsigned char)cneedle[0], haystacklen);
      if (pv1 != NULL && haystacklen - (size_t)(pv1 - (const char *)haystack) < needlelen) { pv1 = NULL; }
    } else {
      pv2 = memchr(haystack, (int)(unsigned char)rneedle[0], haystacklen);
      if (pv2 != NULL && haystacklen - (size_t)(pv2 - (const char *)haystack) < needlelen) { pv2 = NULL; }
    }
  }

  if (icase) { free(rneedle); }
  return pret;
} /* Ende SML3_memmem */


/* SML3_antoi [thread-sicher]:
 * ermittelt (signed) Zahl aus String
 * 1.Arg: String
 * 2.Arg: maximale auszwertende Laenge oder 0 = ohne Begrenzung
 * Rueckgabe: Zahl
 */
SML3_int64
SML3_antoi(const char *zkt, size_t size)
{
  return SML3_strtoi(zkt, size, NULL, 10);
} /* Ende SML3_antoi */


/* SML3_antoui [thread-sicher]:
 * ermittelt (unsigned) Zahl aus String
 * 1.Arg: String
 * 2.Arg: maximale auszwertende Laenge oder 0 = ohne Begrenzung
 * Rueckgabe: Zahl
 */
unsigned SML3_int64
SML3_antoui(const char *zkt, size_t size)
{
  return SML3_strtoui(zkt, size, NULL, 10);
} /* Ende SML3_antoui */


/* SML3_antof [thread-sicher]:
 * ermittelt double-Zahl aus String
 * 1.Arg: String
 * 2.Arg: maximale auszwertende Laenge oder 0 = ohne Begrenzung
 * Rueckgabe: Zahl
 */
double
SML3_antof(const char *zkt, size_t size)
{
  char buf[128];

  if (zkt == NULL || *zkt == '\0') { return .0; }
  if (size == 0) { return atof(zkt); }

  if (size >= sizeof(buf)) { size = sizeof(buf) - 1; }
  memmove(buf, zkt, size);
  return atof(buf);
} /* Ende SML3_antof */


/* SML3_strtoi [thread-sicher]:
 * ermittelt (signed) Zahl aus String, aehnlich strtol()
 * 1.Arg: String
 * 2.Arg: maximale auszwertende Laenge oder 0 = ohne Begrenzung
 * 3.Arg: fuer Rueckgabe Pointer auf erstes ungueltiges Zeichen
 *        oder NULL
 * 4.Arg: Basis (2 bis 36)
 * Rueckgabe: Zahl
 */
SML3_int64
SML3_strtoi(const char *zkt, size_t size, char **endptr, int base)
{
  unsigned SML3_int64 wert;
  SML3_int64 retw;
  int minus = 0, err = 0;

  wert = str_to_uint64(zkt, size, endptr, base, &minus, &err);

  if (err) {
    if (minus) { retw = SMLINT64_MIN; } else { retw = SMLINT64_MAX; }
    return retw;
  }

  if (minus && wert - 1 > SMLINT64_MAX) { retw = SMLINT64_MIN; return retw; }
  if (wert > SMLINT64_MAX) { retw = SMLINT64_MAX; return retw; }

  if (minus) { retw = -wert; } else { retw = wert; }

  return retw;
} /* Ende SML3_strtoi */


/* SML3_strtoui [thread-sicher]:
 * ermittelt (unsigned) Zahl aus String, aehnlich strtoul()
 * 1.Arg: String
 * 2.Arg: maximale auszwertende Laenge oder 0 = ohne Begrenzung
 * 3.Arg: fuer Rueckgabe Pointer auf erstes ungueltiges Zeichen
 *        oder NULL
 * 4.Arg: Basis (2 bis 36)
 * Rueckgabe: Zahl
 */
unsigned SML3_int64
SML3_strtoui(const char *zkt, size_t size, char **endptr, int base)
{
  unsigned SML3_int64 retw;
  int minus, err;

  retw = str_to_uint64(zkt, size, endptr, base, &minus, &err);

  if (err) {
    retw = SMLINT64_UMAX;
    return retw;
  }

  if (minus) { retw = -retw; }

  return retw;
} /* Ende SML3_strtoui */


/* SML3_uitostr [thread-sicher]:
 * gibt Zahl 1.Arg als String mit Basis 2.Arg zurueck
 * 1.Arg: Zahl
 * 2.Arg: Basis (2 bis 36)
 * 3.+4.Arg: Puffer mit Size fuer Rueckgabestring
 *           oder NULL = Rueckgabe ist alloziert
 * Rueckgabe: Pointer auf 3.Arg bzw. auf allozierten String
 *            oder NULL = Fehler
 * SML3-errno-Wert: ENOSPC = 4.Arg ist zu klein
 */
char *
SML3_uitostr(unsigned SML3_int64 wert, int base, char *rets, size_t reti)
{
  static const char bzch[] = "0123456789abcdefghijklmnopqrstuvwxyz";
  const size_t rsize = sizeof(unsigned SML3_int64) * 8;
  char rbuf[rsize + 1];
  size_t rlen;

  if (base < 2 || base > 36) { SML3_fehlerexit("%s", SML3_strerror(EINVAL)); }
  if (rets != NULL && reti == 0) { rets = NULL; }

  rlen = 0;
  while (wert > 0) {
    rbuf[rsize - (++rlen)] = bzch[wert % base];
    wert /= base;
  }
  if (rlen < rsize) { memmove(rbuf, rbuf + (rsize - rlen), rlen); }
  if (rlen == 0) { rbuf[rlen++] = '0'; }
  rbuf[rlen] = '\0';

  if (rets != NULL) {
    if (reti <= rlen) {
      SML3_fehlernew(ENOSPC, "%s: got=%d, needed=%d", SML3_strerror(ENOSPC), (int)reti, (int)(rlen + 1));
      return NULL;
    }
    memmove(rets, rbuf, rlen + 1);
    return rets;
  }

  return SML3_strdup(rbuf);
} /* Ende SML3_uitostr */


/* SML3_zeitpunkt_diff [thread-sicher]:
 * Ermittelt Zeitpunkt-Differenz
 * 1.Arg: Adresse auf: letzter Zeitpunkt oder Wert=0: setze Startzeitpunkt
 *        Rueckgabe: aktualisierter Zeitpunkt
 * Rueckgabe: Zeitpunkt-Differenz in Millisekunden
 *            (ist absoluter Zeitwert, wenn Wert 1.Arg = 0 bzw. 1.Arg = NULL)
 */
SML3_int64
SML3_zeitpunkt_diff(SML3_int64 *ztlast)
{
  struct timeval tv1;
  SML3_int64 ztnun, ztdiff;

  gettimeofday(&tv1, NULL);
  ztnun = tv1.tv_sec * 1000 + tv1.tv_usec / 1000;
  if (ztlast == NULL) { return ztnun; }

  ztdiff = ztnun - (*ztlast);
  if (ztdiff < 0) { ztdiff = 0; }
  *ztlast = ztnun;

  return ztdiff;
} /* Ende SML3_zeitpunkt_diff */


/* SML3_sleep_msek [thread-sicher]:
 * schlaeft 1.Arg Millisekunden
 * 1.Arg: zu schlafende Millisekunden
 */
void
SML3_sleep_msek(int msek)
{
  struct timeval tv;

#if 0
  msek -= SML3_TIME_OF_SELECT;
#endif
  if (msek <= 0) { return; }

  tv.tv_sec = msek / 1000;
  tv.tv_usec = (msek % 1000) * 1000;

  select(0, NULL, NULL, NULL, &tv);
} /* Ende SML3_sleep_msek */


/* SML3_getlock [thread-sicher]:
 * (Ent)Sperrt Datei(bereich). Vorsicht bei NFS!
 * 1.Arg: Dateideskriptor der zu (ent)sperrenden Datei
 * 2.Arg: absolute Startposition des zu (ent)sperrenden Bereichs
 * 3.Arg: Anzahl der zu (ent)sperrenden Bytes
 *        oder 0 = bis EOF (ungut, wenn Dateilaenge sich veraendert!)
 * 4.Arg: Adresse fuer PID des sperrenden Prozesses oder NULL
 * 5.Arg: 0 = entsperren, 1 = sperren, 2 = blockiert sperren
 * Rueckgabe: 1 = OK
 *            0 = Lock nicht erhalten bzw. Fehler
 * SML3-errno-Wert: ENOENT = Fehler bei fcntl()
 *                  ENOLCK = nicht-blockierenden Lock nicht erhalten, 4.Arg ist gesetzt
 */
int
SML3_getlock(int fd, unsigned long start, unsigned long len, pid_t *lockpid, int flag)
{
  struct flock lkinf;

  if (fd < 0 || flag < 0 || flag > 2) { SML3_fehlerexit("%s", SML3_strerror(EINVAL)); }

  memset(&lkinf, 0, sizeof(lkinf));
  lkinf.l_whence = SEEK_SET;
  lkinf.l_start = (off_t)start;
  lkinf.l_len = (off_t)len;
  if (lockpid != NULL) { *lockpid = 0; }

  if (flag > 0) {  /* sperren */
    int modus = fcntl(fd, F_GETFL, 0);
    if (modus < 0) { SML3_fehlernew(ENOENT, "fcntl: %s", SML3_strerror(errno)); return 0; }
    if ((modus & O_ACCMODE) == O_RDONLY) {
      lkinf.l_type = F_RDLCK;
    } else {
      lkinf.l_type = F_WRLCK;
    }
    if (flag == 2) { modus = F_SETLKW; } else { modus = F_SETLK; }
    if (fcntl(fd, modus, &lkinf) == -1) {
      if (flag == 1 && (errno == EACCES || errno == EAGAIN)) {  /* anderer Prozess sperrt */
        SML3_fehlernew(ENOLCK, "lock not obtained");
        if (lockpid != NULL) {  /* PID des sperrehaltenden Prozesses */
          if (fcntl(fd, F_GETLK, &lkinf) == 0) {
            *lockpid = lkinf.l_pid;
          } else {
            SML3_fehlernew(ENOENT, "lock not obtained");
          }
        }
        return 0;
      }
      SML3_fehlernew(ENOENT, "fcntl: %s", SML3_strerror(errno));
      return 0;
    }
  } else {  /* entsperren */
    lkinf.l_type = F_UNLCK;
    fcntl(fd, F_SETLK, &lkinf);
  }

  return 1;
} /* Ende SML3_getlock */


/* SML3_tmpfile [thread-sicher]:
 * wie tmpfile(), aber mit Angabe des Verzeichnisses
 * 1.Arg: Verzeichnis der temporaeren Datei (oder NULL = "/tmp")
 * 2.Arg: Zusatz fuer Dateinamen oder NULL
 * Rueckgabe: Filepointer oder NULL = Fehler
 * SML3-errno-Wert: EINVAL = Fehler Dateierstellung
 */
FILE *
SML3_tmpfile(const char *verz, const char *dzus)
{
  struct timeval tv1;
  char buf[512];
  unsigned long l1, l2, l3;
  FILE *ffp;

  if (verz == NULL || *verz == '\0') { verz = "/tmp"; }

  gettimeofday(&tv1, NULL);

  snprintf(buf, sizeof(buf), "%02d%06d", (int)(tv1.tv_sec % 100), (int)tv1.tv_usec);
  l1 = atol(buf);
  snprintf(buf, sizeof(buf), "%03d", SML3_zufallzahl(1, 999));
  l2 = atol(buf);
  snprintf(buf, sizeof(buf), "%u", (unsigned int)getpid());
  l3 = atol(buf);

  if (dzus != NULL && *dzus != '\0' && strchr(dzus, '/') == NULL) {
    snprintf(buf, sizeof(buf), "%s/sml3_tmpfile.%lu-%lu-%lu.%s", verz, l1, l2, l3, dzus);
  } else {
    snprintf(buf, sizeof(buf), "%s/sml3_tmpfile.%lu-%lu-%lu", verz, l1, l2, l3);
  }
  ffp = fopen(buf, "w+");
  if (ffp == NULL) { SML3_fehlernew(EINVAL, "error creating \"%s\": %s", buf, SML3_strerror(errno)); return NULL; }
  unlink(buf);

  return ffp;
} /* Ende SML3_tmpfile */
