This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / usr.sbin / sendmail / src / util.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1983 Eric P. Allman
78ed81a3 3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
15637ed4
RG
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
78ed81a3 36static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 7/13/93";
15637ed4
RG
37#endif /* not lint */
38
15637ed4 39# include "sendmail.h"
78ed81a3 40# include <sysexits.h>
41\f/*
15637ed4
RG
42** STRIPQUOTES -- Strip quotes & quote bits from a string.
43**
44** Runs through a string and strips off unquoted quote
45** characters and quote bits. This is done in place.
46**
47** Parameters:
48** s -- the string to strip.
15637ed4
RG
49**
50** Returns:
51** none.
52**
53** Side Effects:
54** none.
55**
56** Called By:
57** deliver
58*/
59
78ed81a3 60stripquotes(s)
15637ed4 61 char *s;
15637ed4
RG
62{
63 register char *p;
64 register char *q;
65 register char c;
66
67 if (s == NULL)
68 return;
69
78ed81a3 70 p = q = s;
71 do
15637ed4 72 {
78ed81a3 73 c = *p++;
74 if (c == '\\')
75 c = *p++;
76 else if (c == '"')
77 continue;
78 *q++ = c;
79 } while (c != '\0');
15637ed4
RG
80}
81\f/*
82** XALLOC -- Allocate memory and bitch wildly on failure.
83**
84** THIS IS A CLUDGE. This should be made to give a proper
85** error -- but after all, what can we do?
86**
87** Parameters:
88** sz -- size of area to allocate.
89**
90** Returns:
91** pointer to data region.
92**
93** Side Effects:
94** Memory is allocated.
95*/
96
97char *
98xalloc(sz)
99 register int sz;
100{
101 register char *p;
15637ed4
RG
102
103 p = malloc((unsigned) sz);
104 if (p == NULL)
105 {
106 syserr("Out of memory!!");
107 abort();
108 /* exit(EX_UNAVAILABLE); */
109 }
110 return (p);
111}
112\f/*
113** COPYPLIST -- copy list of pointers.
114**
115** This routine is the equivalent of newstr for lists of
116** pointers.
117**
118** Parameters:
119** list -- list of pointers to copy.
120** Must be NULL terminated.
121** copycont -- if TRUE, copy the contents of the vector
122** (which must be a string) also.
123**
124** Returns:
125** a copy of 'list'.
126**
127** Side Effects:
128** none.
129*/
130
131char **
132copyplist(list, copycont)
133 char **list;
134 bool copycont;
135{
136 register char **vp;
137 register char **newvp;
138
139 for (vp = list; *vp != NULL; vp++)
140 continue;
141
142 vp++;
143
144 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
145 bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
146
147 if (copycont)
148 {
149 for (vp = newvp; *vp != NULL; vp++)
150 *vp = newstr(*vp);
151 }
152
153 return (newvp);
154}
155\f/*
78ed81a3 156** COPYQUEUE -- copy address queue.
157**
158** This routine is the equivalent of newstr for address queues
159** addresses marked with QDONTSEND aren't copied
160**
161** Parameters:
162** addr -- list of address structures to copy.
163**
164** Returns:
165** a copy of 'addr'.
166**
167** Side Effects:
168** none.
169*/
170
171ADDRESS *
172copyqueue(addr)
173 ADDRESS *addr;
174{
175 register ADDRESS *newaddr;
176 ADDRESS *ret;
177 register ADDRESS **tail = &ret;
178
179 while (addr != NULL)
180 {
181 if (!bitset(QDONTSEND, addr->q_flags))
182 {
183 newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
184 STRUCTCOPY(*addr, *newaddr);
185 *tail = newaddr;
186 tail = &newaddr->q_next;
187 }
188 addr = addr->q_next;
189 }
190 *tail = NULL;
191
192 return ret;
193}
194\f/*
15637ed4
RG
195** PRINTAV -- print argument vector.
196**
197** Parameters:
198** av -- argument vector.
199**
200** Returns:
201** none.
202**
203** Side Effects:
204** prints av.
205*/
206
207printav(av)
208 register char **av;
209{
210 while (*av != NULL)
211 {
212 if (tTd(0, 44))
213 printf("\n\t%08x=", *av);
214 else
215 (void) putchar(' ');
216 xputs(*av++);
217 }
218 (void) putchar('\n');
219}
220\f/*
221** LOWER -- turn letter into lower case.
222**
223** Parameters:
224** c -- character to turn into lower case.
225**
226** Returns:
227** c, in lower case.
228**
229** Side Effects:
230** none.
231*/
232
233char
234lower(c)
235 register char c;
236{
78ed81a3 237 return((isascii(c) && isupper(c)) ? tolower(c) : c);
15637ed4
RG
238}
239\f/*
240** XPUTS -- put string doing control escapes.
241**
242** Parameters:
243** s -- string to put.
244**
245** Returns:
246** none.
247**
248** Side Effects:
249** output to stdout
250*/
251
252xputs(s)
253 register char *s;
254{
78ed81a3 255 register int c;
256 register struct metamac *mp;
257 extern struct metamac MetaMacros[];
15637ed4
RG
258
259 if (s == NULL)
260 {
261 printf("<null>");
262 return;
263 }
78ed81a3 264 while ((c = (*s++ & 0377)) != '\0')
15637ed4
RG
265 {
266 if (!isascii(c))
267 {
78ed81a3 268 if (c == MATCHREPL || c == MACROEXPAND)
269 {
270 putchar('$');
271 continue;
272 }
273 for (mp = MetaMacros; mp->metaname != '\0'; mp++)
274 {
275 if ((mp->metaval & 0377) == c)
276 {
277 printf("$%c", mp->metaname);
278 break;
279 }
280 }
281 if (mp->metaname != '\0')
282 continue;
15637ed4
RG
283 (void) putchar('\\');
284 c &= 0177;
285 }
78ed81a3 286 if (isprint(c))
287 {
288 putchar(c);
289 continue;
290 }
291
292 /* wasn't a meta-macro -- find another way to print it */
293 switch (c)
15637ed4 294 {
78ed81a3 295 case '\0':
296 continue;
297
298 case '\n':
299 c = 'n';
300 break;
301
302 case '\r':
303 c = 'r';
304 break;
305
306 case '\t':
307 c = 't';
308 break;
309
310 default:
15637ed4 311 (void) putchar('^');
78ed81a3 312 (void) putchar(c ^ 0100);
313 continue;
15637ed4 314 }
15637ed4 315 }
15637ed4
RG
316 (void) fflush(stdout);
317}
318\f/*
319** MAKELOWER -- Translate a line into lower case
320**
321** Parameters:
322** p -- the string to translate. If NULL, return is
323** immediate.
324**
325** Returns:
326** none.
327**
328** Side Effects:
329** String pointed to by p is translated to lower case.
330**
331** Called By:
332** parse
333*/
334
335makelower(p)
336 register char *p;
337{
338 register char c;
339
340 if (p == NULL)
341 return;
342 for (; (c = *p) != '\0'; p++)
343 if (isascii(c) && isupper(c))
344 *p = tolower(c);
345}
346\f/*
347** BUILDFNAME -- build full name from gecos style entry.
348**
349** This routine interprets the strange entry that would appear
350** in the GECOS field of the password file.
351**
352** Parameters:
353** p -- name to build.
354** login -- the login name of this user (for &).
355** buf -- place to put the result.
356**
357** Returns:
358** none.
359**
360** Side Effects:
361** none.
362*/
363
78ed81a3 364buildfname(gecos, login, buf)
365 register char *gecos;
15637ed4
RG
366 char *login;
367 char *buf;
368{
78ed81a3 369 register char *p;
15637ed4 370 register char *bp = buf;
78ed81a3 371 int l;
372
373 if (*gecos == '*')
374 gecos++;
15637ed4 375
78ed81a3 376 /* find length of final string */
377 l = 0;
378 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
379 {
380 if (*p == '&')
381 l += strlen(login);
382 else
383 l++;
384 }
385
386 /* now fill in buf */
387 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
15637ed4
RG
388 {
389 if (*p == '&')
390 {
391 (void) strcpy(bp, login);
392 *bp = toupper(*bp);
393 while (*bp != '\0')
394 bp++;
15637ed4
RG
395 }
396 else
78ed81a3 397 *bp++ = *p;
15637ed4
RG
398 }
399 *bp = '\0';
400}
401\f/*
402** SAFEFILE -- return true if a file exists and is safe for a user.
403**
404** Parameters:
405** fn -- filename to check.
406** uid -- uid to compare against.
78ed81a3 407** mustown -- to be safe, this uid must own the file.
15637ed4
RG
408** mode -- mode bits that must match.
409**
410** Returns:
78ed81a3 411** 0 if fn exists, is owned by uid, and matches mode.
412** An errno otherwise. The actual errno is cleared.
15637ed4
RG
413**
414** Side Effects:
415** none.
416*/
417
78ed81a3 418#ifndef S_IXOTH
419# define S_IXOTH (S_IEXEC >> 6)
420#endif
421
422#ifndef S_IXUSR
423# define S_IXUSR (S_IEXEC)
424#endif
425
426int
427safefile(fn, uid, mustown, mode)
15637ed4 428 char *fn;
78ed81a3 429 uid_t uid;
430 bool mustown;
15637ed4
RG
431 int mode;
432{
78ed81a3 433 register char *p;
15637ed4
RG
434 struct stat stbuf;
435
78ed81a3 436 if (tTd(54, 4))
437 printf("safefile(%s, %d, %d, %o): ", fn, uid, mustown, mode);
15637ed4 438 errno = 0;
78ed81a3 439
440 for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
441 {
442 *p = '\0';
443 if (stat(fn, &stbuf) < 0 ||
444 !bitset(stbuf.st_uid == uid ? S_IXUSR : S_IXOTH,
445 stbuf.st_mode))
446 {
447 int ret = errno;
448
449 if (ret == 0)
450 ret = EACCES;
451 if (tTd(54, 4))
452 printf("[dir %s] %s\n", fn, errstring(ret));
453 *p = '/';
454 return ret;
455 }
456 }
457
458 if (stat(fn, &stbuf) < 0)
459 {
460 int ret = errno;
461
462 if (tTd(54, 4))
463 printf("%s\n", errstring(ret));
464
465 errno = 0;
466 return ret;
467 }
468 if (stbuf.st_uid != uid || uid == 0 || !mustown)
469 mode >>= 6;
470 if (tTd(54, 4))
471 printf("[uid %d, stat %o] ", stbuf.st_uid, stbuf.st_mode);
472 if ((stbuf.st_uid == uid || uid == 0 || !mustown) &&
473 (stbuf.st_mode & mode) == mode)
474 {
475 if (tTd(54, 4))
476 printf("OK\n");
477 return 0;
478 }
479 if (tTd(54, 4))
480 printf("EACCES\n");
481 return EACCES;
15637ed4
RG
482}
483\f/*
484** FIXCRLF -- fix <CR><LF> in line.
485**
486** Looks for the <CR><LF> combination and turns it into the
487** UNIX canonical <NL> character. It only takes one line,
488** i.e., it is assumed that the first <NL> found is the end
489** of the line.
490**
491** Parameters:
492** line -- the line to fix.
493** stripnl -- if true, strip the newline also.
494**
495** Returns:
496** none.
497**
498** Side Effects:
499** line is changed in place.
500*/
501
502fixcrlf(line, stripnl)
503 char *line;
504 bool stripnl;
505{
506 register char *p;
507
78ed81a3 508 p = strchr(line, '\n');
15637ed4
RG
509 if (p == NULL)
510 return;
511 if (p > line && p[-1] == '\r')
512 p--;
513 if (!stripnl)
514 *p++ = '\n';
515 *p = '\0';
516}
517\f/*
518** DFOPEN -- determined file open
519**
520** This routine has the semantics of fopen, except that it will
521** keep trying a few times to make this happen. The idea is that
522** on very loaded systems, we may run out of resources (inodes,
523** whatever), so this tries to get around it.
524*/
525
78ed81a3 526#ifndef O_ACCMODE
527# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
528#endif
529
530struct omodes
531{
532 int mask;
533 int mode;
534 char *farg;
535} OpenModes[] =
536{
537 O_ACCMODE, O_RDONLY, "r",
538 O_ACCMODE|O_APPEND, O_WRONLY, "w",
539 O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a",
540 O_TRUNC, 0, "w+",
541 O_APPEND, O_APPEND, "a+",
542 0, 0, "r+",
543};
544
15637ed4 545FILE *
78ed81a3 546dfopen(filename, omode, cmode)
15637ed4 547 char *filename;
78ed81a3 548 int omode;
549 int cmode;
15637ed4
RG
550{
551 register int tries;
78ed81a3 552 int fd;
553 register struct omodes *om;
554 struct stat st;
555
556 for (om = OpenModes; om->mask != 0; om++)
557 if ((omode & om->mask) == om->mode)
558 break;
15637ed4
RG
559
560 for (tries = 0; tries < 10; tries++)
561 {
562 sleep((unsigned) (10 * tries));
563 errno = 0;
78ed81a3 564 fd = open(filename, omode, cmode);
565 if (fd >= 0)
15637ed4
RG
566 break;
567 if (errno != ENFILE && errno != EINTR)
568 break;
569 }
78ed81a3 570 if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
571 {
572 int locktype;
573
574 /* lock the file to avoid accidental conflicts */
575 if ((omode & O_ACCMODE) != O_RDONLY)
576 locktype = LOCK_EX;
577 else
578 locktype = LOCK_SH;
579 (void) lockfile(fd, filename, locktype);
580 errno = 0;
581 }
582 if (fd < 0)
583 return NULL;
584 else
585 return fdopen(fd, om->farg);
15637ed4
RG
586}
587\f/*
588** PUTLINE -- put a line like fputs obeying SMTP conventions
589**
590** This routine always guarantees outputing a newline (or CRLF,
591** as appropriate) at the end of the string.
592**
593** Parameters:
594** l -- line to put.
595** fp -- file to put it onto.
596** m -- the mailer used to control output.
597**
598** Returns:
599** none
600**
601** Side Effects:
602** output of l to fp.
603*/
604
15637ed4
RG
605putline(l, fp, m)
606 register char *l;
607 FILE *fp;
608 MAILER *m;
609{
610 register char *p;
611 register char svchar;
612
613 /* strip out 0200 bits -- these can look like TELNET protocol */
78ed81a3 614 if (bitnset(M_7BITS, m->m_flags))
15637ed4 615 {
78ed81a3 616 for (p = l; (svchar = *p) != '\0'; ++p)
617 if (bitset(0200, svchar))
15637ed4
RG
618 *p = svchar &~ 0200;
619 }
620
621 do
622 {
623 /* find the end of the line */
78ed81a3 624 p = strchr(l, '\n');
15637ed4
RG
625 if (p == NULL)
626 p = &l[strlen(l)];
627
78ed81a3 628 if (TrafficLogFile != NULL)
629 fprintf(TrafficLogFile, "%05d >>> ", getpid());
630
15637ed4 631 /* check for line overflow */
78ed81a3 632 while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
15637ed4 633 {
78ed81a3 634 register char *q = &l[m->m_linelimit - 1];
15637ed4
RG
635
636 svchar = *q;
637 *q = '\0';
638 if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
78ed81a3 639 {
15637ed4 640 (void) putc('.', fp);
78ed81a3 641 if (TrafficLogFile != NULL)
642 (void) putc('.', TrafficLogFile);
643 }
15637ed4
RG
644 fputs(l, fp);
645 (void) putc('!', fp);
646 fputs(m->m_eol, fp);
78ed81a3 647 if (TrafficLogFile != NULL)
648 fprintf(TrafficLogFile, "%s!\n%05d >>> ",
649 l, getpid());
15637ed4
RG
650 *q = svchar;
651 l = q;
652 }
653
654 /* output last part */
655 if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
78ed81a3 656 {
15637ed4 657 (void) putc('.', fp);
78ed81a3 658 if (TrafficLogFile != NULL)
659 (void) putc('.', TrafficLogFile);
660 }
661 if (TrafficLogFile != NULL)
662 fprintf(TrafficLogFile, "%.*s\n", p - l, l);
15637ed4
RG
663 for ( ; l < p; ++l)
664 (void) putc(*l, fp);
665 fputs(m->m_eol, fp);
666 if (*l == '\n')
667 ++l;
668 } while (l[0] != '\0');
669}
670\f/*
671** XUNLINK -- unlink a file, doing logging as appropriate.
672**
673** Parameters:
674** f -- name of file to unlink.
675**
676** Returns:
677** none.
678**
679** Side Effects:
680** f is unlinked.
681*/
682
683xunlink(f)
684 char *f;
685{
686 register int i;
687
688# ifdef LOG
78ed81a3 689 if (LogLevel > 98)
690 syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
691# endif /* LOG */
15637ed4
RG
692
693 i = unlink(f);
694# ifdef LOG
78ed81a3 695 if (i < 0 && LogLevel > 97)
15637ed4 696 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
78ed81a3 697# endif /* LOG */
698}
699\f/*
700** XFCLOSE -- close a file, doing logging as appropriate.
701**
702** Parameters:
703** fp -- file pointer for the file to close
704** a, b -- miscellaneous crud to print for debugging
705**
706** Returns:
707** none.
708**
709** Side Effects:
710** fp is closed.
711*/
712
713xfclose(fp, a, b)
714 FILE *fp;
715 char *a, *b;
716{
717 if (tTd(53, 99))
718 printf("xfclose(%x) %s %s\n", fp, a, b);
719 if (fclose(fp) < 0 && tTd(53, 99))
720 printf("xfclose FAILURE: %s\n", errstring(errno));
15637ed4
RG
721}
722\f/*
723** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
724**
725** Parameters:
726** buf -- place to put the input line.
727** siz -- size of buf.
728** fp -- file to read from.
78ed81a3 729** timeout -- the timeout before error occurs.
730** during -- what we are trying to read (for error messages).
15637ed4
RG
731**
732** Returns:
733** NULL on error (including timeout). This will also leave
734** buf containing a null string.
735** buf otherwise.
736**
737** Side Effects:
738** none.
739*/
740
741static jmp_buf CtxReadTimeout;
742
743char *
78ed81a3 744sfgets(buf, siz, fp, timeout, during)
15637ed4
RG
745 char *buf;
746 int siz;
747 FILE *fp;
78ed81a3 748 time_t timeout;
749 char *during;
15637ed4
RG
750{
751 register EVENT *ev = NULL;
752 register char *p;
753 static int readtimeout();
754
755 /* set the timeout */
78ed81a3 756 if (timeout != 0)
15637ed4
RG
757 {
758 if (setjmp(CtxReadTimeout) != 0)
759 {
760# ifdef LOG
761 syslog(LOG_NOTICE,
78ed81a3 762 "timeout waiting for input from %s during %s\n",
763 CurHostName? CurHostName: "local", during);
15637ed4
RG
764# endif
765 errno = 0;
78ed81a3 766 usrerr("451 timeout waiting for input during %s",
767 during);
15637ed4 768 buf[0] = '\0';
78ed81a3 769#ifdef XDEBUG
770 checkfd012(during);
771#endif
15637ed4
RG
772 return (NULL);
773 }
78ed81a3 774 ev = setevent(timeout, readtimeout, 0);
15637ed4
RG
775 }
776
777 /* try to read */
778 p = NULL;
779 while (p == NULL && !feof(fp) && !ferror(fp))
780 {
781 errno = 0;
782 p = fgets(buf, siz, fp);
783 if (errno == EINTR)
784 clearerr(fp);
785 }
786
787 /* clear the event if it has not sprung */
788 clrevent(ev);
789
790 /* clean up the books and exit */
791 LineNumber++;
792 if (p == NULL)
793 {
794 buf[0] = '\0';
78ed81a3 795 if (TrafficLogFile != NULL)
796 fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
15637ed4
RG
797 return (NULL);
798 }
78ed81a3 799 if (TrafficLogFile != NULL)
800 fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
801 if (SevenBit)
802 for (p = buf; *p != '\0'; p++)
803 *p &= ~0200;
15637ed4
RG
804 return (buf);
805}
806
807static
808readtimeout()
809{
810 longjmp(CtxReadTimeout, 1);
811}
812\f/*
813** FGETFOLDED -- like fgets, but know about folded lines.
814**
815** Parameters:
816** buf -- place to put result.
817** n -- bytes available.
818** f -- file to read from.
819**
820** Returns:
78ed81a3 821** input line(s) on success, NULL on error or EOF.
822** This will normally be buf -- unless the line is too
823** long, when it will be xalloc()ed.
15637ed4
RG
824**
825** Side Effects:
826** buf gets lines from f, with continuation lines (lines
827** with leading white space) appended. CRLF's are mapped
828** into single newlines. Any trailing NL is stripped.
829*/
830
831char *
832fgetfolded(buf, n, f)
833 char *buf;
834 register int n;
835 FILE *f;
836{
837 register char *p = buf;
78ed81a3 838 char *bp = buf;
15637ed4
RG
839 register int i;
840
841 n--;
842 while ((i = getc(f)) != EOF)
843 {
844 if (i == '\r')
845 {
846 i = getc(f);
847 if (i != '\n')
848 {
849 if (i != EOF)
850 (void) ungetc(i, f);
851 i = '\r';
852 }
853 }
78ed81a3 854 if (--n <= 0)
855 {
856 /* allocate new space */
857 char *nbp;
858 int nn;
859
860 nn = (p - bp);
861 if (nn < MEMCHUNKSIZE)
862 nn *= 2;
863 else
864 nn += MEMCHUNKSIZE;
865 nbp = xalloc(nn);
866 bcopy(bp, nbp, p - bp);
867 p = &nbp[p - bp];
868 if (bp != buf)
869 free(bp);
870 bp = nbp;
871 n = nn - (p - bp);
872 }
873 *p++ = i;
15637ed4
RG
874 if (i == '\n')
875 {
876 LineNumber++;
877 i = getc(f);
878 if (i != EOF)
879 (void) ungetc(i, f);
880 if (i != ' ' && i != '\t')
78ed81a3 881 break;
15637ed4
RG
882 }
883 }
78ed81a3 884 if (p == bp)
885 return (NULL);
886 *--p = '\0';
887 return (bp);
15637ed4
RG
888}
889\f/*
890** CURTIME -- return current time.
891**
892** Parameters:
893** none.
894**
895** Returns:
896** the current time.
897**
898** Side Effects:
899** none.
900*/
901
902time_t
903curtime()
904{
905 auto time_t t;
906
907 (void) time(&t);
908 return (t);
909}
910\f/*
911** ATOBOOL -- convert a string representation to boolean.
912**
913** Defaults to "TRUE"
914**
915** Parameters:
916** s -- string to convert. Takes "tTyY" as true,
917** others as false.
918**
919** Returns:
920** A boolean representation of the string.
921**
922** Side Effects:
923** none.
924*/
925
926bool
927atobool(s)
928 register char *s;
929{
78ed81a3 930 if (*s == '\0' || strchr("tTyY", *s) != NULL)
15637ed4
RG
931 return (TRUE);
932 return (FALSE);
933}
934\f/*
935** ATOOCT -- convert a string representation to octal.
936**
937** Parameters:
938** s -- string to convert.
939**
940** Returns:
941** An integer representing the string interpreted as an
942** octal number.
943**
944** Side Effects:
945** none.
946*/
947
948atooct(s)
949 register char *s;
950{
951 register int i = 0;
952
953 while (*s >= '0' && *s <= '7')
954 i = (i << 3) | (*s++ - '0');
955 return (i);
956}
957\f/*
958** WAITFOR -- wait for a particular process id.
959**
960** Parameters:
961** pid -- process id to wait for.
962**
963** Returns:
964** status of pid.
965** -1 if pid never shows up.
966**
967** Side Effects:
968** none.
969*/
970
971waitfor(pid)
972 int pid;
973{
974 auto int st;
975 int i;
976
977 do
978 {
979 errno = 0;
980 i = wait(&st);
981 } while ((i >= 0 || errno == EINTR) && i != pid);
982 if (i < 0)
983 st = -1;
984 return (st);
985}
986\f/*
987** BITINTERSECT -- tell if two bitmaps intersect
988**
989** Parameters:
990** a, b -- the bitmaps in question
991**
992** Returns:
993** TRUE if they have a non-null intersection
994** FALSE otherwise
995**
996** Side Effects:
997** none.
998*/
999
1000bool
1001bitintersect(a, b)
1002 BITMAP a;
1003 BITMAP b;
1004{
1005 int i;
1006
1007 for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1008 if ((a[i] & b[i]) != 0)
1009 return (TRUE);
1010 return (FALSE);
1011}
1012\f/*
1013** BITZEROP -- tell if a bitmap is all zero
1014**
1015** Parameters:
1016** map -- the bit map to check
1017**
1018** Returns:
1019** TRUE if map is all zero.
1020** FALSE if there are any bits set in map.
1021**
1022** Side Effects:
1023** none.
1024*/
1025
1026bool
1027bitzerop(map)
1028 BITMAP map;
1029{
1030 int i;
1031
1032 for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1033 if (map[i] != 0)
1034 return (FALSE);
1035 return (TRUE);
1036}
78ed81a3 1037\f/*
1038** STRCONTAINEDIN -- tell if one string is contained in another
1039**
1040** Parameters:
1041** a -- possible substring.
1042** b -- possible superstring.
1043**
1044** Returns:
1045** TRUE if a is contained in b.
1046** FALSE otherwise.
1047*/
1048
1049bool
1050strcontainedin(a, b)
1051 register char *a;
1052 register char *b;
1053{
1054 int l;
1055
1056 l = strlen(a);
1057 for (;;)
1058 {
1059 b = strchr(b, a[0]);
1060 if (b == NULL)
1061 return FALSE;
1062 if (strncmp(a, b, l) == 0)
1063 return TRUE;
1064 b++;
1065 }
1066}
1067\f/*
1068** CHECKFD012 -- check low numbered file descriptors
1069**
1070** File descriptors 0, 1, and 2 should be open at all times.
1071** This routine verifies that, and fixes it if not true.
1072**
1073** Parameters:
1074** where -- a tag printed if the assertion failed
1075**
1076** Returns:
1077** none
1078*/
1079
1080checkfd012(where)
1081 char *where;
1082{
1083#ifdef XDEBUG
1084 register int i;
1085 struct stat stbuf;
1086
1087 for (i = 0; i < 3; i++)
1088 {
1089 if (fstat(i, &stbuf) < 0)
1090 {
1091 /* oops.... */
1092 int fd;
1093
1094 syserr("%s: fd %d not open", where, i);
1095 fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
1096 if (fd != i)
1097 {
1098 (void) dup2(fd, i);
1099 (void) close(fd);
1100 }
1101 }
1102 }
1103#endif XDEBUG
1104}