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