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