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