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