Merge branch 'less_closed'
[unleashed.git] / usr / src / cmd / mailx / aux.c
blob09d0b5d018d40c7385b827b98ee3136edc4145b3
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
23 /* All Rights Reserved */
27 * Copyright 1985-2002 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
32 * University Copyright- Copyright (c) 1982, 1986, 1988
33 * The Regents of the University of California
34 * All Rights Reserved
36 * University Acknowledgment- Portions of this document are derived from
37 * software developed by the University of California, Berkeley, and its
38 * contributors.
41 #pragma ident "%Z%%M% %I% %E% SMI"
43 #include "rcv.h"
44 #include <locale.h>
47 * mailx -- a modified version of a University of California at Berkeley
48 * mail program
50 * Auxiliary functions.
53 static char *phrase(char *name, int token, int comma);
54 static char *ripoff(register char *buf);
57 * Return a pointer to a dynamic copy of the argument.
60 char *
61 savestr(char *str)
63 register char *cp, *cp2, *top;
65 for (cp = str; *cp; cp++)
67 top = (char *)salloc((unsigned)(cp-str + 1));
68 if (top == NOSTR)
69 return(NOSTR);
70 for (cp = str, cp2 = top; *cp; cp++)
71 *cp2++ = *cp;
72 *cp2 = 0;
73 return(top);
77 * Announce a fatal error and die.
80 void
81 panic(char *str)
83 fprintf(stderr, gettext("mailx: Panic - %s\n"), str);
84 exit(1);
85 /* NOTREACHED */
89 * Touch the named message by setting its MTOUCH flag.
90 * Touched messages have the effect of not being sent
91 * back to the system mailbox on exit.
94 void
95 touch(int mesg)
97 register struct message *mp;
99 if (mesg < 1 || mesg > msgCount)
100 return;
101 mp = &message[mesg-1];
102 mp->m_flag |= MTOUCH;
103 if ((mp->m_flag & MREAD) == 0)
104 mp->m_flag |= MREAD|MSTATUS;
108 * Test to see if the passed file name is a directory.
109 * Return true if it is.
112 int
113 isdir(char name[])
115 struct stat sbuf;
117 if (stat(name, &sbuf) < 0)
118 return(0);
119 return((sbuf.st_mode & S_IFMT) == S_IFDIR);
123 * Count the number of arguments in the given string raw list.
126 int
127 argcount(char **argv)
129 register char **ap;
131 for (ap = argv; *ap != NOSTR; ap++)
133 return(ap-argv);
137 * Return the desired header line from the passed message
138 * pointer (or NOSTR if the desired header field is not available).
139 * Read all the header lines and concatenate multiple instances of
140 * the requested header.
143 char *
144 hfield(char field[], struct message *mp, char *(*add)(char *, char *))
146 register FILE *ibuf;
147 char linebuf[LINESIZE];
148 register long lc;
149 char *r = NOSTR;
151 ibuf = setinput(mp);
152 if ((lc = mp->m_lines) <= 0)
153 return(NOSTR);
154 if (readline(ibuf, linebuf) < 0)
155 return(NOSTR);
156 lc--;
157 while ((lc = gethfield(ibuf, linebuf, lc)) >= 0)
158 if (ishfield(linebuf, field))
159 r = (*add)(r, hcontents(linebuf));
160 return r;
164 * Return the next header field found in the given message.
165 * Return > 0 if something found, <= 0 elsewise.
166 * Must deal with \ continuations & other such fraud.
170 gethfield(
171 register FILE *f,
172 char linebuf[],
173 register long rem)
175 char line2[LINESIZE];
176 register char *cp, *cp2;
177 register int c;
179 for (;;) {
180 if (rem <= 0)
181 return(-1);
182 if (readline(f, linebuf) < 0)
183 return(-1);
184 rem--;
185 if (strlen(linebuf) == 0)
186 return(-1);
187 if (isspace(linebuf[0]))
188 continue;
189 if (!headerp(linebuf))
190 return(-1);
193 * I guess we got a headline.
194 * Handle wraparounding
197 for (;;) {
198 if (rem <= 0)
199 break;
200 c = getc(f);
201 ungetc(c, f);
202 if (!isspace(c) || c == '\n')
203 break;
204 if (readline(f, line2) < 0)
205 break;
206 rem--;
207 cp2 = line2;
208 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
210 if (strlen(linebuf) + strlen(cp2) >=
211 (unsigned)LINESIZE-2)
212 break;
213 cp = &linebuf[strlen(linebuf)];
214 while (cp > linebuf &&
215 (isspace(cp[-1]) || cp[-1] == '\\'))
216 cp--;
217 *cp++ = ' ';
218 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
220 nstrcpy(cp, LINESIZE - (cp - linebuf), cp2);
222 if ((c = strlen(linebuf)) > 0) {
223 cp = &linebuf[c-1];
224 while (cp > linebuf && isspace(*cp))
225 cp--;
226 *++cp = 0;
228 return(rem);
230 /* NOTREACHED */
234 * Check whether the passed line is a header line of
235 * the desired breed.
238 int
239 ishfield(char linebuf[], char field[])
241 register char *cp;
243 if ((cp = strchr(linebuf, ':')) == NOSTR)
244 return(0);
245 if (cp == linebuf)
246 return(0);
247 *cp = 0;
248 if (icequal(linebuf, field)) {
249 *cp = ':';
250 return(1);
252 *cp = ':';
253 return(0);
257 * Extract the non label information from the given header field
258 * and return it.
261 char *
262 hcontents(char hfield[])
264 register char *cp;
266 if ((cp = strchr(hfield, ':')) == NOSTR)
267 return(NOSTR);
268 cp++;
269 while (*cp && isspace(*cp))
270 cp++;
271 return(cp);
275 * Compare two strings, ignoring case.
278 int
279 icequal(register char *s1, register char *s2)
282 while (toupper(*s1++) == toupper(*s2))
283 if (*s2++ == 0)
284 return(1);
285 return(0);
289 * Copy a string, lowercasing it as we go. Here dstsize is the size of
290 * the destination buffer dst.
292 void
293 istrcpy(char *dst, int dstsize, char *src)
295 register char *cp, *cp2;
297 cp2 = dst;
298 cp = src;
300 while (--dstsize > 0 && *cp != '\0')
301 *cp2++ = tolower(*cp++);
302 *cp2 = '\0';
306 * The following code deals with input stacking to do source
307 * commands. All but the current file pointer are saved on
308 * the stack.
311 static int ssp = -1; /* Top of file stack */
312 static struct sstack {
313 FILE *s_file; /* File we were in. */
314 int s_cond; /* Saved state of conditionals */
315 int s_loading; /* Loading .mailrc, etc. */
316 } *sstack;
319 * Pushdown current input file and switch to a new one.
320 * Set the global flag "sourcing" so that others will realize
321 * that they are no longer reading from a tty (in all probability).
324 int
325 source(char name[])
327 register FILE *fi;
328 register char *cp;
330 if ((cp = expand(name)) == NOSTR)
331 return(1);
332 if ((fi = fopen(cp, "r")) == NULL) {
333 printf(gettext("Unable to open %s\n"), cp);
334 return(1);
337 if (!maxfiles) {
338 if ((maxfiles = (int)ulimit(4, 0)) < 0)
339 #ifndef _NFILE
340 # define _NFILE 20
341 #endif
342 maxfiles = _NFILE;
343 sstack = (struct sstack *)calloc(maxfiles, sizeof(struct sstack));
344 if (sstack == NULL) {
345 printf(gettext(
346 "Couldn't allocate memory for sourcing stack\n"));
347 fclose(fi);
348 return(1);
352 sstack[++ssp].s_file = input;
353 sstack[ssp].s_cond = cond;
354 sstack[ssp].s_loading = loading;
355 loading = 0;
356 cond = CANY;
357 input = fi;
358 sourcing++;
359 return(0);
363 * Pop the current input back to the previous level.
364 * Update the "sourcing" flag as appropriate.
367 int
368 unstack(void)
370 if (ssp < 0) {
371 printf(gettext("\"Source\" stack over-pop.\n"));
372 sourcing = 0;
373 return(1);
375 fclose(input);
376 if (cond != CANY)
377 printf(gettext("Unmatched \"if\"\n"));
378 cond = sstack[ssp].s_cond;
379 loading = sstack[ssp].s_loading;
380 input = sstack[ssp--].s_file;
381 if (ssp < 0)
382 sourcing = loading;
383 return(0);
387 * Touch the indicated file.
388 * This is nifty for the shell.
389 * If we have the utime() system call, this is better served
390 * by using that, since it will work for empty files.
391 * On non-utime systems, we must sleep a second, then read.
394 void
395 alter(char name[])
397 int rc = utime(name, utimep);
398 extern int errno;
400 if (rc != 0) {
401 fprintf(stderr, gettext("Cannot utime %s in aux:alter\n"),
402 name);
403 fprintf(stderr, gettext("Errno: %d\n"), errno);
408 * Examine the passed line buffer and
409 * return true if it is all blanks and tabs.
412 int
413 blankline(const char linebuf[])
415 register const char *cp;
417 for (cp = linebuf; *cp; cp++)
418 if (!any(*cp, " \t"))
419 return(0);
420 return(1);
424 * Skin an arpa net address according to the RFC 822 interpretation
425 * of "host-phrase."
427 static char *
428 phrase(char *name, int token, int comma)
430 register char c;
431 register char *cp, *cp2;
432 char *bufend, *nbufp;
433 int gotlt, lastsp, didq;
434 char nbuf[LINESIZE];
435 int nesting;
437 if (name == NOSTR)
438 return(NOSTR);
439 if (strlen(name) >= (unsigned)LINESIZE)
440 nbufp = (char *)salloc(strlen(name));
441 else
442 nbufp = nbuf;
443 gotlt = 0;
444 lastsp = 0;
445 bufend = nbufp;
446 for (cp = name, cp2 = bufend; (c = *cp++) != 0;) {
447 switch (c) {
448 case '(':
450 Start of a comment, ignore it.
452 nesting = 1;
453 while ((c = *cp) != 0) {
454 cp++;
455 switch(c) {
456 case '\\':
457 if (*cp == 0) goto outcm;
458 cp++;
459 break;
460 case '(':
461 nesting++;
462 break;
463 case ')':
464 --nesting;
465 break;
467 if (nesting <= 0) break;
469 outcm:
470 lastsp = 0;
471 break;
472 case '"':
474 Start a quoted string.
475 Copy it in its entirety.
477 didq = 0;
478 while ((c = *cp) != 0) {
479 cp++;
480 switch (c) {
481 case '\\':
482 if ((c = *cp) == 0) goto outqs;
483 cp++;
484 break;
485 case '"':
486 goto outqs;
488 if (gotlt == 0 || gotlt == '<') {
489 if (lastsp) {
490 lastsp = 0;
491 *cp2++ = ' ';
493 if (!didq) {
494 *cp2++ = '"';
495 didq++;
497 *cp2++ = c;
500 outqs:
501 if (didq)
502 *cp2++ = '"';
503 lastsp = 0;
504 break;
506 case ' ':
507 case '\t':
508 case '\n':
509 if (token && (!comma || c == '\n')) {
510 done:
511 cp[-1] = 0;
512 return cp;
514 lastsp = 1;
515 break;
517 case ',':
518 *cp2++ = c;
519 if (gotlt != '<') {
520 if (token)
521 goto done;
522 bufend = cp2;
523 gotlt = 0;
525 break;
527 case '<':
528 cp2 = bufend;
529 gotlt = c;
530 lastsp = 0;
531 break;
533 case '>':
534 if (gotlt == '<') {
535 gotlt = c;
536 break;
539 /* FALLTHROUGH . . . */
541 default:
542 if (gotlt == 0 || gotlt == '<') {
543 if (lastsp) {
544 lastsp = 0;
545 *cp2++ = ' ';
547 *cp2++ = c;
549 break;
552 *cp2 = 0;
553 return (token ? --cp : equal(name, nbufp) ? name :
554 nbufp == nbuf ? savestr(nbuf) : nbufp);
557 char *
558 skin(char *name)
560 return phrase(name, 0, 0);
564 * Here sz is the buffer size of word.
566 char *
567 yankword(char *name, char *word, int sz, int comma)
569 char *cp;
571 if (name == 0)
572 return 0;
573 while (isspace(*name))
574 name++;
575 if (*name == 0)
576 return 0;
577 cp = phrase(name, 1, comma);
578 nstrcpy(word, sz, name);
579 return cp;
582 int
583 docomma(char *s)
585 return s && strpbrk(s, "(<,");
589 * Fetch the sender's name from the passed message.
592 char *
593 nameof(register struct message *mp)
595 char namebuf[LINESIZE];
596 char linebuf[LINESIZE];
597 register char *cp, *cp2;
598 register FILE *ibuf;
599 int first = 1, wint = 0;
600 char *tmp;
602 if (value("from") && (cp = hfield("from", mp, addto)) != NOSTR)
603 return ripoff(cp);
604 ibuf = setinput(mp);
605 copy("", namebuf);
606 if (readline(ibuf, linebuf) <= 0)
607 return(savestr(namebuf));
608 newname:
609 for (cp = linebuf; *cp != ' '; cp++)
611 while (any(*cp, " \t"))
612 cp++;
613 for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
614 cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
616 *cp2 = '\0';
617 for (;;) {
618 if (readline(ibuf, linebuf) <= 0)
619 break;
620 if (substr(linebuf,"forwarded by ") != -1)
621 continue;
622 if (linebuf[0] == 'F')
623 cp = linebuf;
624 else if (linebuf[0] == '>')
625 cp = linebuf + 1;
626 else
627 break;
628 if (strncmp(cp, "From ", 5) != 0)
629 break;
630 if ((wint = substr(cp, "remote from ")) != -1) {
631 cp += wint + 12;
632 if (first) {
633 copy(cp, namebuf);
634 first = 0;
635 } else {
636 tmp = strrchr(namebuf, '!') + 1;
637 nstrcpy(tmp,
638 sizeof (namebuf) - (tmp - namebuf),
639 cp);
641 nstrcat(namebuf, sizeof (namebuf), "!");
642 goto newname;
643 } else
644 break;
646 for (cp = namebuf; *cp == '!'; cp++);
647 while (ishost(host, cp))
648 cp = strchr(cp, '!') + 1;
649 if (value("mustbang") && !strchr(cp, '!')) {
650 snprintf(linebuf, sizeof (linebuf), "%s!%s",
651 host, cp);
652 cp = linebuf;
654 if (cp2 = hfield("from", mp, addto))
655 return(splice(cp, cp2));
656 else
657 return(savestr(cp));
661 * Splice an address into a commented recipient header.
663 char *
664 splice(char *addr, char *hdr)
666 char buf[LINESIZE];
667 char *cp, *cp2;
669 if (cp = strchr(hdr, '<')) {
670 cp2 = strchr(cp, '>');
671 if (cp2 == NULL) {
672 nstrcpy(buf, sizeof (buf), addr);
673 } else {
674 snprintf(buf, sizeof (buf), "%.*s%s%s",
675 cp - hdr + 1, hdr, addr, cp2);
677 } else if (cp = strchr(hdr, '(')) {
678 snprintf(buf, sizeof (buf), "%s %s",
679 addr, cp);
680 } else
681 nstrcpy(buf, sizeof (buf), addr);
682 return savestr(ripoff(buf));
685 static char *
686 ripoff(register char *buf)
688 register char *cp;
690 cp = buf + strlen(buf);
691 while (--cp >= buf && isspace(*cp));
692 if (cp >= buf && *cp == ',')
693 cp--;
694 *++cp = 0;
695 return buf;
699 * Are any of the characters in the two strings the same?
702 int
703 anyof(register char *s1, register char *s2)
705 register int c;
707 while ((c = *s1++) != 0)
708 if (any(c, s2))
709 return(1);
710 return(0);
714 * See if the given header field is supposed to be ignored.
715 * Fields of the form "Content-*" can't be ignored when saving.
717 int
718 isign(char *field, int saving)
720 char realfld[BUFSIZ];
723 * Lower-case the string, so that "Status" and "status"
724 * will hash to the same place.
726 istrcpy(realfld, sizeof (realfld), field);
728 if (saving && strncmp(realfld, "content-", 8) == 0)
729 return (0);
731 if (nretained > 0)
732 return (!member(realfld, retain));
733 else
734 return (member(realfld, ignore));
737 int
738 member(register char *realfield, register struct ignore **table)
740 register struct ignore *igp;
742 for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link)
743 if (equal(igp->i_field, realfield))
744 return (1);
746 return (0);
750 * This routine looks for string2 in string1.
751 * If found, it returns the position string2 is found at,
752 * otherwise it returns a -1.
754 int
755 substr(char *string1, char *string2)
757 int i, j, len1, len2;
759 len1 = strlen(string1);
760 len2 = strlen(string2);
761 for (i = 0; i < len1 - len2 + 1; i++) {
762 for (j = 0; j < len2 && string1[i+j] == string2[j]; j++)
764 if (j == len2)
765 return(i);
767 return(-1);
771 * Copies src to the dstsize buffer at dst. The copy will never
772 * overflow the destination buffer and the buffer will always be null
773 * terminated.
775 char *
776 nstrcpy(char *dst, int dstsize, char *src)
778 char *cp, *cp2;
780 cp2 = dst;
781 cp = src;
783 while (--dstsize > 0 && *cp != '\0')
784 *cp2++ = *cp++;
785 *cp2 = '\0';
786 return(dst);
790 * Appends src to the dstsize buffer at dst. The append will never
791 * overflow the destination buffer and the buffer will always be null
792 * terminated.
794 char *
795 nstrcat(char *dst, int dstsize, char *src)
797 char *cp, *cp2;
799 cp2 = dst;
800 cp = src;
802 while (*cp2 != '\0') {
803 cp2++;
804 dstsize--;
806 while (--dstsize > 0 && *cp != '\0')
807 *cp2++ = *cp++;
808 *cp2 = '\0';
809 return(dst);