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