1 /* $NetBSD: support.c,v 1.21 2008/04/24 01:27:07 christos Exp $ */
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)aux.c 8.1 (Berkeley) 6/6/93";
37 __RCSID("$NetBSD: support.c,v 1.21 2008/04/24 01:27:07 christos Exp $");
50 * Mail -- a mail program
52 * Auxiliary functions.
57 * Return a pointer to a dynamic copy of the argument.
60 savestr(const char *str
)
63 size_t size
= strlen(str
) + 1;
65 if ((new = salloc(size
)) != NULL
)
66 (void)memmove(new, str
, size
);
71 * Make a copy of new argument incorporating old one.
74 save2str(char *str
, char *old
)
77 size_t newsize
= strlen(str
) + 1;
78 size_t oldsize
= old
? strlen(old
) + 1 : 0;
80 if ((new = salloc(newsize
+ oldsize
)) != NULL
) {
82 (void)memmove(new, old
, oldsize
);
83 new[oldsize
- 1] = ' ';
85 (void)memmove(new + oldsize
, str
, newsize
);
91 * Like asprintf(), but with result salloc-ed rather than malloc-ed.
94 sasprintf(char **ret
, const char *format
, ...)
100 va_start(args
, format
);
101 rval
= evasprintf(&p
, format
, args
);
108 struct set_m_flag_args_s
{
113 set_m_flag_core(struct message
*mp
, void *v
)
115 struct set_m_flag_args_s
*args
;
117 mp
->m_flag
&= args
->and_bits
;
118 mp
->m_flag
^= args
->xor_bits
;
122 PUBLIC
struct message
*
123 set_m_flag(int msgnum
, int and_bits
, int xor_bits
)
126 struct set_m_flag_args_s args
;
127 mp
= get_message(msgnum
);
128 args
.and_bits
= and_bits
;
129 args
.xor_bits
= xor_bits
;
130 (void)thread_recursion(mp
, set_m_flag_core
, &args
);
135 * Touch the named message by setting its MTOUCH flag.
136 * Touched messages have the effect of not being sent
137 * back to the system mailbox on exit.
140 touch(struct message
*mp
)
143 mp
->m_flag
|= MTOUCH
;
144 if ((mp
->m_flag
& MREAD
) == 0)
145 mp
->m_flag
|= MREAD
|MSTATUS
;
149 * Test to see if the passed file name is a directory.
150 * Return true if it is.
153 isdir(const char name
[])
157 if (stat(name
, &sbuf
) < 0)
159 return S_ISDIR(sbuf
.st_mode
);
163 * Count the number of arguments in the given string raw list.
166 argcount(char **argv
)
170 for (ap
= argv
; *ap
++ != NULL
; /*EMPTY*/)
172 return (int)(ap
- argv
- 1);
176 * Check whether the passed line is a header line of
177 * the desired breed. Return the field body, or NULL.
180 ishfield(const char linebuf
[], char *colon
, const char field
[])
185 if (strcasecmp(linebuf
, field
) != 0) {
190 for (cp
++; is_WSP(*cp
); cp
++)
196 * Return the next header field found in the given message.
197 * Return >= 0 if something found, < 0 elsewise.
198 * "colon" is set to point to the colon in the header.
199 * Must deal with \ continuations & other such fraud.
201 * WARNING - do not call this outside hfield() or decoding will not
205 gethfield(FILE *f
, char linebuf
[], int rem
, char **colon
)
207 char line2
[LINESIZE
];
214 if ((c
= readline(f
, linebuf
, LINESIZE
, 0)) <= 0)
217 isprint((unsigned char)*cp
) && *cp
!= ' ' && *cp
!= ':';
220 if (*cp
!= ':' || cp
== linebuf
)
223 * I guess we got a headline.
224 * Handle wraparounding
229 while (--cp
>= linebuf
&& is_WSP(*cp
))
234 (void)ungetc(c
= getc(f
), f
);
237 if ((c
= readline(f
, line2
, LINESIZE
, 0)) < 0)
240 cp2
= skip_WSP(line2
);
241 c
-= (int)(cp2
- line2
);
242 if (cp
+ c
>= linebuf
+ LINESIZE
- 2)
245 (void)memmove(cp
, cp2
, (size_t)c
);
255 * Return the desired header line from the passed message
256 * pointer (or NULL if the desired header field is not available).
259 hfield(const char field
[], const struct message
*mp
)
262 char linebuf
[LINESIZE
];
265 char *colon
, *oldhfield
= NULL
;
269 decode
= value(ENAME_MIME_DECODE_MSG
) &&
270 value(ENAME_MIME_DECODE_HDR
);
274 if ((lc
= (int)(mp
->m_lines
- 1)) < 0)
276 if (readline(ibuf
, linebuf
, LINESIZE
, 0) < 0)
279 if ((lc
= gethfield(ibuf
, linebuf
, lc
, &colon
)) < 0)
282 if ((headerfield
= ishfield(linebuf
, colon
, field
)) != NULL
) {
283 char linebuf2
[LINESIZE
];
285 headerfield
= mime_decode_hfield(linebuf2
,
286 sizeof(linebuf2
), linebuf
, headerfield
);
287 oldhfield
= save2str(headerfield
, oldhfield
);
290 if ((headerfield
= ishfield(linebuf
, colon
, field
)) != NULL
)
291 oldhfield
= save2str(headerfield
, oldhfield
);
298 * Copy a string, lowercasing it as we go.
301 istrcpy(char *dest
, const char *src
)
305 *dest
++ = tolower((unsigned char)*src
);
306 } while (*src
++ != 0);
310 * The following code deals with input stacking to do source
311 * commands. All but the current file pointer are saved on
315 static int ssp
; /* Top of file stack */
316 static struct sstack
{
317 FILE *s_file
; /* File we were in. */
318 int s_cond
; /* Saved state of conditionals */
319 #ifdef NEW_CONDITIONAL
320 struct cond_stack_s
*s_cond_stack
; /* Saved conditional stack */
322 int s_loading
; /* Loading .mailrc, etc. */
326 * Pushdown current input file and switch to a new one.
327 * Set the global flag "sourcing" so that others will realize
328 * that they are no longer reading from a tty (in all probability).
337 if ((cp
= expand(*arglist
)) == NULL
)
339 if ((fi
= Fopen(cp
, "r")) == NULL
) {
343 if (ssp
>= NOFILE
- 1) {
344 (void)printf("Too much \"sourcing\" going on.\n");
348 sstack
[ssp
].s_file
= input
;
349 sstack
[ssp
].s_cond
= cond
;
350 #ifdef NEW_CONDITIONAL
351 sstack
[ssp
].s_cond_stack
= cond_stack
;
353 sstack
[ssp
].s_loading
= loading
;
363 * Pop the current input back to the previous level.
364 * Update the "sourcing" flag as appropriate.
370 (void)printf("\"Source\" stack over-pop.\n");
375 if (cond
!= CNONE
|| cond_stack
!= NULL
)
376 (void)printf("Unmatched \"if\"\n");
378 input
= sstack
[ssp
].s_file
;
379 cond
= sstack
[ssp
].s_cond
;
380 #ifdef NEW_CONDITIONAL
381 cond_stack
= sstack
[ssp
].s_cond_stack
;
383 loading
= sstack
[ssp
].s_loading
;
390 * Touch the indicated file.
391 * This is nifty for the shell.
397 struct timeval tv
[2];
401 (void)gettimeofday(&tv
[0], NULL
);
403 TIMESPEC_TO_TIMEVAL(&tv
[1], &sb
.st_mtimespec
);
404 (void)utimes(name
, tv
);
408 * Examine the passed line buffer and
409 * return true if it is all blanks and tabs.
412 blankline(char linebuf
[])
416 for (cp
= linebuf
; *cp
; cp
++)
423 * Start of a "comment".
427 skip_comment(char *cp
)
431 for (/*EMPTY*/; nesting
> 0 && *cp
; cp
++) {
449 * Skin an arpa net address according to the RFC 822 interpretation
463 if (strchr(name
, '(') == NULL
&& strchr(name
, '<') == NULL
464 && strchr(name
, ' ') == NULL
)
469 for (cp
= name
, cp2
= bufend
; (c
= *cp
++) != '\0'; /*EMPTY*/) {
472 cp
= skip_comment(cp
);
478 * Start of a "quoted-string".
479 * Copy it in its entirety.
481 while ((c
= *cp
) != '\0') {
487 else if ((c
= *cp
) != '\0') {
496 if (cp
[0] == 'a' && cp
[1] == 't' && cp
[2] == ' ')
497 cp
+= 3, *cp2
++ = '@';
499 if (cp
[0] == '@' && cp
[1] == ' ')
500 cp
+= 2, *cp2
++ = '@';
514 while ((c
= *cp
) && c
!= ',') {
517 cp
= skip_comment(cp
);
519 while ((c
= *cp
) != '\0') {
523 if (c
== '\\' && *cp
)
538 if (c
== ',' && !gotlt
) {
540 for (/*EMPTY*/; *cp
== ' '; cp
++)
549 return savestr(nbuf
);
553 * Fetch the sender's name from the passed message.
555 * 0 -- get sender's name for display purposes
556 * 1 -- get sender's name for reply
557 * 2 -- get sender's name for Reply
560 name1(struct message
*mp
, int reptype
)
562 char namebuf
[LINESIZE
];
563 char linebuf
[LINESIZE
];
568 if ((cp
= hfield("from", mp
)) != NULL
)
570 if (reptype
== 0 && (cp
= hfield("sender", mp
)) != NULL
)
574 if (readline(ibuf
, linebuf
, LINESIZE
, 0) < 0)
575 return savestr(namebuf
);
577 for (cp
= linebuf
; *cp
&& *cp
!= ' '; cp
++)
580 for (cp2
= &namebuf
[strlen(namebuf
)];
581 *cp
&& !is_WSP(*cp
) && cp2
< namebuf
+ LINESIZE
- 1;
585 if (readline(ibuf
, linebuf
, LINESIZE
, 0) < 0)
586 return savestr(namebuf
);
587 if ((cp
= strchr(linebuf
, 'F')) == NULL
)
588 return savestr(namebuf
);
589 if (strncmp(cp
, "From", 4) != 0)
590 return savestr(namebuf
);
591 while ((cp
= strchr(cp
, 'r')) != NULL
) {
592 if (strncmp(cp
, "remote", 6) == 0) {
593 if ((cp
= strchr(cp
, 'f')) == NULL
)
595 if (strncmp(cp
, "from", 4) != 0)
597 if ((cp
= strchr(cp
, ' ')) == NULL
)
604 cp2
= strrchr(namebuf
, '!') + 1;
605 while (*cp
&& cp2
< namebuf
+ LINESIZE
- 1)
607 if (cp2
< namebuf
+ LINESIZE
- 1)
610 if (cp2
< namebuf
+ LINESIZE
- 1)
617 return savestr(namebuf
);
621 * Count the occurrences of c in str
624 charcount(char *str
, int c
)
629 for (i
= 0, cp
= str
; *cp
; cp
++)
636 * Get sender's name from this message. If the message has
637 * a bunch of arpanet stuff in it, we may have to skin the name
638 * before returning it.
641 nameof(struct message
*mp
, int reptype
)
645 cp
= skin(name1(mp
, reptype
));
646 if (reptype
!= 0 || charcount(cp
, '!') < 2)
648 cp2
= strrchr(cp
, '!');
650 while (cp2
> cp
&& *cp2
!= '!')
658 * Copy s1 to s2, return pointer to null in s2.
661 copy(char *s1
, char *s2
)
664 while ((*s2
++ = *s1
++) != '\0')
670 * The core routine to check the ignore table for a field.
671 * Note: realfield should be lower-cased!
674 member(char *realfield
, struct ignoretab
*table
)
678 for (igp
= table
->i_head
[hash(realfield
)]; igp
!= 0; igp
= igp
->i_link
)
679 if (*igp
->i_field
== *realfield
&&
680 equal(igp
->i_field
, realfield
))
686 * See if the given header field is supposed to be ignored.
689 isign(const char *field
, struct ignoretab ignoretabs
[2])
691 char realfld
[LINESIZE
];
693 if (ignoretabs
== ignoreall
)
696 * Lower-case the string, so that "Status" and "status"
697 * will hash to the same place.
699 istrcpy(realfld
, field
);
700 if (ignoretabs
[1].i_count
> 0)
701 return !member(realfld
, ignoretabs
+ 1);
703 return member(realfld
, ignoretabs
);
707 add_ignore(const char *name
, struct ignoretab
*tab
)
709 char field
[LINESIZE
];
713 istrcpy(field
, name
);
714 if (member(field
, tab
))
717 igp
= ecalloc(1, sizeof(struct ignore
));
718 igp
->i_field
= ecalloc(strlen(field
) + 1, sizeof(char));
719 (void)strcpy(igp
->i_field
, field
);
720 igp
->i_link
= tab
->i_head
[h
];
721 tab
->i_head
[h
] = igp
;
726 * Write a file to stdout, skipping comment lines.
729 cathelp(const char *fname
)
735 if ((f
= Fopen(fname
, "r")) == NULL
) {
739 while ((line
= fgetln(f
, &len
)) != NULL
) {
742 if (fwrite(line
, 1, len
, stdout
) != len
)