/* sml3_support.c: Wrapper fuer nichtunterstuetzte Funktionen */

/* 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 <ctype.h>
#include <signal.h>
#include <pwd.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"
#include "sml3_support.h"

#ifndef S_ISCHR
# define S_ISCHR(m)  (((m)&S_IFMT)==S_IFCHR)
#endif

int SML3_daemon(int, int);
int SML3_fnmatch(const char *, const char *, int);
int SML3_pselect(int, fd_set *, fd_set *, fd_set *, const struct timespec *, const sigset_t *);
int SML3_ppoll(struct pollfd *, nfds_t, const struct timespec *, const sigset_t *);
SML3_int64 SML3_strtoint64(const char *, char **, int);
unsigned SML3_int64 SML3_strtouint64(const char *, char **, int);
size_t SML3_strlcpy(char *, const char *, size_t);
size_t SML3_strlcat(char *, const char *, size_t);
struct passwd * SML3_getpwnam(const char *, struct passwd *, char *, size_t);
struct passwd * SML3_getpwuid(uid_t, struct passwd *, char *, size_t);

#ifndef SML3_HAVE_GETPWGR_R
static int move_pwd(struct passwd *, struct passwd *, char *, size_t);
#endif


#ifndef SML3_HAVE_GETPWGR_R
/* passwd: verschiebt 1.Arg in restliche Args */
static int
move_pwd(struct passwd *pwdp, struct passwd *pwd, char *buf, size_t buflen)
{
  size_t sl_gesamt, sl_name, sl_passwd, sl_dir, sl_shell, sl_gecos;
  char *bptr;

  if (pwdp == NULL || pwd == NULL || buf == NULL || buflen == 0) { SML3_fehlernew(EINVAL, "%s", SML3_strerror(EINVAL)); return -1; }

  sl_gesamt = 0;
  if (pwdp->pw_name != NULL) { sl_name = strlen(pwdp->pw_name) + 1; } else { pwdp->pw_name = ""; sl_name = 1; }
  sl_gesamt += sl_name;
  if (pwdp->pw_passwd != NULL) { sl_passwd = strlen(pwdp->pw_passwd) + 1; } else { pwdp->pw_passwd = ""; sl_passwd = 1; }
  sl_gesamt += sl_passwd;
  if (pwdp->pw_dir != NULL) { sl_dir = strlen(pwdp->pw_dir) + 1; } else { pwdp->pw_dir = ""; sl_dir = 1; }
  sl_gesamt += sl_dir;
  if (pwdp->pw_shell != NULL) { sl_shell = strlen(pwdp->pw_shell) + 1; } else { pwdp->pw_shell = ""; sl_shell = 1; }
  sl_gesamt += sl_shell;
  if (pwdp->pw_gecos != NULL) { sl_gecos = strlen(pwdp->pw_gecos) + 1; } else { pwdp->pw_gecos = ""; sl_gecos = 1; }
  sl_gesamt += sl_gecos;
  if (sl_gesamt - sl_gecos + 1 > buflen) { SML3_fehlernew(ERANGE, "%s", SML3_strerror(ERANGE)); return -1; }
  if (sl_gesamt > buflen) { sl_gecos -= (sl_gesamt - buflen); sl_gesamt = buflen; }

  memset(pwd, 0, sizeof(*pwd));
  bptr = buf;
  memmove(bptr, pwdp->pw_name, sl_name);
  pwd->pw_name = bptr;
  bptr += sl_name;
  memmove(bptr, pwdp->pw_passwd, sl_passwd);
  pwd->pw_passwd = bptr;
  bptr += sl_passwd;
  memmove(bptr, pwdp->pw_dir, sl_dir);
  pwd->pw_dir = bptr;
  bptr += sl_dir;
  memmove(bptr, pwdp->pw_shell, sl_shell);
  pwd->pw_shell = bptr;
  bptr += sl_shell;
  memmove(bptr, pwdp->pw_gecos, sl_gecos); bptr[sl_gecos - 1] = '\0';
  pwd->pw_gecos = bptr;
  bptr += sl_gecos;
  pwd->pw_uid = pwdp->pw_uid;
  pwd->pw_gid = pwdp->pw_gid;

  return 0;
} /* Ende move_pwd */
#endif /* SML3_HAVE_GETPWGR_R */


