(root)/
make-4.4/
src/
misc.c
       1  /* Miscellaneous generic support functions for GNU Make.
       2  Copyright (C) 1988-2022 Free Software Foundation, Inc.
       3  This file is part of GNU Make.
       4  
       5  GNU Make is free software; you can redistribute it and/or modify it under the
       6  terms of the GNU General Public License as published by the Free Software
       7  Foundation; either version 3 of the License, or (at your option) any later
       8  version.
       9  
      10  GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
      11  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      12  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      13  
      14  You should have received a copy of the GNU General Public License along with
      15  this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  #include "makeint.h"
      18  #include "filedef.h"
      19  #include "dep.h"
      20  #include "os.h"
      21  #include "debug.h"
      22  
      23  /* GNU make no longer supports pre-ANSI89 environments.  */
      24  
      25  #include <stdarg.h>
      26  
      27  #ifdef WINDOWS32
      28  # include <windows.h>
      29  # include <io.h>
      30  #endif
      31  
      32  #ifdef HAVE_FCNTL_H
      33  # include <fcntl.h>
      34  #else
      35  # include <sys/file.h>
      36  #endif
      37  
      38  unsigned int
      39  make_toui (const char *str, const char **error)
      40  {
      41    char *end;
      42    unsigned long val = strtoul (str, &end, 10);
      43  
      44    if (error)
      45      {
      46        if (str[0] == '\0')
      47          *error = "Missing value";
      48        else if (*end != '\0')
      49          *error = "Invalid value";
      50        else
      51          *error = NULL;
      52      }
      53  
      54    return val;
      55  }
      56  
      57  /* Convert val into a string, written to buf.  buf must be large enough
      58     to hold the largest possible value, plus a nul byte.  Returns buf.
      59     We can't use standard PRI* here: those are based on intNN_t types.  */
      60  
      61  char *
      62  make_lltoa (long long val, char *buf)
      63  {
      64    sprintf (buf, "%" MK_PRI64_PREFIX "d", val);
      65    return buf;
      66  }
      67  
      68  char *
      69  make_ulltoa (unsigned long long val, char *buf)
      70  {
      71    sprintf (buf, "%" MK_PRI64_PREFIX "u", val);
      72    return buf;
      73  }
      74  
      75  /* Simple random number generator, for use with shuffle.
      76     This doesn't need to be truly random, just pretty random.  Use our own
      77     implementation rather than relying on the C runtime's rand() so we always
      78     get the same results for a given seed, regardless of C runtime.  */
      79  
      80  static unsigned int mk_state = 0;
      81  
      82  void
      83  make_seed (unsigned int seed)
      84  {
      85    mk_state = seed;
      86  }
      87  
      88  unsigned int
      89  make_rand ()
      90  {
      91    /* mk_state must never be 0.  */
      92    if (mk_state == 0)
      93      mk_state = (unsigned int)(time (NULL) ^ make_pid ()) + 1;
      94  
      95    /* A simple xorshift RNG.  */
      96    mk_state ^= mk_state << 13;
      97    mk_state ^= mk_state >> 17;
      98    mk_state ^= mk_state << 5;
      99  
     100    return mk_state;
     101  }
     102  
     103  /* Compare strings *S1 and *S2.
     104     Return negative if the first is less, positive if it is greater,
     105     zero if they are equal.  */
     106  
     107  int
     108  alpha_compare (const void *v1, const void *v2)
     109  {
     110    const char *s1 = *((char **)v1);
     111    const char *s2 = *((char **)v2);
     112  
     113    if (*s1 != *s2)
     114      return *s1 - *s2;
     115    return strcmp (s1, s2);
     116  }
     117  
     118  /* Discard each backslash-newline combination from LINE.
     119     Backslash-backslash-newline combinations become backslash-newlines.
     120     This is done by copying the text at LINE into itself.  */
     121  
     122  void
     123  collapse_continuations (char *line)
     124  {
     125    char *out = line;
     126    char *in = line;
     127    char *q;
     128  
     129    q = strchr(in, '\n');
     130    if (q == 0)
     131      return;
     132  
     133    do
     134      {
     135        char *p = q;
     136        int i;
     137        size_t out_line_length;
     138  
     139        if (q > line && q[-1] == '\\')
     140          {
     141            /* Search for more backslashes.  */
     142            i = -2;
     143            while (&p[i] >= line && p[i] == '\\')
     144              --i;
     145            ++i;
     146          }
     147        else
     148          i = 0;
     149  
     150        /* The number of backslashes is now -I, keep half of them.  */
     151        out_line_length = (p - in) + i - i/2;
     152        if (out != in)
     153          memmove (out, in, out_line_length);
     154        out += out_line_length;
     155  
     156        /* When advancing IN, skip the newline too.  */
     157        in = q + 1;
     158  
     159        if (i & 1)
     160          {
     161            /* Backslash/newline handling:
     162               In traditional GNU make all trailing whitespace, consecutive
     163               backslash/newlines, and any leading non-newline whitespace on the
     164               next line is reduced to a single space.
     165               In POSIX, each backslash/newline and is replaced by a space.  */
     166            while (ISBLANK (*in))
     167              ++in;
     168            if (! posix_pedantic)
     169              while (out > line && ISBLANK (out[-1]))
     170                --out;
     171            *out++ = ' ';
     172          }
     173        else
     174          {
     175            /* If the newline isn't quoted, put it in the output.  */
     176            *out++ = '\n';
     177          }
     178  
     179        q = strchr(in, '\n');
     180      }
     181    while (q);
     182  
     183    memmove(out, in, strlen(in) + 1);
     184  }
     185  
     186  /* Print N spaces (used in debug for target-depth).  */
     187  
     188  void
     189  print_spaces (unsigned int n)
     190  {
     191    while (n-- > 0)
     192      putchar (' ');
     193  }
     194  
     195  
     196  /* Return a string whose contents concatenate the NUM strings provided
     197     This string lives in static, re-used memory.  */
     198  
     199  const char *
     200  concat (unsigned int num, ...)
     201  {
     202    static size_t rlen = 0;
     203    static char *result = NULL;
     204    size_t ri = 0;
     205    va_list args;
     206  
     207    va_start (args, num);
     208  
     209    while (num-- > 0)
     210      {
     211        const char *s = va_arg (args, const char *);
     212        size_t l = xstrlen (s);
     213  
     214        if (l == 0)
     215          continue;
     216  
     217        if (ri + l > rlen)
     218          {
     219            rlen = ((rlen ? rlen : 60) + l) * 2;
     220            result = xrealloc (result, rlen);
     221          }
     222  
     223        memcpy (result + ri, s, l);
     224        ri += l;
     225      }
     226  
     227    va_end (args);
     228  
     229    /* Get some more memory if we don't have enough space for the
     230       terminating '\0'.   */
     231    if (ri == rlen)
     232      {
     233        rlen = (rlen ? rlen : 60) * 2;
     234        result = xrealloc (result, rlen);
     235      }
     236  
     237    result[ri] = '\0';
     238  
     239    return result;
     240  }
     241  
     242  
     243  #ifndef HAVE_UNISTD_H
     244  pid_t getpid ();
     245  #endif
     246  
     247  pid_t make_pid ()
     248  {
     249    return getpid ();
     250  }
     251  
     252  /* Like malloc but get fatal error if memory is exhausted.  */
     253  /* Don't bother if we're using dmalloc; it provides these for us.  */
     254  
     255  #ifndef HAVE_DMALLOC_H
     256  
     257  #undef xmalloc
     258  #undef xcalloc
     259  #undef xrealloc
     260  #undef xstrdup
     261  
     262  void *
     263  xmalloc (size_t size)
     264  {
     265    /* Make sure we don't allocate 0, for pre-ISO implementations.  */
     266    void *result = malloc (size ? size : 1);
     267    if (result == 0)
     268      out_of_memory ();
     269    return result;
     270  }
     271  
     272  
     273  void *
     274  xcalloc (size_t size)
     275  {
     276    /* Make sure we don't allocate 0, for pre-ISO implementations.  */
     277    void *result = calloc (size ? size : 1, 1);
     278    if (result == 0)
     279      out_of_memory ();
     280    return result;
     281  }
     282  
     283  
     284  void *
     285  xrealloc (void *ptr, size_t size)
     286  {
     287    void *result;
     288  
     289    /* Some older implementations of realloc() don't conform to ISO.  */
     290    if (! size)
     291      size = 1;
     292    result = ptr ? realloc (ptr, size) : malloc (size);
     293    if (result == 0)
     294      out_of_memory ();
     295    return result;
     296  }
     297  
     298  
     299  char *
     300  xstrdup (const char *ptr)
     301  {
     302    char *result;
     303  
     304  #ifdef HAVE_STRDUP
     305    result = strdup (ptr);
     306  #else
     307    result = malloc (strlen (ptr) + 1);
     308  #endif
     309  
     310    if (result == 0)
     311      out_of_memory ();
     312  
     313  #ifdef HAVE_STRDUP
     314    return result;
     315  #else
     316    return strcpy (result, ptr);
     317  #endif
     318  }
     319  
     320  #endif  /* HAVE_DMALLOC_H */
     321  
     322  char *
     323  xstrndup (const char *str, size_t length)
     324  {
     325    char *result;
     326  
     327  #ifdef HAVE_STRNDUP
     328    result = strndup (str, length);
     329    if (result == 0)
     330      out_of_memory ();
     331  #else
     332    result = xmalloc (length + 1);
     333    if (length > 0)
     334      strncpy (result, str, length);
     335    result[length] = '\0';
     336  #endif
     337  
     338    return result;
     339  }
     340  
     341  #ifndef HAVE_MEMRCHR
     342  void *
     343  memrchr(const void* str, int ch, size_t len)
     344  {
     345    const char* sp = str;
     346    const char* cp = sp;
     347  
     348    if (len == 0)
     349      return NULL;
     350  
     351    cp += len - 1;
     352  
     353    while (cp[0] != ch)
     354      {
     355        if (cp == sp)
     356          return NULL;
     357        --cp;
     358      }
     359  
     360    return (void*)cp;
     361  }
     362  #endif
     363  
     364  
     365  
     366  /* Limited INDEX:
     367     Search through the string STRING, which ends at LIMIT, for the character C.
     368     Returns a pointer to the first occurrence, or nil if none is found.
     369     Like INDEX except that the string searched ends where specified
     370     instead of at the first null.  */
     371  
     372  char *
     373  lindex (const char *s, const char *limit, int c)
     374  {
     375    while (s < limit)
     376      if (*s++ == c)
     377        return (char *)(s - 1);
     378  
     379    return 0;
     380  }
     381  
     382  /* Return the address of the first whitespace or null in the string S.  */
     383  
     384  char *
     385  end_of_token (const char *s)
     386  {
     387    while (! END_OF_TOKEN (*s))
     388      ++s;
     389    return (char *)s;
     390  }
     391  
     392  /* Return the address of the first nonwhitespace or null in the string S.  */
     393  
     394  char *
     395  next_token (const char *s)
     396  {
     397    NEXT_TOKEN (s);
     398    return (char *)s;
     399  }
     400  
     401  /* Find the next token in PTR; return the address of it, and store the length
     402     of the token into *LENGTHPTR if LENGTHPTR is not nil.  Set *PTR to the end
     403     of the token, so this function can be called repeatedly in a loop.  */
     404  
     405  char *
     406  find_next_token (const char **ptr, size_t *lengthptr)
     407  {
     408    const char *p = next_token (*ptr);
     409  
     410    if (*p == '\0')
     411      return 0;
     412  
     413    *ptr = end_of_token (p);
     414    if (lengthptr != 0)
     415      *lengthptr = *ptr - p;
     416  
     417    return (char *)p;
     418  }
     419  
     420  /* Write a BUFFER of size LEN to file descriptor FD.
     421     Retry short writes from EINTR.  Return LEN, or -1 on error.  */
     422  ssize_t
     423  writebuf (int fd, const void *buffer, size_t len)
     424  {
     425    const char *msg = buffer;
     426    size_t l = len;
     427    while (l)
     428      {
     429        ssize_t r;
     430  
     431        EINTRLOOP (r, write (fd, msg, l));
     432        if (r < 0)
     433          return r;
     434  
     435        l -= r;
     436        msg += r;
     437      }
     438  
     439    return (ssize_t)len;
     440  }
     441  
     442  /* Read until we get LEN bytes from file descriptor FD, into BUFFER.
     443     Retry short reads on EINTR.  If we get an error, return it.
     444     Return 0 at EOF.  */
     445  ssize_t
     446  readbuf (int fd, void *buffer, size_t len)
     447  {
     448    char *msg = buffer;
     449    while (len)
     450      {
     451        ssize_t r;
     452  
     453        EINTRLOOP (r, read (fd, msg, len));
     454        if (r < 0)
     455          return r;
     456        if (r == 0)
     457          break;
     458  
     459        len -= r;
     460        msg += r;
     461      }
     462  
     463    return (ssize_t)(msg - (char*)buffer);
     464  }
     465  
     466  
     467  /* Copy a chain of 'struct dep'.  For 2nd expansion deps, dup the name.  */
     468  
     469  struct dep *
     470  copy_dep_chain (const struct dep *d)
     471  {
     472    struct dep *firstnew = 0;
     473    struct dep *lastnew = 0;
     474  
     475    while (d != 0)
     476      {
     477        struct dep *c = xmalloc (sizeof (struct dep));
     478        memcpy (c, d, sizeof (struct dep));
     479  
     480        if (c->need_2nd_expansion)
     481          c->name = xstrdup (c->name);
     482  
     483        c->next = 0;
     484        if (firstnew == 0)
     485          firstnew = lastnew = c;
     486        else
     487          lastnew = lastnew->next = c;
     488  
     489        d = d->next;
     490      }
     491  
     492    return firstnew;
     493  }
     494  
     495  /* Free a chain of struct nameseq.
     496     For struct dep chains use free_dep_chain.  */
     497  
     498  void
     499  free_ns_chain (struct nameseq *ns)
     500  {
     501    while (ns != 0)
     502      {
     503        struct nameseq *t = ns;
     504        ns = ns->next;
     505        free_ns (t);
     506      }
     507  }
     508  
     509  
     510  #ifdef MAKE_MAINTAINER_MODE
     511  
     512  void
     513  spin (const char* type)
     514  {
     515    char filenm[256];
     516    struct stat dummy;
     517  
     518    sprintf (filenm, ".make-spin-%s", type);
     519  
     520    if (stat (filenm, &dummy) == 0)
     521      {
     522        fprintf (stderr, "SPIN on %s\n", filenm);
     523        do
     524  #ifdef WINDOWS32
     525          Sleep (1000);
     526  #else
     527          sleep (1);
     528  #endif
     529        while (stat (filenm, &dummy) == 0);
     530      }
     531  }
     532  
     533  void
     534  dbg (const char *fmt, ...)
     535  {
     536    FILE *fp = fopen ("/tmp/gmkdebug.log", "a+");
     537    va_list args;
     538    char buf[4096];
     539  
     540    va_start (args, fmt);
     541    vsprintf (buf, fmt, args);
     542    va_end (args);
     543  
     544    fprintf(fp, "%u: %s\n", (unsigned) make_pid (), buf);
     545    fflush (fp);
     546    fclose (fp);
     547  }
     548  
     549  #endif
     550  
     551  
     552  
     553  /* Provide support for temporary files.  */
     554  
     555  #ifndef HAVE_STDLIB_H
     556  # ifdef HAVE_MKSTEMP
     557  int mkstemp (char *template);
     558  # else
     559  char *mktemp (char *template);
     560  # endif
     561  #endif
     562  
     563  #ifndef HAVE_UMASK
     564  mode_t
     565  umask (mode_t mask)
     566  {
     567    return 0;
     568  }
     569  #endif
     570  
     571  #ifdef VMS
     572  # define DEFAULT_TMPFILE    "sys$scratch:gnv$make_cmdXXXXXX.com"
     573  #else
     574  # define DEFAULT_TMPFILE    "GmXXXXXX"
     575  #endif
     576  
     577  const char *
     578  get_tmpdir ()
     579  {
     580    static const char *tmpdir = NULL;
     581  
     582    if (!tmpdir)
     583      {
     584  #if defined (__MSDOS__) || defined (WINDOWS32) || defined (__EMX__)
     585  # define TMP_EXTRAS   "TMP", "TEMP",
     586  #else
     587  # define TMP_EXTRAS
     588  #endif
     589        const char *tlist[] = { "MAKE_TMPDIR", "TMPDIR", TMP_EXTRAS NULL };
     590        const char **tp;
     591        unsigned int found = 0;
     592  
     593        for (tp = tlist; *tp; ++tp)
     594          if ((tmpdir = getenv (*tp)) && *tmpdir != '\0')
     595            {
     596              struct stat st;
     597              int r;
     598              found = 1;
     599              EINTRLOOP(r, stat (tmpdir, &st));
     600              if (r < 0)
     601                OSSS (error, NILF,
     602                      _("%s value %s: %s"), *tp, tmpdir, strerror (errno));
     603              else if (! S_ISDIR (st.st_mode))
     604                OSS (error, NILF,
     605                     _("%s value %s: not a directory"), *tp, tmpdir);
     606              else
     607                return tmpdir;
     608            }
     609  
     610        tmpdir = DEFAULT_TMPDIR;
     611  
     612        if (found)
     613          OS (error, NILF, _("using default temporary directory '%s'"), tmpdir);
     614      }
     615  
     616    return tmpdir;
     617  }
     618  
     619  static char *
     620  get_tmptemplate ()
     621  {
     622    const char *tmpdir = get_tmpdir ();
     623    char *template;
     624    char *cp;
     625  
     626    template = xmalloc (strlen (tmpdir) + CSTRLEN (DEFAULT_TMPFILE) + 2);
     627    cp = stpcpy (template, tmpdir);
     628  
     629  #if !defined VMS
     630    /* It's not possible for tmpdir to be empty.  */
     631    if (! ISDIRSEP (cp[-1]))
     632      *(cp++) = '/';
     633  #endif
     634  
     635    strcpy (cp, DEFAULT_TMPFILE);
     636  
     637    return template;
     638  }
     639  
     640  #if !HAVE_MKSTEMP || !HAVE_FDOPEN
     641  /* Generate a temporary filename.  This is not safe as another program could
     642     snipe our filename after we've generated it: use this only on systems
     643     without more secure alternatives.  */
     644  
     645  static char *
     646  get_tmppath ()
     647  {
     648    char *path;
     649  
     650  # ifdef HAVE_MKTEMP
     651    path = get_tmptemplate ();
     652    if (*mktemp (path) == '\0')
     653      pfatal_with_name ("mktemp");
     654  # else
     655    path = xmalloc (L_tmpnam + 1);
     656    if (tmpnam (path) == NULL)
     657      pfatal_with_name ("tmpnam");
     658  # endif
     659  
     660    return path;
     661  }
     662  #endif
     663  
     664  /* Generate a temporary file and return an fd for it.  If name is NULL then
     665     the temp file is anonymous and will be deleted when the process exits.  */
     666  int
     667  get_tmpfd (char **name)
     668  {
     669    int fd = -1;
     670    char *tmpnm;
     671    mode_t mask;
     672  
     673    /* If there's an os-specific way to get an anoymous temp file use it.  */
     674    if (!name)
     675      {
     676        fd = os_anontmp ();
     677        if (fd >= 0)
     678          return fd;
     679      }
     680  
     681    /* Preserve the current umask, and set a restrictive one for temp files.
     682       Only really needed for mkstemp() but won't hurt for the open method.  */
     683    mask = umask (0077);
     684  
     685  #if defined(HAVE_MKSTEMP)
     686    tmpnm = get_tmptemplate ();
     687  
     688    /* It's safest to use mkstemp(), if we can.  */
     689    EINTRLOOP (fd, mkstemp (tmpnm));
     690  #else
     691    tmpnm = get_tmppath ();
     692  
     693    /* Can't use mkstemp(), but try to guard against a race condition.  */
     694    EINTRLOOP (fd, open (tmpnm, O_CREAT|O_EXCL|O_RDWR, 0600));
     695  #endif
     696    if (fd < 0)
     697      OSS (fatal, NILF,
     698           _("create temporary file %s: %s"), tmpnm, strerror (errno));
     699  
     700    if (name)
     701      *name = tmpnm;
     702    else
     703      {
     704        int r;
     705        EINTRLOOP (r, unlink (tmpnm));
     706        if (r < 0)
     707          OSS (fatal, NILF,
     708               _("unlink temporary file %s: %s"), tmpnm, strerror (errno));
     709        free (tmpnm);
     710      }
     711  
     712    umask (mask);
     713  
     714    return fd;
     715  }
     716  
     717  /* Return a FILE* for a temporary file, opened in the safest way possible.
     718     Set name to point to an allocated buffer containing the name of the file.
     719     Note, this cannot be NULL!  */
     720  FILE *
     721  get_tmpfile (char **name)
     722  {
     723    /* Be consistent with tmpfile, which opens as if by "wb+".  */
     724    const char *tmpfile_mode = "wb+";
     725    FILE *file;
     726  
     727  #if defined(HAVE_FDOPEN)
     728    int fd = get_tmpfd (name);
     729  
     730    ENULLLOOP (file, fdopen (fd, tmpfile_mode));
     731    if (file == NULL)
     732      OSS (fatal, NILF,
     733           _("fdopen: temporary file %s: %s"), *name, strerror (errno));
     734  #else
     735    /* Preserve the current umask, and set a restrictive one for temp files.  */
     736    mode_t mask = umask (0077);
     737    int err;
     738  
     739    *name = get_tmppath ();
     740  
     741    /* Although this fopen is insecure, it is executed only on non-fdopen
     742       platforms, which should be a rarity nowadays.  */
     743  
     744    ENULLLOOP (file, fopen (*name, tmpfile_mode));
     745    if (file == NULL)
     746      OSS (fatal, NILF,
     747           _("fopen: temporary file %s: %s"), *name, strerror (errno));
     748  
     749    umask (mask);
     750  #endif
     751  
     752    return file;
     753  }
     754  
     755  
     756  #if !HAVE_STRCASECMP && !HAVE_STRICMP && !HAVE_STRCMPI
     757  /* If we don't have strcasecmp() (from POSIX), or anything that can substitute
     758     for it, define our own version.  */
     759  
     760  int
     761  strcasecmp (const char *s1, const char *s2)
     762  {
     763    while (1)
     764      {
     765        int c1 = (unsigned char) *(s1++);
     766        int c2 = (unsigned char) *(s2++);
     767  
     768        if (isalpha (c1))
     769          c1 = tolower (c1);
     770        if (isalpha (c2))
     771          c2 = tolower (c2);
     772  
     773        if (c1 != '\0' && c1 == c2)
     774          continue;
     775  
     776        return (c1 - c2);
     777      }
     778  }
     779  #endif
     780  
     781  #if !HAVE_STRNCASECMP && !HAVE_STRNICMP && !HAVE_STRNCMPI
     782  /* If we don't have strncasecmp() (from POSIX), or anything that can
     783     substitute for it, define our own version.  */
     784  
     785  int
     786  strncasecmp (const char *s1, const char *s2, size_t n)
     787  {
     788    while (n-- > 0)
     789      {
     790        int c1 = (unsigned char) *(s1++);
     791        int c2 = (unsigned char) *(s2++);
     792  
     793        if (isalpha (c1))
     794          c1 = tolower (c1);
     795        if (isalpha (c2))
     796          c2 = tolower (c2);
     797  
     798        if (c1 != '\0' && c1 == c2)
     799          continue;
     800  
     801        return (c1 - c2);
     802      }
     803  
     804    return 0;
     805  }
     806  #endif
     807  
     808  
     809  #ifdef NEED_GET_PATH_MAX
     810  unsigned int
     811  get_path_max (void)
     812  {
     813    static unsigned int value;
     814  
     815    if (value == 0)
     816      {
     817        long x = pathconf ("/", _PC_PATH_MAX);
     818        if (x > 0)
     819          value = (unsigned int) x;
     820        else
     821          value = PATH_MAX;
     822      }
     823  
     824    return value;
     825  }
     826  #endif
     827  
     828  #if !HAVE_MEMPCPY
     829  void *
     830  mempcpy (void *dest, const void *src, size_t n)
     831  {
     832    return (char *) memcpy (dest, src, n) + n;
     833  }
     834  #endif
     835  
     836  #if !HAVE_STPCPY
     837  char *
     838  stpcpy (char *dest, const char *src)
     839  {
     840    char *d = dest;
     841    const char *s = src;
     842  
     843    do
     844      *d++ = *s;
     845    while (*s++ != '\0');
     846  
     847    return d - 1;
     848  }
     849  #endif
     850  
     851  #if !HAVE_STRTOLL
     852  # undef UNSIGNED
     853  # undef USE_NUMBER_GROUPING
     854  # undef USE_WIDE_CHAR
     855  # define QUAD 1
     856  # include <strtol.c>
     857  #endif
     858  
     859  #if !HAVE_STRERROR
     860  char *
     861  strerror (int errnum)
     862  {
     863    static char msg[256];
     864  
     865  #define SETMSG(_e, _m) case _e: strcpy(msg, _m); break
     866  
     867    switch (errnum)
     868      {
     869  #ifdef EPERM
     870      SETMSG (EPERM  , "Operation not permitted");
     871  #endif
     872  #ifdef ENOENT
     873      SETMSG (ENOENT , "No such file or directory");
     874  #endif
     875  #ifdef ESRCH
     876      SETMSG (ESRCH  , "No such process");
     877  #endif
     878  #ifdef EINTR
     879      SETMSG (EINTR  , "Interrupted system call");
     880  #endif
     881  #ifdef EIO
     882      SETMSG (EIO    , "I/O error");
     883  #endif
     884  #ifdef ENXIO
     885      SETMSG (ENXIO  , "No such device or address");
     886  #endif
     887  #ifdef E2BIG
     888      SETMSG (E2BIG  , "Argument list too long");
     889  #endif
     890  #ifdef ENOEXEC
     891      SETMSG (ENOEXEC, "Exec format error");
     892  #endif
     893  #ifdef EBADF
     894      SETMSG (EBADF  , "Bad file number");
     895  #endif
     896  #ifdef ECHILD
     897      SETMSG (ECHILD , "No child processes");
     898  #endif
     899  #ifdef EAGAIN
     900      SETMSG (EAGAIN , "Try again");
     901  #endif
     902  #ifdef ENOMEM
     903      SETMSG (ENOMEM , "Out of memory");
     904  #endif
     905  #ifdef EACCES
     906      SETMSG (EACCES , "Permission denied");
     907  #endif
     908  #ifdef EFAULT
     909      SETMSG (EFAULT , "Bad address");
     910  #endif
     911  #ifdef ENOTBLK
     912      SETMSG (ENOTBLK, "Block device required");
     913  #endif
     914  #ifdef EBUSY
     915      SETMSG (EBUSY  , "Device or resource busy");
     916  #endif
     917  #ifdef EEXIST
     918      SETMSG (EEXIST , "File exists");
     919  #endif
     920  #ifdef EXDEV
     921      SETMSG (EXDEV  , "Cross-device link");
     922  #endif
     923  #ifdef ENODEV
     924      SETMSG (ENODEV , "No such device");
     925  #endif
     926  #ifdef ENOTDIR
     927      SETMSG (ENOTDIR, "Not a directory");
     928  #endif
     929  #ifdef EISDIR
     930      SETMSG (EISDIR , "Is a directory");
     931  #endif
     932  #ifdef EINVAL
     933      SETMSG (EINVAL , "Invalid argument");
     934  #endif
     935  #ifdef ENFILE
     936      SETMSG (ENFILE , "File table overflow");
     937  #endif
     938  #ifdef EMFILE
     939      SETMSG (EMFILE , "Too many open files");
     940  #endif
     941  #ifdef ENOTTY
     942      SETMSG (ENOTTY , "Not a typewriter");
     943  #endif
     944  #ifdef ETXTBSY
     945      SETMSG (ETXTBSY, "Text file busy");
     946  #endif
     947  #ifdef EFBIG
     948      SETMSG (EFBIG  , "File too large");
     949  #endif
     950  #ifdef ENOSPC
     951      SETMSG (ENOSPC , "No space left on device");
     952  #endif
     953  #ifdef ESPIPE
     954      SETMSG (ESPIPE , "Illegal seek");
     955  #endif
     956  #ifdef EROFS
     957      SETMSG (EROFS  , "Read-only file system");
     958  #endif
     959  #ifdef EMLINK
     960      SETMSG (EMLINK , "Too many links");
     961  #endif
     962  #ifdef EPIPE
     963      SETMSG (EPIPE  , "Broken pipe");
     964  #endif
     965  #ifdef EDOM
     966      SETMSG (EDOM   , "Math argument out of domain of func");
     967  #endif
     968  #ifdef ERANGE
     969      SETMSG (ERANGE , "Math result not representable");
     970  #endif
     971      default: sprintf (msg, "Unknown error %d", errnum); break;
     972      }
     973  
     974    return msg;
     975  }
     976  #endif