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