Annotation of src/usr.bin/mail/aux.c, Revision 1.2
1.2 ! deraadt 1: /* $OpenBSD: aux.c,v 1.4 1996/06/08 19:48:10 christos Exp $ */
! 2: /* $NetBSD: aux.c,v 1.4 1996/06/08 19:48:10 christos Exp $ */
! 3:
1.1 deraadt 4: /*
5: * Copyright (c) 1980, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
1.2 ! deraadt 38: #if 0
! 39: static char sccsid[] = "@(#)aux.c 8.1 (Berkeley) 6/6/93";
! 40: #else
! 41: static char rcsid[] = "$OpenBSD: aux.c,v 1.4 1996/06/08 19:48:10 christos Exp $";
! 42: #endif
1.1 deraadt 43: #endif /* not lint */
44:
45: #include "rcv.h"
46: #include "extern.h"
47:
48: /*
49: * Mail -- a mail program
50: *
51: * Auxiliary functions.
52: */
1.2 ! deraadt 53: static char *save2str __P((char *, char *));
1.1 deraadt 54:
55: /*
56: * Return a pointer to a dynamic copy of the argument.
57: */
58: char *
59: savestr(str)
60: char *str;
61: {
62: char *new;
63: int size = strlen(str) + 1;
64:
65: if ((new = salloc(size)) != NOSTR)
66: bcopy(str, new, size);
67: return new;
68: }
69:
70: /*
71: * Make a copy of new argument incorporating old one.
72: */
1.2 ! deraadt 73: static char *
1.1 deraadt 74: save2str(str, old)
75: char *str, *old;
76: {
77: char *new;
78: int newsize = strlen(str) + 1;
79: int oldsize = old ? strlen(old) + 1 : 0;
80:
81: if ((new = salloc(newsize + oldsize)) != NOSTR) {
82: if (oldsize) {
83: bcopy(old, new, oldsize);
84: new[oldsize - 1] = ' ';
85: }
86: bcopy(str, new + oldsize, newsize);
87: }
88: return new;
89: }
90:
91: /*
92: * Announce a fatal error and die.
93: */
94: #if __STDC__
95: #include <stdarg.h>
96: #else
97: #include <varargs.h>
98: #endif
99:
100: void
101: #if __STDC__
102: panic(const char *fmt, ...)
103: #else
104: panic(fmt, va_alist)
105: char *fmt;
106: va_dcl
107: #endif
108: {
109: va_list ap;
110: #if __STDC__
111: va_start(ap, fmt);
112: #else
113: va_start(ap);
114: #endif
115: (void)fprintf(stderr, "panic: ");
116: vfprintf(stderr, fmt, ap);
117: va_end(ap);
118: (void)fprintf(stderr, "\n");
119: fflush(stderr);
120: abort();
121: }
122:
123: /*
124: * Touch the named message by setting its MTOUCH flag.
125: * Touched messages have the effect of not being sent
126: * back to the system mailbox on exit.
127: */
128: void
129: touch(mp)
130: register struct message *mp;
131: {
132:
133: mp->m_flag |= MTOUCH;
134: if ((mp->m_flag & MREAD) == 0)
135: mp->m_flag |= MREAD|MSTATUS;
136: }
137:
138: /*
139: * Test to see if the passed file name is a directory.
140: * Return true if it is.
141: */
142: int
143: isdir(name)
144: char name[];
145: {
146: struct stat sbuf;
147:
148: if (stat(name, &sbuf) < 0)
149: return(0);
150: return((sbuf.st_mode & S_IFMT) == S_IFDIR);
151: }
152:
153: /*
154: * Count the number of arguments in the given string raw list.
155: */
156: int
157: argcount(argv)
158: char **argv;
159: {
160: register char **ap;
161:
162: for (ap = argv; *ap++ != NOSTR;)
163: ;
164: return ap - argv - 1;
165: }
166:
167: /*
168: * Return the desired header line from the passed message
169: * pointer (or NOSTR if the desired header field is not available).
170: */
171: char *
172: hfield(field, mp)
173: char field[];
174: struct message *mp;
175: {
176: register FILE *ibuf;
177: char linebuf[LINESIZE];
178: register int lc;
179: register char *hfield;
180: char *colon, *oldhfield = NOSTR;
181:
182: ibuf = setinput(mp);
183: if ((lc = mp->m_lines - 1) < 0)
184: return NOSTR;
185: if (readline(ibuf, linebuf, LINESIZE) < 0)
186: return NOSTR;
187: while (lc > 0) {
188: if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
189: return oldhfield;
1.2 ! deraadt 190: if ((hfield = ishfield(linebuf, colon, field)) != NULL)
1.1 deraadt 191: oldhfield = save2str(hfield, oldhfield);
192: }
193: return oldhfield;
194: }
195:
196: /*
197: * Return the next header field found in the given message.
198: * Return >= 0 if something found, < 0 elsewise.
199: * "colon" is set to point to the colon in the header.
200: * Must deal with \ continuations & other such fraud.
201: */
202: int
203: gethfield(f, linebuf, rem, colon)
204: register FILE *f;
205: char linebuf[];
206: register int rem;
207: char **colon;
208: {
209: char line2[LINESIZE];
210: register char *cp, *cp2;
211: register int c;
212:
213: for (;;) {
214: if (--rem < 0)
215: return -1;
216: if ((c = readline(f, linebuf, LINESIZE)) <= 0)
217: return -1;
218: for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':';
219: cp++)
220: ;
221: if (*cp != ':' || cp == linebuf)
222: continue;
223: /*
224: * I guess we got a headline.
225: * Handle wraparounding
226: */
227: *colon = cp;
228: cp = linebuf + c;
229: for (;;) {
230: while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
231: ;
232: cp++;
233: if (rem <= 0)
234: break;
235: ungetc(c = getc(f), f);
236: if (c != ' ' && c != '\t')
237: break;
238: if ((c = readline(f, line2, LINESIZE)) < 0)
239: break;
240: rem--;
241: for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
242: ;
243: c -= cp2 - line2;
244: if (cp + c >= linebuf + LINESIZE - 2)
245: break;
246: *cp++ = ' ';
247: bcopy(cp2, cp, c);
248: cp += c;
249: }
250: *cp = 0;
251: return rem;
252: }
253: /* NOTREACHED */
254: }
255:
256: /*
257: * Check whether the passed line is a header line of
258: * the desired breed. Return the field body, or 0.
259: */
260:
261: char*
262: ishfield(linebuf, colon, field)
263: char linebuf[], field[];
264: char *colon;
265: {
266: register char *cp = colon;
267:
268: *cp = 0;
269: if (strcasecmp(linebuf, field) != 0) {
270: *cp = ':';
271: return 0;
272: }
273: *cp = ':';
274: for (cp++; *cp == ' ' || *cp == '\t'; cp++)
275: ;
276: return cp;
277: }
278:
279: /*
280: * Copy a string, lowercasing it as we go.
281: */
282: void
283: istrcpy(dest, src)
284: register char *dest, *src;
285: {
286:
287: do {
288: if (isupper(*src))
289: *dest++ = tolower(*src);
290: else
291: *dest++ = *src;
292: } while (*src++ != 0);
293: }
294:
295: /*
296: * The following code deals with input stacking to do source
297: * commands. All but the current file pointer are saved on
298: * the stack.
299: */
300:
301: static int ssp; /* Top of file stack */
302: struct sstack {
303: FILE *s_file; /* File we were in. */
304: int s_cond; /* Saved state of conditionals */
305: int s_loading; /* Loading .mailrc, etc. */
306: } sstack[NOFILE];
307:
308: /*
309: * Pushdown current input file and switch to a new one.
310: * Set the global flag "sourcing" so that others will realize
311: * that they are no longer reading from a tty (in all probability).
312: */
313: int
1.2 ! deraadt 314: source(v)
! 315: void *v;
1.1 deraadt 316: {
1.2 ! deraadt 317: char **arglist = v;
1.1 deraadt 318: FILE *fi;
319: char *cp;
320:
321: if ((cp = expand(*arglist)) == NOSTR)
322: return(1);
323: if ((fi = Fopen(cp, "r")) == NULL) {
324: perror(cp);
325: return(1);
326: }
327: if (ssp >= NOFILE - 1) {
328: printf("Too much \"sourcing\" going on.\n");
329: Fclose(fi);
330: return(1);
331: }
332: sstack[ssp].s_file = input;
333: sstack[ssp].s_cond = cond;
334: sstack[ssp].s_loading = loading;
335: ssp++;
336: loading = 0;
337: cond = CANY;
338: input = fi;
339: sourcing++;
340: return(0);
341: }
342:
343: /*
344: * Pop the current input back to the previous level.
345: * Update the "sourcing" flag as appropriate.
346: */
347: int
348: unstack()
349: {
350: if (ssp <= 0) {
351: printf("\"Source\" stack over-pop.\n");
352: sourcing = 0;
353: return(1);
354: }
355: Fclose(input);
356: if (cond != CANY)
357: printf("Unmatched \"if\"\n");
358: ssp--;
359: cond = sstack[ssp].s_cond;
360: loading = sstack[ssp].s_loading;
361: input = sstack[ssp].s_file;
362: if (ssp == 0)
363: sourcing = loading;
364: return(0);
365: }
366:
367: /*
368: * Touch the indicated file.
369: * This is nifty for the shell.
370: */
371: void
372: alter(name)
373: char *name;
374: {
375: struct stat sb;
376: struct timeval tv[2];
377:
378: if (stat(name, &sb))
379: return;
380: tv[0].tv_sec = time((time_t *)0) + 1;
381: tv[1].tv_sec = sb.st_mtime;
382: tv[0].tv_usec = tv[1].tv_usec = 0;
383: (void)utimes(name, tv);
384: }
385:
386: /*
387: * Examine the passed line buffer and
388: * return true if it is all blanks and tabs.
389: */
390: int
391: blankline(linebuf)
392: char linebuf[];
393: {
394: register char *cp;
395:
396: for (cp = linebuf; *cp; cp++)
397: if (*cp != ' ' && *cp != '\t')
398: return(0);
399: return(1);
400: }
401:
402: /*
403: * Get sender's name from this message. If the message has
404: * a bunch of arpanet stuff in it, we may have to skin the name
405: * before returning it.
406: */
407: char *
408: nameof(mp, reptype)
409: register struct message *mp;
410: int reptype;
411: {
412: register char *cp, *cp2;
413:
414: cp = skin(name1(mp, reptype));
415: if (reptype != 0 || charcount(cp, '!') < 2)
416: return(cp);
417: cp2 = rindex(cp, '!');
418: cp2--;
419: while (cp2 > cp && *cp2 != '!')
420: cp2--;
421: if (*cp2 == '!')
422: return(cp2 + 1);
423: return(cp);
424: }
425:
426: /*
427: * Start of a "comment".
428: * Ignore it.
429: */
430: char *
431: skip_comment(cp)
432: register char *cp;
433: {
434: register nesting = 1;
435:
436: for (; nesting > 0 && *cp; cp++) {
437: switch (*cp) {
438: case '\\':
439: if (cp[1])
440: cp++;
441: break;
442: case '(':
443: nesting++;
444: break;
445: case ')':
446: nesting--;
447: break;
448: }
449: }
450: return cp;
451: }
452:
453: /*
454: * Skin an arpa net address according to the RFC 822 interpretation
455: * of "host-phrase."
456: */
457: char *
458: skin(name)
459: char *name;
460: {
461: register int c;
462: register char *cp, *cp2;
463: char *bufend;
464: int gotlt, lastsp;
465: char nbuf[BUFSIZ];
466:
467: if (name == NOSTR)
468: return(NOSTR);
469: if (index(name, '(') == NOSTR && index(name, '<') == NOSTR
470: && index(name, ' ') == NOSTR)
471: return(name);
472: gotlt = 0;
473: lastsp = 0;
474: bufend = nbuf;
1.2 ! deraadt 475: for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) {
1.1 deraadt 476: switch (c) {
477: case '(':
478: cp = skip_comment(cp);
479: lastsp = 0;
480: break;
481:
482: case '"':
483: /*
484: * Start of a "quoted-string".
485: * Copy it in its entirety.
486: */
1.2 ! deraadt 487: while ((c = *cp) != '\0') {
1.1 deraadt 488: cp++;
489: if (c == '"')
490: break;
491: if (c != '\\')
492: *cp2++ = c;
1.2 ! deraadt 493: else if ((c = *cp) != '\0') {
1.1 deraadt 494: *cp2++ = c;
495: cp++;
496: }
497: }
498: lastsp = 0;
499: break;
500:
501: case ' ':
502: if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
503: cp += 3, *cp2++ = '@';
504: else
505: if (cp[0] == '@' && cp[1] == ' ')
506: cp += 2, *cp2++ = '@';
507: else
508: lastsp = 1;
509: break;
510:
511: case '<':
512: cp2 = bufend;
513: gotlt++;
514: lastsp = 0;
515: break;
516:
517: case '>':
518: if (gotlt) {
519: gotlt = 0;
520: while ((c = *cp) && c != ',') {
521: cp++;
522: if (c == '(')
523: cp = skip_comment(cp);
524: else if (c == '"')
1.2 ! deraadt 525: while ((c = *cp) != '\0') {
1.1 deraadt 526: cp++;
527: if (c == '"')
528: break;
529: if (c == '\\' && *cp)
530: cp++;
531: }
532: }
533: lastsp = 0;
534: break;
535: }
536: /* Fall into . . . */
537:
538: default:
539: if (lastsp) {
540: lastsp = 0;
541: *cp2++ = ' ';
542: }
543: *cp2++ = c;
544: if (c == ',' && !gotlt) {
545: *cp2++ = ' ';
546: for (; *cp == ' '; cp++)
547: ;
548: lastsp = 0;
549: bufend = cp2;
550: }
551: }
552: }
553: *cp2 = 0;
554:
555: return(savestr(nbuf));
556: }
557:
558: /*
559: * Fetch the sender's name from the passed message.
560: * Reptype can be
561: * 0 -- get sender's name for display purposes
562: * 1 -- get sender's name for reply
563: * 2 -- get sender's name for Reply
564: */
565: char *
566: name1(mp, reptype)
567: register struct message *mp;
568: int reptype;
569: {
570: char namebuf[LINESIZE];
571: char linebuf[LINESIZE];
572: register char *cp, *cp2;
573: register FILE *ibuf;
574: int first = 1;
575:
576: if ((cp = hfield("from", mp)) != NOSTR)
577: return cp;
578: if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
579: return cp;
580: ibuf = setinput(mp);
581: namebuf[0] = 0;
582: if (readline(ibuf, linebuf, LINESIZE) < 0)
583: return(savestr(namebuf));
584: newname:
585: for (cp = linebuf; *cp && *cp != ' '; cp++)
586: ;
587: for (; *cp == ' ' || *cp == '\t'; cp++)
588: ;
589: for (cp2 = &namebuf[strlen(namebuf)];
590: *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
591: *cp2++ = *cp++;
592: *cp2 = '\0';
593: if (readline(ibuf, linebuf, LINESIZE) < 0)
594: return(savestr(namebuf));
595: if ((cp = index(linebuf, 'F')) == NULL)
596: return(savestr(namebuf));
597: if (strncmp(cp, "From", 4) != 0)
598: return(savestr(namebuf));
599: while ((cp = index(cp, 'r')) != NULL) {
600: if (strncmp(cp, "remote", 6) == 0) {
601: if ((cp = index(cp, 'f')) == NULL)
602: break;
603: if (strncmp(cp, "from", 4) != 0)
604: break;
605: if ((cp = index(cp, ' ')) == NULL)
606: break;
607: cp++;
608: if (first) {
609: strcpy(namebuf, cp);
610: first = 0;
611: } else
612: strcpy(rindex(namebuf, '!')+1, cp);
613: strcat(namebuf, "!");
614: goto newname;
615: }
616: cp++;
617: }
618: return(savestr(namebuf));
619: }
620:
621: /*
622: * Count the occurances of c in str
623: */
624: int
625: charcount(str, c)
626: char *str;
627: int c;
628: {
629: register char *cp;
630: register int i;
631:
632: for (i = 0, cp = str; *cp; cp++)
633: if (*cp == c)
634: i++;
635: return(i);
636: }
637:
638: /*
639: * Are any of the characters in the two strings the same?
640: */
641: int
642: anyof(s1, s2)
643: register char *s1, *s2;
644: {
645:
646: while (*s1)
647: if (index(s2, *s1++))
648: return 1;
649: return 0;
650: }
651:
652: /*
653: * Convert c to upper case
654: */
655: int
656: raise(c)
657: register int c;
658: {
659:
660: if (islower(c))
661: return toupper(c);
662: return c;
663: }
664:
665: /*
666: * Copy s1 to s2, return pointer to null in s2.
667: */
668: char *
669: copy(s1, s2)
670: register char *s1, *s2;
671: {
672:
1.2 ! deraadt 673: while ((*s2++ = *s1++) != '\0')
1.1 deraadt 674: ;
675: return s2 - 1;
676: }
677:
678: /*
679: * See if the given header field is supposed to be ignored.
680: */
681: int
682: isign(field, ignore)
683: char *field;
684: struct ignoretab ignore[2];
685: {
686: char realfld[BUFSIZ];
687:
688: if (ignore == ignoreall)
689: return 1;
690: /*
691: * Lower-case the string, so that "Status" and "status"
692: * will hash to the same place.
693: */
694: istrcpy(realfld, field);
695: if (ignore[1].i_count > 0)
696: return (!member(realfld, ignore + 1));
697: else
698: return (member(realfld, ignore));
699: }
700:
701: int
702: member(realfield, table)
703: register char *realfield;
704: struct ignoretab *table;
705: {
706: register struct ignore *igp;
707:
708: for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
709: if (*igp->i_field == *realfield &&
710: equal(igp->i_field, realfield))
711: return (1);
712: return (0);
713: }