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