/* SML3_daemon: siehe daemon()
 * The daemon() function is for programs wishing to detach themselves from
 * the controlling terminal and run in the background as system daemons.
 *
 * Unless the argument nochdir is non-zero, daemon() changes the current
 * working directory to the root (/).
 *
 * Unless the argument noclose is non-zero, daemon() will redirect standard
 * input, standard output and standard error to /dev/null.
 *
 * 1.Arg: nochdir
 * 2.Arg: noclose
 * Rueckgabe: 0 = OK oder -1 = Fehler
 * SML3-errno-Wert: ECHILD = Fehler bei fork()
 *                  ENODEV = Fehler bei /dev/null 
 *                  ENOENT = Fehler bei dup2()
 */
int
SML3_daemon(int nochdir, int noclose)
{
#ifndef SML3_HAVE_DAEMON
  pid_t pd;

  if ((pd = fork()) == (pid_t)-1) { SML3_fehlernew(ECHILD, "fork: %s", SML3_strerror(errno)); return -1; }
  if (pd != 0) { _exit(0); }  /* parent */

  setsid();
  if (!nochdir) { chdir("/"); }
  if (!noclose) {
    int fd;
    struct stat sbuf;

    if ((fd = open("/dev/null", O_RDWR)) < 0) {
      SML3_fehlernew(ENODEV, "open: %s", SML3_strerror(errno));
      return -1;
    }
    if (fstat(fd, &sbuf) < 0) {
      SML3_fehlernew(ENODEV, "fstat: %s", SML3_strerror(errno));
      close(fd);
      return -1;
    }
    if (!S_ISCHR(sbuf.st_mode)) {
      SML3_fehlernew(ENODEV, "/dev/null: no character device");
      close(fd);
      return -1;
    }
    if (fd != STDIN_FILENO) {
      if (dup2(fd, STDIN_FILENO) < 0) {
        SML3_fehlernew(ENOENT, "dup2: %s", SML3_strerror(errno));
        close(fd);
        return -1;
      }
    }
    if (fd != STDOUT_FILENO) {
      if (dup2(fd, STDOUT_FILENO) < 0) {
        SML3_fehlernew(ENOENT, "dup2: %s", SML3_strerror(errno));
        if (fd != STDIN_FILENO) { close(STDIN_FILENO); }
        close(fd);
        return -1;
      }
    }
    if (fd != STDERR_FILENO) {
      if (dup2(fd, STDERR_FILENO) < 0) {
        SML3_fehlernew(ENOENT, "dup2: %s", SML3_strerror(errno));
        if (fd != STDIN_FILENO) { close(STDIN_FILENO); }
        if (fd != STDOUT_FILENO) { close(STDOUT_FILENO); }
        close(fd);
        return -1;
      }
    }
    if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) { close(fd); }
  }
#else /* SML3_HAVE_DAEMON */
  errno = 0;
  if (daemon(nochdir, noclose) < 0) {
    SML3_fehlernew(ECHILD, "daemon: %s", SML3_strerror(errno));
    return -1;
  }
#endif /* SML3_HAVE_DAEMON */
  return 0;
} /* Ende SML3_daemon */


/* SML3_fnmatch: siehe fnmatch(), verwendet aber SML3_FNM_*-Definitionen;
 *  - Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
 *  - Modified for the Linux C library by H.J. Lu (hlu@eecs.wsu.edu)
 * Match 2.Arg against the filename pattern 1.Arg
 * 1.Arg: Pattern
 * 2.Arg: String
 * 3.Arg: SML3_FNM_*-Definitionen
 * Rueckgabe: 0 = gefunden, SML3_FNM_NOMATCH = nicht gefunden
 */
