Imported Upstream version 4.4
[platform/upstream/make.git] / 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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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