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