int
SML3_fnmatch(const char *pattern, const char *string, int flags)
{
  /* Note that this evalutes C many times.  */
#define FOLD(c)  ((flags & SML3_FNM_CASEFOLD) && isupper((int)(unsigned char)c) ? tolower((int)(unsigned char)c) : ((int)c))
  const char *p = pattern, *n = string;
  char c;

  if (pattern == NULL || string == NULL) { return SML3_FNM_NOMATCH; }

  while ((c = *p++) != '\0') {
    c = FOLD(c);
    switch (c) {
      case '?':
        if (*n == '\0') {
          return SML3_FNM_NOMATCH;
        } else if ((flags & SML3_FNM_FILE_NAME) && *n == '/') {
          return SML3_FNM_NOMATCH;
        } else if ((flags & SML3_FNM_PERIOD) && *n == '.' &&
                   (n == string || ((flags & SML3_FNM_FILE_NAME) && n[-1] == '/'))) {
          return SML3_FNM_NOMATCH;
        }
        break;

        case '\\':
          if (!(flags & SML3_FNM_NOESCAPE)) { c = *p++; c = FOLD(c); }
          if (FOLD(*n) != c) { return SML3_FNM_NOMATCH; }
          break;

        case '*':
          if ((flags & SML3_FNM_PERIOD) && *n == '.' &&
              (n == string || ((flags & SML3_FNM_FILE_NAME) && n[-1] == '/'))) {
            return SML3_FNM_NOMATCH;
          }

          for (c = *p++; (c == '?' || c == '*'); c = *p++) {
            if (((flags & SML3_FNM_FILE_NAME) && *n == '/') || (c == '?' && *n == '\0')) {
              return SML3_FNM_NOMATCH;
            }
            if (c=='?') { n++; }
          }

          if (c == '\0') { return 0; }

          {
            char c1 = (!(flags & SML3_FNM_NOESCAPE) && c == '\\') ? *p : c;
            c1 = FOLD(c1);
            for (--p; *n != '\0'; ++n) {
              if ((c == '[' || FOLD(*n) == c1) && SML3_fnmatch(p, n, flags & ~SML3_FNM_PERIOD) == 0) {
                return 0;
              }
            }
            return SML3_FNM_NOMATCH;
          }

        case '[':
          {
            /* Nonzero if the sense of the character class is inverted */
            int not;

            if (*n == '\0') { return SML3_FNM_NOMATCH; }

            if ((flags & SML3_FNM_PERIOD) && *n == '.' &&
                (n == string || ((flags & SML3_FNM_FILE_NAME) && n[-1] == '/'))) {
              return SML3_FNM_NOMATCH;
            }

            not = (*p == '!' || *p == '^');
            if (not) { ++p; }

            c = *p++;
            for (;;) {
              char cstart = c, cend = c;

              if (!(flags & SML3_FNM_NOESCAPE) && c == '\\') { cstart = cend = *p++; }
              cstart = cend = FOLD(cstart);

              if (c == '\0') { return SML3_FNM_NOMATCH; }  /* [ (unterminated) loses */

              c = *p++;
              c = FOLD(c);

              if ((flags & SML3_FNM_FILE_NAME) && c == '/') {  /* [/] can never match */
                return SML3_FNM_NOMATCH;
              }

              if (c == '-' && *p != ']') {
                cend = *p++;
                if (!(flags & SML3_FNM_NOESCAPE) && cend == '\\') { cend = *p++; }
                if (cend == '\0') { return SML3_FNM_NOMATCH; }
                cend = FOLD(cend);
                c = *p++;
              }

              if (FOLD(*n) >= cstart && FOLD(*n) <= cend) { goto _matched; }

              if (c == ']') { break; }
            }
            if (!not) { return SML3_FNM_NOMATCH; }
            break;

          _matched:
            /* Skip the rest of the [...] that already matched */
            while (c != ']') {
              if (c == '\0') { return SML3_FNM_NOMATCH; }  /* [... (unterminated) loses */
              c = *p++;
              if (!(flags & SML3_FNM_NOESCAPE) && c == '\\') { ++p; }  /* 1003.2d11 is unclear if this is right */
            }
            if (not) { return SML3_FNM_NOMATCH; }
          }
          break;

        default:
          if (c != FOLD(*n)) { return SML3_FNM_NOMATCH; }
    }
    ++n;
  }

  if (*n == '\0') { return 0; }

  /* The SML3_FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz" */
  if ((flags & SML3_FNM_LEADING_DIR) && *n == '/') { return 0; }

  return SML3_FNM_NOMATCH;
#undef FOLD
} /* Ende SML3_fnmatch */


