Fixed bug in marking a range; modified markall() to only mark messages
[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
ac57be53 13static char *SccsId = "@(#)aux.c 2.8 %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);
549 if (index(name, '(') == NOSTR && index(name, '<') == NOSTR)
550 return(name);
551 gotlt = 0;
552 lastsp = 0;
553 for (cp = name, cp2 = nbuf, c = *cp++; *cp; c = *cp++) {
554 switch (c) {
555 case '(':
556 while (*cp != ')' && *cp != 0)
557 cp++;
558 if (*cp)
559 cp++;
560 break;
561
562 case ' ':
563 lastsp = 1;
564 break;
565
566 case '<':
567 cp2 = nbuf;
568 gotlt++;
569 lastsp = 0;
570 break;
571
572 case '>':
573 if (gotlt)
574 goto done;
575
576 /* Fall into . . . */
577
578 default:
579 if (lastsp) {
580 lastsp = 0;
581 *cp2++ = ' ';
582 }
583 *cp2++ = c;
584 break;
585 }
586 }
587done:
588 *cp2 = 0;
589
590 return(savestr(nbuf));
591}
592
6447a23f
KS
593/*
594 * Fetch the sender's name from the passed message.
12388009
KS
595 * Reptype can be
596 * 0 -- get sender's name for display purposes
597 * 1 -- get sender's name for reply
598 * 2 -- get sender's name for Reply
6447a23f
KS
599 */
600
601char *
12388009 602name1(mp, reptype)
6447a23f
KS
603 register struct message *mp;
604{
605 char namebuf[LINESIZE];
606 char linebuf[LINESIZE];
607 register char *cp, *cp2;
608 register FILE *ibuf;
609 int first = 1;
610
ac57be53 611#ifndef SENDMAIL
12388009
KS
612 if ((cp = hfield("from", mp)) != NOSTR)
613 return(cp);
614 if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
615 return(cp);
616#endif
6447a23f
KS
617 ibuf = setinput(mp);
618 copy("", namebuf);
619 if (readline(ibuf, linebuf) <= 0)
620 return(savestr(namebuf));
621newname:
622 for (cp = linebuf; *cp != ' '; cp++)
623 ;
624 while (any(*cp, " \t"))
625 cp++;
626 for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
627 cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
628 ;
629 *cp2 = '\0';
630 if (readline(ibuf, linebuf) <= 0)
631 return(savestr(namebuf));
632 if ((cp = index(linebuf, 'F')) == NULL)
633 return(savestr(namebuf));
634 if (strncmp(cp, "From", 4) != 0)
635 return(savestr(namebuf));
636 while ((cp = index(cp, 'r')) != NULL) {
637 if (strncmp(cp, "remote", 6) == 0) {
638 if ((cp = index(cp, 'f')) == NULL)
639 break;
640 if (strncmp(cp, "from", 4) != 0)
641 break;
642 if ((cp = index(cp, ' ')) == NULL)
643 break;
644 cp++;
645 if (first) {
646 copy(cp, namebuf);
647 first = 0;
648 } else
649 strcpy(rindex(namebuf, '!')+1, cp);
650 strcat(namebuf, "!");
651 goto newname;
652 }
653 cp++;
654 }
655 return(savestr(namebuf));
656}
657
8bcfa450
KS
658/*
659 * Count the occurances of c in str
660 */
661charcount(str, c)
662 char *str;
663{
664 register char *cp;
665 register int i;
666
667 for (i = 0, cp = str; *cp; cp++)
668 if (*cp == c)
669 i++;
670 return(i);
671}
672
6447a23f
KS
673/*
674 * Find the rightmost pointer to an instance of the
675 * character in the string and return it.
676 */
6447a23f
KS
677char *
678rindex(str, c)
679 char str[];
680 register int c;
681{
682 register char *cp, *cp2;
683
684 for (cp = str, cp2 = NOSTR; *cp; cp++)
685 if (c == *cp)
686 cp2 = cp;
687 return(cp2);
688}
689
690/*
691 * See if the string is a number.
692 */
693
694numeric(str)
695 char str[];
696{
697 register char *cp = str;
698
699 while (*cp)
700 if (!isdigit(*cp++))
701 return(0);
702 return(1);
703}
704
705/*
706 * Are any of the characters in the two strings the same?
707 */
708
709anyof(s1, s2)
710 register char *s1, *s2;
711{
712 register int c;
713
714 while (c = *s1++)
715 if (any(c, s2))
716 return(1);
717 return(0);
718}
719
720/*
721 * Determine the leftmost index of the character
722 * in the string.
723 */
724
725char *
726index(str, ch)
727 char *str;
728{
729 register char *cp;
730 register int c;
731
732 for (c = ch, cp = str; *cp; cp++)
733 if (*cp == c)
734 return(cp);
735 return(NOSTR);
736}
737
738/*
739 * String compare two strings of bounded length.
740 */
741
742strncmp(as1, as2, an)
743 char *as1, *as2;
744{
745 register char *s1, *s2;
746 register int n;
747
748 s1 = as1;
749 s2 = as2;
750 n = an;
751 while (--n >= 0 && *s1 == *s2++)
752 if (*s1++ == '\0')
753 return(0);
754 return(n<0 ? 0 : *s1 - *--s2);
755}
756
da0d3a6d
KS
757/*
758 * See if the given header field is supposed to be ignored.
759 */
760isign(field)
761 char *field;
762{
763 char realfld[BUFSIZ];
764 register int h;
765 register struct ignore *igp;
766
767 istrcpy(realfld, field);
768 h = hash(realfld);
769 for (igp = ignore[h]; igp != 0; igp = igp->i_link)
770 if (strcmp(igp->i_field, realfld) == 0)
771 return(1);
772 return(0);
773}