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