/* SML3_pselect: siehe pselect()
 * Die Signalmaske sigmask enthaelt die gesamten zu blockierenden Signale
 * Rueckgabe: >=0 = OK oder -1 = Fehler
 * SML3-errno-Wert: EIO | EINTR = Fehler bei (p)select()
 *
 * Bsp:
 *   sigset_t sig1, sig2;
 *   sigemptyset(&sig1); sigemptyset(&sig2);
 *   // SIGCHLD zu den blockierten Signalen hinzufuegen
 *   sigaddset(&sig1, SIGCHLD);
 *   sigprocmask(SIG_BLOCK, &sig1, &sig2);  // sig2 enthaelt die vorigen blockierten Signale
 *   // SIGCHLD fuer pselect() entblockieren, indem sig2 uebergeben wird
 *   SML3_pselect(n, readfds, writefds, exceptfds, timeout, &sig2);
 */
int
SML3_pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask)
{
  int serg;
#ifndef SML3_HAVE_PSELECT
  if (timeout != NULL) {
    struct timeval tv;
    tv.tv_sec = timeout->tv_sec;
    tv.tv_usec = timeout->tv_nsec / 1000L;
    if (sigmask != NULL) {
      sigset_t sigrestore;
      sigemptyset(&sigrestore);
      sigprocmask(SIG_SETMASK, sigmask, &sigrestore);
      serg = select(n, readfds, writefds, exceptfds, &tv);
      sigprocmask(SIG_SETMASK, &sigrestore, NULL);
    } else {
      serg = select(n, readfds, writefds, exceptfds, &tv);
    }
  } else {
    if (sigmask != NULL) {
      sigset_t sigrestore;
      sigemptyset(&sigrestore);
      sigprocmask(SIG_SETMASK, sigmask, &sigrestore);
      serg = select(n, readfds, writefds, exceptfds, NULL);
      sigprocmask(SIG_SETMASK, &sigrestore, NULL);
    } else {
      serg = select(n, readfds, writefds, exceptfds, NULL);
    }
  }
  if (serg < 0) { SML3_fehlernew(errno == EINTR ? EINTR : EIO, "select: %s", SML3_strerror(errno)); }
#else /* SML3_HAVE_PSELECT */
  if ((serg = pselect(n, readfds, writefds, exceptfds, timeout, sigmask)) < 0) {
    SML3_fehlernew(errno == EINTR ? EINTR : EIO, "pselect: %s", SML3_strerror(errno));
    return -1;
  }
#endif /* SML3_HAVE_PSELECT */
  return serg;
} /* Ende SML3_pselect */


/* SML3_ppoll: siehe ppoll()
 * Die Signalmaske sigmask enthaelt die gesamten zu blockierenden Signale
 * Rueckgabe: >=0 = OK oder -1 = Fehler
 * SML3-errno-Wert: EIO | EINTR = Fehler bei (p)poll()
 *
 * Bsp:
 *   sigset_t sig1, sig2;
 *   sigemptyset(&sig1); sigemptyset(&sig2);
 *   // SIGCHLD zu den blockierten Signalen hinzufuegen
 *   sigaddset(&sig1, SIGCHLD);
 *   sigprocmask(SIG_BLOCK, &sig1, &sig2);  // sig2 enthaelt die vorigen blockierten Signale
 *   // SIGCHLD fuer ppoll() entblockieren, indem sig2 uebergeben wird
 *   SML3_ppoll(fds, nfds, timeout, &sig2);
 */
