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