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