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