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