int
SML3_ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts, const sigset_t *sigmask)
{
  int serg;
#ifndef SML3_HAVE_PPOLL
  int timeout = (timeout_ts == NULL) ? -1 : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000);
  if (sigmask != NULL) {
    sigset_t sigrestore;
    sigemptyset(&sigrestore);
    sigprocmask(SIG_SETMASK, sigmask, &sigrestore);
    serg = poll(fds, nfds, timeout);
    sigprocmask(SIG_SETMASK, &sigrestore, NULL);
  } else {
    serg = poll(fds, nfds, timeout);
  }
  if (serg < 0) { SML3_fehlernew(errno == EINTR ? EINTR : EIO, "poll: %s", SML3_strerror(errno)); }
#else /* SML3_HAVE_PPOLL */
  extern int ppoll(struct pollfd *, nfds_t, const struct timespec *, const sigset_t *);
  if ((serg = ppoll(fds, nfds, timeout_ts, sigmask)) < 0) {
    SML3_fehlernew(errno == EINTR ? EINTR : EIO, "ppoll: %s", SML3_strerror(errno));
    return -1;
  }
#endif /* SML3_HAVE_PPOLL */
  return serg;
} /* Ende SML3_ppoll */


/* SML3_strtoint64: siehe strtol()
 * Wandelt einen String in ein SML3_int64 um.
 * Copyright (c) 1992 The Regents of the University of California.
 * Ignores `locale' stuff.  Assumes that the upper and lower case
 * alphabets and digits are each contiguous.
 * 1.Arg: String
 * 2.Arg: fuer Rueckgabe auf Zeichen hinter Zahlenwert oder NULL
 * 3.Arg: Basis (0=ermitteln auf dez,hex,okt oder 2 bis 36)
 * Rueckgabe: Zahlenwert
 *            oder Wert = SMLINT64_MAX und SML3-errno-Wert = ERANGE: Overflow
 *            oder Wert = SMLINT64_MIN und SML3-errno-Wert = ERANGE: Underflow
 *            oder Wert = 0            und SML3-errno-Wert = EINVAL: Basis falsch
 */
