backslash and ^ handling in getrawlist() fixed
[unix-history] / usr / src / usr.bin / mail / aux.c
CommitLineData
d0aeaf5a 1/*
a12ff486
KB
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
0c5f72fb 4 *
f15db449 5 * %sccs.include.redist.c%
d0aeaf5a
DF
6 */
7
acfc7e9b 8#ifndef lint
a12ff486 9static char sccsid[] = "@(#)aux.c 8.1 (Berkeley) %G%";
acfc7e9b 10#endif /* not lint */
6447a23f
KS
11
12#include "rcv.h"
a0d23834 13#include "extern.h"
6447a23f
KS
14
15/*
16 * Mail -- a mail program
17 *
18 * Auxiliary functions.
19 */
20
6447a23f
KS
21/*
22 * Return a pointer to a dynamic copy of the argument.
23 */
6447a23f
KS
24char *
25savestr(str)
26 char *str;
27{
470c33f3
EW
28 char *new;
29 int size = strlen(str) + 1;
6447a23f 30
470c33f3
EW
31 if ((new = salloc(size)) != NOSTR)
32 bcopy(str, new, size);
33 return new;
6447a23f
KS
34}
35
e3e4b2bc
EW
36/*
37 * Make a copy of new argument incorporating old one.
38 */
39char *
40save2str(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
6447a23f
KS
57/*
58 * Announce a fatal error and die.
59 */
a0d23834
KB
60#if __STDC__
61#include <stdarg.h>
62#else
63#include <varargs.h>
64#endif
65
66void
67#if __STDC__
68panic(const char *fmt, ...)
69#else
70panic(fmt, va_alist)
828615a1 71 char *fmt;
a0d23834
KB
72 va_dcl
73#endif
6447a23f 74{
a0d23834
KB
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);
07b0d286 86 abort();
6447a23f
KS
87}
88
6447a23f
KS
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 */
a0d23834 94void
470c33f3 95touch(mp)
dffe7e4f 96 register struct message *mp;
470c33f3 97{
dffe7e4f 98
dffe7e4f
KS
99 mp->m_flag |= MTOUCH;
100 if ((mp->m_flag & MREAD) == 0)
101 mp->m_flag |= MREAD|MSTATUS;
6447a23f
KS
102}
103
104/*
105 * Test to see if the passed file name is a directory.
106 * Return true if it is.
107 */
a0d23834 108int
6447a23f
KS
109isdir(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
6447a23f
KS
119/*
120 * Count the number of arguments in the given string raw list.
121 */
a0d23834 122int
6447a23f
KS
123argcount(argv)
124 char **argv;
125{
126 register char **ap;
127
828615a1 128 for (ap = argv; *ap++ != NOSTR;)
6447a23f 129 ;
828615a1 130 return ap - argv - 1;
6447a23f
KS
131}
132
6447a23f
KS
133/*
134 * Return the desired header line from the passed message
135 * pointer (or NOSTR if the desired header field is not available).
136 */
6447a23f
KS
137char *
138hfield(field, mp)
139 char field[];
140 struct message *mp;
141{
142 register FILE *ibuf;
143 char linebuf[LINESIZE];
144 register int lc;
828615a1 145 register char *hfield;
e3e4b2bc 146 char *colon, *oldhfield = NOSTR;
6447a23f
KS
147
148 ibuf = setinput(mp);
828615a1
EW
149 if ((lc = mp->m_lines - 1) < 0)
150 return NOSTR;
38ed7007 151 if (readline(ibuf, linebuf, LINESIZE) < 0)
828615a1
EW
152 return NOSTR;
153 while (lc > 0) {
154 if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
e3e4b2bc 155 return oldhfield;
828615a1 156 if (hfield = ishfield(linebuf, colon, field))
e3e4b2bc 157 oldhfield = save2str(hfield, oldhfield);
828615a1 158 }
e3e4b2bc 159 return oldhfield;
6447a23f
KS
160}
161
162/*
163 * Return the next header field found in the given message.
828615a1
EW
164 * Return >= 0 if something found, < 0 elsewise.
165 * "colon" is set to point to the colon in the header.
6447a23f
KS
166 * Must deal with \ continuations & other such fraud.
167 */
a0d23834 168int
828615a1 169gethfield(f, linebuf, rem, colon)
6447a23f
KS
170 register FILE *f;
171 char linebuf[];
172 register int rem;
828615a1 173 char **colon;
6447a23f
KS
174{
175 char line2[LINESIZE];
6447a23f
KS
176 register char *cp, *cp2;
177 register int c;
178
6447a23f 179 for (;;) {
828615a1
EW
180 if (--rem < 0)
181 return -1;
38ed7007 182 if ((c = readline(f, linebuf, LINESIZE)) <= 0)
828615a1
EW
183 return -1;
184 for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':';
185 cp++)
186 ;
187 if (*cp != ':' || cp == linebuf)
6447a23f 188 continue;
6447a23f
KS
189 /*
190 * I guess we got a headline.
191 * Handle wraparounding
192 */
828615a1
EW
193 *colon = cp;
194 cp = linebuf + c;
6447a23f 195 for (;;) {
828615a1
EW
196 while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
197 ;
198 cp++;
6447a23f
KS
199 if (rem <= 0)
200 break;
828615a1
EW
201 ungetc(c = getc(f), f);
202 if (c != ' ' && c != '\t')
6447a23f 203 break;
38ed7007 204 if ((c = readline(f, line2, LINESIZE)) < 0)
6447a23f
KS
205 break;
206 rem--;
828615a1 207 for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
6447a23f 208 ;
828615a1
EW
209 c -= cp2 - line2;
210 if (cp + c >= linebuf + LINESIZE - 2)
6447a23f 211 break;
6447a23f 212 *cp++ = ' ';
828615a1
EW
213 bcopy(cp2, cp, c);
214 cp += c;
6447a23f 215 }
828615a1
EW
216 *cp = 0;
217 return rem;
6447a23f
KS
218 }
219 /* NOTREACHED */
220}
221
222/*
223 * Check whether the passed line is a header line of
828615a1 224 * the desired breed. Return the field body, or 0.
6447a23f
KS
225 */
226
828615a1
EW
227char*
228ishfield(linebuf, colon, field)
6447a23f 229 char linebuf[], field[];
828615a1 230 char *colon;
6447a23f 231{
828615a1 232 register char *cp = colon;
6447a23f 233
6447a23f 234 *cp = 0;
470c33f3 235 if (strcasecmp(linebuf, field) != 0) {
828615a1
EW
236 *cp = ':';
237 return 0;
6447a23f 238 }
828615a1
EW
239 *cp = ':';
240 for (cp++; *cp == ' ' || *cp == '\t'; cp++)
241 ;
242 return cp;
6447a23f
KS
243}
244
da0d3a6d
KS
245/*
246 * Copy a string, lowercasing it as we go.
247 */
a0d23834 248void
da0d3a6d 249istrcpy(dest, src)
828615a1 250 register char *dest, *src;
da0d3a6d 251{
da0d3a6d 252
da0d3a6d 253 do {
828615a1
EW
254 if (isupper(*src))
255 *dest++ = tolower(*src);
256 else
257 *dest++ = *src;
258 } while (*src++ != 0);
da0d3a6d
KS
259}
260
6447a23f
KS
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
e4920814 267static int ssp; /* Top of file stack */
e48b42f1
KS
268struct sstack {
269 FILE *s_file; /* File we were in. */
270 int s_cond; /* Saved state of conditionals */
f4aa38b5 271 int s_loading; /* Loading .mailrc, etc. */
46053c99 272} sstack[NOFILE];
6447a23f
KS
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 */
a0d23834 279int
23464f86
EW
280source(arglist)
281 char **arglist;
6447a23f 282{
23464f86
EW
283 FILE *fi;
284 char *cp;
6447a23f 285
23464f86 286 if ((cp = expand(*arglist)) == NOSTR)
5c7ba847 287 return(1);
07b0d286 288 if ((fi = Fopen(cp, "r")) == NULL) {
5c7ba847 289 perror(cp);
6447a23f
KS
290 return(1);
291 }
e4920814 292 if (ssp >= NOFILE - 1) {
6447a23f 293 printf("Too much \"sourcing\" going on.\n");
07b0d286 294 Fclose(fi);
6447a23f
KS
295 return(1);
296 }
e4920814 297 sstack[ssp].s_file = input;
e48b42f1 298 sstack[ssp].s_cond = cond;
f4aa38b5 299 sstack[ssp].s_loading = loading;
e4920814 300 ssp++;
f4aa38b5 301 loading = 0;
e48b42f1 302 cond = CANY;
6447a23f
KS
303 input = fi;
304 sourcing++;
305 return(0);
306}
307
6447a23f
KS
308/*
309 * Pop the current input back to the previous level.
310 * Update the "sourcing" flag as appropriate.
311 */
a0d23834 312int
6447a23f
KS
313unstack()
314{
e4920814 315 if (ssp <= 0) {
6447a23f
KS
316 printf("\"Source\" stack over-pop.\n");
317 sourcing = 0;
318 return(1);
319 }
07b0d286 320 Fclose(input);
e48b42f1
KS
321 if (cond != CANY)
322 printf("Unmatched \"if\"\n");
e4920814 323 ssp--;
e48b42f1 324 cond = sstack[ssp].s_cond;
f4aa38b5 325 loading = sstack[ssp].s_loading;
e4920814
EW
326 input = sstack[ssp].s_file;
327 if (ssp == 0)
f4aa38b5 328 sourcing = loading;
6447a23f
KS
329 return(0);
330}
331
332/*
333 * Touch the indicated file.
334 * This is nifty for the shell.
6447a23f 335 */
a0d23834 336void
6447a23f 337alter(name)
9a7dcab2 338 char *name;
6447a23f 339{
9a7dcab2
KB
340 struct stat sb;
341 struct timeval tv[2];
342 time_t time();
6447a23f 343
9a7dcab2 344 if (stat(name, &sb))
6447a23f 345 return;
9a7dcab2
KB
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);
6447a23f
KS
350}
351
352/*
353 * Examine the passed line buffer and
354 * return true if it is all blanks and tabs.
355 */
a0d23834 356int
6447a23f
KS
357blankline(linebuf)
358 char linebuf[];
359{
360 register char *cp;
361
362 for (cp = linebuf; *cp; cp++)
46053c99 363 if (*cp != ' ' && *cp != '\t')
6447a23f
KS
364 return(0);
365 return(1);
366}
367
12388009
KS
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 */
373char *
374nameof(mp, reptype)
375 register struct message *mp;
a0d23834 376 int reptype;
12388009 377{
8bcfa450 378 register char *cp, *cp2;
12388009 379
8bcfa450
KS
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);
12388009
KS
390}
391
52111d08
EW
392/*
393 * Start of a "comment".
394 * Ignore it.
395 */
396char *
397skip_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
12388009 419/*
b4817aab 420 * Skin an arpa net address according to the RFC 822 interpretation
12388009
KS
421 * of "host-phrase."
422 */
423char *
424skin(name)
425 char *name;
426{
427 register int c;
428 register char *cp, *cp2;
b4817aab 429 char *bufend;
12388009
KS
430 int gotlt, lastsp;
431 char nbuf[BUFSIZ];
432
433 if (name == NOSTR)
434 return(NOSTR);
e557a09a 435 if (index(name, '(') == NOSTR && index(name, '<') == NOSTR
828615a1 436 && index(name, ' ') == NOSTR)
12388009
KS
437 return(name);
438 gotlt = 0;
439 lastsp = 0;
b4817aab
KM
440 bufend = nbuf;
441 for (cp = name, cp2 = bufend; c = *cp++; ) {
12388009
KS
442 switch (c) {
443 case '(':
52111d08 444 cp = skip_comment(cp);
b4817aab
KM
445 lastsp = 0;
446 break;
447
448 case '"':
449 /*
450 * Start of a "quoted-string".
451 * Copy it in its entirety.
452 */
52111d08 453 while (c = *cp) {
b4817aab 454 cp++;
52111d08 455 if (c == '"')
b4817aab 456 break;
52111d08
EW
457 if (c != '\\')
458 *cp2++ = c;
459 else if (c = *cp) {
460 *cp2++ = c;
461 cp++;
b4817aab 462 }
b4817aab 463 }
e557a09a 464 lastsp = 0;
12388009
KS
465 break;
466
467 case ' ':
e557a09a
CL
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;
12388009
KS
475 break;
476
477 case '<':
b4817aab 478 cp2 = bufend;
12388009
KS
479 gotlt++;
480 lastsp = 0;
481 break;
482
483 case '>':
b4817aab
KM
484 if (gotlt) {
485 gotlt = 0;
52111d08 486 while ((c = *cp) && c != ',') {
b4817aab 487 cp++;
52111d08
EW
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;
b4817aab
KM
500 break;
501 }
12388009
KS
502 /* Fall into . . . */
503
504 default:
505 if (lastsp) {
506 lastsp = 0;
507 *cp2++ = ' ';
508 }
509 *cp2++ = c;
52111d08
EW
510 if (c == ',' && !gotlt) {
511 *cp2++ = ' ';
512 for (; *cp == ' '; cp++)
513 ;
514 lastsp = 0;
515 bufend = cp2;
516 }
12388009
KS
517 }
518 }
12388009
KS
519 *cp2 = 0;
520
521 return(savestr(nbuf));
522}
523
6447a23f
KS
524/*
525 * Fetch the sender's name from the passed message.
12388009
KS
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
6447a23f 530 */
6447a23f 531char *
12388009 532name1(mp, reptype)
6447a23f 533 register struct message *mp;
a0d23834 534 int reptype;
6447a23f
KS
535{
536 char namebuf[LINESIZE];
537 char linebuf[LINESIZE];
538 register char *cp, *cp2;
539 register FILE *ibuf;
540 int first = 1;
541
12388009 542 if ((cp = hfield("from", mp)) != NOSTR)
828615a1 543 return cp;
12388009 544 if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
828615a1 545 return cp;
6447a23f 546 ibuf = setinput(mp);
828615a1 547 namebuf[0] = 0;
38ed7007 548 if (readline(ibuf, linebuf, LINESIZE) < 0)
6447a23f
KS
549 return(savestr(namebuf));
550newname:
828615a1 551 for (cp = linebuf; *cp && *cp != ' '; cp++)
6447a23f 552 ;
828615a1 553 for (; *cp == ' ' || *cp == '\t'; cp++)
6447a23f 554 ;
828615a1
EW
555 for (cp2 = &namebuf[strlen(namebuf)];
556 *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
557 *cp2++ = *cp++;
6447a23f 558 *cp2 = '\0';
38ed7007 559 if (readline(ibuf, linebuf, LINESIZE) < 0)
6447a23f
KS
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) {
828615a1 575 strcpy(namebuf, cp);
6447a23f
KS
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
8bcfa450
KS
587/*
588 * Count the occurances of c in str
589 */
a0d23834 590int
8bcfa450
KS
591charcount(str, c)
592 char *str;
a0d23834 593 int c;
8bcfa450
KS
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
6447a23f 604/*
828615a1 605 * Are any of the characters in the two strings the same?
6447a23f 606 */
a0d23834 607int
828615a1
EW
608anyof(s1, s2)
609 register char *s1, *s2;
6447a23f 610{
6447a23f 611
828615a1
EW
612 while (*s1)
613 if (index(s2, *s1++))
614 return 1;
615 return 0;
6447a23f
KS
616}
617
618/*
828615a1 619 * Convert c to upper case
6447a23f 620 */
a0d23834 621int
828615a1 622raise(c)
a0d23834 623 register int c;
828615a1
EW
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 */
828615a1
EW
634char *
635copy(s1, s2)
6447a23f
KS
636 register char *s1, *s2;
637{
6447a23f 638
828615a1
EW
639 while (*s2++ = *s1++)
640 ;
641 return s2 - 1;
642}
643
da0d3a6d
KS
644/*
645 * See if the given header field is supposed to be ignored.
646 */
a0d23834 647int
887efe38 648isign(field, ignore)
da0d3a6d 649 char *field;
887efe38 650 struct ignoretab ignore[2];
da0d3a6d
KS
651{
652 char realfld[BUFSIZ];
da0d3a6d 653
2de8fc95
EW
654 if (ignore == ignoreall)
655 return 1;
46053c99
S
656 /*
657 * Lower-case the string, so that "Status" and "status"
658 * will hash to the same place.
659 */
da0d3a6d 660 istrcpy(realfld, field);
887efe38
EW
661 if (ignore[1].i_count > 0)
662 return (!member(realfld, ignore + 1));
46053c99
S
663 else
664 return (member(realfld, ignore));
665}
666
a0d23834 667int
46053c99
S
668member(realfield, table)
669 register char *realfield;
887efe38 670 struct ignoretab *table;
46053c99
S
671{
672 register struct ignore *igp;
673
887efe38 674 for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
828615a1
EW
675 if (*igp->i_field == *realfield &&
676 equal(igp->i_field, realfield))
46053c99 677 return (1);
46053c99 678 return (0);
da0d3a6d 679}