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