SML3_int64
SML3_strtoint64(const char *nptr, char **endptr, int base)
{
  const char *s;
  SML3_int64 acc, cutoff;
  int c;
  int neg, any, cutlim;

  if (base != 0 && (base < 2 || base > 36)) {
    SML3_fehlernew(EINVAL, "%s", SML3_strerror(EINVAL));
    return 0;
  }
  /*
   * Skip white space and pick up leading +/- sign if any.
   * If base is 0, allow 0x for hex and 0 for octal, else
   * assume decimal; if base is already 16, allow 0x.
   */
  s = nptr;
  do {
    c = (unsigned char)*s++;
  } while (isspace(c));
  if (c == '-') {
    neg = 1;
    c = *s++;
  } else {
    neg = 0;
    if (c == '+') { c = *s++; }
  }
  if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) {
    c = s[1];
    s += 2;
    base = 16;
  }
  if (base == 0) { base = c == '0' ? 8 : 10; }

  /*
   * Compute the cutoff value between legal numbers and illegal
   * numbers.  That is the largest legal value, divided by the
   * base.  An input number that is greater than this value, if
   * followed by a legal input character, is too big.  One that
   * is equal to this value may be valid or not; the limit
   * between valid and invalid numbers is then based on the last
   * digit.  For instance, if the range for long longs is
   * [-9223372036854775808..9223372036854775807] and the input base
   * is 10, cutoff will be set to 922337203685477580 and cutlim to
   * either 7 (neg==0) or 8 (neg==1), meaning that if we have
   * accumulated a value > 922337203685477580, or equal but the
   * next digit is > 7 (or 8), the number is too big, and we will
   * return a range error.
   *
   * Set any if any `digits' consumed; make it negative to indicate
   * overflow.
   */
  cutoff = neg ? SMLINT64_MIN : SMLINT64_MAX;
  cutlim = cutoff % base;
  cutoff /= base;
  if (neg) {
    if (cutlim > 0) { cutlim -= base; cutoff += 1; }
    cutlim = -cutlim;
  }
  errno = 0;
  for (acc = 0, any = 0; ; c = (unsigned char)*s++) {
    if (isdigit(c)) {
      c -= '0';
    } else if (isupper(c)) {
      c -= 'A' - 10;
    } else if (islower(c)) {
      c -= 'a' - 10;
    } else {
      break;
    }
    if (c >= base) { break; }
    if (any < 0) { continue; }
    if (neg) {
      if (acc < cutoff || (acc == cutoff && c > cutlim)) {
        any = -1;
        acc = SMLINT64_MIN;
        errno = ERANGE;
      } else {
        any = 1;
        acc *= base;
        acc -= c;
      }
    } else {
      if (acc > cutoff || (acc == cutoff && c > cutlim)) {
        any = -1;
        acc = SMLINT64_MAX;
        errno = ERANGE;
      } else {
        any = 1;
        acc *= base;
        acc += c;
      }
    }
  }
  if (endptr != 0) { *endptr = (char *)(any ? s - 1 : nptr); }
  if (errno != 0) { SML3_fehlernew(errno, "%s", SML3_strerror(errno)); }
  return acc;
} /* Ende SML3_strtoint64 */


/* SML3_strtouint64: siehe strtoul()
 * Wandelt einen String in ein unsigned SML3_int64 um.
 * Copyright (c) 1992 The Regents of the University of California.
 * Ignores `locale' stuff.  Assumes that the upper and lower case
 * alphabets and digits are each contiguous.
 * 1.Arg: String
 * 2.Arg: fuer Rueckgabe auf Zeichen hinter Zahlenwert oder NULL
 * 3.Arg: Basis (0=ermitteln auf dez,hex,okt oder 2 bis 36)
 * Rueckgabe: Zahlenwert
 *            oder Wert = SMLINT64_MAX und SML3-errno-Wert = ERANGE: Overflow
 *            oder Wert = 0            und SML3-errno-Wert = EINVAL: Basis falsch
 */
unsigned SML3_int64
SML3_strtouint64(const char *nptr, char **endptr, int base)
{
  const char *s;
  unsigned SML3_int64 acc, cutoff;
  int c;
  int neg, any, cutlim;

  if (base != 0 && (base < 2 || base > 36)) {
    SML3_fehlernew(EINVAL, "%s", SML3_strerror(EINVAL));
    return 0;
  }
  /*
   * Skip white space and pick up leading +/- sign if any.
   * If base is 0, allow 0x for hex and 0 for octal, else
   * assume decimal; if base is already 16, allow 0x.
   */
  s = nptr;
  do {
    c = (unsigned char)*s++;
  } while (isspace(c));
  if (c == '-') {
    neg = 1;
    c = *s++;
  } else {
    neg = 0;
    if (c == '+') { c = *s++; }
  }
  if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) {
    c = s[1];
    s += 2;
    base = 16;
  }
  if (base == 0) { base = c == '0' ? 8 : 10; }

  cutoff = SMLINT64_UMAX / (unsigned SML3_int64)base;
  cutlim = SMLINT64_UMAX % (unsigned SML3_int64)base;
  errno = 0;
  for (acc = 0, any = 0; ; c = (unsigned char)*s++) {
    if (isdigit(c)) {
      c -= '0';
    } else if (isupper(c)) {
      c -= 'A' - 10;
    } else if (islower(c)) {
      c -= 'a' - 10;
    } else {
      break;
    }
    if (c >= base) { break; }
    if (any < 0) { continue; }
    if (acc > cutoff || (acc == cutoff && c > cutlim)) {
      any = -1;
      acc = SMLINT64_UMAX;
      errno = ERANGE;
    } else {
      any = 1;
      acc *= (unsigned SML3_int64)base;
      acc += c;
    }
  }
  if (neg && any > 0) { acc = -acc; }
  if (endptr != 0) { *endptr = (char *)(any ? s - 1 : nptr); }
  if (errno != 0) { SML3_fehlernew(errno, "%s", SML3_strerror(errno)); }
  return acc;
} /* Ende SML3_strtouint64 */


