1 /*
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #ifndef lint
9 static char sccsid[] = "@(#)aux.c 8.1 (Berkeley) 06/06/93";
10 #endif /* not lint */
11
12 #include "rcv.h"
13 #include "extern.h"
14
15 /*
16 * Mail -- a mail program
17 *
18 * Auxiliary functions.
19 */
20
21 /*
22 * Return a pointer to a dynamic copy of the argument.
23 */
24 char *
savestr(str)25 savestr(str)
26 char *str;
27 {
28 char *new;
29 int size = strlen(str) + 1;
30
31 if ((new = salloc(size)) != NOSTR)
32 bcopy(str, new, size);
33 return new;
34 }
35
36 /*
37 * Make a copy of new argument incorporating old one.
38 */
39 char *
save2str(str,old)40 save2str(str, old)
41 char *str, *old;
42 {
43 char *new;
44 int newsize = strlen(str) + 1;
45 int oldsize = old ? strlen(old) + 1 : 0;
46
47 if ((new = salloc(newsize + oldsize)) != NOSTR) {
48 if (oldsize) {
49 bcopy(old, new, oldsize);
50 new[oldsize - 1] = ' ';
51 }
52 bcopy(str, new + oldsize, newsize);
53 }
54 return new;
55 }
56
57 /*
58 * Announce a fatal error and die.
59 */
60 #if __STDC__
61 #include <stdarg.h>
62 #else
63 #include <varargs.h>
64 #endif
65
66 void
67 #if __STDC__
panic(const char * fmt,...)68 panic(const char *fmt, ...)
69 #else
70 panic(fmt, va_alist)
71 char *fmt;
72 va_dcl
73 #endif
74 {
75 va_list ap;
76 #if __STDC__
77 va_start(ap, fmt);
78 #else
79 va_start(ap);
80 #endif
81 (void)fprintf(stderr, "panic: ");
82 vfprintf(stderr, fmt, ap);
83 va_end(ap);
84 (void)fprintf(stderr, "\n");
85 fflush(stderr);
86 abort();
87 }
88
89 /*
90 * Touch the named message by setting its MTOUCH flag.
91 * Touched messages have the effect of not being sent
92 * back to the system mailbox on exit.
93 */
94 void
touch(mp)95 touch(mp)
96 register struct message *mp;
97 {
98
99 mp->m_flag |= MTOUCH;
100 if ((mp->m_flag & MREAD) == 0)
101 mp->m_flag |= MREAD|MSTATUS;
102 }
103
104 /*
105 * Test to see if the passed file name is a directory.
106 * Return true if it is.
107 */
108 int
isdir(name)109 isdir(name)
110 char name[];
111 {
112 struct stat sbuf;
113
114 if (stat(name, &sbuf) < 0)
115 return(0);
116 return((sbuf.st_mode & S_IFMT) == S_IFDIR);
117 }
118
119 /*
120 * Count the number of arguments in the given string raw list.
121 */
122 int
argcount(argv)123 argcount(argv)
124 char **argv;
125 {
126 register char **ap;
127
128 for (ap = argv; *ap++ != NOSTR;)
129 ;
130 return ap - argv - 1;
131 }
132
133 /*
134 * Return the desired header line from the passed message
135 * pointer (or NOSTR if the desired header field is not available).
136 */
137 char *
hfield(field,mp)138 hfield(field, mp)
139 char field[];
140 struct message *mp;
141 {
142 register FILE *ibuf;
143 char linebuf[LINESIZE];
144 register int lc;
145 register char *hfield;
146 char *colon, *oldhfield = NOSTR;
147
148 ibuf = setinput(mp);
149 if ((lc = mp->m_lines - 1) < 0)
150 return NOSTR;
151 if (readline(ibuf, linebuf, LINESIZE) < 0)
152 return NOSTR;
153 while (lc > 0) {
154 if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
155 return oldhfield;
156 if (hfield = ishfield(linebuf, colon, field))
157 oldhfield = save2str(hfield, oldhfield);
158 }
159 return oldhfield;
160 }
161
162 /*
163 * Return the next header field found in the given message.
164 * Return >= 0 if something found, < 0 elsewise.
165 * "colon" is set to point to the colon in the header.
166 * Must deal with \ continuations & other such fraud.
167 */
168 int
gethfield(f,linebuf,rem,colon)169 gethfield(f, linebuf, rem, colon)
170 register FILE *f;
171 char linebuf[];
172 register int rem;
173 char **colon;
174 {
175 char line2[LINESIZE];
176 register char *cp, *cp2;
177 register int c;
178
179 for (;;) {
180 if (--rem < 0)
181 return -1;
182 if ((c = readline(f, linebuf, LINESIZE)) <= 0)
183 return -1;
184 for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':';
185 cp++)
186 ;
187 if (*cp != ':' || cp == linebuf)
188 continue;
189 /*
190 * I guess we got a headline.
191 * Handle wraparounding
192 */
193 *colon = cp;
194 cp = linebuf + c;
195 for (;;) {
196 while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
197 ;
198 cp++;
199 if (rem <= 0)
200 break;
201 ungetc(c = getc(f), f);
202 if (c != ' ' && c != '\t')
203 break;
204 if ((c = readline(f, line2, LINESIZE)) < 0)
205 break;
206 rem--;
207 for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
208 ;
209 c -= cp2 - line2;
210 if (cp + c >= linebuf + LINESIZE - 2)
211 break;
212 *cp++ = ' ';
213 bcopy(cp2, cp, c);
214 cp += c;
215 }
216 *cp = 0;
217 return rem;
218 }
219 /* NOTREACHED */
220 }
221
222 /*
223 * Check whether the passed line is a header line of
224 * the desired breed. Return the field body, or 0.
225 */
226
227 char*
ishfield(linebuf,colon,field)228 ishfield(linebuf, colon, field)
229 char linebuf[], field[];
230 char *colon;
231 {
232 register char *cp = colon;
233
234 *cp = 0;
235 if (strcasecmp(linebuf, field) != 0) {
236 *cp = ':';
237 return 0;
238 }
239 *cp = ':';
240 for (cp++; *cp == ' ' || *cp == '\t'; cp++)
241 ;
242 return cp;
243 }
244
245 /*
246 * Copy a string, lowercasing it as we go.
247 */
248 void
istrcpy(dest,src)249 istrcpy(dest, src)
250 register char *dest, *src;
251 {
252
253 do {
254 if (isupper(*src))
255 *dest++ = tolower(*src);
256 else
257 *dest++ = *src;
258 } while (*src++ != 0);
259 }
260
261 /*
262 * The following code deals with input stacking to do source
263 * commands. All but the current file pointer are saved on
264 * the stack.
265 */
266
267 static int ssp; /* Top of file stack */
268 struct sstack {
269 FILE *s_file; /* File we were in. */
270 int s_cond; /* Saved state of conditionals */
271 int s_loading; /* Loading .mailrc, etc. */
272 } sstack[NOFILE];
273
274 /*
275 * Pushdown current input file and switch to a new one.
276 * Set the global flag "sourcing" so that others will realize
277 * that they are no longer reading from a tty (in all probability).
278 */
279 int
source(arglist)280 source(arglist)
281 char **arglist;
282 {
283 FILE *fi;
284 char *cp;
285
286 if ((cp = expand(*arglist)) == NOSTR)
287 return(1);
288 if ((fi = Fopen(cp, "r")) == NULL) {
289 perror(cp);
290 return(1);
291 }
292 if (ssp >= NOFILE - 1) {
293 printf("Too much \"sourcing\" going on.\n");
294 Fclose(fi);
295 return(1);
296 }
297 sstack[ssp].s_file = input;
298 sstack[ssp].s_cond = cond;
299 sstack[ssp].s_loading = loading;
300 ssp++;
301 loading = 0;
302 cond = CANY;
303 input = fi;
304 sourcing++;
305 return(0);
306 }
307
308 /*
309 * Pop the current input back to the previous level.
310 * Update the "sourcing" flag as appropriate.
311 */
312 int
unstack()313 unstack()
314 {
315 if (ssp <= 0) {
316 printf("\"Source\" stack over-pop.\n");
317 sourcing = 0;
318 return(1);
319 }
320 Fclose(input);
321 if (cond != CANY)
322 printf("Unmatched \"if\"\n");
323 ssp--;
324 cond = sstack[ssp].s_cond;
325 loading = sstack[ssp].s_loading;
326 input = sstack[ssp].s_file;
327 if (ssp == 0)
328 sourcing = loading;
329 return(0);
330 }
331
332 /*
333 * Touch the indicated file.
334 * This is nifty for the shell.
335 */
336 void
alter(name)337 alter(name)
338 char *name;
339 {
340 struct stat sb;
341 struct timeval tv[2];
342 time_t time();
343
344 if (stat(name, &sb))
345 return;
346 tv[0].tv_sec = time((time_t *)0) + 1;
347 tv[1].tv_sec = sb.st_mtime;
348 tv[0].tv_usec = tv[1].tv_usec = 0;
349 (void)utimes(name, tv);
350 }
351
352 /*
353 * Examine the passed line buffer and
354 * return true if it is all blanks and tabs.
355 */
356 int
blankline(linebuf)357 blankline(linebuf)
358 char linebuf[];
359 {
360 register char *cp;
361
362 for (cp = linebuf; *cp; cp++)
363 if (*cp != ' ' && *cp != '\t')
364 return(0);
365 return(1);
366 }
367
368 /*
369 * Get sender's name from this message. If the message has
370 * a bunch of arpanet stuff in it, we may have to skin the name
371 * before returning it.
372 */
373 char *
nameof(mp,reptype)374 nameof(mp, reptype)
375 register struct message *mp;
376 int reptype;
377 {
378 register char *cp, *cp2;
379
380 cp = skin(name1(mp, reptype));
381 if (reptype != 0 || charcount(cp, '!') < 2)
382 return(cp);
383 cp2 = rindex(cp, '!');
384 cp2--;
385 while (cp2 > cp && *cp2 != '!')
386 cp2--;
387 if (*cp2 == '!')
388 return(cp2 + 1);
389 return(cp);
390 }
391
392 /*
393 * Start of a "comment".
394 * Ignore it.
395 */
396 char *
skip_comment(cp)397 skip_comment(cp)
398 register char *cp;
399 {
400 register nesting = 1;
401
402 for (; nesting > 0 && *cp; cp++) {
403 switch (*cp) {
404 case '\\':
405 if (cp[1])
406 cp++;
407 break;
408 case '(':
409 nesting++;
410 break;
411 case ')':
412 nesting--;
413 break;
414 }
415 }
416 return cp;
417 }
418
419 /*
420 * Skin an arpa net address according to the RFC 822 interpretation
421 * of "host-phrase."
422 */
423 char *
skin(name)424 skin(name)
425 char *name;
426 {
427 register int c;
428 register char *cp, *cp2;
429 char *bufend;
430 int gotlt, lastsp;
431 char nbuf[BUFSIZ];
432
433 if (name == NOSTR)
434 return(NOSTR);
435 if (index(name, '(') == NOSTR && index(name, '<') == NOSTR
436 && index(name, ' ') == NOSTR)
437 return(name);
438 gotlt = 0;
439 lastsp = 0;
440 bufend = nbuf;
441 for (cp = name, cp2 = bufend; c = *cp++; ) {
442 switch (c) {
443 case '(':
444 cp = skip_comment(cp);
445 lastsp = 0;
446 break;
447
448 case '"':
449 /*
450 * Start of a "quoted-string".
451 * Copy it in its entirety.
452 */
453 while (c = *cp) {
454 cp++;
455 if (c == '"')
456 break;
457 if (c != '\\')
458 *cp2++ = c;
459 else if (c = *cp) {
460 *cp2++ = c;
461 cp++;
462 }
463 }
464 lastsp = 0;
465 break;
466
467 case ' ':
468 if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
469 cp += 3, *cp2++ = '@';
470 else
471 if (cp[0] == '@' && cp[1] == ' ')
472 cp += 2, *cp2++ = '@';
473 else
474 lastsp = 1;
475 break;
476
477 case '<':
478 cp2 = bufend;
479 gotlt++;
480 lastsp = 0;
481 break;
482
483 case '>':
484 if (gotlt) {
485 gotlt = 0;
486 while ((c = *cp) && c != ',') {
487 cp++;
488 if (c == '(')
489 cp = skip_comment(cp);
490 else if (c == '"')
491 while (c = *cp) {
492 cp++;
493 if (c == '"')
494 break;
495 if (c == '\\' && *cp)
496 cp++;
497 }
498 }
499 lastsp = 0;
500 break;
501 }
502 /* Fall into . . . */
503
504 default:
505 if (lastsp) {
506 lastsp = 0;
507 *cp2++ = ' ';
508 }
509 *cp2++ = c;
510 if (c == ',' && !gotlt) {
511 *cp2++ = ' ';
512 for (; *cp == ' '; cp++)
513 ;
514 lastsp = 0;
515 bufend = cp2;
516 }
517 }
518 }
519 *cp2 = 0;
520
521 return(savestr(nbuf));
522 }
523
524 /*
525 * Fetch the sender's name from the passed message.
526 * Reptype can be
527 * 0 -- get sender's name for display purposes
528 * 1 -- get sender's name for reply
529 * 2 -- get sender's name for Reply
530 */
531 char *
name1(mp,reptype)532 name1(mp, reptype)
533 register struct message *mp;
534 int reptype;
535 {
536 char namebuf[LINESIZE];
537 char linebuf[LINESIZE];
538 register char *cp, *cp2;
539 register FILE *ibuf;
540 int first = 1;
541
542 if ((cp = hfield("from", mp)) != NOSTR)
543 return cp;
544 if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
545 return cp;
546 ibuf = setinput(mp);
547 namebuf[0] = 0;
548 if (readline(ibuf, linebuf, LINESIZE) < 0)
549 return(savestr(namebuf));
550 newname:
551 for (cp = linebuf; *cp && *cp != ' '; cp++)
552 ;
553 for (; *cp == ' ' || *cp == '\t'; cp++)
554 ;
555 for (cp2 = &namebuf[strlen(namebuf)];
556 *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
557 *cp2++ = *cp++;
558 *cp2 = '\0';
559 if (readline(ibuf, linebuf, LINESIZE) < 0)
560 return(savestr(namebuf));
561 if ((cp = index(linebuf, 'F')) == NULL)
562 return(savestr(namebuf));
563 if (strncmp(cp, "From", 4) != 0)
564 return(savestr(namebuf));
565 while ((cp = index(cp, 'r')) != NULL) {
566 if (strncmp(cp, "remote", 6) == 0) {
567 if ((cp = index(cp, 'f')) == NULL)
568 break;
569 if (strncmp(cp, "from", 4) != 0)
570 break;
571 if ((cp = index(cp, ' ')) == NULL)
572 break;
573 cp++;
574 if (first) {
575 strcpy(namebuf, cp);
576 first = 0;
577 } else
578 strcpy(rindex(namebuf, '!')+1, cp);
579 strcat(namebuf, "!");
580 goto newname;
581 }
582 cp++;
583 }
584 return(savestr(namebuf));
585 }
586
587 /*
588 * Count the occurances of c in str
589 */
590 int
charcount(str,c)591 charcount(str, c)
592 char *str;
593 int c;
594 {
595 register char *cp;
596 register int i;
597
598 for (i = 0, cp = str; *cp; cp++)
599 if (*cp == c)
600 i++;
601 return(i);
602 }
603
604 /*
605 * Are any of the characters in the two strings the same?
606 */
607 int
anyof(s1,s2)608 anyof(s1, s2)
609 register char *s1, *s2;
610 {
611
612 while (*s1)
613 if (index(s2, *s1++))
614 return 1;
615 return 0;
616 }
617
618 /*
619 * Convert c to upper case
620 */
621 int
raise(c)622 raise(c)
623 register int c;
624 {
625
626 if (islower(c))
627 return toupper(c);
628 return c;
629 }
630
631 /*
632 * Copy s1 to s2, return pointer to null in s2.
633 */
634 char *
copy(s1,s2)635 copy(s1, s2)
636 register char *s1, *s2;
637 {
638
639 while (*s2++ = *s1++)
640 ;
641 return s2 - 1;
642 }
643
644 /*
645 * See if the given header field is supposed to be ignored.
646 */
647 int
isign(field,ignore)648 isign(field, ignore)
649 char *field;
650 struct ignoretab ignore[2];
651 {
652 char realfld[BUFSIZ];
653
654 if (ignore == ignoreall)
655 return 1;
656 /*
657 * Lower-case the string, so that "Status" and "status"
658 * will hash to the same place.
659 */
660 istrcpy(realfld, field);
661 if (ignore[1].i_count > 0)
662 return (!member(realfld, ignore + 1));
663 else
664 return (member(realfld, ignore));
665 }
666
667 int
member(realfield,table)668 member(realfield, table)
669 register char *realfield;
670 struct ignoretab *table;
671 {
672 register struct ignore *igp;
673
674 for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
675 if (*igp->i_field == *realfield &&
676 equal(igp->i_field, realfield))
677 return (1);
678 return (0);
679 }
680