(face-try-color-list): Treat `underline' as valid.
[bpt/emacs.git] / lib-src / movemail.c
CommitLineData
237e0016
RS
1/* movemail foo bar -- move file foo to file bar,
2 locking file foo the way /bin/mail respects.
39356651 3 Copyright (C) 1986, 1992, 1993, 1994 Free Software Foundation, Inc.
237e0016
RS
4
5This file is part of GNU Emacs.
6
93320c23
JA
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
39356651 9the Free Software Foundation; either version 2, or (at your option)
93320c23
JA
10any later version.
11
237e0016 12GNU Emacs is distributed in the hope that it will be useful,
93320c23
JA
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
237e0016 16
93320c23
JA
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING. If not, write to
19the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
237e0016 20
63cf923d
RS
21/* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
22 cause loss of mail* if you do it on a system that does not normally
23 use flock as its way of interlocking access to inbox files. The
24 setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
25 system's own conventions. It is not a choice that is up to you.
08d0752f
RS
26
27 So, if your system uses lock files rather than flock, then the only way
28 you can get proper operation is to enable movemail to write lockfiles there.
29 This means you must either give that directory access modes
30 that permit everyone to write lockfiles in it, or you must make movemail
31 a setuid or setgid program. */
32
237e0016
RS
33/*
34 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
35 *
36 * Added POP (Post Office Protocol) service. When compiled -DPOP
37 * movemail will accept input filename arguments of the form
38 * "po:username". This will cause movemail to open a connection to
39 * a pop server running on $MAILHOST (environment variable). Movemail
40 * must be setuid to root in order to work with POP.
41 *
42 * New module: popmail.c
43 * Modified routines:
cfa191ff 44 * main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
237e0016
RS
45 * after POP code.
46 * New routines in movemail.c:
47 * get_errmsg - return pointer to system error message
48 *
2e82e3c3
RS
49 * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
50 *
51 * Move all of the POP code into a separate file, "pop.c".
52 * Use strerror instead of get_errmsg.
53 *
237e0016
RS
54 */
55
3b9ee819
RS
56#define NO_SHORTNAMES /* Tell config not to load remap.h */
57#include <../src/config.h>
237e0016
RS
58#include <sys/types.h>
59#include <sys/stat.h>
60#include <sys/file.h>
e2f9d9af 61#include <stdio.h>
237e0016 62#include <errno.h>
8ca83cfd 63#include <../src/syswait.h>
2e82e3c3
RS
64#ifdef MAIL_USE_POP
65#include "pop.h"
66#endif
237e0016 67
91cf09ac
RS
68#ifdef MSDOS
69#undef access
70#endif /* MSDOS */
71
237e0016
RS
72#ifdef USG
73#include <fcntl.h>
74#include <unistd.h>
4ec9a77a
RS
75#ifndef F_OK
76#define F_OK 0
77#define X_OK 1
78#define W_OK 2
79#define R_OK 4
80#endif
237e0016
RS
81#endif /* USG */
82
29beb080
RS
83#ifdef HAVE_UNISTD_H
84#include <unistd.h>
85#endif
86
237e0016
RS
87#ifdef XENIX
88#include <sys/locking.h>
89#endif
90
63cf923d
RS
91#ifdef MAIL_USE_LOCKF
92#define MAIL_USE_SYSTEM_LOCK
93#endif
94
95#ifdef MAIL_USE_FLOCK
96#define MAIL_USE_SYSTEM_LOCK
97#endif
98
4293ba7f
RS
99#ifdef MAIL_USE_MMDF
100extern int lk_open (), lk_close ();
101#endif
102
237e0016
RS
103/* Cancel substitutions made by config.h for Emacs. */
104#undef open
105#undef read
106#undef write
107#undef close
108
e97dd183 109#ifndef errno
237e0016 110extern int errno;
e97dd183 111#endif
e2f9d9af 112char *strerror ();
e2f9d9af
DM
113
114void fatal ();
115void error ();
116void pfatal_with_name ();
117void pfatal_and_delete ();
118char *concat ();
119char *xmalloc ();
120int popmail ();
121int pop_retr ();
122int mbx_write ();
123int mbx_delimit_begin ();
124int mbx_delimit_end ();
237e0016
RS
125
126/* Nonzero means this is name of a lock file to delete on fatal error. */
127char *delete_lockname;
128
e2f9d9af 129int
237e0016
RS
130main (argc, argv)
131 int argc;
132 char **argv;
133{
134 char *inname, *outname;
135 int indesc, outdesc;
237e0016 136 int nread;
8ca83cfd 137 WAITTYPE status;
237e0016 138
63cf923d 139#ifndef MAIL_USE_SYSTEM_LOCK
237e0016
RS
140 struct stat st;
141 long now;
142 int tem;
143 char *lockname, *p;
906ad89d 144 char *tempname;
237e0016 145 int desc;
63cf923d 146#endif /* not MAIL_USE_SYSTEM_LOCK */
237e0016
RS
147
148 delete_lockname = 0;
149
150 if (argc < 3)
e2f9d9af
DM
151 {
152 fprintf (stderr, "Usage: movemail inbox destfile");
153 exit(1);
154 }
237e0016
RS
155
156 inname = argv[1];
157 outname = argv[2];
158
4293ba7f
RS
159#ifdef MAIL_USE_MMDF
160 mmdf_init (argv[0]);
161#endif
162
b1ce62a8 163 /* Check access to output file. */
237e0016
RS
164 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
165 pfatal_with_name (outname);
166
167 /* Also check that outname's directory is writeable to the real uid. */
168 {
91cf09ac 169 char *buf = (char *) xmalloc (strlen (outname) + 1);
e2f9d9af 170 char *p;
237e0016
RS
171 strcpy (buf, outname);
172 p = buf + strlen (buf);
173 while (p > buf && p[-1] != '/')
174 *--p = 0;
175 if (p == buf)
176 *p++ = '.';
177 if (access (buf, W_OK) != 0)
178 pfatal_with_name (buf);
179 free (buf);
180 }
181
182#ifdef MAIL_USE_POP
12a0565a 183 if (!strncmp (inname, "po:", 3))
237e0016
RS
184 {
185 int status; char *user;
186
12a0565a
JB
187 for (user = &inname[strlen (inname) - 1]; user >= inname; user--)
188 if (*user == ':')
189 break;
190
237e0016
RS
191 status = popmail (user, outname);
192 exit (status);
193 }
194
cfa191ff 195 setuid (getuid ());
237e0016
RS
196#endif /* MAIL_USE_POP */
197
b1ce62a8
RS
198 /* Check access to input file. */
199 if (access (inname, R_OK | W_OK) != 0)
200 pfatal_with_name (inname);
201
4293ba7f 202#ifndef MAIL_USE_MMDF
63cf923d 203#ifndef MAIL_USE_SYSTEM_LOCK
237e0016
RS
204 /* Use a lock file named /usr/spool/mail/$USER.lock:
205 If it exists, the mail file is locked. */
06000e3c
RS
206 /* Note: this locking mechanism is *required* by the mailer
207 (on systems which use it) to prevent loss of mail.
208
209 On systems that use a lock file, extracting the mail without locking
210 WILL occasionally cause loss of mail due to timing errors!
211
212 So, if creation of the lock file fails
213 due to access permission on /usr/spool/mail,
214 you simply MUST change the permission
215 and/or make movemail a setgid program
216 so it can create lock files properly.
217
218 You might also wish to verify that your system is one
219 which uses lock files for this purpose. Some systems use other methods.
220
221 If your system uses the `flock' system call for mail locking,
63cf923d 222 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
06000e3c 223 and recompile movemail. If the s- file for your system
63cf923d 224 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
06000e3c
RS
225 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
226
237e0016 227 lockname = concat (inname, ".lock", "");
3f628ebd
RS
228 tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
229 strcpy (tempname, inname);
237e0016
RS
230 p = tempname + strlen (tempname);
231 while (p != tempname && p[-1] != '/')
232 p--;
233 *p = 0;
234 strcpy (p, "EXXXXXX");
235 mktemp (tempname);
cfa191ff 236 unlink (tempname);
237e0016
RS
237
238 while (1)
239 {
240 /* Create the lock file, but not under the lock file name. */
241 /* Give up if cannot do that. */
8ca83cfd 242 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
237e0016 243 if (desc < 0)
8ca83cfd 244 pfatal_with_name ("lock file--see source file lib-src/movemail.c");
237e0016
RS
245 close (desc);
246
247 tem = link (tempname, lockname);
cfa191ff 248 unlink (tempname);
237e0016
RS
249 if (tem >= 0)
250 break;
251 sleep (1);
252
253 /* If lock file is a minute old, unlock it. */
254 if (stat (lockname, &st) >= 0)
255 {
256 now = time (0);
257 if (st.st_ctime < now - 60)
cfa191ff 258 unlink (lockname);
237e0016
RS
259 }
260 }
261
262 delete_lockname = lockname;
63cf923d
RS
263#endif /* not MAIL_USE_SYSTEM_LOCK */
264#endif /* not MAIL_USE_MMDF */
237e0016 265
8ca83cfd
RS
266 if (fork () == 0)
267 {
d5216c20 268 setuid (getuid ());
8ca83cfd 269
63cf923d
RS
270#ifndef MAIL_USE_MMDF
271#ifdef MAIL_USE_SYSTEM_LOCK
8ca83cfd 272 indesc = open (inname, O_RDWR);
63cf923d 273#else /* if not MAIL_USE_SYSTEM_LOCK */
8ca83cfd 274 indesc = open (inname, O_RDONLY);
63cf923d 275#endif /* not MAIL_USE_SYSTEM_LOCK */
8ca83cfd
RS
276#else /* MAIL_USE_MMDF */
277 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
4293ba7f
RS
278#endif /* MAIL_USE_MMDF */
279
8ca83cfd
RS
280 if (indesc < 0)
281 pfatal_with_name (inname);
237e0016 282
cfa191ff 283#if defined (BSD) || defined (XENIX)
8ca83cfd
RS
284 /* In case movemail is setuid to root, make sure the user can
285 read the output file. */
286 /* This is desirable for all systems
287 but I don't want to assume all have the umask system call */
288 umask (umask (0) & 0333);
237e0016 289#endif /* BSD or Xenix */
8ca83cfd
RS
290 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
291 if (outdesc < 0)
292 pfatal_with_name (outname);
63cf923d
RS
293#ifdef MAIL_USE_SYSTEM_LOCK
294#ifdef MAIL_USE_LOCKF
295 if (lockf (indesc, F_LOCK, 0) < 0) pfatal_with_name (inname);
296#else /* not MAIL_USE_LOCKF */
237e0016 297#ifdef XENIX
8ca83cfd 298 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
237e0016 299#else
8ca83cfd 300 if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
237e0016 301#endif
63cf923d
RS
302#endif /* not MAIL_USE_LOCKF */
303#endif /* MAIL_USE_SYSTEM_LOCK */
237e0016 304
08564963 305 {
8ca83cfd
RS
306 char buf[1024];
307
308 while (1)
08564963 309 {
8ca83cfd
RS
310 nread = read (indesc, buf, sizeof buf);
311 if (nread != write (outdesc, buf, nread))
312 {
313 int saved_errno = errno;
314 unlink (outname);
315 errno = saved_errno;
316 pfatal_with_name (outname);
317 }
318 if (nread < sizeof buf)
319 break;
08564963 320 }
08564963 321 }
237e0016
RS
322
323#ifdef BSD
8ca83cfd
RS
324 if (fsync (outdesc) < 0)
325 pfatal_and_delete (outname);
237e0016
RS
326#endif
327
8ca83cfd
RS
328 /* Check to make sure no errors before we zap the inbox. */
329 if (close (outdesc) != 0)
330 pfatal_and_delete (outname);
237e0016 331
63cf923d 332#ifdef MAIL_USE_SYSTEM_LOCK
cfa191ff 333#if defined (STRIDE) || defined (XENIX)
8ca83cfd
RS
334 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
335 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
237e0016 336#else
8ca83cfd 337 ftruncate (indesc, 0L);
237e0016 338#endif /* STRIDE or XENIX */
63cf923d 339#endif /* MAIL_USE_SYSTEM_LOCK */
4293ba7f
RS
340
341#ifdef MAIL_USE_MMDF
8ca83cfd 342 lk_close (indesc, 0, 0, 0);
4293ba7f 343#else
8ca83cfd 344 close (indesc);
4293ba7f 345#endif
237e0016 346
63cf923d 347#ifndef MAIL_USE_SYSTEM_LOCK
e5f7ea68
RM
348 /* Delete the input file; if we can't, at least get rid of its
349 contents. */
e97dd183 350#ifdef MAIL_UNLINK_SPOOL
8ca83cfd
RS
351 /* This is generally bad to do, because it destroys the permissions
352 that were set on the file. Better to just empty the file. */
353 if (unlink (inname) < 0 && errno != ENOENT)
e97dd183 354#endif /* MAIL_UNLINK_SPOOL */
8ca83cfd 355 creat (inname, 0600);
63cf923d 356#endif /* not MAIL_USE_SYSTEM_LOCK */
8ca83cfd
RS
357
358 exit (0);
359 }
360
361 wait (&status);
362 if (!WIFEXITED (status))
363 exit (1);
364 else if (WRETCODE (status) != 0)
365 exit (WRETCODE (status));
366
63cf923d 367#if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
4293ba7f 368 unlink (lockname);
63cf923d 369#endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
e2f9d9af 370 return 0;
237e0016
RS
371}
372\f
373/* Print error message and exit. */
374
e2f9d9af 375void
237e0016
RS
376fatal (s1, s2)
377 char *s1, *s2;
378{
379 if (delete_lockname)
380 unlink (delete_lockname);
381 error (s1, s2);
382 exit (1);
383}
384
385/* Print error message. `s1' is printf control string, `s2' is arg for it. */
386
e2f9d9af 387void
b1ce62a8
RS
388error (s1, s2, s3)
389 char *s1, *s2, *s3;
237e0016 390{
e2f9d9af
DM
391 fprintf (stderr, "movemail: ");
392 fprintf (stderr, s1, s2, s3);
393 fprintf (stderr, "\n");
237e0016
RS
394}
395
e2f9d9af 396void
237e0016
RS
397pfatal_with_name (name)
398 char *name;
399{
e2f9d9af 400 char *s = concat ("", strerror (errno), " for %s");
237e0016
RS
401 fatal (s, name);
402}
403
e2f9d9af 404void
cfa191ff
RS
405pfatal_and_delete (name)
406 char *name;
407{
e2f9d9af 408 char *s = concat ("", strerror (errno), " for %s");
cfa191ff
RS
409 unlink (name);
410 fatal (s, name);
411}
412
237e0016
RS
413/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
414
415char *
416concat (s1, s2, s3)
417 char *s1, *s2, *s3;
418{
419 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
420 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
421
422 strcpy (result, s1);
423 strcpy (result + len1, s2);
424 strcpy (result + len1 + len2, s3);
425 *(result + len1 + len2 + len3) = 0;
426
427 return result;
428}
429
430/* Like malloc but get fatal error if memory is exhausted. */
431
e97dd183 432char *
237e0016 433xmalloc (size)
e97dd183 434 unsigned size;
237e0016 435{
91cf09ac 436 char *result = (char *) malloc (size);
237e0016
RS
437 if (!result)
438 fatal ("virtual memory exhausted", 0);
439 return result;
440}
441\f
442/* This is the guts of the interface to the Post Office Protocol. */
443
444#ifdef MAIL_USE_POP
445
446#include <sys/socket.h>
447#include <netinet/in.h>
448#include <netdb.h>
449#include <stdio.h>
cecf0f21 450#include <pwd.h>
237e0016
RS
451
452#ifdef USG
453#include <fcntl.h>
454/* Cancel substitutions made by config.h for Emacs. */
455#undef open
456#undef read
457#undef write
458#undef close
459#endif /* USG */
460
461#define NOTOK (-1)
462#define OK 0
463#define DONE 1
464
465char *progname;
466FILE *sfi;
467FILE *sfo;
2e82e3c3
RS
468char ibuffer[BUFSIZ];
469char obuffer[BUFSIZ];
237e0016
RS
470char Errmsg[80];
471
b1ce62a8
RS
472popmail (user, outfile)
473 char *user;
474 char *outfile;
237e0016 475{
b1ce62a8 476 int nmsgs, nbytes;
b1ce62a8
RS
477 register int i;
478 int mbfi;
479 FILE *mbf;
2e82e3c3
RS
480 char *getenv ();
481 int mbx_write ();
b32701a7 482 popserver server;
2e82e3c3 483 extern char *strerror ();
237e0016 484
2e82e3c3
RS
485 server = pop_open (0, user, 0, POP_NO_GETPASS);
486 if (! server)
b1ce62a8 487 {
2e82e3c3
RS
488 error (pop_error);
489 return (1);
237e0016
RS
490 }
491
2e82e3c3 492 if (pop_stat (server, &nmsgs, &nbytes))
b1ce62a8 493 {
2e82e3c3
RS
494 error (pop_error);
495 return (1);
237e0016
RS
496 }
497
b1ce62a8
RS
498 if (!nmsgs)
499 {
2e82e3c3
RS
500 pop_close (server);
501 return (0);
b1ce62a8
RS
502 }
503
504 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
505 if (mbfi < 0)
506 {
2e82e3c3
RS
507 pop_close (server);
508 error ("Error in open: %s, %s", strerror (errno), outfile);
509 return (1);
b1ce62a8
RS
510 }
511 fchown (mbfi, getuid (), -1);
512
513 if ((mbf = fdopen (mbfi, "w")) == NULL)
514 {
2e82e3c3
RS
515 pop_close (server);
516 error ("Error in fdopen: %s", strerror (errno));
517 close (mbfi);
518 unlink (outfile);
519 return (1);
b1ce62a8
RS
520 }
521
522 for (i = 1; i <= nmsgs; i++)
523 {
524 mbx_delimit_begin (mbf);
2e82e3c3 525 if (pop_retr (server, i, mbx_write, mbf) != OK)
b1ce62a8 526 {
2e82e3c3 527 error (Errmsg);
b1ce62a8 528 close (mbfi);
2e82e3c3 529 return (1);
237e0016 530 }
b1ce62a8
RS
531 mbx_delimit_end (mbf);
532 fflush (mbf);
2e82e3c3
RS
533 if (ferror (mbf))
534 {
535 error ("Error in fflush: %s", strerror (errno));
536 pop_close (server);
537 close (mbfi);
538 return (1);
539 }
237e0016
RS
540 }
541
2e82e3c3
RS
542 /* On AFS, a call to write only modifies the file in the local
543 * workstation's AFS cache. The changes are not written to the server
544 * until a call to fsync or close is made. Users with AFS home
545 * directories have lost mail when over quota because these checks were
546 * not made in previous versions of movemail. */
547
340ff9de 548#ifdef BSD
cfa191ff
RS
549 if (fsync (mbfi) < 0)
550 {
2e82e3c3
RS
551 error ("Error in fsync: %s", strerror (errno));
552 return (1);
cfa191ff 553 }
340ff9de 554#endif
cfa191ff
RS
555
556 if (close (mbfi) == -1)
557 {
2e82e3c3
RS
558 error ("Error in close: %s", strerror (errno));
559 return (1);
cfa191ff
RS
560 }
561
b1ce62a8
RS
562 for (i = 1; i <= nmsgs; i++)
563 {
2e82e3c3 564 if (pop_delete (server, i))
b1ce62a8 565 {
2e82e3c3
RS
566 error (pop_error);
567 pop_close (server);
568 return (1);
237e0016
RS
569 }
570 }
571
2e82e3c3 572 if (pop_quit (server))
b1ce62a8 573 {
2e82e3c3
RS
574 error (pop_error);
575 return (1);
237e0016 576 }
237e0016 577
2e82e3c3 578 return (0);
237e0016
RS
579}
580
2e82e3c3 581pop_retr (server, msgno, action, arg)
b32701a7 582 popserver server;
b1ce62a8 583 int (*action)();
237e0016 584{
2e82e3c3
RS
585 extern char *strerror ();
586 char *line;
587 int ret;
237e0016 588
2e82e3c3 589 if (pop_retrieve_first (server, msgno, &line))
b1ce62a8 590 {
2e82e3c3
RS
591 strncpy (Errmsg, pop_error, sizeof (Errmsg));
592 Errmsg[sizeof (Errmsg)-1] = '\0';
593 return (NOTOK);
237e0016
RS
594 }
595
2e82e3c3 596 while (! (ret = pop_retrieve_next (server, &line)))
b1ce62a8 597 {
2e82e3c3
RS
598 if (! line)
599 break;
600
601 if ((*action)(line, arg) != OK)
b1ce62a8 602 {
2e82e3c3
RS
603 strcpy (Errmsg, strerror (errno));
604 pop_close (server);
605 return (NOTOK);
237e0016
RS
606 }
607 }
237e0016 608
2e82e3c3 609 if (ret)
b1ce62a8 610 {
2e82e3c3
RS
611 strncpy (Errmsg, pop_error, sizeof (Errmsg));
612 Errmsg[sizeof (Errmsg)-1] = '\0';
613 return (NOTOK);
237e0016
RS
614 }
615
2e82e3c3 616 return (OK);
237e0016
RS
617}
618
2e82e3c3
RS
619/* Do this as a macro instead of using strcmp to save on execution time. */
620#define IS_FROM_LINE(a) ((a[0] == 'F') \
621 && (a[1] == 'r') \
622 && (a[2] == 'o') \
623 && (a[3] == 'm') \
624 && (a[4] == ' '))
237e0016 625
2e82e3c3 626int
b1ce62a8
RS
627mbx_write (line, mbf)
628 char *line;
629 FILE *mbf;
237e0016 630{
2e82e3c3
RS
631 if (IS_FROM_LINE (line))
632 {
633 if (fputc ('>', mbf) == EOF)
634 return (NOTOK);
635 }
636 if (fputs (line, mbf) == EOF)
637 return (NOTOK);
638 if (fputc (0x0a, mbf) == EOF)
639 return (NOTOK);
640 return (OK);
237e0016
RS
641}
642
2e82e3c3 643int
b1ce62a8
RS
644mbx_delimit_begin (mbf)
645 FILE *mbf;
237e0016 646{
2e82e3c3
RS
647 if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
648 return (NOTOK);
649 return (OK);
237e0016
RS
650}
651
b1ce62a8
RS
652mbx_delimit_end (mbf)
653 FILE *mbf;
237e0016 654{
2e82e3c3
RS
655 if (putc ('\037', mbf) == EOF)
656 return (NOTOK);
657 return (OK);
237e0016
RS
658}
659
660#endif /* MAIL_USE_POP */
e5f7ea68
RM
661\f
662#ifndef HAVE_STRERROR
663char *
664strerror (errnum)
665 int errnum;
666{
667 extern char *sys_errlist[];
668 extern int sys_nerr;
669
670 if (errnum >= 0 && errnum < sys_nerr)
671 return sys_errlist[errnum];
672 return (char *) "Unknown error";
673}
674
675#endif /* ! HAVE_STRERROR */