/* SML3_strlcpy: siehe strlcpy()
 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
 * Kopiert 2.Arg in 1.Arg mit Groesse 3.Arg.
 * Maximal 3.Arg - 1 Zeichen werden kopiert, immer mit Ende-0.
 * 1.Arg: Ziel
 * 2.Arg: Quelle
 * 3.Arg: sizeof 1.Arg
 * Rueckgabe: strlen(2.Arg),
 *            falls Rueckgabe >= 3.Arg, wurde abgeschnitten
 */
size_t
SML3_strlcpy(char *dst, const char *src, size_t siz)
{
#ifndef SML3_HAVE_STRLCPY
  char *d = dst;
  const char *s = src;
  size_t n = siz;

  /* Copy as many bytes as will fit */
  if (n != 0 && --n != 0) {
    do {
      if ((*d++ = *s++) == 0) { break; }
    } while (--n != 0);
  }

  /* Not enough room in dst, add NUL and traverse rest of src */
  if (n == 0) {
    if (siz != 0) { *d = '\0'; }  /* NUL-terminate dst */
    while (*s++) {;}
  }

  return s - src - 1;  /* count does not include NUL */
#else  /* SML3_HAVE_STRLCPY */
  return strlcpy(dst, src, siz);
#endif  /* SML3_HAVE_STRLCPY */
} /* Ende SML3_strlcpy */


/* SML3_strlcat: siehe strlcat()
 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
 * Fuegt 2.Arg an 1.Arg mit Gesamtgroesse 3.Arg an.
 * Maximal 3.Arg - 1 Zeichen werden kopiert, immer mit Ende-0 (ausser 3.Arg <= strlen(1.Arg))
 * 1.Arg: Ziel
 * 2.Arg: Quelle
 * 3.Arg: sizeof 1.Arg
 * Rueckgabe: strlen(2.Arg) + MIN(3.Arg, strlen(Initial-1.Arg)),
 *            falls Rueckgabe >= 3.Arg, wurde abgeschnitten
 */
size_t
SML3_strlcat(char *dst, const char *src, size_t siz)
{
#ifndef SML3_HAVE_STRLCAT
  char *d = dst;
  const char *s = src;
  size_t n = siz;
  size_t dlen;

  /* Find the end of dst and adjust bytes left but don't go past end */
  while (n-- != 0 && *d != '\0') { d++; }
  dlen = d - dst;
  n = siz - dlen;

  if (n == 0) { return dlen + strlen(s); }
  while (*s != '\0') {
    if (n != 1) {
      *d++ = *s;
      n--;
    }
    s++;
  }
  *d = '\0';

  return dlen + (s - src);  /* count does not include NUL */
#else  /* SML3_HAVE_STRLCAT */
  return strlcat(dst, src, siz);
#endif  /* SML3_HAVE_STRLCAT */
} /* Ende SML3_strlcat */


/* SML3_getpwnam: wie getpwnam(), aber threadsicher
 * 1.Arg: Name
 * 2.Arg: Puffer fuer Antwort
 * 3.+4.Arg: interner Puffer fuer Antwort mit Groessenangabe
 *           (vorgeschlagene Groesse: sysconf(_SC_GETPW_R_SIZE_MAX), i.a. 1024 Bytes)
 * Rueckgabe: Pointer auf 2.Arg
 *            oder NULL = nicht gefunden (SML3_fehlererrno() = 0) bzw. Fehler
 * SML3-errno-Wert: wie getpwnam()
 */
struct passwd *
SML3_getpwnam(const char *name, struct passwd *pwd, char *buf, size_t buflen)
{
  struct passwd *pwdp = NULL;
#ifndef SML3_HAVE_GETPWGR_R
# ifdef SML3_HAVE_PTHREAD
  static pthread_mutex_t f_mtx = PTHREAD_MUTEX_INITIALIZER;
  pthread_mutex_lock(&f_mtx);
# endif
  if (buf == NULL || buflen == 0 || pwd == NULL) { SML3_fehlernew(EINVAL, "%s", SML3_strerror(EINVAL)); goto _fend; }
  errno = 0;
  pwdp = getpwnam(name);
  if (pwdp == NULL) { SML3_fehlernew(errno, "getpwnam: %s", SML3_strerror(errno)); goto _fend; }
  if (move_pwd(pwdp, pwd, buf, buflen) < 0) { pwdp = NULL; SML3_fehleradd(NULL); goto _fend; }
  pwdp = pwd;
_fend:
# ifdef SML3_HAVE_PTHREAD
  pthread_mutex_unlock(&f_mtx);
# endif
#else  /* SML3_HAVE_GETPWGR_R */
  int errnum = getpwnam_r(name, pwd, buf, buflen, &pwdp);
  if (errnum != 0) { SML3_fehlernew(errnum, "getpwnam_r: %s", SML3_strerror(errnum)); return NULL; } else { SML3_fehlernew(0, NULL); }
#endif  /* SML3_HAVE_GETPWGR_R */
  return pwdp;
} /* Ende SML3_getpwnam */


/* SML3_getpwuid: wie getpwuid(), aber threadsicher
 * 1.Arg: UserID
 * 2.Arg: Puffer fuer Antwort
 * 3.+4.Arg: interner Puffer fuer Antwort mit Groessenangabe
 *           (vorgeschlagene Groesse: sysconf(_SC_GETPW_R_SIZE_MAX), i.a. 1024 Bytes)
 * Rueckgabe: Pointer auf 2.Arg
 *            oder NULL = nicht gefunden (SML3_fehlererrno() = 0) bzw. Fehler
 * SML3-errno-Wert: wie getpwuid()
 */
struct passwd *
SML3_getpwuid(uid_t uid, struct passwd *pwd, char *buf, size_t buflen)
{
  struct passwd *pwdp = NULL;
#ifndef SML3_HAVE_GETPWGR_R
# ifdef SML3_HAVE_PTHREAD
  static pthread_mutex_t f_mtx = PTHREAD_MUTEX_INITIALIZER;
  pthread_mutex_lock(&f_mtx);
# endif
  if (buf == NULL || buflen == 0 || pwd == NULL) { SML3_fehlernew(EINVAL, "%s", SML3_strerror(EINVAL)); goto _fend; }
  errno = 0;
  pwdp = getpwuid(uid);
  if (pwdp == NULL) { SML3_fehlernew(errno, "getpwuid: %s", SML3_strerror(errno)); goto _fend; }
  if (move_pwd(pwdp, pwd, buf, buflen) < 0) { pwdp = NULL; SML3_fehleradd(NULL); goto _fend; }
  pwdp = pwd;
_fend:
# ifdef SML3_HAVE_PTHREAD
  pthread_mutex_unlock(&f_mtx);
# endif
#else  /* SML3_HAVE_GETPWGR_R */
  int errnum = getpwuid_r(uid, pwd, buf, buflen, &pwdp);
  if (errnum != 0) { SML3_fehlernew(errnum, "getpwuid_r: %s", SML3_strerror(errnum)); return NULL; } else { SML3_fehlernew(0, NULL); }
#endif  /* SML3_HAVE_GETPWGR_R */
  return pwdp;
} /* Ende SML3_getpwuid */
