# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2018 Joyent, Inc. # Copyright 2019 Garrett D'Amore # # cmd/mailx/Makefile PROG= mailx OBJS= myfopen.o cmd1.o cmd2.o cmd3.o \ cmd4.o cmdtab.o collect.o config.o edit.o \ init.o fio.o getname.o head.o \ hostname.o lex.o list.o lock.o lpaths.o \ main.o names.o optim.o popen.o quit.o \ receipt.o send.o sigretro.o stralloc.o temp.o \ translate.o tty.o usg.local.o util.o vars.o SRCS= $(OBJS:.o=.c) MISC= misc include ../Makefile.cmd # # for messaging catalogue file # POFILE= mailx.po POFILES= $(SRCS:%.c=%.po) ROOTMAILXD= $(ROOTSHLIB)/mailx MAILXHELP= mailx.help mailx.help.~ ROOTMAILXHELP= $(MAILXHELP:%=$(ROOTMAILXD)/%) # Hammerhead: /usr/ucb removed — no ucb symlinks $(ROOTPROG) : FILEMODE = 02511 $(ROOTMAILXHELP) : FILEMODE = 0644 CPPFLAGS= -Ihdr -DUSG -DOPTIM $(CPPFLAGS.master) CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-unused-variable CERRWARN += -Wno-clobbered # Hammerhead: Suppress pointer/int cast warnings in legacy mailx code CERRWARN += -Wno-pointer-to-int-cast CERRWARN += -Wno-int-to-pointer-cast CERRWARN += -Wno-return-local-addr # not linted SMATCH=off LDLIBS += -lmail -lcustr LDFLAGS += $(MAPFILE.NGB:%=-Wl,-M%) CLOBBERFILES += $(MAILXHELP) # install rules $(ROOTMAILXD)/% : % $(INS.file) .KEEP_STATE: all: $(PROG) $(MAILXHELP) $(PROG): $(OBJS) $(LIBMAIL) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) install: all $(ROOTMAILXD) $(ROOTPROG) $(ROOTMAILXHELP) $(ROOTMAILXD): $(INS.dir) $(MAILXHELP): $(MISC)/$$@ $(GREP) -v '^#.*@(' $(MISC)/$@ > $@ $(POFILE): $(POFILES) $(RM) $@ cat $(POFILES) > $@ clean: $(RM) $(OBJS) include ../Makefile.targ FRC: /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2014 Joyent, Inc. */ /* * Copyright 2001 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include #include "rcv.h" #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * User commands. */ static char *dispname(char *hdr); static void print(register struct message *mp, FILE *obuf, int doign); static int type1(int *msgvec, int doign, int page); static int topputs(const char *line, FILE *obuf); void brokpipe(int sig); jmp_buf pipestop; /* * Print the current active headings. * Don't change dot if invoker didn't give an argument. */ static int curscreen = 0, oldscreensize = 0; int headers(int *msgvec) { register int n, mesg, flag; register struct message *mp; int size; size = screensize(); n = msgvec[0]; if (n != 0) curscreen = (n-1)/size; if (curscreen < 0) curscreen = 0; mp = &message[curscreen * size]; if (mp >= &message[msgCount]) mp = &message[msgCount - size]; if (mp < &message[0]) mp = &message[0]; flag = 0; mesg = mp - &message[0]; if (dot != &message[n-1]) dot = mp; if (Hflag) mp = message; for (; mp < &message[msgCount]; mp++) { mesg++; if (mp->m_flag & MDELETED) continue; if (flag++ >= size && !Hflag) break; printhead(mesg); sreset(); } if (flag == 0) { printf(gettext("No more mail.\n")); return (1); } return (0); } /* * Scroll to the next/previous screen */ int scroll(char arg[]) { register int s, size; int cur[1]; cur[0] = 0; size = screensize(); s = curscreen; switch (*arg) { case 0: case '+': s++; if (s * size > msgCount) { printf(gettext("On last screenful of messages\n")); return (0); } curscreen = s; break; case '-': if (--s < 0) { printf(gettext("On first screenful of messages\n")); return (0); } curscreen = s; break; default: printf(gettext("Unrecognized scrolling command \"%s\"\n"), arg); return (1); } return (headers(cur)); } /* * Compute what the screen size should be. * We use the following algorithm: * If user specifies with screen option, use that. * If baud rate < 1200, use 5 * If baud rate = 1200, use 10 * If baud rate > 1200, use 20 */ int screensize(void) { register char *cp; register int newscreensize, tmp; #ifdef TIOCGWINSZ struct winsize ws; #endif if ((cp = value("screen")) != NOSTR && (tmp = atoi(cp)) > 0) newscreensize = tmp; else if (baud < B1200) newscreensize = 5; else if (baud == B1200) newscreensize = 10; #ifdef TIOCGWINSZ else if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) == 0 && ws.ws_row > 4) newscreensize = ws.ws_row - 4; #endif else newscreensize = 20; /* renormalize the value of curscreen */ if (newscreensize != oldscreensize) { curscreen = curscreen * oldscreensize / newscreensize; oldscreensize = newscreensize; } return (newscreensize); } /* * Print out the headlines for each message * in the passed message list. */ int from(int *msgvec) { int *ip; for (ip = msgvec; *ip != 0; ip++) { printhead(*ip); sreset(); } if (--ip >= msgvec) dot = &message[*ip - 1]; return (0); } /* * Print out the header of a specific message. * This is a slight improvement to the standard one. */ void printhead(int mesg) { struct message *mp; FILE *ibuf; char headline[LINESIZE], *subjline, dispc, curind; char *fromline; char pbuf[LINESIZE]; char name[LINESIZE]; headline_t *hl; register char *cp; int showto; if (headline_alloc(&hl) != 0) { err(1, "could not allocate memory"); } mp = &message[mesg-1]; ibuf = setinput(mp); readline(ibuf, headline); if ((subjline = hfield("subject", mp, addone)) == NOSTR && (subjline = hfield("subj", mp, addone)) == NOSTR && (subjline = hfield("message-status", mp, addone)) == NOSTR) subjline = ""; curind = (!Hflag && dot == mp) ? '>' : ' '; dispc = ' '; showto = 0; if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW)) dispc = 'R'; if (!(int)value("bsdcompat") && (mp->m_flag & (MREAD|MNEW)) == MREAD) dispc = 'O'; if ((mp->m_flag & (MREAD|MNEW)) == MNEW) dispc = 'N'; if ((mp->m_flag & (MREAD|MNEW)) == 0) dispc = 'U'; if (mp->m_flag & MSAVED) if ((int)value("bsdcompat")) dispc = '*'; else dispc = 'S'; if (mp->m_flag & MPRESERVE) if ((int)value("bsdcompat")) dispc = 'P'; else dispc = 'H'; if (mp->m_flag & MBOX) dispc = 'M'; if (parse_headline(headline, hl) == -1) { headline_reset(hl); } if (custr_len(hl->hl_date) == 0) { if (custr_append(hl->hl_date, "") != 0) { err(1, "could not print header"); } } /* * Netnews interface? */ if (newsflg) { if ((fromline = hfield("newsgroups", mp, addone)) == NOSTR && (fromline = hfield("article-id", mp, addone)) == NOSTR) fromline = "<>"; else for (cp = fromline; *cp; cp++) { /* limit length */ if (any(*cp, " ,\n")) { *cp = '\0'; break; } } /* * else regular. */ } else { fromline = nameof(mp); if (value("showto") && samebody(myname, skin(fromline), FALSE) && (cp = hfield("to", mp, addto))) { showto = 1; yankword(cp, fromline = name, sizeof (name), docomma(cp)); } if (value("showname")) fromline = dispname(fromline); else fromline = skin(fromline); } printf("%c%c%3d ", curind, dispc, mesg); if ((int)value("showfull")) { if (showto) printf("To %-15s ", fromline); else printf("%-18s ", fromline); } else { if (showto) printf("To %-15.15s ", fromline); else printf("%-18.18s ", fromline); } if (mp->m_text) { printf("%16.16s %4ld/%-5ld %-.25s\n", custr_cstr(hl->hl_date), mp->m_lines, mp->m_size, subjline); } else { printf("%16.16s binary/%-5ld %-.25s\n", custr_cstr(hl->hl_date), mp->m_size, subjline); } headline_free(hl); } /* * Return the full name from an RFC-822 header line * or the last two (or one) component of the address. */ static char * dispname(char *hdr) { char *cp, *cp2; if (hdr == 0) return (0); if (((cp = strchr(hdr, '<')) != 0) && (cp > hdr)) { *cp = 0; if ((*hdr == '"') && ((cp = strrchr(++hdr, '"')) != 0)) *cp = 0; return (hdr); } else if ((cp = strchr(hdr, '(')) != 0) { hdr = ++cp; if ((cp = strchr(hdr, '+')) != 0) *cp = 0; if ((cp = strrchr(hdr, ')')) != 0) *cp = 0; return (hdr); } cp = skin(hdr); if ((cp2 = strrchr(cp, '!')) != 0) { while (cp2 >= cp && *--cp2 != '!'); cp = ++cp2; } return (cp); } /* * Print out the value of dot. */ int pdot(void) { printf("%d\n", dot - &message[0] + 1); return (0); } /* * Print out all the possible commands. */ int pcmdlist(void) { register const struct cmd *cp; register int cc; printf("Commands are:\n"); for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) { cc += strlen(cp->c_name) + 2; if (cc > 72) { printf("\n"); cc = strlen(cp->c_name) + 2; } if ((cp+1)->c_name != NOSTR) printf("%s, ", cp->c_name); else printf("%s\n", cp->c_name); } return (0); } /* * Paginate messages, honor ignored fields. */ int more(int *msgvec) { return (type1(msgvec, 1, 1)); } /* * Paginate messages, even printing ignored fields. */ int More(int *msgvec) { return (type1(msgvec, 0, 1)); } /* * Type out messages, honor ignored fields. */ int type(int *msgvec) { return (type1(msgvec, 1, 0)); } /* * Type out messages, even printing ignored fields. */ int Type(int *msgvec) { return (type1(msgvec, 0, 0)); } /* * Type out the messages requested. */ static int type1(int *msgvec, int doign, int page) { int *ip; register struct message *mp; register int mesg; register char *cp; long nlines; FILE *obuf; void (*sigint)(int), (*sigpipe)(int); int setsigs = 0; obuf = stdout; if (setjmp(pipestop)) { if (obuf != stdout) { pipef = NULL; npclose(obuf); } goto ret0; } if (intty && outtty && (page || (cp = value("crt")) != NOSTR)) { if (!page) { nlines = 0; for (ip = msgvec, nlines = 0; *ip && ip-msgvec < msgCount; ip++) nlines += message[*ip - 1].m_lines; } if (page || nlines > (*cp == '\0' ? screensize() - 2 : atoi(cp))) { obuf = npopen(MORE, "w"); if (obuf == NULL) { perror(MORE); obuf = stdout; } else { pipef = obuf; sigint = sigset(SIGINT, SIG_IGN); sigpipe = sigset(SIGPIPE, brokpipe); setsigs++; } } } for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mesg = *ip; touch(mesg); mp = &message[mesg-1]; dot = mp; print(mp, obuf, doign); } if (obuf != stdout) { pipef = NULL; npclose(obuf); } ret0: if (setsigs) { sigset(SIGPIPE, sigpipe); sigset(SIGINT, sigint); } return (0); } /* * Respond to a broken pipe signal -- * probably caused by user quitting more. */ void #ifdef __cplusplus brokpipe(int) #else /* ARGSUSED */ brokpipe(int s) #endif { #ifdef OLD_BSD_SIGS sigrelse(SIGPIPE); #endif longjmp(pipestop, 1); } /* * Print the indicated message on standard output. */ static void print(register struct message *mp, FILE *obuf, int doign) { if (value("quiet") == NOSTR && (!doign || !isign("message", 0))) fprintf(obuf, "Message %2d:\n", mp - &message[0] + 1); touch(mp - &message[0] + 1); if (mp->m_text) { (void) msend(mp, obuf, doign ? M_IGNORE : 0, fputs); } else { fprintf(obuf, "\n%s\n", gettext(binmsg)); } } /* * Print the top so many lines of each desired message. * The number of lines is taken from the variable "toplines" * and defaults to 5. */ static long top_linecount, top_maxlines, top_lineb; static jmp_buf top_buf; int top(int *msgvec) { register int *ip; register struct message *mp; register int mesg; char *valtop; top_maxlines = 5; valtop = value("toplines"); if (valtop != NOSTR) { top_maxlines = atoi(valtop); if (top_maxlines < 0 || top_maxlines > 10000) top_maxlines = 5; } top_lineb = 1; for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mesg = *ip; touch(mesg); mp = &message[mesg-1]; dot = mp; if (value("quiet") == NOSTR) printf("Message %2d:\n", mesg); if (!top_lineb) printf("\n"); top_linecount = 0; if (setjmp(top_buf) == 0) { if (mp->m_text) { (void) msend(mp, stdout, M_IGNORE, topputs); } else { printf("\n%s\n", gettext(binmsg)); } } } return (0); } int topputs(const char *line, FILE *obuf) { if (top_linecount++ >= top_maxlines) longjmp(top_buf, 1); top_lineb = blankline(line); return (fputs(line, obuf)); } /* * Touch all the given messages so that they will * get mboxed. */ int stouch(int msgvec[]) { register int *ip; for (ip = msgvec; *ip != 0; ip++) { dot = &message[*ip-1]; dot->m_flag |= MTOUCH; dot->m_flag &= ~MPRESERVE; } return (0); } /* * Make sure all passed messages get mboxed. */ int mboxit(int msgvec[]) { register int *ip; for (ip = msgvec; *ip != 0; ip++) { dot = &message[*ip-1]; dot->m_flag |= MTOUCH|MBOX; dot->m_flag &= ~MPRESERVE; } return (0); } /* * List the folders the user currently has. */ int folders(char **arglist) { char dirname[BUFSIZ], cmd[BUFSIZ]; if (getfold(dirname) < 0) { printf(gettext("No value set for \"folder\"\n")); return (-1); } if (*arglist) { nstrcat(dirname, sizeof (dirname), "/"); nstrcat(dirname, sizeof (dirname), *arglist); } snprintf(cmd, sizeof (cmd), "%s %s", LS, dirname); return (system(cmd)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * More user commands. */ static int igshow(void); static int igcomp(const void *l, const void *r); static int save1(char str[], int mark); static int Save1(int *msgvec, int mark); static void savemsglist(char *file, int *msgvec, int flag); static int put1(char str[], int doign); static int svputs(const char *line, FILE *obuf); static int wrputs(const char *line, FILE *obuf); static int retshow(void); /* flags for savemsglist() */ #define S_MARK 1 /* mark the message as saved */ #define S_NOHEADER 2 /* don't write out the header */ #define S_SAVING 4 /* doing save/copy */ #define S_NOIGNORE 8 /* don't do ignore processing */ /* * If any arguments were given, print the first message * identified by the first argument. If no arguments are given, * print the next applicable message after dot. */ int next(int *msgvec) { register struct message *mp; int list[2]; if (*msgvec != 0) { if (*msgvec < 0) { printf((gettext("Negative message given\n"))); return (1); } mp = &message[*msgvec - 1]; if ((mp->m_flag & MDELETED) == 0) { dot = mp; goto hitit; } printf(gettext("No applicable message\n")); return (1); } /* * If this is the first command, select message 1. * Note that this must exist for us to get here at all. */ if (!sawcom) goto hitit; /* * Just find the next good message after dot, no * wraparound. */ for (mp = dot+1; mp < &message[msgCount]; mp++) if ((mp->m_flag & (MDELETED|MSAVED)) == 0) break; if (mp >= &message[msgCount]) { printf(gettext("At EOF\n")); return (0); } dot = mp; hitit: /* * Print dot. */ list[0] = dot - &message[0] + 1; list[1] = 0; return (type(list)); } /* * Save a message in a file. Mark the message as saved * so we can discard when the user quits. */ int save(char str[]) { return (save1(str, S_MARK)); } /* * Copy a message to a file without affected its saved-ness */ int copycmd(char str[]) { return (save1(str, 0)); } /* * Save/copy the indicated messages at the end of the passed file name. * If mark is true, mark the message "saved." */ static int save1(char str[], int mark) { char *file, *cmd; int f, *msgvec; cmd = mark ? "save" : "copy"; msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec)); if ((file = snarf(str, &f, 0)) == NOSTR) file = Getf("MBOX"); if (f == -1) return (1); if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf(gettext("No messages to %s.\n"), cmd); return (1); } msgvec[1] = 0; } if (f && getmsglist(str, msgvec, 0) < 0) return (1); if ((file = expand(file)) == NOSTR) return (1); savemsglist(file, msgvec, mark | S_SAVING); return (0); } int Save(int *msgvec) { return (Save1(msgvec, S_MARK)); } int Copy(int *msgvec) { return (Save1(msgvec, 0)); } /* * save/copy the indicated messages at the end of a file named * by the sender of the first message in the msglist. */ static int Save1(int *msgvec, int mark) { register char *from; char recfile[BUFSIZ]; #ifdef notdef from = striphosts(nameof(&message[*msgvec-1], 0)); #else from = nameof(&message[*msgvec-1]); #endif getrecf(from, recfile, 1, sizeof (recfile)); if (*recfile != '\0') savemsglist(safeexpand(recfile), msgvec, mark | S_SAVING); return (0); } int sput(char str[]) { return (put1(str, 0)); } int Sput(char str[]) { return (put1(str, S_NOIGNORE)); } /* * Put the indicated messages at the end of the passed file name. */ static int put1(char str[], int doign) { char *file; int f, *msgvec; msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec)); if ((file = snarf(str, &f, 0)) == NOSTR) file = Getf("MBOX"); if (f == -1) return (1); if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf(gettext("No messages to put.\n")); return (1); } msgvec[1] = 0; } if (f && getmsglist(str, msgvec, 0) < 0) return (1); if ((file = expand(file)) == NOSTR) return (1); savemsglist(file, msgvec, doign); return (0); } /* * save a message list in a file. * if wr set, doing "write" instead * of "save" or "copy" so don't put * out header. */ static int wr_linecount; /* count of lines written */ static int wr_charcount; /* char count of lines written */ static int wr_inlines; /* count of lines read */ static long wr_maxlines; /* total lines in message */ static int wr_inhead; /* in header of message */ static void savemsglist(char *file, int *msgvec, int flag) { register int *ip, mesg; register struct message *mp; char *disp; FILE *obuf; struct stat statb; long lc, cc, t; int bnry, mflag; printf("\"%s\" ", file); flush(); if (stat(file, &statb) >= 0) disp = "[Appended]"; else disp = "[New file]"; if ((obuf = fopen(file, "a")) == NULL) { perror(""); return; } lc = cc = 0; bnry = 0; if (flag & S_SAVING) mflag = (int)value("alwaysignore")?(M_IGNORE|M_SAVING):M_SAVING; else if (flag & S_NOIGNORE) mflag = 0; else mflag = M_IGNORE; for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mesg = *ip; mp = &message[mesg-1]; if (!mp->m_text) { bnry = 1; } wr_linecount = 0; wr_charcount = 0; if (flag & S_NOHEADER) { wr_inhead = 1; wr_maxlines = mp->m_lines; wr_inlines = 0; t = msend(mp, obuf, 0, wrputs); } else { t = msend(mp, obuf, mflag, svputs); } if (t < 0) { perror(file); fclose(obuf); return; } touch(mesg); dot = mp; lc += wr_linecount; cc += wr_charcount; if (flag & S_MARK) mp->m_flag |= MSAVED; } fflush(obuf); if (fferror(obuf)) perror(file); fclose(obuf); if (!bnry) { printf("%s %ld/%ld\n", disp, lc, cc); } else { printf("%s binary/%ld\n", disp, cc); } } static int svputs(const char *line, FILE *obuf) { wr_linecount++; wr_charcount += strlen(line); return (fputs(line, obuf)); } static int wrputs(const char *line, FILE *obuf) { /* * If this is a header line or * the last line, don't write it out. Since we may add a * "Status" line the line count may be off by one so insist * that the last line is blank before we skip it. */ wr_inlines++; if (wr_inhead) { if (strcmp(line, "\n") == 0) wr_inhead = 0; return (0); } if (wr_inlines >= wr_maxlines && strcmp(line, "\n") == 0) return (0); wr_linecount++; wr_charcount += strlen(line); return (fputs(line, obuf)); } /* * Write the indicated messages at the end of the passed * file name, minus header and trailing blank line. */ int swrite(char str[]) { register char *file; int f, *msgvec; msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec)); if ((file = snarf(str, &f, 1)) == NOSTR) return (1); if (f == -1) return (1); if ((file = expand(file)) == NOSTR) return (1); if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf(gettext("No messages to write.\n")); return (1); } msgvec[1] = 0; } if (f && getmsglist(str, msgvec, 0) < 0) return (1); savemsglist(file, msgvec, S_MARK|S_NOHEADER); return (0); } /* * Snarf the file from the end of the command line and * return a pointer to it. If there is no file attached, * just return NOSTR. Put a null in front of the file * name so that the message list processing won't see it, * unless the file name is the only thing on the line, in * which case, return 0 in the reference flag variable. */ /* * The following definitions are used to characterize the syntactic * category of the preceding character in the following parse procedure. * The variable pc_type assumes these values. */ #define SN_DELIM 1 /* Delimiter ( or line beginning) */ #define SN_TOKEN 2 /* A part of a token */ #define SN_QUOTE 4 /* An entire quoted string (ie, "...") */ char * snarf(char linebuf[], int *flag, int erf) { register char *p; /* utility pointer */ register char qc; /* quotation character to match */ register unsigned int pc_type; /* preceding character type */ register char *tok_beg; /* beginning of last token */ register char *tok_end; /* end of last token */ char *line_beg; /* beginning of line, after */ /* leading whitespace */ /* * Skip leading whitespace. */ for (line_beg = linebuf; *line_beg && any(*line_beg, " \t"); line_beg++) { /* empty body */ } if (!*line_beg) { if (erf) { printf(gettext("No file specified.\n")); } *flag = 0; return (NOSTR); } /* * Process line from left-to-right, 1 char at a time. */ pc_type = SN_DELIM; tok_beg = tok_end = NOSTR; p = line_beg; while (*p != '\0') { if (any(*p, " \t")) { /* This character is a DELIMITER */ if (pc_type & (SN_TOKEN|SN_QUOTE)) { tok_end = p - 1; } pc_type = SN_DELIM; p++; } else if ((qc = *p) == '"' || qc == '\'') { /* This character is a QUOTE character */ if (pc_type == SN_TOKEN) { /* embedded quotation symbols are simply */ /* token characters. */ p++; continue; } /* Search for the matching QUOTE character */ for (tok_beg = p, tok_end = NOSTR, p++; *p != '\0' && *p != qc; p++) { if (*p == '\\' && *(p+1) == qc) { p++; } } if (*p == '\0') { printf(gettext("Syntax error: missing " "%c.\n"), qc); *flag = -1; return (NOSTR); } tok_end = p; pc_type = SN_QUOTE; p++; } else { /* This character should be a TOKEN character */ if (pc_type & (SN_DELIM|SN_TOKEN)) { if (pc_type & SN_DELIM) { tok_beg = p; tok_end = NOSTR; } } else { printf(gettext("improper quotes" " at \"%s\".\n"), p); *flag = -1; return (NOSTR); } if (*p == '\\' && *++p == '\0') { printf(gettext("\'\\\' at " "end of line.\n")); *flag = -1; return (NOSTR); } pc_type = SN_TOKEN; p++; } } if (pc_type == SN_TOKEN) { tok_end = p - 1; } if (tok_beg != NOSTR && tok_end != NOSTR) { if (tok_beg == line_beg) { *flag = 0; } else { tok_beg[-1] = '\0'; *flag = 1; } tok_end[1] = '\0'; return (tok_beg); } else { if (erf) { printf(gettext("No file specified.\n")); } *flag = 0; return (NOSTR); } } /* * Delete messages, then type the new dot. */ int deltype(int msgvec[]) { int list[2]; int lastdot; lastdot = dot - &message[0] + 1; if (delm(msgvec) >= 0) { list[0] = dot - &message[0]; list[0]++; if (list[0] > lastdot) { touch(list[0]); list[1] = 0; return (type(list)); } printf(gettext("At EOF\n")); return (0); } else { printf(gettext("No more messages\n")); return (0); } } /* * Delete the indicated messages. * Set dot to some nice place afterwards. */ int delm(int *msgvec) { register struct message *mp; int *ip, mesg; int last; last = 0; for (ip = msgvec; *ip != 0; ip++) { mesg = *ip; touch(mesg); mp = &message[mesg-1]; mp->m_flag |= MDELETED|MTOUCH; mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); last = mesg; } if (last != 0) { dot = &message[last-1]; last = first(0, MDELETED); if (last != 0) { dot = &message[last-1]; return (0); } else { dot = &message[0]; return (-1); } } /* * Following can't happen -- it keeps lint happy */ return (-1); } /* * Undelete the indicated messages. */ int undelete(int *msgvec) { register struct message *mp; int *ip, mesg; for (ip = msgvec; ip-msgvec < msgCount; ip++) { mesg = *ip; if (mesg == 0) return (0); touch(mesg); mp = &message[mesg-1]; dot = mp; mp->m_flag &= ~MDELETED; } return (0); } /* * Add the given header fields to the retained list. * If no arguments, print the current list of retained fields. */ int retfield(char *list[]) { char field[BUFSIZ]; register int h; register struct ignore *igp; char **ap; if (argcount(list) == 0) return (retshow()); for (ap = list; *ap != 0; ap++) { istrcpy(field, sizeof (field), *ap); if (member(field, retain)) continue; h = hash(field); if ((igp = (struct ignore *) calloc(1, sizeof (struct ignore))) == NULL) { panic("Couldn't allocate memory"); } if ((igp->i_field = (char *) calloc(strlen(field) + 1, sizeof (char))) == NULL) { panic("Couldn't allocate memory"); } strcpy(igp->i_field, field); igp->i_link = retain[h]; retain[h] = igp; nretained++; } return (0); } /* * Print out all currently retained fields. */ static int retshow(void) { register int h, count; struct ignore *igp; char **ap, **ring; count = 0; for (h = 0; h < HSHSIZE; h++) for (igp = retain[h]; igp != 0; igp = igp->i_link) count++; if (count == 0) { printf(gettext("No fields currently being retained.\n")); return (0); } ring = (char **)salloc((count + 1) * sizeof (char *)); ap = ring; for (h = 0; h < HSHSIZE; h++) for (igp = retain[h]; igp != 0; igp = igp->i_link) *ap++ = igp->i_field; *ap = 0; qsort(ring, count, sizeof (char *), igcomp); for (ap = ring; *ap != 0; ap++) printf("%s\n", *ap); return (0); } /* * Remove a list of fields from the retain list. */ int unretfield(char *list[]) { char **ap, field[BUFSIZ]; register int h, count = 0; register struct ignore *ig1, *ig2; if (argcount(list) == 0) { for (h = 0; h < HSHSIZE; h++) { ig1 = retain[h]; while (ig1) { free(ig1->i_field); ig2 = ig1->i_link; free((char *)ig1); ig1 = ig2; count++; } retain[h] = NULL; } if (count == 0) printf(gettext( "No fields currently being retained.\n")); nretained = 0; return (0); } for (ap = list; *ap; ap++) { istrcpy(field, sizeof (field), *ap); h = hash(field); for (ig1 = retain[h]; ig1; ig2 = ig1, ig1 = ig1->i_link) if (strcmp(ig1->i_field, field) == 0) { if (ig1 == retain[h]) retain[h] = ig1->i_link; else ig2->i_link = ig1->i_link; free(ig1->i_field); free((char *)ig1); nretained--; break; } } return (0); } /* * Add the given header fields to the ignored list. * If no arguments, print the current list of ignored fields. */ int igfield(char *list[]) { char field[BUFSIZ]; register int h; register struct ignore *igp; char **ap; if (argcount(list) == 0) return (igshow()); for (ap = list; *ap != 0; ap++) { if (isign(*ap, 0)) continue; istrcpy(field, sizeof (field), *ap); h = hash(field); if ((igp = (struct ignore *) calloc(1, sizeof (struct ignore))) == NULL) { panic("Couldn't allocate memory"); } if ((igp->i_field = (char *) calloc((unsigned)strlen(field) + 1, sizeof (char))) == NULL) { panic("Couldn't allocate memory"); } strcpy(igp->i_field, field); igp->i_link = ignore[h]; ignore[h] = igp; } return (0); } /* * Print out all currently ignored fields. */ static int igshow(void) { register int h, count; struct ignore *igp; char **ap, **ring; count = 0; for (h = 0; h < HSHSIZE; h++) for (igp = ignore[h]; igp != 0; igp = igp->i_link) count++; if (count == 0) { printf(gettext("No fields currently being ignored.\n")); return (0); } ring = (char **)salloc((count + 1) * sizeof (char *)); ap = ring; for (h = 0; h < HSHSIZE; h++) for (igp = ignore[h]; igp != 0; igp = igp->i_link) *ap++ = igp->i_field; *ap = 0; qsort((char *)ring, (unsigned)count, sizeof (char *), igcomp); for (ap = ring; *ap != 0; ap++) printf("%s\n", *ap); return (0); } /* * Compare two names for sorting ignored field list. */ static int igcomp(const void *l, const void *r) { return (strcmp(*(char **)l, *(char **)r)); } /* * Remove a list of fields from the ignore list. */ int unigfield(char *list[]) { char **ap, field[BUFSIZ]; register int h, count = 0; register struct ignore *ig1, *ig2; if (argcount(list) == 0) { for (h = 0; h < HSHSIZE; h++) { ig1 = ignore[h]; while (ig1) { free(ig1->i_field); ig2 = ig1->i_link; free((char *)ig1); ig1 = ig2; count++; } ignore[h] = NULL; } if (count == 0) printf(gettext("No fields currently being ignored.\n")); return (0); } for (ap = list; *ap; ap++) { istrcpy(field, sizeof (field), *ap); h = hash(field); for (ig1 = ignore[h]; ig1; ig2 = ig1, ig1 = ig1->i_link) if (strcmp(ig1->i_field, field) == 0) { if (ig1 == ignore[h]) ignore[h] = ig1->i_link; else ig2->i_link = ig1->i_link; free(ig1->i_field); free((char *)ig1); break; } } return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Still more user commands. */ static int bangexp(char *str); static int diction(const void *a, const void *b); static char *getfilename(char *name, int *aedit); static int resp1(int *msgvec, int useauthor); static int Resp1(int *msgvec, int useauthor); static char *reedit(char *subj); static int shell1(char *str); static void sort(char **list); static char *replyto(struct message *mp, char **f); static int reply2sender(void); static char prevfile[PATHSIZE]; static char origprevfile[PATHSIZE]; static char lastbang[BUFSIZ]; /* * Process a shell escape by saving signals, ignoring signals, * and forking a sh -c */ int shell(char *str) { shell1(str); printf("!\n"); return(0); } static int shell1(char *str) { void (*sig[2])(int); int t; pid_t p; char *Shell; char cmd[BUFSIZ]; nstrcpy(cmd, sizeof (cmd), str); if (bangexp(cmd) < 0) return(-1); if ((Shell = value("SHELL")) == NOSTR || *Shell=='\0') Shell = SHELL; for (t = SIGINT; t <= SIGQUIT; t++) sig[t-SIGINT] = sigset(t, SIG_IGN); p = vfork(); if (p == 0) { setuid(getuid()); sigchild(); for (t = SIGINT; t <= SIGQUIT; t++) if (sig[t-SIGINT] != SIG_IGN) sigsys(t, SIG_DFL); execlp(Shell, Shell, "-c", cmd, (char *)0); perror(Shell); _exit(1); } while (wait(0) != p) ; if (p == (pid_t)-1) perror("fork"); for (t = SIGINT; t <= SIGQUIT; t++) sigset(t, sig[t-SIGINT]); return(0); } /* * Fork an interactive shell. */ int #ifdef __cplusplus dosh(char *) #else /* ARGSUSED */ dosh(char *s) #endif { void (*sig[2])(int); int t; pid_t p; char *Shell; if ((Shell = value("SHELL")) == NOSTR || *Shell=='\0') Shell = SHELL; for (t = SIGINT; t <= SIGQUIT; t++) sig[t-SIGINT] = sigset(t, SIG_IGN); p = vfork(); if (p == 0) { setuid(getuid()); sigchild(); for (t = SIGINT; t <= SIGQUIT; t++) if (sig[t-SIGINT] != SIG_IGN) sigset(t, SIG_DFL); execlp(Shell, Shell, (char *)0); perror(Shell); _exit(1); } while (wait(0) != p) ; if (p == (pid_t)-1) perror("fork"); for (t = SIGINT; t <= SIGQUIT; t++) sigset(t, sig[t-SIGINT]); putchar('\n'); return(0); } /* * Expand the shell escape by expanding unescaped !'s into the * last issued command where possible. */ static int bangexp(char *str) { char bangbuf[BUFSIZ]; char *cp, *cp2; int n; int changed = 0; int bangit = (value("bang")!=NOSTR); cp = str; cp2 = bangbuf; n = BUFSIZ; while (*cp) { if (*cp=='!' && bangit) { if (n < (int)strlen(lastbang)) { overf: printf(gettext("Command buffer overflow\n")); return(-1); } changed++; strcpy(cp2, lastbang); cp2 += strlen(lastbang); n -= strlen(lastbang); cp++; continue; } if (*cp == '\\' && cp[1] == '!') { if (--n <= 1) goto overf; *cp2++ = '!'; cp += 2; changed++; } if (--n <= 1) goto overf; *cp2++ = *cp++; } *cp2 = 0; if (changed) { printf("!%s\n", bangbuf); fflush(stdout); } nstrcpy(str, BUFSIZ, bangbuf); nstrcpy(lastbang, sizeof (lastbang), bangbuf); return(0); } /* * Print out a nice help message from some file or another. */ int help(void) { int c; FILE *f; if ((f = fopen(HELPFILE, "r")) == NULL) { printf(gettext("No help just now.\n")); return(1); } while ((c = getc(f)) != EOF) putchar(c); fclose(f); return(0); } /* * Change user's working directory. */ int schdir(char *str) { char *cp; char cwd[PATHSIZE], file[PATHSIZE]; static char efile[PATHSIZE]; for (cp = str; *cp == ' '; cp++) ; if (*cp == '\0') cp = homedir; else if ((cp = expand(cp)) == NOSTR) return(1); if (editfile != NOSTR && (*editfile != '/' || mailname[0] != '/')) { if (getcwd(cwd, (int)sizeof (cwd)) == 0) { fprintf(stderr, gettext("Can't get current directory: %s\n"), cwd); return(1); } } if (chdir(cp) < 0) { perror(cp); return(1); } /* * Convert previously relative names to absolute names. */ if (editfile != NOSTR && *editfile != '/') { snprintf(file, sizeof (file), "%s/%s", cwd, editfile); nstrcpy(efile, sizeof (efile), file); editfile = efile; } if (mailname[0] != '/') { snprintf(file, sizeof (file), "%s/%s", cwd, mailname); nstrcpy(mailname, PATHSIZE, file); } return(0); } /* * Two versions of reply. Reply to all names in message or reply * to only sender of message, depending on setting of "replyall". */ int respond(int *msgvec) { if (reply2sender()) return(resp1(msgvec, 0)); else return(Resp1(msgvec, 0)); } int followup(int *msgvec) { if (reply2sender()) return(resp1(msgvec, 1)); else return(Resp1(msgvec, 1)); } int replyall(int *msgvec) { return(resp1(msgvec, 0)); } static int resp1(int *msgvec, int useauthor) { struct message *mp; char *cp, *buf, *rcv, *skin_rcv, *reply2, **ap, *returnaddr; struct name *np; struct header head; char mylocalname[BUFSIZ], mydomname[BUFSIZ]; if (msgvec[1] != 0) { printf(gettext( "Sorry, can't reply to multiple messages at once\n")); return(1); } snprintf(mydomname, sizeof (mydomname), "%s@%s", myname, domain); snprintf(mylocalname, sizeof (mylocalname), "%s@%s", myname, host); returnaddr = value("returnaddr"); mp = &message[msgvec[0] - 1]; dot = mp; reply2 = replyto(mp, &rcv); cp = skin(hfield("to", mp, addto)); if (cp != NOSTR) { buf = (char *)salloc(strlen(reply2) + strlen(cp) + 2); strcpy(buf, reply2); strcat(buf, " "); strcat(buf, cp); } else buf = reply2; np = elide(extract(buf, GTO)); #ifdef OPTIM /* rcv = netrename(rcv); */ #endif /* OPTIM */ /* * Delete my name from the reply list, * and with it, all my alternate names. */ skin_rcv = skin(rcv); mapf(np, skin_rcv); np = delname(np, myname); np = delname(np, mylocalname); np = delname(np, mydomname); if (returnaddr && *returnaddr) np = delname(np, returnaddr); if (altnames != 0) for (ap = altnames; *ap; ap++) np = delname(np, *ap); head.h_seq = 1; cp = detract(np, 0); if (cp == NOSTR) { if (reply2) cp = unuucp(reply2); else cp = unuucp(rcv); } head.h_to = cp; head.h_subject = hfield("subject", mp, addone); if (head.h_subject == NOSTR) head.h_subject = hfield("subj", mp, addone); head.h_subject = reedit(head.h_subject); head.h_cc = NOSTR; cp = skin(hfield("cc", mp, addto)); if (cp != NOSTR) { np = elide(extract(cp, GCC)); mapf(np, skin_rcv); np = delname(np, myname); np = delname(np, mylocalname); np = delname(np, mydomname); if (returnaddr && *returnaddr) np = delname(np, returnaddr); np = delname(np, skin_rcv); if (altnames != 0) for (ap = altnames; *ap; ap++) np = delname(np, *ap); head.h_cc = detract(np, 0); } head.h_bcc = NOSTR; head.h_defopt = NOSTR; head.h_others = NOSTRPTR; mail1(&head, useauthor, useauthor ? rcv : NOSTR); return(0); } void getrecf(char *buf, char *recfile, int useauthor, int sz_recfile) { char *bp, *cp; char *recf = recfile; int folderize; char fldr[BUFSIZ]; folderize = (value("outfolder")!=NOSTR && getfold(fldr) == 0); if (useauthor) { if (folderize) *recf++ = '+'; if (debug) fprintf(stderr, "buf='%s'\n", buf); for (bp=skin(buf), cp=recf; *bp && !any(*bp, ", "); bp++) { if (*bp=='!') cp = recf; else *cp++ = *bp; if (cp >= &recfile[sz_recfile - 1]) { printf(gettext("File name buffer overflow\n")); break; } } *cp = '\0'; if (cp==recf) *recfile = '\0'; /* now strip off any Internet host names */ if ((cp = strchr(recf, '%')) == NOSTR) cp = strchr(recf, '@'); if (cp != NOSTR) *cp = '\0'; } else { if (cp = value("record")) { int sz = PATHSIZE; if (folderize && *cp!='+' && *cp!='/' && *safeexpand(cp)!='/') { *recf++ = '+'; sz--; } nstrcpy(recf, sz, cp); } else *recf = '\0'; } if (debug) fprintf(stderr, "recfile='%s'\n", recfile); } /* * Modify the subject we are replying to to begin with Re: if * it does not already. */ static char * reedit(char *subj) { char sbuf[10]; char *newsubj; if (subj == NOSTR) return(NOSTR); strncpy(sbuf, subj, 3); sbuf[3] = 0; if (icequal(sbuf, "re:")) return(subj); newsubj = (char *)salloc((unsigned)(strlen(subj) + 5)); sprintf(newsubj, "Re: %s", subj); return(newsubj); } /* * Preserve the named messages, so that they will be sent * back to the system mailbox. */ int preserve(int *msgvec) { struct message *mp; int *ip, mesg; if (edit) { printf(gettext("Cannot \"preserve\" in edit mode\n")); return(1); } for (ip = msgvec; *ip != 0; ip++) { mesg = *ip; mp = &message[mesg-1]; mp->m_flag |= MPRESERVE; mp->m_flag &= ~MBOX; dot = mp; } return(0); } /* * Mark all given messages as unread. */ int unread(int msgvec[]) { int *ip; for (ip = msgvec; *ip != 0; ip++) { dot = &message[*ip-1]; dot->m_flag &= ~(MREAD|MTOUCH); dot->m_flag |= MSTATUS; } return(0); } /* * Print the size of each message. */ int messize(int *msgvec) { struct message *mp; int *ip, mesg; for (ip = msgvec; *ip != 0; ip++) { mesg = *ip; mp = &message[mesg-1]; dot = mp; printf("%d: %ld\n", mesg, mp->m_size); } return(0); } /* * Quit quickly. If we are sourcing, just pop the input level * by returning an error. */ int rexit(int e) { if (sourcing) return(1); if (Tflag != NOSTR) close(creat(Tflag, TEMPPERM)); if (!edit) Verhogen(); exit(e ? e : rpterr); /* NOTREACHED */ return (0); /* shut up lint and CC */ } /* * Set or display a variable value. Syntax is similar to that * of csh. */ int set(char **arglist) { struct var *vp; char *cp, *cp2; char varbuf[BUFSIZ], **ap, **p; int errs, h, s; if (argcount(arglist) == 0) { for (h = 0, s = 1; h < HSHSIZE; h++) for (vp = variables[h]; vp != NOVAR; vp = vp->v_link) s++; ap = (char **) salloc(s * sizeof *ap); for (h = 0, p = ap; h < HSHSIZE; h++) for (vp = variables[h]; vp != NOVAR; vp = vp->v_link) *p++ = vp->v_name; *p = NOSTR; sort(ap); for (p = ap; *p != NOSTR; p++) if (((cp = value(*p)) != 0) && *cp) printf("%s=\"%s\"\n", *p, cp); else printf("%s\n", *p); return(0); } errs = 0; for (ap = arglist; *ap != NOSTR; ap++) { cp = *ap; cp2 = varbuf; while (*cp != '=' && *cp != '\0') *cp2++ = *cp++; *cp2 = '\0'; if (*cp == '\0') cp = ""; else cp++; if (equal(varbuf, "")) { printf(gettext("Non-null variable name required\n")); errs++; continue; } assign(varbuf, cp); } return(errs); } /* * Unset a bunch of variable values. */ int unset(char **arglist) { int errs; char **ap; errs = 0; for (ap = arglist; *ap != NOSTR; ap++) errs += deassign(*ap); return(errs); } /* * Add users to a group. */ int group(char **argv) { struct grouphead *gh; struct mgroup *gp; int h; int s; char **ap, *gname, **p; if (argcount(argv) == 0) { for (h = 0, s = 1; h < HSHSIZE; h++) for (gh = groups[h]; gh != NOGRP; gh = gh->g_link) s++; ap = (char **) salloc(s * sizeof *ap); for (h = 0, p = ap; h < HSHSIZE; h++) for (gh = groups[h]; gh != NOGRP; gh = gh->g_link) *p++ = gh->g_name; *p = NOSTR; sort(ap); for (p = ap; *p != NOSTR; p++) printgroup(*p); return(0); } if (argcount(argv) == 1) { printgroup(*argv); return(0); } gname = *argv; h = hash(gname); if ((gh = findgroup(gname)) == NOGRP) { if ((gh = (struct grouphead *) calloc(sizeof (*gh), 1)) == NULL) { panic("Failed to allocate memory for group"); } gh->g_name = vcopy(gname); gh->g_list = NOGE; gh->g_link = groups[h]; groups[h] = gh; } /* * Insert names from the command list into the group. * Who cares if there are duplicates? They get tossed * later anyway. */ for (ap = argv+1; *ap != NOSTR; ap++) { if ((gp = (struct mgroup *) calloc(sizeof (*gp), 1)) == NULL) { panic("Failed to allocate memory for group"); } gp->ge_name = vcopy(*ap); gp->ge_link = gh->g_list; gh->g_list = gp; } return(0); } /* * Remove users from a group. */ int ungroup(char **argv) { struct grouphead *gh, **ghp; struct mgroup *gp, *gpnext; int h; char **ap, *gname; if (argcount(argv) == 0) { printf("Must specify alias or group to remove\n"); return(1); } /* * Remove names on the command list from the group list. */ for (ap = argv; *ap != NOSTR; ap++) { gname = *ap; h = hash(gname); for (ghp = &groups[h]; *ghp != NOGRP; ghp = &((*ghp)->g_link)) { gh = *ghp; if (equal(gh->g_name, gname)) { /* remove from list */ *ghp = gh->g_link; /* free each member of gorup */ for (gp = gh->g_list; gp != NOGE; gp = gpnext) { gpnext = gp->ge_link; vfree(gp->ge_name); free(gp); } vfree(gh->g_name); free(gh); break; } } } return(0); } /* * Sort the passed string vecotor into ascending dictionary * order. */ static void sort(char **list) { char **ap; for (ap = list; *ap != NOSTR; ap++) ; if (ap-list < 2) return; qsort((char *) list, (unsigned) (ap-list), sizeof *list, diction); } /* * Do a dictionary order comparison of the arguments from * qsort. */ static int diction(const void *a, const void *b) { return(strcmp(*(char **)a, *(char **)b)); } /* * The do nothing command for comments. */ int #ifdef __cplusplus null(char *) #else /* ARGSUSED */ null(char *s) #endif { return(0); } /* * Print out the current edit file, if we are editing. * Otherwise, print the name of the person who's mail * we are reading. */ int file(char **argv) { char *cp; int editing, mdot; if (argv[0] == NOSTR) { mdot = newfileinfo(1); dot = &message[mdot - 1]; return(0); } /* * Acker's! Must switch to the new file. * We use a funny interpretation -- * # -- gets the previous file * % -- gets the invoker's post office box * %user -- gets someone else's post office box * & -- gets invoker's mbox file * string -- reads the given file */ cp = getfilename(argv[0], &editing); if (cp == NOSTR) return(-1); if (setfile(cp, editing)) { nstrcpy(origname, PATHSIZE, origprevfile); return(-1); } mdot = newfileinfo(1); dot = &message[mdot - 1]; return(0); } /* * Evaluate the string given as a new mailbox name. * Ultimately, we want this to support a number of meta characters. * Possibly: * % -- for my system mail box * %user -- for user's system mail box * # -- for previous file * & -- get's invoker's mbox file * file name -- for any other file */ static char * getfilename(char *name, int *aedit) { char *cp; char savename[BUFSIZ]; char oldmailname[BUFSIZ]; char tmp[BUFSIZ]; /* * Assume we will be in "edit file" mode, until * proven wrong. */ *aedit = 1; switch (*name) { case '%': *aedit = 0; nstrcpy(prevfile, sizeof (prevfile), editfile); nstrcpy(origprevfile, sizeof (origprevfile), origname); if (name[1] != 0) { nstrcpy(oldmailname, sizeof (oldmailname), mailname); findmail(name+1); cp = savestr(mailname); nstrcpy(origname, PATHSIZE, cp); nstrcpy(mailname, PATHSIZE, oldmailname); return(cp); } nstrcpy(oldmailname, sizeof (oldmailname), mailname); findmail(NULL); cp = savestr(mailname); nstrcpy(mailname, PATHSIZE, oldmailname); nstrcpy(origname, PATHSIZE, cp); return(cp); case '#': if (name[1] != 0) goto regular; if (prevfile[0] == 0) { printf(gettext("No previous file\n")); return(NOSTR); } cp = savestr(prevfile); nstrcpy(prevfile, sizeof (prevfile), editfile); nstrcpy(tmp, sizeof (tmp), origname); nstrcpy(origname, PATHSIZE, origprevfile); nstrcpy(origprevfile, sizeof (origprevfile), tmp); return(cp); case '&': nstrcpy(prevfile, sizeof (prevfile), editfile); nstrcpy(origprevfile, sizeof (origprevfile), origname); if (name[1] == 0) { cp=Getf("MBOX"); nstrcpy(origname, PATHSIZE, cp); return(cp); } /* FALLTHROUGH */ default: regular: nstrcpy(prevfile, sizeof (prevfile), editfile); nstrcpy(origprevfile, sizeof (origprevfile), origname); cp = safeexpand(name); nstrcpy(origname, PATHSIZE, cp); if (cp[0] != '/') { name = getcwd(NOSTR, PATHSIZE); nstrcat(name, PATHSIZE, "/"); nstrcat(name, PATHSIZE, cp); cp = name; } return(cp); } } /* * Expand file names like echo */ int echo(char **argv) { char *cp; int neednl = 0; while (*argv != NOSTR) { cp = *argv++; if ((cp = expand(cp)) != NOSTR) { neednl++; printf("%s", cp); if (*argv!=NOSTR) putchar(' '); } } if (neednl) putchar('\n'); return(0); } /* * Reply to a series of messages by simply mailing to the senders * and not messing around with the To: and Cc: lists as in normal * reply. */ int Respond(int *msgvec) { if (reply2sender()) return(Resp1(msgvec, 0)); else return(resp1(msgvec, 0)); } int Followup(int *msgvec) { if (reply2sender()) return(Resp1(msgvec, 1)); else return(resp1(msgvec, 1)); } int replysender(int *msgvec) { return(Resp1(msgvec, 0)); } static int Resp1(int *msgvec, int useauthor) { struct header head; struct message *mp; int s, *ap; char *cp, *cp2, *subject; for (s = 0, ap = msgvec; *ap != 0; ap++) { mp = &message[*ap - 1]; dot = mp; cp = replyto(mp, NOSTRPTR); s += strlen(cp) + 1; } if (s == 0) return(0); cp = (char *)salloc(s + 2); head.h_to = cp; for (ap = msgvec; *ap != 0; ap++) { mp = &message[*ap - 1]; cp2 = replyto(mp, NOSTRPTR); cp = copy(cp2, cp); *cp++ = ' '; } *--cp = 0; mp = &message[msgvec[0] - 1]; subject = hfield("subject", mp, addone); head.h_seq = 1; if (subject == NOSTR) subject = hfield("subj", mp, addone); head.h_subject = reedit(subject); if (subject != NOSTR) head.h_seq++; head.h_cc = NOSTR; head.h_bcc = NOSTR; head.h_defopt = NOSTR; head.h_others = NOSTRPTR; mail1(&head, useauthor, NOSTR); return(0); } /* * Conditional commands. These allow one to parameterize one's * .mailrc and do some things if sending, others if receiving. */ int ifcmd(char **argv) { char *cp; if (cond != CANY) { printf(gettext("Illegal nested \"if\"\n")); return(1); } cond = CANY; cp = argv[0]; switch (*cp) { case 'r': case 'R': cond = CRCV; break; case 's': case 'S': cond = CSEND; break; case 't': case 'T': cond = CTTY; break; default: printf(gettext("Unrecognized if-keyword: \"%s\"\n"), cp); return(1); } return(0); } /* * Implement 'else'. This is pretty simple -- we just * flip over the conditional flag. */ int elsecmd(void) { switch (cond) { case CANY: printf(gettext("\"Else\" without matching \"if\"\n")); return(1); case CSEND: cond = CRCV; break; case CRCV: cond = CSEND; break; case CTTY: cond = CNOTTY; break; case CNOTTY: cond = CTTY; break; default: printf(gettext("invalid condition encountered\n")); cond = CANY; break; } return(0); } /* * End of if statement. Just set cond back to anything. */ int endifcmd(void) { if (cond == CANY) { printf(gettext("\"Endif\" without matching \"if\"\n")); return(1); } cond = CANY; return(0); } /* * Set the list of alternate names. */ int alternates(char **namelist) { int c; char **ap, **ap2, *cp; c = argcount(namelist) + 1; if (c == 1) { if (altnames == 0) return(0); for (ap = altnames; *ap; ap++) printf("%s ", *ap); printf("\n"); return (0); } if (altnames != 0) free((char *)altnames); if ((altnames = (char **) calloc((unsigned)c, sizeof (char *))) == NULL) panic("Failed to allocate memory"); for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) { if ((cp = (char *) calloc((unsigned)strlen(*ap) + 1, sizeof (char))) == NULL) panic("Failed to allocate memory"); strcpy(cp, *ap); *ap2 = cp; } *ap2 = 0; return(0); } /* * Figure out who to reply to. * Return the real sender in *f. */ static char * replyto(struct message *mp, char **f) { char *r, *rf; if ((rf = skin(hfield("from", mp, addto)))==NOSTR) rf = skin(addto(NOSTR, nameof(mp))); if ((r = skin(hfield("reply-to", mp, addto)))==NOSTR) r = rf; if (f) *f = rf; return (r); } /* * reply2sender - determine whether a "reply" command should reply to the * sender of the messages, or to all the recipients of the * message. * * With the advent of POSIX.2 compliance, this has become * a bit more complicated, and so should be done in one * place, for all to use. */ static int reply2sender (void) { int rep = (value("replyall") != NOSTR); int flp = (value("flipr") != NOSTR); return((rep && !flp)|| (!rep && flp)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1995 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * More commands.. */ static char *stripquotes(char *str); /* * pipe messages to cmd. */ int dopipe(char str[]) { int *ip, mesg; struct message *mp; char *cp, *cmd; int f, *msgvec, nowait=0; void (*sigint)(int), (*sigpipe)(int); long lc, cc, t; pid_t pid; int page, s, pivec[2]; char *Shell; FILE *pio = NULL; extern jmp_buf pipestop; extern void brokpipe(int); msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec); if ((cmd = stripquotes(snarf(str, &f, 0))) == NOSTR) { if (f == -1) { printf(gettext("pipe command error\n")); return(1); } if ( (cmd = value("cmd")) == NOSTR) { printf(gettext("\"cmd\" not set, ignored.\n")); return(1); } } if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf(gettext("No messages to pipe.\n")); return(1); } msgvec[1] = 0; } if (f && getmsglist(str, msgvec, 0) < 0) return(1); if (*(cp=cmd+strlen(cmd)-1)=='&') { *cp=0; nowait++; } printf(gettext("Pipe to: \"%s\"\n"), cmd); flush(); if (setjmp(pipestop)) goto err; /* setup pipe */ if (pipe(pivec) < 0) { perror("pipe"); return(0); } if ((pid = vfork()) == 0) { close(pivec[1]); /* child */ close(0); dup(pivec[0]); close(pivec[0]); if ((Shell = value("SHELL")) == NOSTR || *Shell=='\0') Shell = SHELL; execlp(Shell, Shell, "-c", cmd, 0); perror(Shell); _exit(1); } if (pid == (pid_t)-1) { /* error */ perror("fork"); close(pivec[0]); close(pivec[1]); return(0); } close(pivec[0]); /* parent */ pio=fdopen(pivec[1],"w"); sigint = sigset(SIGINT, SIG_IGN); sigpipe = sigset(SIGPIPE, brokpipe); /* send all messages to cmd */ page = (value("page")!=NOSTR); lc = cc = 0; for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mesg = *ip; touch(mesg); mp = &message[mesg-1]; dot = mp; if ((t = msend(mp, pio, (value("alwaysignore") != NOSTR || value("pipeignore") != NOSTR) ? M_IGNORE : 0, fputs)) < 0) { perror(cmd); sigset(SIGPIPE, sigpipe); sigset(SIGINT, sigint); fclose(pio); return(1); } lc += t; cc += mp->m_size; if (page) putc('\f', pio); } fflush(pio); if (ferror(pio)) perror(cmd); fclose(pio); pio = NULL; /* wait */ if (!nowait) { while (wait(&s) != pid); s &= 0377; if (s != 0) goto err; } printf("\"%s\" %ld/%ld\n", cmd, lc, cc); sigset(SIGPIPE, sigpipe); sigset(SIGINT, sigint); return(0); err: printf(gettext("Pipe to \"%s\" failed\n"), cmd); if (pio) fclose(pio); sigset(SIGPIPE, sigpipe); sigset(SIGINT, sigint); return(0); } /* * Load the named message from the named file. */ int loadmsg(char str[]) { char *file; int f, *msgvec; int c, lastc = '\n'; int blank; int lines; long ms; FILE *ibuf; struct message *mp; off_t size; msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec); if ((file = snarf(str, &f, 1)) == NOSTR) return(1); if (f==-1) return(1); if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf(gettext("No message to load into.\n")); return(1); } msgvec[1] = 0; } if (f && getmsglist(str, msgvec, 0) < 0) return(1); if (msgvec[1] != 0) { printf(gettext("Can only load into a single message.\n")); return(1); } if ((file = expand(file)) == NOSTR) return(1); printf("\"%s\" ", file); fflush(stdout); if ((ibuf = fopen(file, "r")) == NULL) { perror(""); return(1); } mp = &message[*msgvec-1]; dot = mp; mp->m_flag |= MODIFY; mp->m_flag &= ~MSAVED; /* should probably turn off more */ fseek(otf, (long) 0, 2); size = fsize(otf); mp->m_offset = size; ms = 0L; lines = 0; while ((c = getc(ibuf)) != EOF) { if (c == '\n') { lines++; blank = lastc == '\n'; } lastc = c; putc(c, otf); if (ferror(otf)) break; ms++; } if (!blank) { putc('\n', otf); ms++; lines++; } mp->m_size = ms; mp->m_lines = lines; if (fferror(otf)) perror("/tmp"); fclose(ibuf); setclen(mp); printf(gettext("[Loaded] %d/%ld\n"), lines, ms); return(0); } /* * Display the named field. */ int field(char str[]) { int *ip; struct message *mp; char *cp, *fld; int f, *msgvec; msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec); if ((fld = stripquotes(snarf(str, &f, 0))) == NOSTR) { if (f == -1) printf(gettext("Bad field\n")); else printf(gettext("No field specified\n")); return(1); } if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf(gettext("No messages to display.\n")); return(1); } msgvec[1] = 0; } if (f && getmsglist(str, msgvec, 0) < 0) return(1); for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; dot = mp; if ((cp = hfield(fld, mp, addone)) != NULL) printf("%s\n", cp); } return(0); } /* * Remove the quotes from around the string passed in (if any). Return * the beginning of the result. */ static char * stripquotes(char *str) { int lastch; if (str == NOSTR) { return(NOSTR); } lastch = strlen(str)-1; if (any(*str, "\"'") && str[lastch] == *str) { str[lastch] = '\0'; ++str; } return(str); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "def.h" /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Define all of the command names and bindings. */ /* * Since the type of the argument to the procedures in the * command table depends on the flags in the table, and since * the argument type must be specified in struct cmd for C++ * compatibility, and since I didn't want to change all the * command procedures to expect an argument of type void *, * the following cast "makes it all work". Yes, it's ugly. */ #define C (int (*)(void *))(uintptr_t) const struct cmd cmdtab[] = { "next", C next, NDMLIST, 0, MMNDEL, "alias", C group, M|RAWLIST, 0, 1000, "print", C type, MSGLIST, 0, MMNDEL, "type", C type, MSGLIST, 0, MMNDEL, "Type", C Type, MSGLIST, 0, MMNDEL, "Print", C Type, MSGLIST, 0, MMNDEL, "visual", C visual, I|MSGLIST, 0, MMNORM, "top", C top, MSGLIST, 0, MMNDEL, "touch", C stouch, W|MSGLIST, 0, MMNDEL, "preserve", C preserve, I|W|MSGLIST, 0, MMNDEL, "delete", C delm, W|P|MSGLIST, 0, MMNDEL, "dp", C deltype, W|MSGLIST, 0, MMNDEL, "dt", C deltype, W|MSGLIST, 0, MMNDEL, "undelete", C undelete, P|MSGLIST, MDELETED, MMNDEL, "unset", C unset, M|RAWLIST, 1, 1000, "mail", C sendm, R|M|I|STRLIST, 0, 0, "Mail", C Sendm, R|M|I|STRLIST, 0, 0, "mbox", C mboxit, W|MSGLIST, 0, 0, "more", C more, MSGLIST, 0, MMNDEL, "page", C more, MSGLIST, 0, MMNDEL, "More", C More, MSGLIST, 0, MMNDEL, "Page", C More, MSGLIST, 0, MMNDEL, "unread", C unread, MSGLIST, 0, MMNDEL, "Unread", C unread, MSGLIST, 0, MMNDEL, "new", C unread, MSGLIST, 0, MMNDEL, "New", C unread, MSGLIST, 0, MMNDEL, "!", C shell, I|STRLIST, 0, 0, "copy", C copycmd, M|STRLIST, 0, 0, "Copy", C Copy, M|MSGLIST, 0, 0, "chdir", C schdir, M|STRLIST, 0, 0, "cd", C schdir, M|STRLIST, 0, 0, "save", C save, STRLIST, 0, 0, "Save", C Save, MSGLIST, 0, 0, "source", C source, M|STRLIST, 0, 0, "set", C set, M|RAWLIST, 0, 1000, "shell", C dosh, I|NOLIST, 0, 0, "version", C pversion, M|NOLIST, 0, 0, "group", C group, M|RAWLIST, 0, 1000, "unalias", C ungroup, M|RAWLIST, 0, 1000, "ungroup", C ungroup, M|RAWLIST, 0, 1000, "write", C swrite, STRLIST, 0, 0, "from", C from, MSGLIST, 0, MMNORM, "followup", C followup, R|I|MSGLIST, 0, MMNDEL, "Followup", C Followup, R|I|MSGLIST, 0, MMNDEL, "file", C file, T|M|RAWLIST, 0, 1, "folder", C file, T|M|RAWLIST, 0, 1, "folders", C folders, T|M|RAWLIST, 0, 1, "?", C help, M|NOLIST, 0, 0, "z", C scroll, M|STRLIST, 0, 0, "headers", C headers, MSGLIST, 0, MMNDEL, "help", C help, M|NOLIST, 0, 0, "=", C pdot, NOLIST, 0, 0, "Reply", C Respond, R|I|MSGLIST, 0, MMNDEL, "Respond", C Respond, R|I|MSGLIST, 0, MMNDEL, "reply", C respond, R|I|MSGLIST, 0, MMNDEL, "respond", C respond, R|I|MSGLIST, 0, MMNDEL, "replyall", C replyall, R|I|MSGLIST, 0, MMNDEL, "replysender", C replysender, R|I|MSGLIST, 0, MMNDEL, "edit", C editor, I|MSGLIST, 0, MMNORM, "echo", C echo, M|RAWLIST, 0, 1000, "quit", C edstop, NOLIST, 0, 0, "list", C pcmdlist, M|NOLIST, 0, 0, "load", C loadmsg, W|STRLIST, 0, 0, "xit", C rexit, M|NOLIST, 0, 0, "exit", C rexit, M|NOLIST, 0, 0, "size", C messize, MSGLIST, 0, MMNDEL, "hold", C preserve, I|W|MSGLIST, 0, MMNDEL, "if", C ifcmd, F|M|RAWLIST, 1, 1, "else", C elsecmd, F|M|RAWLIST, 0, 0, "endif", C endifcmd, F|M|RAWLIST, 0, 0, "alternates", C alternates, M|RAWLIST, 0, 1000, "ignore", C igfield, M|RAWLIST, 0, 1000, "discard", C igfield, M|RAWLIST, 0, 1000, "unignore", C unigfield, M|RAWLIST, 0, 1000, "undiscard", C unigfield, M|RAWLIST, 0, 1000, "retain", C retfield, M|RAWLIST, 0, 1000, "unretain", C unretfield, M|RAWLIST, 0, 1000, /* "Header", C Header, STRLIST, 0, 1000, */ "#", C null, M|NOLIST, 0, 0, "pipe", C dopipe, STRLIST, 0, 0, "|", C dopipe, STRLIST, 0, 0, "inc", C inc, T|NOLIST, 0, 0, "field", C field, STRLIST, 0, 0, "put", C sput, STRLIST, 0, 0, "Put", C Sput, STRLIST, 0, 0, 0, C 0, 0, 0, 0 }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1985, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Collect input from standard input, handling * ~ escapes. */ #include "rcv.h" #include #ifdef SIGCONT static void collcont(int); #endif static void collrub(int s); static void cpout(char *str, FILE *ofd); static int exwrite(char name[], FILE *ibuf); static int forward(char ms[], FILE *obuf, int f); static void intack(int); static int forward(char ms[], FILE *obuf, int f); static FILE *mesedit(FILE *ibuf, FILE *obuf, int c, struct header *hp); static FILE *mespipe(FILE *ibuf, FILE *obuf, char cmd[]); static void resetsigs(int resethup); static int stripnulls(char *linebuf, int nread); static void xhalt(void); static char **Xaddone(char **hf, char news[]); static int tabputs(const char *line, FILE *obuf); /* * Read a message from standard output and return a read file to it * or NULL on error. */ /* * The following hokiness with global variables is so that on * receipt of an interrupt signal, the partial message can be salted * away on dead.letter. The output file must be available to flush, * and the input to read. Several open files could be saved all through * mailx if stdio allowed simultaneous read/write access. */ static void (*savesig)(int); /* Previous SIGINT value */ static void (*savehup)(int); /* Previous SIGHUP value */ #ifdef SIGCONT static void (*savecont)(int); /* Previous SIGCONT value */ #endif static FILE *newi; /* File for saving away */ static FILE *newo; /* Output side of same */ static int ignintr; /* Ignore interrups */ static int hadintr; /* Have seen one SIGINT so far */ static struct header *savehp; static jmp_buf coljmp; /* To get back to work */ FILE * collect(struct header *hp) { FILE *ibuf, *fbuf, *obuf; int escape, eof; long lc, cc; int c, t; int hdrs; char linebuf[LINESIZE+1], *cp; char *iprompt; int inhead; void (*sigpipe)(int), (*sigint)(int); int fd = -1; noreset++; ibuf = obuf = NULL; newi = newo = NULL; if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || (obuf = fdopen(fd, "w")) == NULL) { perror(tempMail); goto err; } newo = obuf; if ((ibuf = fopen(tempMail, "r")) == NULL) { perror(tempMail); newo = NULL; fclose(obuf); goto err; } newi = ibuf; removefile(tempMail); ignintr = (int)value("ignore"); hadintr = 1; inhead = 1; savehp = hp; # ifdef VMUNIX if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) sigset(SIGINT, ignintr ? intack : collrub), sigblock(sigmask(SIGINT)); if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) sigset(SIGHUP, collrub), sigblock(sigmask(SIGHUP)); # else /* VMUNIX */ # ifdef OLD_BSD_SIGS if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) sigset(SIGINT, ignintr ? intack : collrub); if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) sigset(SIGHUP, collrub); # else if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigset(SIGINT, ignintr ? intack : collrub); sigprocmask(SIG_BLOCK, &mask, NULL); } if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGHUP); sigset(SIGHUP, collrub); sigprocmask(SIG_BLOCK, &mask, NULL); } # endif # endif /* VMUNIX */ #ifdef SIGCONT savecont = sigset(SIGCONT, collcont); #endif /* * If we are going to prompt for subject/cc/bcc, * refrain from printing a newline after * the headers (since some people mind). */ if (hp->h_subject == NOSTR) { hp->h_subject = sflag; sflag = NOSTR; } if (hp->h_cc == NOSTR) { hp->h_cc = cflag; cflag = NOSTR; } if (hp->h_bcc == NOSTR) { hp->h_bcc = bflag; bflag = NOSTR; } t = GMASK; hdrs = 0; if (intty && !tflag) { if (hp->h_to == NOSTR) hdrs |= GTO; if (hp->h_subject == NOSTR && value("asksub")) hdrs |= GSUBJECT; if (hp->h_cc == NOSTR && value("askcc")) hdrs |= GCC; if (hp->h_bcc == NOSTR && value("askbcc")) hdrs |= GBCC; if (hdrs) t &= ~GNL; } if (hp->h_seq != 0) { puthead(hp, stdout, t, 0); fflush(stdout); } if (setjmp(coljmp)) goto err; escape = SENDESC; if ((cp = value("escape")) != NOSTR) escape = *cp; eof = 0; if ((cp = value("MAILX_HEAD")) != NOSTR) { cpout( cp, obuf); if (isatty(fileno(stdin))) cpout( cp, stdout); } iprompt = value("iprompt"); fflush(obuf); hadintr = 0; for (;;) { int nread, hasnulls; # ifdef VMUNIX int omask = sigblock(0) &~ (sigmask(SIGINT)|sigmask(SIGHUP)); # else # ifndef OLD_BSD_SIGS sigset_t omask; sigprocmask(0, NULL, &omask); sigdelset(&omask, SIGINT); sigdelset(&omask, SIGHUP); # endif # endif setjmp(coljmp); # ifdef VMUNIX sigsetmask(omask); # else /* VMUNIX */ # ifdef OLD_BSD_SIGS sigrelse(SIGINT); sigrelse(SIGHUP); # else sigprocmask(SIG_SETMASK, &omask, NULL); # endif # endif /* VMUNIX */ if (intty && !tflag && outtty && iprompt) fputs(iprompt, stdout); flush(); if (hdrs) { grabh(hp, hdrs, 1); hdrs = 0; continue; } if ((nread = getaline(linebuf,LINESIZE,stdin,&hasnulls)) == 0) { if (intty && value("ignoreeof") != NOSTR) { if (++eof > 35) break; printf(gettext( "Use \".\" to terminate letter\n")); continue; } break; } eof = 0; hadintr = 0; if (intty && equal(".\n", linebuf) && (value("dot") != NOSTR || value("ignoreeof") != NOSTR)) break; /* * If -t, scan text for headers. */ if (tflag) { char *cp2; if (!inhead) { writeit: if (write(fileno(obuf),linebuf,nread) != nread) goto werr; continue; } if (linebuf[0] == '\n') { /* got blank line after header, ignore it */ inhead = 0; continue; } if (!headerp(linebuf)) { /* got non-header line, save it */ inhead = 0; goto writeit; } if (hasnulls) nread = stripnulls(linebuf, nread); for (;;) { char line2[LINESIZE]; c = getc(stdin); ungetc(c, stdin); if (!isspace(c) || c == '\n') break; if (readline(stdin, line2) < 0) break; for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) ; if (strlen(linebuf) + strlen(cp2) >= (unsigned)LINESIZE-2) break; cp = &linebuf[strlen(linebuf)]; while (cp > linebuf && (isspace(cp[-1]) || cp[-1] == '\\')) cp--; *cp++ = ' '; strcpy(cp, cp2); } if ((c = strlen(linebuf)) > 0) { cp = &linebuf[c-1]; while (cp > linebuf && isspace(*cp)) cp--; *++cp = 0; } if (ishfield(linebuf, "to")) hp->h_to = addto(hp->h_to, hcontents(linebuf)); else if (ishfield(linebuf, "subject")) hp->h_subject = addone(hp->h_subject, hcontents(linebuf)); else if (ishfield(linebuf, "cc")) hp->h_cc = addto(hp->h_cc, hcontents(linebuf)); else if (ishfield(linebuf, "bcc")) hp->h_bcc = addto(hp->h_bcc, hcontents(linebuf)); else if (ishfield(linebuf, "default-options")) hp->h_defopt = addone(hp->h_defopt, hcontents(linebuf)); else hp->h_others = Xaddone(hp->h_others, linebuf); hp->h_seq++; continue; } if ((linebuf[0] != escape) || (rflag != NOSTR) || (!intty && !(int)value("escapeok"))) { if (write(fileno(obuf),linebuf,nread) != nread) goto werr; continue; } /* * On double escape, just send the single one. */ if ((nread > 1) && (linebuf[1] == escape)) { if (write(fileno(obuf),linebuf+1,nread-1) != (nread-1)) goto werr; continue; } if (hasnulls) nread = stripnulls(linebuf, nread); c = linebuf[1]; linebuf[nread - 1] = '\0'; switch (c) { default: /* * Otherwise, it's an error. */ printf(gettext("Unknown tilde escape.\n")); break; case 'a': case 'A': /* * autograph; sign the letter. */ if (cp = value(c=='a' ? "sign":"Sign")) { if (*cp) cpout( cp, obuf); if (isatty(fileno(stdin))) { if (*cp) cpout( cp, stdout); } } break; case 'i': /* * insert string */ for (cp = &linebuf[2]; any(*cp, " \t"); cp++) ; if (*cp) cp = value(cp); if (cp != NOSTR) { if (*cp) cpout(cp, obuf); if (isatty(fileno(stdout))) { if (*cp) cpout(cp, stdout); } } break; case '!': /* * Shell escape, send the balance of the * line to sh -c. */ shell(&linebuf[2]); break; case ':': case '_': /* * Escape to command mode, but be nice! */ execute(&linebuf[2], 1); iprompt = value("iprompt"); if (cp = value("escape")) escape = *cp; printf(gettext("(continue)\n")); break; case '.': /* * Simulate end of file on input. */ goto eofl; case 'q': case 'Q': /* * Force a quit of sending mail. * Act like an interrupt happened. */ hadintr++; collrub(SIGINT); exit(1); /* NOTREACHED */ case 'x': xhalt(); break; /* not reached */ case 'h': /* * Grab a bunch of headers. */ if (!intty || !outtty) { printf(gettext("~h: no can do!?\n")); break; } grabh(hp, GMASK, (int)value("bsdcompat")); printf(gettext("(continue)\n")); break; case 't': /* * Add to the To list. */ hp->h_to = addto(hp->h_to, &linebuf[2]); hp->h_seq++; break; case 's': /* * Set the Subject list. */ cp = &linebuf[2]; while (any(*cp, " \t")) cp++; hp->h_subject = savestr(cp); hp->h_seq++; break; case 'c': /* * Add to the CC list. */ hp->h_cc = addto(hp->h_cc, &linebuf[2]); hp->h_seq++; break; case 'b': /* * Add stuff to blind carbon copies list. */ hp->h_bcc = addto(hp->h_bcc, &linebuf[2]); hp->h_seq++; break; case 'R': hp->h_defopt = addone(hp->h_defopt, myname); hp->h_seq++; fprintf(stderr, gettext("Return receipt marked.\n")); receipt_flg = 1; break; case 'd': copy(Getf("DEAD"), &linebuf[2]); /* FALLTHROUGH */ case '<': case 'r': { int ispip; /* * Invoke a file: * Search for the file name, * then open it and copy the contents to obuf. * * if name begins with '!', read from a command */ cp = &linebuf[2]; while (any(*cp, " \t")) cp++; if (*cp == '\0') { printf(gettext("Interpolate what file?\n")); break; } if (*cp=='!') { /* take input from a command */ ispip = 1; if ((fbuf = npopen(++cp, "r"))==NULL) { perror(""); break; } sigint = sigset(SIGINT, SIG_IGN); } else { ispip = 0; cp = expand(cp); if (cp == NOSTR) break; if (isdir(cp)) { printf(gettext("%s: directory\n"), cp); break; } if ((fbuf = fopen(cp, "r")) == NULL) { perror(cp); break; } } printf("\"%s\" ", cp); flush(); lc = cc = 0; while ((t = getc(fbuf)) != EOF) { if (t == '\n') lc++; if (putc(t, obuf) == EOF) { if (ispip) { npclose(fbuf); sigset(SIGINT, sigint); } else fclose(fbuf); goto werr; } cc++; } if (ispip) { npclose(fbuf); sigset(SIGINT, sigint); } else fclose(fbuf); printf("%ld/%ld\n", lc, cc); fflush(obuf); break; } case 'w': /* * Write the message on a file. */ cp = &linebuf[2]; while (any(*cp, " \t")) cp++; if (*cp == '\0') { fprintf(stderr, gettext("Write what file!?\n")); break; } if ((cp = expand(cp)) == NOSTR) break; fflush(obuf); rewind(ibuf); exwrite(cp, ibuf); break; case 'm': case 'M': case 'f': case 'F': /* * Interpolate the named messages, if we * are in receiving mail mode. Does the * standard list processing garbage. * If ~f or ~F is given, we don't shift over. */ if (!rcvmode) { printf(gettext( "No messages to send from!?!\n")); break; } cp = &linebuf[2]; while (any(*cp, " \t")) cp++; if (forward(cp, obuf, c) < 0) goto werr; fflush(obuf); printf(gettext("(continue)\n")); break; case '?': if ((fbuf = fopen(THELPFILE, "r")) == NULL) { printf(gettext("No help just now.\n")); break; } t = getc(fbuf); while (t != -1) { putchar(t); t = getc(fbuf); } fclose(fbuf); break; case 'p': { /* * Print out the current state of the * message without altering anything. */ int nlines; extern jmp_buf pipestop; extern void brokpipe(int); fflush(obuf); rewind(ibuf); fbuf = stdout; if (setjmp(pipestop)) goto ret0; if (intty && outtty && (cp = value("crt")) != NOSTR) { nlines = (*cp == '\0' ? screensize() : atoi(cp)) - 7; /* 7 for hdr lines */ while ((t = getc(ibuf)) != EOF) { if (t == '\n') if (--nlines <= 0) break; } rewind(ibuf); if (nlines <= 0) { fbuf = npopen(MORE, "w"); if (fbuf == NULL) { perror(MORE); fbuf = stdout; } else { sigint = sigset(SIGINT, SIG_IGN); sigpipe = sigset(SIGPIPE, brokpipe); } } } fprintf(fbuf, gettext("-------\nMessage contains:\n")); puthead(hp, fbuf, GMASK, 0); while ((t = getc(ibuf))!=EOF) putc(t, fbuf); ret0: if (fbuf != stdout) { npclose(fbuf); sigset(SIGPIPE, sigpipe); sigset(SIGINT, sigint); } printf(gettext("(continue)\n")); break; } case '^': case '|': /* * Pipe message through command. * Collect output as new message. */ obuf = mespipe(ibuf, obuf, &linebuf[2]); newo = obuf; ibuf = newi; newi = ibuf; printf(gettext("(continue)\n")); break; case 'v': case 'e': /* * Edit the current message. * 'e' means to use EDITOR * 'v' means to use VISUAL */ if ((obuf = mesedit(ibuf, obuf, c, hp)) == NULL) goto err; newo = obuf; ibuf = newi; printf(gettext("(continue)\n")); break; } fflush(obuf); } eofl: fflush(obuf); if ((cp = value("MAILX_TAIL")) != NOSTR) { cpout( cp, obuf); if (isatty(fileno(stdin))) cpout( cp, stdout); } fclose(obuf); rewind(ibuf); resetsigs(0); noreset = 0; return(ibuf); werr: /* * Write error occurred on tmp file, save partial * message in dead.letter. */ perror(tempMail); fflush(obuf); rewind(ibuf); if (fsize(ibuf) > 0) { char *deadletter; deadletter = Getf("DEAD"); fprintf(stderr, gettext("Saving partial message in %s\n"), deadletter); if ((fbuf = fopen(deadletter, value("appenddeadletter") == NOSTR ? "w" : "a")) != NULL) { chmod(deadletter, DEADPERM); puthead(hp, fbuf, GMASK|GCLEN, fsize(ibuf)); lcwrite(deadletter, ibuf, fbuf, value("appenddeadletter") != NOSTR); fclose(fbuf); } else perror(deadletter); } err: if (ibuf != NULL) fclose(ibuf); if (obuf != NULL) fclose(obuf); resetsigs(0); noreset = 0; return(NULL); } static void resetsigs(int resethup) { (void) sigset(SIGINT, savesig); if (resethup) (void) sigset(SIGHUP, savehup); #ifdef SIGCONT # ifdef preSVr4 (void) sigset(SIGCONT, savecont); # else { struct sigaction nsig; nsig.sa_handler = (void (*)())savecont; sigemptyset(&nsig.sa_mask); nsig.sa_flags = SA_RESTART; (void) sigaction(SIGCONT, &nsig, (struct sigaction*)0); } # endif #endif } /* * Write a file ex-like. */ static int exwrite(char name[], FILE *ibuf) { FILE *of; struct stat junk; void (*sigint)(int), (*sigpipe)(int); int pi = (*name == '!'); if ((of = pi ? npopen(++name, "w") : fopen(name, "a")) == NULL) { perror(name); return(-1); } if (pi) { sigint = sigset(SIGINT, SIG_IGN); sigpipe = sigset(SIGPIPE, SIG_IGN); } lcwrite(name, ibuf, of, 0); pi ? npclose(of) : fclose(of); if (pi) { sigset(SIGPIPE, sigpipe); sigset(SIGINT, sigint); } return(0); } void lcwrite(char *fn, FILE *fi, FILE *fo, int addnl) { int c; long lc, cc; printf("\"%s\" ", fn); fflush(stdout); lc = cc = 0; while ((c = getc(fi)) != EOF) { cc++; if (putc(c, fo) == '\n') lc++; if (ferror(fo)) { perror(""); return; } } if (addnl) { putc('\n', fo); lc++; cc++; } fflush(fo); if (fferror(fo)) { perror(""); return; } printf("%ld/%ld\n", lc, cc); fflush(stdout); } /* * Edit the message being collected on ibuf and obuf. * Write the message out onto some poorly-named temp file * and point an editor at it. * * On return, make the edit file the new temp file. */ static FILE * mesedit(FILE *ibuf, FILE *obuf, int c, struct header *hp) { pid_t pid; FILE *fbuf; int t; void (*sigint)(int); #ifdef SIGCONT void (*sigcont)(int); #endif struct stat sbuf; char *edit; char hdr[LINESIZE]; char *oto, *osubject, *occ, *obcc, **oothers; int fd = -1; if (stat(tempEdit, &sbuf) >= 0) { printf(gettext("%s: file exists\n"), tempEdit); goto out; } if ((fd = open(tempEdit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || (fbuf = fdopen(fd, "w")) == NULL) { perror(tempEdit); goto out; } fflush(obuf); rewind(ibuf); puthead(hp, fbuf, GMASK, 0); while ((t = getc(ibuf)) != EOF) putc(t, fbuf); fflush(fbuf); if (fferror(fbuf)) { perror(tempEdit); removefile(tempEdit); goto out; } fclose(fbuf); if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR || *edit == '\0') edit = c == 'e' ? EDITOR : VISUAL; edit = safeexpand(edit); /* * Fork/execlp the editor on the edit file */ pid = vfork(); if (pid == (pid_t)-1) { perror("fork"); removefile(tempEdit); goto out; } if (pid == 0) { char ecmd[BUFSIZ]; char *Shell; sigchild(); execlp(edit, edit, tempEdit, (char *)0); /* * If execlp fails, "edit" might really be a complete * shell command, not a simple pathname. Try using * the shell to run it. */ snprintf(ecmd, sizeof (ecmd), "exec %s %s", edit, tempEdit); if ((Shell = value("SHELL")) == NULL || *Shell=='\0') Shell = SHELL; execlp(Shell, Shell, "-c", ecmd, NULL); perror(edit); _exit(1); } sigint = sigset(SIGINT, SIG_IGN); #ifdef SIGCONT sigcont = sigset(SIGCONT, SIG_DFL); #endif while (wait((int *)0) != pid) ; sigset(SIGINT, sigint); #ifdef SIGCONT sigset(SIGCONT, sigcont); #endif /* * Now switch to new file. */ if ((fbuf = fopen(tempEdit, "r")) == NULL) { perror(tempEdit); removefile(tempEdit); goto out; } removefile(tempEdit); /* save the old headers, in case they are accidentally deleted */ osubject = hp->h_subject; oto = hp->h_to; occ = hp->h_cc; obcc = hp->h_bcc; oothers = hp->h_others; hp->h_to = hp->h_subject = hp->h_cc = hp->h_bcc = hp->h_defopt = NOSTR; hp->h_others = NOSTRPTR; hp->h_seq = 0; while (gethfield(fbuf, hdr, 9999L) > 0) { if (ishfield(hdr, "to")) hp->h_to = addto(hp->h_to, hcontents(hdr)); else if (ishfield(hdr, "subject")) hp->h_subject = addone(hp->h_subject, hcontents(hdr)); else if (ishfield(hdr, "cc")) hp->h_cc = addto(hp->h_cc, hcontents(hdr)); else if (ishfield(hdr, "bcc")) hp->h_bcc = addto(hp->h_bcc, hcontents(hdr)); else if (ishfield(hdr, "default-options")) hp->h_defopt = addone(hp->h_defopt, hcontents(hdr)); else hp->h_others = Xaddone(hp->h_others, hdr); hp->h_seq++; } if (hp->h_seq == 0) { /* if we didn't see any headers, restore the original headers */ hp->h_subject = osubject; hp->h_to = oto; hp->h_cc = occ; hp->h_bcc = obcc; hp->h_others = oothers; printf(gettext( "(Deleted headers restored to original values)\n")); } if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || (obuf = fdopen(fd, "w")) == NULL) { perror(tempMail); fclose(fbuf); goto out; } if ((ibuf = fopen(tempMail, "r")) == NULL) { perror(tempMail); removefile(tempMail); fclose(fbuf); fclose(obuf); goto out; } removefile(tempMail); if (strlen(hdr) != 0) { fputs(hdr, obuf); putc('\n', obuf); } while ((t = getc(fbuf)) != EOF) putc(t, obuf); fclose(fbuf); fclose(newo); fclose(newi); newo = obuf; newi = ibuf; out: return(newo); } /* * Pipe the message through the command. * Old message is on stdin of command; * New message collected from stdout. * Sh -c must return 0 to accept the new message. */ static FILE * mespipe(FILE *ibuf, FILE *obuf, char cmd[]) { FILE *ni, *no; pid_t pid; int s; void (*sigint)(int); char *Shell; int fd = -1; newi = ibuf; if ((fd = open(tempEdit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || (no = fdopen(fd, "w")) == NULL) { perror(tempEdit); return(obuf); } if ((ni = fopen(tempEdit, "r")) == NULL) { perror(tempEdit); fclose(no); removefile(tempEdit); return(obuf); } removefile(tempEdit); fflush(obuf); rewind(ibuf); if ((Shell = value("SHELL")) == NULL || *Shell=='\0') Shell = SHELL; if ((pid = vfork()) == (pid_t)-1) { perror("fork"); goto err; } if (pid == 0) { /* * stdin = current message. * stdout = new message. */ sigchild(); close(0); dup(fileno(ibuf)); close(1); dup(fileno(no)); for (s = 4; s < 15; s++) close(s); execlp(Shell, Shell, "-c", cmd, (char *)0); perror(Shell); _exit(1); } sigint = sigset(SIGINT, SIG_IGN); while (wait(&s) != pid) ; sigset(SIGINT, sigint); if (s != 0 || pid == (pid_t)-1) { fprintf(stderr, gettext("\"%s\" failed!?\n"), cmd); goto err; } if (fsize(ni) == 0) { fprintf(stderr, gettext("No bytes from \"%s\" !?\n"), cmd); goto err; } /* * Take new files. */ newi = ni; fclose(ibuf); fclose(obuf); return(no); err: fclose(no); fclose(ni); return(obuf); } static char *indentprefix; /* used instead of tab by tabputs */ /* * Interpolate the named messages into the current * message, preceding each line with a tab. * Return a count of the number of characters now in * the message, or -1 if an error is encountered writing * the message temporary. The flag argument is 'm' if we * should shift over and 'f' if not. */ static int forward(char ms[], FILE *obuf, int f) { int *msgvec, *ip; msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec); if (msgvec == NOINTPTR) return(0); if (getmsglist(ms, msgvec, 0) < 0) return(0); if (*msgvec == 0) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf(gettext("No appropriate messages\n")); return(0); } msgvec[1] = 0; } if (tolower(f) == 'm') indentprefix = value("indentprefix"); printf(gettext("Interpolating:")); for (ip = msgvec; *ip != 0; ip++) { touch(*ip); printf(" %d", *ip); if (msend(&message[*ip-1], obuf, islower(f) ? M_IGNORE : 0, tolower(f) == 'm' ? tabputs : fputs) < 0) { perror(tempMail); return(-1); } } fflush(obuf); if (fferror(obuf)) { perror(tempMail); return(-1); } printf("\n"); return(0); } static int tabputs(const char *line, FILE *obuf) { if (indentprefix) fputs(indentprefix, obuf); /* Don't create lines with only a tab on them */ else if (line[0] != '\n') fputc('\t', obuf); return (fputs(line, obuf)); } /* * Print (continue) when continued after ^Z. */ #ifdef SIGCONT static void #ifdef __cplusplus collcont(int) #else /* ARGSUSED */ collcont(int s) #endif { printf(gettext("(continue)\n")); fflush(stdout); } #endif /* SIGCONT */ /* * On interrupt, go here to save the partial * message on ~/dead.letter. * Then restore signals and execute the normal * signal routine. We only come here if signals * were previously set anyway. */ static void collrub(int s) { FILE *dbuf; char *deadletter; # ifdef OLD_BSD_SIGS if (s == SIGHUP) sigignore(SIGHUP); # endif if (s == SIGINT && hadintr == 0) { hadintr++; fflush(stdout); fprintf(stderr, gettext("\n(Interrupt -- one more to kill letter)\n")); # ifdef OLD_BSD_SIGS sigrelse(s); # endif longjmp(coljmp, 1); } fclose(newo); rewind(newi); if (s == SIGINT && value("save")==NOSTR || fsize(newi) == 0) goto done; deadletter = Getf("DEAD"); if ((dbuf = fopen(deadletter, (value("appenddeadletter") == NOSTR ? "w" : "a"))) == NULL) { perror(deadletter); goto done; } chmod(deadletter, DEADPERM); puthead(savehp, dbuf, GMASK|GCLEN, fsize(newi)); lcwrite(deadletter, newi, dbuf, value("appenddeadletter") != NOSTR); fclose(dbuf); done: fclose(newi); resetsigs(1); if (rcvmode) { if (s == SIGHUP) hangup(s); else stop(s); } else exit(1); } /* * Acknowledge an interrupt signal from the tty by typing an @ */ static void #ifdef __cplusplus intack(int) #else /* ARGSUSED */ intack(int s) #endif { puts("@"); fflush(stdout); clearerr(stdin); longjmp(coljmp,1); } /* Read line from stdin, noting any NULL characters. Return the number of characters read. Note that the buffer passed must be 1 larger than "size" for the trailing NUL byte. */ int getaline(char *line, int size, FILE *f, int *hasnulls) { int i, ch; for (i = 0; (i < size) && ((ch=getc(f)) != EOF); ) { if ( ch == '\0' ) *hasnulls = 1; if ((line[i++] = (char)ch) == '\n') break; } line[i] = '\0'; return(i); } void #ifdef __cplusplus savedead(int) #else /* ARGSUSED */ savedead(int s) #endif { collrub(SIGINT); exit(1); /* NOTREACHED */ } /* * Add a list of addresses to the end of a header entry field. */ char * addto(char hf[], char news[]) { char name[LINESIZE]; int comma = docomma(news); while (news = yankword(news, name, sizeof (name), comma)) { nstrcat(name, sizeof (name), ", "); hf = addone(hf, name); } return hf; } /* * Add a string to the end of a header entry field. */ char * addone(char hf[], char news[]) { char *cp, *cp2, *linebuf; if (hf == NOSTR) hf = savestr(""); if (*news == '\0') return(hf); linebuf = (char *)srealloc(hf, (unsigned)(strlen(hf) + strlen(news) + 2)); cp2 = strchr(linebuf, '\0'); if (cp2 > linebuf && cp2[-1] != ' ') *cp2++ = ' '; for (cp = news; any(*cp, " \t"); cp++) ; while (*cp != '\0') *cp2++ = *cp++; *cp2 = '\0'; return(linebuf); } static int nptrs(char **hf) { int i; if (!hf) return(0); for (i = 0; *hf; hf++) i++; return(i); } /* * Add a non-standard header to the end of the non-standard headers. */ static char ** Xaddone(char **hf, char news[]) { char *linebuf; char **ohf = hf; int nhf = nptrs(hf); if (hf == NOSTRPTR) hf = (char**)salloc(sizeof(char*) * 2); else hf = (char**)srealloc(hf, sizeof(char*) * (nhf + 2)); if (hf == NOSTRPTR) { fprintf(stderr, gettext("No room, header lost: %s\n"), news); return(ohf); } linebuf = (char *)salloc((unsigned)(strlen(news) + 1)); strcpy(linebuf, news); hf[nhf++] = linebuf; hf[nhf] = NOSTR; return(hf); } static void cpout(char *str, FILE *ofd) { char *cp = str; while (*cp) { if (*cp == '\\') { switch (*(cp+1)) { case 'n': putc('\n', ofd); cp++; break; case 't': putc('\t', ofd); cp++; break; default: putc('\\', ofd); } } else { putc(*cp, ofd); } cp++; } putc('\n', ofd); fflush(ofd); } static void xhalt(void) { fclose(newo); fclose(newi); sigset(SIGINT, savesig); sigset(SIGHUP, savehup); if (rcvmode) stop(0); exit(1); /* NOTREACHED */ } /* * Strip the nulls from a buffer of length n */ static int stripnulls(char *linebuf, int nread) { int i, j; for (i = 0; i < nread; i++) if (linebuf[i] == '\0') break; for (j = i; j < nread; j++) if (linebuf[j] != '\0') linebuf[i++] = linebuf[j]; linebuf[i] = '\0'; return(i); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program */ /* * This file contains definitions of network data used by mailx * when replying. See also: configdefs.h and optim.c */ /* * The subterfuge with CONFIGFILE is to keep cc from seeing the * external definitions in configdefs.h. */ #define CONFIGFILE #include "configdefs.h" /* * Set of network separator characters. */ char *metanet = "!^:%@"; /* * Host table of "known" hosts. See the comment in configdefs.h; * not all accessible hosts need be here (fortunately). */ struct netmach netmach[] = { EMPTY, EMPTYID, BN|AN, /* Filled in dynamically */ EMPTY, EMPTYID, BN|AN, /* Filled in dynamically */ 0, 0, 0 }; #ifdef OPTIM /* * Table of ordered of preferred networks. You probably won't need * to fuss with this unless you add a new network character (foolishly). */ struct netorder netorder[] = { AN, '@', AN, '%', SN, ':', BN, '!', -1, 0 }; #endif /* * Table to convert from network separator code in address to network * bit map kind. With this transformation, we can deal with more than * one character having the same meaning easily. */ struct ntypetab ntypetab[] = { '%', AN, '@', AN, ':', SN, '!', BN, '^', BN, 0, 0 }; #ifdef OPTIM struct nkindtab nkindtab[] = { AN, IMPLICIT, BN, EXPLICIT, SN, IMPLICIT, 0, 0 }; #endif /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1996 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Perform message editing functions. */ static void edit1(int *msgvec, char *ed); /* * Edit a message list. */ int editor(int *msgvec) { char *edname; if ((edname = value("EDITOR")) == NOSTR || *edname == '\0') edname = EDITOR; edit1(msgvec, edname); return(0); } /* * Invoke the visual editor on a message list. */ int visual(int *msgvec) { char *edname; if ((edname = value("VISUAL")) == NOSTR || *edname == '\0') edname = VISUAL; edit1(msgvec, edname); return(0); } /* * Edit a message by writing the message into a funnily-named file * (which should not exist) and forking an editor on it. * We get the editor from the stuff above. */ static void edit1(int *msgvec, char *ed) { register int c, lastc = '\n'; pid_t pid; int *ip, mesg, blank = 1; long ms, lines; void (*sigint)(int), (*sigquit)(int); FILE *ibuf, *obuf; struct message *mp; off_t size; struct stat statb; long modtime; int fd = -1; /* * Set signals; locate editor. */ sigint = sigset(SIGINT, SIG_IGN); sigquit = sigset(SIGQUIT, SIG_IGN); ed = safeexpand(ed); /* * Deal with each message to be edited . . . */ for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mesg = *ip; touch(mesg); mp = &message[mesg-1]; dot = mp; if (mp->m_text) { if (!access(tempZedit, 2)) { printf(gettext("%s: file exists\n"), tempZedit); goto out; } /* * Copy the message into the edit file. */ if ((fd = open(tempZedit, O_RDWR|O_CREAT| O_EXCL, 0600)) < 0 || (obuf = fdopen(fd, "w")) == NULL) { perror(tempZedit); goto out; } if (msend(mp, obuf, 0, fputs) < 0) { perror(tempZedit); fclose(obuf); removefile(tempZedit); goto out; } fflush(obuf); if (fferror(obuf)) { perror(tempZedit); fclose(obuf); removefile(tempZedit); goto out; } fclose(obuf); /* * If we are in read only mode, make the * temporary message file readonly as well. */ if (readonly) chmod(tempZedit, 0400); /* * Fork/execl the editor on the edit file. */ if (stat(tempZedit, &statb) < 0) modtime = 0; else modtime = statb.st_mtime; pid = vfork(); if (pid == (pid_t)-1) { perror("fork"); removefile(tempZedit); goto out; } if (pid == 0) { sigchild(); if (sigint != SIG_IGN) sigset(SIGINT, SIG_DFL); if (sigquit != SIG_IGN) sigset(SIGQUIT, SIG_DFL); execlp(ed, ed, tempZedit, (char *)0); perror(ed); _exit(1); } while (wait(&mesg) != pid) ; /* * If in read only mode, just remove the editor * temporary and return. */ if (readonly) { removefile(tempZedit); continue; } /* * Now copy the message to the end of the * temp file. */ if (stat(tempZedit, &statb) < 0) { perror(tempZedit); continue; } if (modtime == statb.st_mtime) { removefile(tempZedit); continue; } if ((ibuf = fopen(tempZedit, "r")) == NULL) { perror(tempZedit); removefile(tempZedit); continue; } removefile(tempZedit); fseek(otf, (long) 0, 2); size = fsize(otf); mp->m_flag |= MODIFY; mp->m_offset = size; ms = 0L; lines = 0; while ((c = getc(ibuf)) != EOF) { if (c == '\n') { lines++; blank = lastc == '\n'; } lastc = c; putc(c, otf); if (ferror(otf)) break; ms++; } if (!blank) { putc('\n', otf); ms++; lines++; } mp->m_size = ms; mp->m_lines = lines; fflush(otf); if (fferror(otf)) perror("/tmp"); fclose(ibuf); setclen(mp); } else { printf("\n%s\n", gettext( "*** Message content is not printable: pipe to command or save to a file ***")); } } /* * Restore signals and return. */ out: sigset(SIGINT, sigint); sigset(SIGQUIT, sigquit); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2014 Joyent, Inc. */ /* * Copyright 1999 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include "rcv.h" #include #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * File I/O. */ static int getln(char *line, int max, FILE *f); static int linecount(char *lp, long size); /* * Set up the input pointers while copying the mail file into * /tmp. */ void setptr(register FILE *ibuf) { int n, newline = 1, blankline = 1; int StartNewMsg = TRUE; int ToldUser = FALSE; long clen = -1L; int hdr = 0; int cflg = 0; /* found Content-length in header */ register char *cp; register int l; register long s; off_t offset; char linebuf[LINESIZE]; int inhead, newmail, Odot; short flag; if (!space) { msgCount = 0; offset = 0; space = 32; newmail = 0; message = (struct message *)calloc(space, sizeof (struct message)); if (message == NULL) { fprintf(stderr, gettext( "calloc: insufficient memory for %d messages\n"), space); exit(1); /* NOTREACHED */ } dot = message; } else { newmail = 1; offset = fsize(otf); } s = 0L; l = 0; /* * Set default flags. When reading from * a folder, assume the message has been * previously read. */ if (edit) flag = MUSED|MREAD; else flag = MUSED|MNEW; inhead = 0; while ((n = getln(linebuf, sizeof (linebuf), ibuf)) > 0) { if (!newline) { goto putout; } top: hdr = inhead && (headerp(linebuf) || (linebuf[0] == ' ' || linebuf[0] == '\t')); if (!hdr && cflg) { /* nonheader, Content-length seen */ if (clen > 0 && clen < n) { /* read too much */ /* * NB: this only can happen if there is a * small content that is NOT \n terminated * and has no leading blank line, i.e., never. */ if (fwrite(linebuf, 1, (int)clen, otf) != clen) { fclose(ibuf); fflush(otf); } else { l += linecount(linebuf, clen); } offset += clen; s += clen; n -= (int)clen; /* shift line to the left, copy null as well */ memcpy(linebuf, linebuf+clen, n+1); cflg = 0; message[msgCount-1].m_clen = clen + 1; blankline = 1; StartNewMsg = TRUE; goto top; } /* here, clen == 0 or clen >= n */ if (n == 1 && linebuf[0] == '\n') { /* leading empty line */ clen++; /* cheat */ inhead = 0; } offset += clen; s += (long)clen; message[msgCount-1].m_clen = clen; for (;;) { if (fwrite(linebuf, 1, n, otf) != n) { fclose(ibuf); fflush(otf); } else { l += linecount(linebuf, n); } clen -= n; if (clen <= 0) { break; } n = clen < sizeof (linebuf) ? (int)clen : (int)sizeof (linebuf); if ((n = fread(linebuf, 1, n, ibuf)) <= 0) { fprintf(stderr, gettext( "%s:\tYour mailfile was found to be corrupted.\n"), progname); fprintf(stderr, gettext( "\t(Unexpected end-of-file).\n")); fprintf(stderr, gettext( "\tMessage #%d may be truncated.\n\n"), msgCount); offset -= clen; s -= clen; clen = 0; /* stop the loop */ } } /* All done, go to top for next message */ cflg = 0; blankline = 1; StartNewMsg = TRUE; continue; } /* Look for a From line that starts a new message */ if (blankline && linebuf[0] == 'F' && is_headline(linebuf)) { if (msgCount > 0 && !newmail) { message[msgCount-1].m_size = s; message[msgCount-1].m_lines = l; message[msgCount-1].m_flag = flag; } if (msgCount >= space) { /* * Limit the speed at which the * allocated space grows. */ if (space < 512) space = space*2; else space += 512; errno = 0; Odot = dot - &(message[0]); message = (struct message *) realloc(message, space*(sizeof (struct message))); if (message == NULL) { perror("realloc failed"); fprintf(stderr, gettext( "realloc: insufficient memory for %d messages\n"), space); exit(1); } dot = &message[Odot]; } message[msgCount].m_offset = offset; message[msgCount].m_text = TRUE; message[msgCount].m_clen = 0; newmail = 0; msgCount++; if (edit) flag = MUSED|MREAD; else flag = MUSED|MNEW; inhead = 1; s = 0L; l = 0; StartNewMsg = FALSE; ToldUser = FALSE; goto putout; } /* if didn't get a header line, we're no longer in the header */ if (!hdr) inhead = 0; if (!inhead) goto putout; /* * Look for Status: line. Do quick check for second character, * many headers start with "S" but few have "t" as second char. */ if ((linebuf[1] == 't' || linebuf[1] == 'T') && ishfield(linebuf, "status")) { cp = hcontents(linebuf); flag = MUSED|MNEW; if (strchr(cp, 'R')) flag |= MREAD; if (strchr(cp, 'O')) flag &= ~MNEW; } /* * Look for Content-Length and Content-Type headers. Like * above, do a quick check for the "-", which is rare. */ if (linebuf[7] == '-') { if (ishfield(linebuf, "content-length")) { if (!cflg) { clen = atol(hcontents(linebuf)); cflg = clen >= 0; } } else if (ishfield(linebuf, "content-type")) { char word[LINESIZE]; char *cp2; cp = hcontents(linebuf); cp2 = word; while (!isspace(*cp)) *cp2++ = *cp++; *cp2 = '\0'; if (icequal(word, "binary")) message[msgCount-1].m_text = FALSE; } } putout: offset += n; s += (long)n; if (fwrite(linebuf, 1, n, otf) != n) { fclose(ibuf); fflush(otf); } else { l++; } if (ferror(otf)) { perror("/tmp"); exit(1); } if (msgCount == 0) { fclose(ibuf); fflush(otf); } if (linebuf[n-1] == '\n') { blankline = newline && n == 1; newline = 1; if (n == 1) { /* Blank line. Skip StartNewMsg check below */ continue; } } else { newline = 0; } if (StartNewMsg && !ToldUser) { fprintf(stderr, gettext( "%s:\tYour mailfile was found to be corrupted\n"), progname); fprintf(stderr, gettext("\t(Content-length mismatch).\n")); fprintf(stderr, gettext( "\tMessage #%d may be truncated,\n"), msgCount); fprintf(stderr, gettext( "\twith another message concatenated to it.\n\n")); ToldUser = TRUE; } } if (n == 0) { fflush(otf); if (fferror(otf)) { perror("/tmp"); exit(1); } if (msgCount) { message[msgCount-1].m_size = s; message[msgCount-1].m_lines = l; message[msgCount-1].m_flag = flag; } fclose(ibuf); } } /* * Compute the content length of a message and set it into m_clen. */ void setclen(register struct message *mp) { long c; FILE *ibuf; char line[LINESIZE]; int fline, nread; ibuf = setinput(mp); c = mp->m_size; fline = 1; while (c > 0L) { nread = getln(line, sizeof (line), ibuf); c -= nread; /* * First line is the From line, so no headers * there to worry about. */ if (fline) { fline = 0; continue; } /* * If line is blank, we've reached end of headers. */ if (line[0] == '\n') break; /* * If this line is a continuation * of a previous header field, keep going. */ if (isspace(line[0])) continue; /* * If we are no longer looking at real * header lines, we're done. * This happens in uucp style mail where * there are no headers at all. */ if (!headerp(line)) { c += nread; break; } } if (c == 0) c = 1; mp->m_clen = c; } static int getln(char *line, int max, FILE *f) { register int c; register char *cp, *ecp; cp = line; ecp = cp + max - 1; while (cp < ecp && (c = getc(f)) != EOF) if ((*cp++ = (char)c) == '\n') break; *cp = '\0'; return (cp - line); } /* * Read up a line from the specified input into the line * buffer. Return the number of characters read. Do not * include the newline at the end. */ int readline(FILE *ibuf, char *linebuf) { register char *cp; register int c; int seennulls = 0; clearerr(ibuf); c = getc(ibuf); for (cp = linebuf; c != '\n' && c != EOF; c = getc(ibuf)) { if (c == 0) { if (!seennulls) { fprintf(stderr, gettext("mailx: NUL changed to @\n")); seennulls++; } c = '@'; } if (cp - linebuf < LINESIZE-2) *cp++ = (char)c; } *cp = 0; if (c == EOF && cp == linebuf) return (0); return (cp - linebuf + 1); } /* * linecount - determine the number of lines in a printable file. */ static int linecount(char *lp, long size) { register char *cp, *ecp; register int count; count = 0; cp = lp; ecp = cp + size; while (cp < ecp) if (*cp++ == '\n') count++; return (count); } /* * Return a file buffer all ready to read up the * passed message pointer. */ FILE * setinput(register struct message *mp) { fflush(otf); if (fseek(itf, mp->m_offset, 0) < 0) { perror("fseek"); panic("temporary file seek"); } return (itf); } /* * Delete a file, but only if the file is a plain file. */ int removefile(char name[]) { struct stat statb; extern int errno; if (stat(name, &statb) < 0) if (errno == ENOENT) return (0); /* it's already gone, no error */ else return (-1); if ((statb.st_mode & S_IFMT) != S_IFREG) { errno = EISDIR; return (-1); } return (unlink(name)); } /* * Terminate an editing session by attempting to write out the user's * file from the temporary. Save any new stuff appended to the file. */ int edstop( int noremove /* don't allow the file to be removed, trunc instead */ ) { register int gotcha, c; register struct message *mp; FILE *obuf, *ibuf, *tbuf = 0, *readstat; struct stat statb; char tempname[STSIZ], *id; int tmpfd = -1; if (readonly) return (0); holdsigs(); if (Tflag != NOSTR) { if ((readstat = fopen(Tflag, "w")) == NULL) Tflag = NOSTR; } for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) { mp->m_flag &= ~MNEW; mp->m_flag |= MSTATUS; } if (mp->m_flag & (MODIFY|MDELETED|MSTATUS)) gotcha++; if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) { if ((id = hfield("article-id", mp, addone)) != NOSTR) fprintf(readstat, "%s\n", id); } } if (Tflag != NOSTR) fclose(readstat); if (!gotcha || Tflag != NOSTR) goto done; if ((ibuf = fopen(editfile, "r+")) == NULL) { perror(editfile); relsesigs(); longjmp(srbuf, 1); } lock(ibuf, "r+", 1); if (fstat(fileno(ibuf), &statb) >= 0 && statb.st_size > mailsize) { nstrcpy(tempname, STSIZ, "/tmp/mboxXXXXXX"); if ((tmpfd = mkstemp(tempname)) == -1) { perror(tempname); fclose(ibuf); relsesigs(); longjmp(srbuf, 1); } if ((obuf = fdopen(tmpfd, "w")) == NULL) { perror(tempname); fclose(ibuf); removefile(tempname); relsesigs(); (void) close(tmpfd); longjmp(srbuf, 1); } fseek(ibuf, mailsize, 0); while ((c = getc(ibuf)) != EOF) putc(c, obuf); fclose(obuf); if ((tbuf = fopen(tempname, "r")) == NULL) { perror(tempname); fclose(ibuf); removefile(tempname); relsesigs(); longjmp(srbuf, 1); } removefile(tempname); } if ((obuf = fopen(editfile, "r+")) == NULL) { if ((obuf = fopen(editfile, "w")) == NULL) { perror(editfile); fclose(ibuf); if (tbuf) fclose(tbuf); relsesigs(); longjmp(srbuf, 1); } } printf("\"%s\" ", editfile); flush(); c = 0; for (mp = &message[0]; mp < &message[msgCount]; mp++) { if ((mp->m_flag & MDELETED) != 0) continue; c++; if (msend(mp, obuf, 0, fputs) < 0) { perror(editfile); fclose(ibuf); fclose(obuf); if (tbuf) fclose(tbuf); relsesigs(); longjmp(srbuf, 1); } } gotcha = (c == 0 && tbuf == NULL); if (tbuf != NULL) { while ((c = getc(tbuf)) != EOF) putc(c, obuf); fclose(tbuf); } fflush(obuf); if (fferror(obuf)) { perror(editfile); fclose(ibuf); fclose(obuf); relsesigs(); longjmp(srbuf, 1); } if (gotcha && !noremove && (value("keep") == NOSTR)) { removefile(editfile); printf(gettext("removed.\n")); } else printf(gettext("updated.\n")); fclose(ibuf); trunc(obuf); fclose(obuf); flush(); done: relsesigs(); return (1); } #ifndef OLD_BSD_SIGS static int sigdepth = 0; /* depth of holdsigs() */ #ifdef VMUNIX static int omask = 0; #else static sigset_t mask, omask; #endif #endif /* * Hold signals SIGHUP - SIGQUIT. */ void holdsigs(void) { #ifndef OLD_BSD_SIGS if (sigdepth++ == 0) { #ifdef VMUNIX omask = sigblock(sigmask(SIGHUP) | sigmask(SIGINT)|sigmask(SIGQUIT)); #else sigemptyset(&mask); sigaddset(&mask, SIGHUP); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT); sigprocmask(SIG_BLOCK, &mask, &omask); #endif } #else sighold(SIGHUP); sighold(SIGINT); sighold(SIGQUIT); #endif } /* * Release signals SIGHUP - SIGQUIT */ void relsesigs(void) { #ifndef OLD_BSD_SIGS if (--sigdepth == 0) #ifdef VMUNIX sigsetmask(omask); #else sigprocmask(SIG_SETMASK, &omask, NULL); #endif #else sigrelse(SIGHUP); sigrelse(SIGINT); sigrelse(SIGQUIT); #endif } #if !defined(OLD_BSD_SIGS) && !defined(VMUNIX) void (*sigset(int sig, void (*act)(int)))(int) { struct sigaction sa, osa; sa.sa_handler = (void (*)())act; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(sig, &sa, &osa) < 0) return ((void (*)(int))-1); return ((void (*)(int))osa.sa_handler); } #endif /* * Flush the standard output. */ void flush(void) { fflush(stdout); fflush(stderr); } /* * Determine the size of the file possessed by * the passed buffer. */ off_t fsize(FILE *iob) { register int f; struct stat sbuf; f = fileno(iob); if (fstat(f, &sbuf) < 0) return (0); return (sbuf.st_size); } /* * Check for either a stdio recognized error, or * a possibly delayed write error (in case it's * an NFS file, for instance). */ int fferror(FILE *iob) { return (ferror(iob) || fsync(fileno(iob)) < 0); } /* * Take a file name, possibly with shell meta characters * in it and expand it by using wordexp(). * Return the file name as a dynamic string. * If the name cannot be expanded (for whatever reason) * return NULL. */ char * expand(char *name) { char xname[BUFSIZ]; char foldbuf[BUFSIZ]; register char *cp; register int l; wordexp_t wrdexp_buf; if (debug) fprintf(stderr, "expand(%s)=", name); cp = strchr(name, '\0') - 1; /* pointer to last char of name */ if (isspace(*cp)) { /* strip off trailing blanks */ while (cp > name && isspace(*cp)) cp--; l = *++cp; /* save char */ *cp = '\0'; name = savestr(name); *cp = (char)l; /* restore char */ } if (name[0] == '+' && getfold(foldbuf) >= 0) { snprintf(xname, sizeof (xname), "%s/%s", foldbuf, name + 1); cp = safeexpand(savestr(xname)); if (debug) fprintf(stderr, "%s\n", cp); return (cp); } if (!anyof(name, "~{[*?$`'\"\\")) { if (debug) fprintf(stderr, "%s\n", name); return (name); } if (wordexp(name, &wrdexp_buf, WRDE_NOCMD) != 0) { fprintf(stderr, gettext("Syntax error in \"%s\"\n"), name); fflush(stderr); return (NOSTR); } if (wrdexp_buf.we_wordc > 1) { fprintf(stderr, gettext("\"%s\": Ambiguous\n"), name); fflush(stderr); return (NOSTR); } if (debug) fprintf(stderr, "%s\n", wrdexp_buf.we_wordv[0]); return (savestr(wrdexp_buf.we_wordv[0])); } /* * Take a file name, possibly with shell meta characters * in it and expand it by using "sh -c echo filename" * Return the file name as a dynamic string. * If the name cannot be expanded (for whatever reason) * return the original file name. */ char * safeexpand(char name[]) { char *t = expand(name); return (t) ? t : savestr(name); } /* * Determine the current folder directory name. */ int getfold(char *name) { char *folder; if ((folder = value("folder")) == NOSTR || *folder == '\0') return (-1); /* * If name looks like a folder name, don't try * to expand it, to prevent infinite recursion. */ if (*folder != '+' && (folder = expand(folder)) == NOSTR || *folder == '\0') return (-1); if (*folder == '/') { nstrcpy(name, BUFSIZ, folder); } else snprintf(name, BUFSIZ, "%s/%s", homedir, folder); return (0); } /* * A nicer version of Fdopen, which allows us to fclose * without losing the open file. */ FILE * Fdopen(int fildes, char *mode) { register int f; f = dup(fildes); if (f < 0) { perror("dup"); return (NULL); } return (fdopen(f, mode)); } /* * return the filename associated with "s". This function always * returns a non-null string (no error checking is done on the receiving end) */ char * Getf(register char *s) { register char *cp; static char defbuf[PATHSIZE]; if (((cp = value(s)) != 0) && *cp) { return (safeexpand(cp)); } else if (strcmp(s, "MBOX") == 0) { snprintf(defbuf, sizeof (defbuf), "%s/%s", Getf("HOME"), "mbox"); return (defbuf); } else if (strcmp(s, "DEAD") == 0) { snprintf(defbuf, sizeof (defbuf), "%s/%s", Getf("HOME"), "dead.letter"); return (defbuf); } else if (strcmp(s, "MAILRC") == 0) { snprintf(defbuf, sizeof (defbuf), "%s/%s", Getf("HOME"), ".mailrc"); return (defbuf); } else if (strcmp(s, "HOME") == 0) { /* no recursion allowed! */ return ("."); } return ("DEAD"); /* "cannot happen" */ } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program */ #include "rcv.h" #include #ifdef preSVr4 extern struct passwd *getpwnam(); extern struct passwd *getpwuid(); #endif /* * Search the passwd file for a uid. Return name through ref parameter * if found, indicating success with 0 return. Return -1 on error. * If -1 is passed as the user id, close the passwd file. */ int getname(uid_t uid, char namebuf[]) { struct passwd *pw; if (uid == (uid_t)-1) { endpwent(); return(0); } setpwent(); pw = getpwuid(uid); if (pw) { strcpy(namebuf, pw->pw_name); return(0); } return(-1); } /* * Convert the passed name to a user id and return it. Return -1 * on error. Iff the name passed is 0, close the passwd file. */ uid_t getuserid(char name[]) { struct passwd *pw; if (name == 0) { endpwent(); return(0); } setpwent(); pw = getpwnam(name); return pw ? pw->pw_uid : (uid_t)-1; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * This file contains the definitions of data structures used in * configuring the network behavior of Mail when replying. */ /* * The following constants are used when you are running 4.1a bsd or * later on a local network. The name thus found is inserted * into the host table slot whose name was originally EMPTY. */ #define EMPTY "** empty **" #define EMPTYID 'E' /* * The following data structure is the host table. You must have * an entry here for your own machine, plus any special stuff you * expect the mailer to know about. Not all hosts need be here, however: * mailx can dope out stuff about hosts on the fly by looking * at addresses. The machines needed here are: * 1) The local machine * 2) Any machines on the path to a network gateway * 3) Any machines with nicknames that you want to have considered * the same. * The machine id letters can be anything you like and are not seen * externally. Be sure not to use characters with the 0200 bit set -- * these have special meanings. */ struct netmach { char *nt_machine; char nt_mid; short nt_type; }; /* * Network type codes. Basically, there is one for each different * network, if the network can be discerned by the separator character, * such as @ for the arpa net. The purpose of these codes is to * coalesce cases where more than one character means the same thing, * such as % and @ for the arpanet. Also, the host table uses a * bit map of these codes to show what it is connected to. * BN -- connected to Bell Net. * AN -- connected to ARPA net, SN -- connected to Schmidt net. */ #define AN 1 /* Connected to ARPA net */ #define BN 2 /* Connected to BTL net */ #define SN 4 /* Connected to Schmidt net */ /* * Data structure for table mapping network characters to network types. */ struct ntypetab { char nt_char; /* Actual character separator */ int nt_bcode; /* Type bit code */ }; /* * Codes for the "kind" of a network. IMPLICIT means that if there are * physically several machines on the path, one does not list them in the * address. The arpa net is like this. EXPLICIT means you list them, * as in UUCP. * By the way, this distinction means we lose if anyone actually uses the * arpa net subhost convention: name@subhost@arpahost */ #define IMPLICIT 1 #define EXPLICIT 2 /* * Table for mapping a network code to its type -- IMPLICIT routing or * IMPLICIT routing. */ struct nkindtab { int nk_type; /* Its bit code */ int nk_kind; /* Whether explicit or implicit */ }; /* * The following table gives the order of preference of the various * networks. Thus, if we have a choice of how to get somewhere, we * take the preferred route. */ struct netorder { short no_stat; char no_char; }; /* * External declarations for above defined tables. */ extern struct netmach netmach[]; extern struct ntypetab ntypetab[]; extern struct nkindtab nkindtab[]; extern struct netorder netorder[]; extern char *metanet; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2018 Joyent, Inc. */ /* * Copyright (c) 1985, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #ifndef _MAILX_DEF_H #define _MAILX_DEF_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifndef preSVr4 #include #include #include #include #include #endif #ifdef VMUNIX #include #endif #include "local.h" #include "uparm.h" /* * mailx -- a modified version of a University of California at Berkeley * mail program */ #define SENDESC '~' /* Default escape for sending */ #define NMLSIZE 1024 /* max names in a message list */ #define PATHSIZE 1024 /* Size of pathnames throughout */ #define HSHSIZE 59 /* Hash size for aliases and vars */ #define HDRFIELDS 3 /* Number of header fields */ #define LINESIZE 5120 /* max readable line width */ #define STRINGSIZE ((unsigned)128) /* Dynamic allocation units */ #define MAXARGC 1024 /* Maximum list of raw strings */ #define NOSTR ((char *)0) /* Nill string pointer */ #define NOSTRPTR ((char **)0) /* Nill pointer to string pointer */ #define NOINTPTR ((int *)0) /* Nill pointer */ #define MAXEXP 25 /* Maximum expansion of aliases */ /* A nice function to string compare */ #define equal(a, b) (strcmp(a, b) == 0) /* Keep a list of all opened files */ #define fopen(s, t) my_fopen(s, t) /* Delete closed file from the list */ #define fclose(s) my_fclose(s) struct message { off_t m_offset; /* offset in block of message */ long m_size; /* Bytes in the message */ long m_lines; /* Lines in the message */ long m_clen; /* Content-Length of the mesg */ short m_flag; /* flags, see below */ char m_text; /* TRUE if the contents is text */ /* False otherwise */ }; typedef struct fplst { FILE *fp; struct fplst *next; } NODE; /* * flag bits. */ #define MUSED (1<<0) /* entry is used, but this bit isn't */ #define MDELETED (1<<1) /* entry has been deleted */ #define MSAVED (1<<2) /* entry has been saved */ #define MTOUCH (1<<3) /* entry has been noticed */ #define MPRESERVE (1<<4) /* keep entry in sys mailbox */ #define MMARK (1<<5) /* message is marked! */ #define MODIFY (1<<6) /* message has been modified */ #define MNEW (1<<7) /* message has never been seen */ #define MREAD (1<<8) /* message has been read sometime. */ #define MSTATUS (1<<9) /* message status has changed */ #define MBOX (1<<10) /* Send this to mbox, regardless */ #define MBOXED (1<<11) /* message has been sent to mbox */ #define H_AFWDCNT 1 /* "Auto-Forward-Count:" */ #define H_AFWDFROM 2 /* "Auto-Forwarded-From:" */ #define H_CLEN 3 /* "Content-Length:" */ #define H_CTYPE 4 /* "Content-Type:" */ #define H_DATE 5 /* "Date:" */ #define H_DEFOPTS 6 /* "Default-Options:" */ #define H_EOH 7 /* "End-of-Header:" */ #define H_FROM 8 /* "From " */ #define H_FROM1 9 /* ">From " */ #define H_FROM2 10 /* "From: " */ #define H_MTSID 11 /* "MTS-Message-ID:" */ #define H_MTYPE 12 /* "Message-Type:" */ #define H_MVERS 13 /* "Message-Version:" */ #define H_MSVC 14 /* "Message-Service:" */ #define H_RECEIVED 15 /* "Received:" */ #define H_RVERS 16 /* "Report-Version:" */ #define H_STATUS 17 /* "Status:" */ #define H_SUBJ 18 /* "Subject:" */ #define H_TO 19 /* "To:" */ #define H_TCOPY 20 /* ">To:" */ #define H_TROPTS 21 /* "Transport-Options:" */ #define H_UAID 22 /* "UA-Content-ID:" */ #define H_DAFWDFROM 23 /* Hold A-F-F when sending Del. Notf. */ #define H_DTCOPY 24 /* Hold ">To:" when sending Del. Notf. */ #define H_DRECEIVED 25 /* Hold Rcvd: when sending Del. Notf. */ #define H_CONT 26 /* Continuation of previous line */ #define H_NAMEVALUE 27 /* unrecognized "name: value" hdr line */ /* * Format of the command description table. * The actual table is declared and initialized * in lex.c */ struct cmd { char *c_name; /* Name of command */ int (*c_func)(void *); /* Implementor of the command */ short c_argtype; /* Type of arglist (see below) */ short c_msgflag; /* Required flags of messages */ short c_msgmask; /* Relevant flags of messages */ }; /* can't initialize unions */ #define c_minargs c_msgflag /* Minimum argcount for RAWLIST */ #define c_maxargs c_msgmask /* Max argcount for RAWLIST */ /* * Argument types. */ #define MSGLIST 0 /* Message list type */ #define STRLIST 1 /* A pure string */ #define RAWLIST 2 /* Shell string list */ #define NOLIST 3 /* Just plain 0 */ #define NDMLIST 4 /* Message list, no defaults */ #define P 040 /* Autoprint dot after command */ #define I 0100 /* Interactive command bit */ #define M 0200 /* Legal from send mode bit */ #define W 0400 /* Illegal when read only bit */ #define F 01000 /* Is a conditional command */ #define T 02000 /* Is a transparent command */ #define R 04000 /* Cannot be called from collect */ /* * Oft-used mask values */ #define MMNORM (MDELETED|MSAVED) /* Look at both save and delete bits */ #define MMNDEL MDELETED /* Look only at deleted bit */ /* * Structure used to return a break down of a head * line */ typedef struct headline { custr_t *hl_from; /* The name of the sender */ custr_t *hl_tty; /* Its tty string (if any) */ custr_t *hl_date; /* The entire date string */ } headline_t; #define GTO 1 /* Grab To: line */ #define GSUBJECT 2 /* Likewise, Subject: line */ #define GCC 4 /* And the Cc: line */ #define GBCC 8 /* And also the Bcc: line */ #define GDEFOPT 16 /* And the Default-Options: lines */ #define GNL 32 /* Print blank line after */ #define GOTHER 64 /* Other header lines */ #define GMASK (GTO|GSUBJECT|GCC|GBCC|GDEFOPT|GNL|GOTHER) /* Mask of all header lines */ #define GDEL 128 /* Entity removed from list */ #define GCLEN 256 /* Include Content-Length header */ /* * Structure used to pass about the current * state of the user-typed message header. */ struct header { char *h_to; /* Dynamic "To:" string */ char *h_subject; /* Subject string */ char *h_cc; /* Carbon copies string */ char *h_bcc; /* Blind carbon copies */ char *h_defopt; /* Default options */ char **h_others; /* Other header lines */ int h_seq; /* Sequence for optimization */ }; /* * Structure of namelist nodes used in processing * the recipients of mail and aliases and all that * kind of stuff. */ struct name { struct name *n_flink; /* Forward link in list. */ struct name *n_blink; /* Backward list link */ short n_type; /* From which list it came */ char *n_name; /* This fella's name */ char *n_full; /* Full name */ }; /* * Structure of a variable node. All variables are * kept on a singly-linked list of these, rooted by * "variables" */ struct var { struct var *v_link; /* Forward link to next variable */ char *v_name; /* The variable's name */ char *v_value; /* And it's current value */ }; struct mgroup { struct mgroup *ge_link; /* Next person in this group */ char *ge_name; /* This person's user name */ }; struct grouphead { struct grouphead *g_link; /* Next grouphead in list */ char *g_name; /* Name of this group */ struct mgroup *g_list; /* Users in group. */ }; #define NIL ((struct name *)0) /* The nil pointer for namelists */ #define NONE ((struct cmd *)0) /* The nil pointer to command tab */ #define NOVAR ((struct var *)0) /* The nil pointer to variables */ #define NOGRP ((struct grouphead *)0) /* The nil grouphead pointer */ #define NOGE ((struct mgroup *)0) /* The nil group pointer */ #define NOFP ((struct fplst *)0) /* The nil file pointer */ #define TRUE 1 #define FALSE 0 #define DEADPERM 0600 /* permissions of dead.letter */ #define TEMPPERM 0600 /* permissions of temp files */ #define MBOXPERM 0600 /* permissions of ~/mbox */ #ifndef MFMODE #define MFMODE 0600 /* create mode for `/var/mail' files */ #endif /* * Structure of the hash table of ignored header fields */ struct ignore { struct ignore *i_link; /* Next ignored field in bucket */ char *i_field; /* This ignored field */ }; #ifdef preSVr4 struct utimbuf { time_t actime; time_t modtime; }; #else #include #endif /* * Token values returned by the scanner used for argument lists. * Also, sizes of scanner-related things. */ #define TEOL 0 /* End of the command line */ #define TNUMBER 1 /* A message number */ #define TDASH 2 /* A simple dash */ #define TSTRING 3 /* A string (possibly containing -) */ #define TDOT 4 /* A "." */ #define TUP 5 /* An "^" */ #define TDOLLAR 6 /* A "$" */ #define TSTAR 7 /* A "*" */ #define TOPEN 8 /* An '(' */ #define TCLOSE 9 /* A ')' */ #define TPLUS 10 /* A '+' */ #define REGDEP 2 /* Maximum regret depth. */ #define STRINGLEN 1024 /* Maximum length of string token */ /* * Constants for conditional commands. These describe whether * we should be executing stuff or not. */ #define CANY 0 /* Execute in send or receive mode */ #define CRCV 1 /* Execute in receive mode only */ #define CSEND 2 /* Execute in send mode only */ #define CTTY 3 /* Execute if attached to a tty only */ #define CNOTTY 4 /* Execute if not attached to a tty */ /* * Flags for msend(). */ #define M_IGNORE 1 /* Do "ignore/retain" processing */ #define M_SAVING 2 /* Saving to a file/folder */ /* * VM/UNIX has a vfork system call which is faster than forking. If we * don't have it, fork(2) will do . . . */ #if !defined(VMUNIX) && defined(preSVr4) #define vfork() fork() #endif #ifndef SIGRETRO #define sigchild() #endif /* * 4.2bsd signal interface help... */ #ifdef VMUNIX #define sigset(s, a) signal(s, a) #define sigsys(s, a) signal(s, a) #else #ifndef preSVr4 /* SVr4 version of sigset() in fio.c */ #define sigsys(s, a) signal(s, a) #define setjmp(x) sigsetjmp((x), 1) #define longjmp siglongjmp #define jmp_buf sigjmp_buf #else #define OLD_BSD_SIGS #endif #endif /* * Truncate a file to the last character written. This is * useful just before closing an old file that was opened * for read/write. */ #define trunc(stream) ftruncate(fileno(stream), (long)ftell(stream)) /* * The pointers for the string allocation routines, * there are NSPACE independent areas. * The first holds STRINGSIZE bytes, the next * twice as much, and so on. */ #define NSPACE 25 /* Total number of string spaces */ struct strings { char *s_topFree; /* Beginning of this area */ char *s_nextFree; /* Next alloctable place here */ unsigned s_nleft; /* Number of bytes left here */ }; /* The following typedefs must be used in SVR4 */ #ifdef preSVr4 #ifndef sun typedef int gid_t; typedef int uid_t; typedef int mode_t; typedef int pid_t; #endif #endif #define STSIZ 40 #define TMPSIZ 14 /* * Forward declarations of routine types to keep lint and cc happy. */ extern int Copy(int *msgvec); extern FILE *Fdopen(int fildes, char *mode); extern int Followup(int *msgvec); extern char *Getf(register char *s); extern int More(int *msgvec); extern int Respond(int *msgvec); extern int Save(int *msgvec); extern int Sendm(char *str); extern int Sput(char str[]); extern int Type(int *msgvec); extern void Verhogen(void); extern char *addone(char hf[], char news[]); extern char *addto(char hf[], char news[]); extern void alter(char name[]); extern int alternates(char **namelist); extern void announce(void); extern int any(int ch, char *str); extern int anyof(register char *s1, register char *s2); extern int argcount(char **argv); extern void assign(char name[], char value[]); extern int blankline(const char linebuf[]); extern struct name *cat(struct name *n1, struct name *n2); extern FILE *collect(struct header *hp); extern void commands(void); extern char *copy(char *str1, char *str2); extern int copycmd(char str[]); extern int deassign(register char *s); extern int delm(int *msgvec); extern struct name *delname(register struct name *np, char name[]); extern int deltype(int msgvec[]); extern char *detract(register struct name *np, int ntype); extern int docomma(char *s); extern int dopipe(char str[]); extern int dosh(char *str); extern int echo(register char **argv); extern int editor(int *msgvec); extern int edstop(int noremove); extern struct name *elide(struct name *names); extern int elsecmd(void); extern int endifcmd(void); extern int execute(char linebuf[], int contxt); extern char *expand(char *name); extern struct name *extract(char line[], int arg_ntype); extern int fferror(FILE *iob); extern int field(char str[]); extern int file(char **argv); extern struct grouphead *findgroup(char name[]); extern void findmail(char *name); extern int first(int f, int m); extern void flush(void); extern int folders(char **arglist); extern int followup(int *msgvec); extern int from(int *msgvec); extern off_t fsize(FILE *iob); extern int getfold(char *name); extern int gethfield(register FILE *f, char linebuf[], register long rem); extern int getaline(char *line, int size, FILE *f, int *hasnulls); extern int getmessage(char *buf, int *vector, int flags); extern int getmsglist(char *buf, int *vector, int flags); extern int getname(uid_t uid, char namebuf[]); extern int getrawlist(char line[], char **argv, int argc); extern void getrecf(char *buf, char *recfile, int useauthor, int sz_recfile); extern uid_t getuserid(char name[]); extern int grabh(register struct header *hp, int gflags, int subjtop); extern int group(char **argv); extern void hangup(int); extern int hash(char name[]); extern char *hcontents(char hfield[]); extern int headerp(register char *line); extern int headers(int *msgvec); extern int headline_alloc(headline_t **); extern void headline_free(headline_t *); extern void headline_reset(headline_t *); extern int help(void); extern char *helppath(char *file); extern char *hfield(char field[], struct message *mp, char *(*add)(char *, char *)); extern void holdsigs(void); extern int icequal(register char *s1, register char *s2); extern int ifcmd(char **argv); extern int igfield(char *list[]); extern int inc(void); extern void inithost(void); extern int isdir(char name[]); extern boolean_t is_headline(const char *); extern int ishfield(char linebuf[], char field[]); extern int ishost(char *sys, char *rest); extern int isign(char *field, int saving); extern void istrcpy(char *dest, int dstsize, char *src); extern void lcwrite(char *fn, FILE *fi, FILE *fo, int addnl); extern void load(char *name); extern int loadmsg(char str[]); extern int lock(FILE *fp, char *mode, int blk); extern void lockmail(void); extern int mail(char **people); extern void mail1(struct header *hp, int use_to, char *orig_to); extern void mapf(register struct name *np, char *from); extern int mboxit(int msgvec[]); extern void mechk(struct name *names); extern int member(register char *realfield, register struct ignore **table); extern int messize(int *msgvec); extern void minit(void); extern int more(int *msgvec); extern long msend(struct message *mailp, FILE *obuf, int flag, int (*fp)(const char *, FILE *)); extern int my_fclose(register FILE *iop); extern FILE *my_fopen(char *file, char *mode); extern char *nameof(register struct message *mp); extern char *netmap(char name[], char from[]); extern int newfileinfo(int start); extern int next(int *msgvec); extern int npclose(FILE *ptr); extern FILE *npopen(char *cmd, char *mode); extern char *nstrcpy(char *dst, int dstsize, char *src); extern char *nstrcat(char *dst, int dstsize, char *src); extern int null(char *e); extern int outof(struct name *names, FILE *fo); extern struct name *outpre(struct name *to); extern void panic(char *str); extern int parse_headline(const char *, headline_t *); extern int pcmdlist(void); extern int pdot(void); extern int preserve(int *msgvec); extern void printgroup(char name[]); extern void printhead(int mesg); extern int puthead(struct header *hp, FILE *fo, int w, long clen); extern int pversion(char *e); extern void quit(int noremove); extern int readline(FILE *ibuf, char *linebuf); extern void receipt(struct message *mp); extern void relsesigs(void); extern int removefile(char name[]); extern int replyall(int *msgvec); extern int replysender(int *msgvec); extern int respond(int *msgvec); extern int retfield(char *list[]); extern int rexit(int e); extern char *safeexpand(char name[]); extern void *salloc(unsigned size); extern void *srealloc(void *optr, unsigned size); extern int samebody(register char *user, register char *addr, int fuzzy); extern int save(char str[]); extern void savedead(int s); extern char *savestr(char *str); extern int schdir(char *str); extern int screensize(void); extern int scroll(char arg[]); extern int sendm(char *str); extern int set(char **arglist); extern void setclen(register struct message *mp); extern int setfile(char *name, int isedit); extern FILE *setinput(register struct message *mp); extern void setptr(register FILE *ibuf); extern int shell(char *str); #ifndef sigchild extern void sigchild(void); #endif #ifndef sigset extern void (*sigset())(); #endif extern char *skin(char *name); extern char *snarf(char linebuf[], int *flag, int erf); extern int source(char name[]); extern char *splice(char *addr, char *hdr); extern int sput(char str[]); extern void sreset(void); extern void stop(int s); extern int stouch(int msgvec[]); extern int substr(char *string1, char *string2); extern int swrite(char str[]); extern struct name *tailof(struct name *name); extern void tinit(void); extern int tmail(void); extern int top(int *msgvec); extern void touch(int mesg); extern struct name *translate(struct name *np); extern int type(int *msgvec); extern int undelete(int *msgvec); extern int ungroup(char **argv); extern int unigfield(char *list[]); extern void unlockmail(void); extern char **unpack(struct name *np); extern int unread(int msgvec[]); extern int unretfield(char *list[]); extern int unset(char **arglist); extern int unstack(void); extern char *unuucp(char *name); extern struct name *usermap(struct name *names); extern char *value(char name[]); extern char *vcopy(char str[]); extern void vfree(register char *cp); extern int visual(int *msgvec); extern char *yankword(char *name, char *word, int sz, int comma); /* * These functions are defined in libmail.a */ #ifdef __cplusplus extern "C" { #endif extern int delempty(mode_t, char *); extern char *maildomain(void); extern void touchlock(void); extern char *xgetenv(char *); extern int xsetenv(char *); #ifdef __cplusplus } #endif /* * Standard functions from the C library. * These are all defined in and in SVr4. */ #ifdef preSVr4 extern long atol(); extern char *getcwd(); extern char *calloc(); extern char *getenv(); extern void exit(); extern void free(); extern char *malloc(); extern time_t time(); extern long ulimit(); extern int utime(); extern int wait(); extern int fputs(); #endif #ifdef __cplusplus } #endif #endif /* _MAILX_DEF_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #ifndef _HDR_GLOB_H #define _HDR_GLOB_H /* * mailx -- a modified version of a University of California at Berkeley * mail program * * A bunch of global variable declarations lie herein. * def.h must be included first. */ extern int Fflag; /* -F option (followup) */ extern int Hflag; /* print headers and exit */ extern char *Tflag; /* -T temp file for netnews */ extern int UnUUCP; /* -U flag */ extern char **altnames; /* List of alternate names for user */ extern int askme; /* ???? */ extern int baud; /* Output baud rate */ extern char *bflag; /* Bcc given from non tty */ extern char *binmsg; /* Message: content unprintable */ extern char *cflag; /* Cc given from non tty */ extern const struct cmd cmdtab[]; /* ???? */ extern int cond; /* Current state of conditional exc. */ extern NODE *curptr; /* ???? */ extern int debug; /* Debug flag set */ extern char domain[]; /* ???? */ extern struct message *dot; /* Pointer to current message */ extern int edit; /* Indicates editing a file */ extern char *editfile; /* Name of file being edited */ extern int exitflg; /* -e for mail test */ extern NODE *fplist; /* ???? */ extern struct grouphead *groups[]; /* Pointer to active groups */ extern int hflag; /* Sequence number for network -h */ extern char homedir[]; /* Name of home directory */ extern char host[]; /* ???? */ extern struct ignore *ignore[]; /* Pointer to ignored fields */ extern int image; /* File descriptor for image of msg */ extern FILE *input; /* Current command input file */ extern int intty; /* True if standard input a tty */ extern int issysmbox; /* mailname is a system mailbox */ extern FILE *itf; /* Input temp file buffer */ extern int lexnumber; /* Number of TNUMBER from scan() */ extern char lexstring[]; /* String from TSTRING, scan() */ extern int loading; /* Loading user definitions */ extern char *lockname; /* named used for /var/mail locking */ extern char *maildir; /* directory for mail files */ extern char mailname[]; /* Name of /var/mail system mailbox */ extern off_t mailsize; /* Size of system mailbox */ extern int maxfiles; /* Maximum number of open files */ extern struct message *message; /* The actual message structure */ extern char *metanet; /* ???? */ extern int msgCount; /* Count of messages read in */ extern gid_t myegid; /* User's effective gid */ extern uid_t myeuid; /* User's effective uid */ extern char myname[]; /* My login id */ extern pid_t mypid; /* Current process id */ extern gid_t myrgid; /* User's real gid */ extern uid_t myruid; /* User's real uid */ extern int newsflg; /* -I option for netnews */ extern char noheader; /* Suprress initial header listing */ extern int noreset; /* String resets suspended */ extern char nosrc; /* Don't source /etc/mail/mailx.rc */ extern int nretained; /* Number of retained fields */ extern int numberstack[]; /* Stack of regretted numbers */ extern char origname[]; /* Original name of mail file */ extern FILE *otf; /* Output temp file buffer */ extern int outtty; /* True if standard output a tty */ extern FILE *pipef; /* Pipe file we have opened */ extern char *progname; /* program name (argv[0]) */ extern char *prompt; /* prompt string */ extern int rcvmode; /* True if receiving mail */ extern int readonly; /* Will be unable to rewrite file */ extern int regretp; /* Pointer to TOS of regret tokens */ extern int regretstack[]; /* Stack of regretted tokens */ extern struct ignore *retain[HSHSIZE]; /* Pointer to retained fields */ extern char *rflag; /* -r address for network */ extern int rmail; /* Being called as rmail */ extern int sawcom; /* Set after first command */ extern int selfsent; /* User sent self something */ extern int senderr; /* An error while checking */ extern int rpterr; /* An error msg was sent to stderr */ extern char *sflag; /* Subject given from non tty */ extern int sourcing; /* Currently reading variant file */ extern int space; /* Current maximum number of messages */ extern jmp_buf srbuf; /* ???? */ extern struct strings stringdope[]; /* pointer for the salloc routines */ extern char *stringstack[]; /* Stack of regretted strings */ extern char tempEdit[]; /* ???? */ extern char tempMail[]; /* ???? */ extern char tempMesg[]; /* ???? */ extern char tempQuit[]; /* ???? */ extern char tempResid[]; /* temp file in :saved */ extern char tempZedit[]; /* ???? */ extern int tflag; /* Read headers from text */ extern uid_t uid; /* The invoker's user id */ extern struct utimbuf *utimep; /* ???? */ extern struct var *variables[]; /* Pointer to active var list */ extern const char *const version; /* ???? */ extern int receipt_flg; /* Flag for return receipt */ /* * Standard external variables from the C library. */ extern char *optarg; extern int optind; #endif /* _HDR_GLOB_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program */ #ifdef V7 #include "v7.local.h" #endif #ifdef USG #include "usg.local.h" #endif /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * This file is included by normal files which want both * globals and declarations. */ /*#define USG 1 */ /* System V */ #define USG_TTY 1 /* termio(4I) */ #include "def.h" #include "glob.h" /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program */ extern char *libpath(); #ifdef preSVr4 #ifdef USG # define LIBPATH "/usr/lib/mailx" #else # define LIBPATH "/usr/lib" #endif #else # define LIBPATH "/usr/share/lib/mailx" #endif #define LOCALEPATH "/usr/lib/locale" /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1991 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * Declarations and constants specific to an installation. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program */ #define LOCAL EMPTYID /* Dynamically determined local host */ #ifdef preSVr4 # define MAIL "/bin/rmail" /* Mail delivery agent */ #else # define MAIL "/usr/bin/rmail"/* Mail delivery agent */ #endif #define SENDMAIL "/usr/lib/sendmail" /* Name of classy mail deliverer */ #define EDITOR "ed" /* Name of text editor */ #define VISUAL "vi" /* Name of display editor */ #define PG (value("PAGER") ? value("PAGER") : \ (value("bsdcompat") ? "more" : "pg -e")) /* Standard output pager */ #define MORE PG #define LS (value("LISTER") ? value("LISTER") : "ls") /* Name of directory listing prog*/ #ifdef preSVr4 # define SHELL "/bin/sh" /* Standard shell */ #else # define SHELL "/usr/bin/sh" /* Standard shell */ #endif #define HELPFILE helppath("mailx.help") /* Name of casual help file */ #define THELPFILE helppath("mailx.help.~") /* Name of casual tilde help */ #ifdef preSVr4 # define MASTER (value("bsdcompat") ? libpath("Mail.rc") : \ libpath("mailx.rc") #else # define MASTER (value("bsdcompat") ? "/etc/mail/Mail.rc" : \ "/etc/mail/mailx.rc") #endif #define APPEND /* New mail goes to end of mailbox */ #define CANLOCK /* Locking protocol actually works */ #define UTIME /* System implements utime(2) */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2014 Joyent, Inc. */ /* * Copyright 1995 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include #include "rcv.h" /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Routines for processing and detecting headlines. */ static int nextword(const char *, custr_t *, const char **); /* * See if the passed line buffer is a mail header. * Return true if yes. */ boolean_t is_headline(const char *linebuf) { headline_t *hl; boolean_t ret; if (strncmp("From ", linebuf, 5) != 0) { return (B_FALSE); } if (headline_alloc(&hl) != 0 || parse_headline(linebuf, hl) != 0) { err(1, "could not parse headline"); } ret = custr_len(hl->hl_from) > 0 ? B_TRUE : B_FALSE; headline_free(hl); return (ret); } /* * Manage headline_t objects: */ void headline_free(headline_t *hl) { custr_free(hl->hl_from); custr_free(hl->hl_tty); custr_free(hl->hl_date); free(hl); } int headline_alloc(headline_t **hl) { int en; headline_t *t; if ((t = calloc(1, sizeof (*t))) == NULL) { return (-1); } if (custr_alloc(&t->hl_from) != 0 || custr_alloc(&t->hl_tty) != 0 || custr_alloc(&t->hl_date) != 0) { en = errno; headline_free(t); errno = en; return (-1); } *hl = t; return (0); } /* * Clear all of the strings in a headline_t: */ void headline_reset(headline_t *hl) { custr_reset(hl->hl_from); custr_reset(hl->hl_tty); custr_reset(hl->hl_date); } int parse_headline(const char *line, headline_t *hl) { const char *c = line; headline_reset(hl); /* * Load the first word from the line and ensure that it is "From". */ if (nextword(c, hl->hl_from, &c) != 0) { return (-1); } if (strcmp(custr_cstr(hl->hl_from), "From") != 0) { errno = EINVAL; return (-1); } custr_reset(hl->hl_from); /* * The next word will be the From address. */ if (nextword(c, hl->hl_from, &c) != 0) { return (-1); } /* * If there is a next word, the rest of the string is the Date. */ if (c != NULL) { if (custr_append(hl->hl_date, c) != 0) { return (-1); } } errno = 0; return (0); } /* * Collect a space- or tab-delimited word into the word buffer, if one is * passed. The double quote character (") can be used to include whitespace * within a word. Set "nextword" to the location of the first character of the * _next_ word, or NULL if there were no more words. Returns 0 on success or * -1 otherwise. */ static int nextword(const char *input, custr_t *word, const char **nextword) { boolean_t in_quotes = B_FALSE; const char *c = input != NULL ? input : ""; /* * Collect the first word into the word buffer, if one is provided. */ for (;;) { if (*c == '\0') { /* * We have reached the end of the string. */ *nextword = NULL; return (0); } if (*c == '"') { /* * Either beginning or ending a quoted string. */ in_quotes = in_quotes ? B_FALSE : B_TRUE; } if (!in_quotes && (*c == ' ' || *c == '\t')) { /* * We have reached a whitespace region. */ break; } /* * Copy this character into the word buffer. */ if (word != NULL) { if (custr_appendc(word, *c) != 0) { return (-1); } } c++; } /* * Find the beginning of the next word, if there is one. */ for (;;) { if (*c == '\0') { /* * We have reached the end of the string. */ *nextword = NULL; return (0); } else if (*c != ' ' && *c != '\t') { /* * We have located the next word. */ *nextword = c; return (0); } c++; } } /* * Copy str1 to str2, return pointer to null in str2. */ char * copy(char *str1, char *str2) { register char *s1, *s2; s1 = str1; s2 = str2; while (*s1) *s2++ = *s1++; *s2 = 0; return(s2); } /* * Is ch any of the characters in str? */ int any(int ch, char *str) { register char *f; int c; f = str; c = ch; while (*f) if (c == *f++) return(1); return(0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1998 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Code to figure out what host we are on. */ #include "rcv.h" #include "configdefs.h" #include #include #define MAILCNFG "/etc/mail/mailcnfg" char host[64]; char domain[128]; /* * Initialize the network name of the current host. */ void inithost(void) { register struct netmach *np; struct utsname name; char *fp; xsetenv(MAILCNFG); if (fp = xgetenv("CLUSTER")) { nstrcpy(host, sizeof (host), fp); } else { uname(&name); nstrcpy(host, sizeof (host), name.nodename); } snprintf(domain, sizeof (domain), "%s%s", host, maildomain()); for (np = netmach; np->nt_machine != 0; np++) if (strcmp(np->nt_machine, EMPTY) == 0) break; if (np->nt_machine == 0) { printf( gettext("Cannot find empty slot for dynamic host entry\n")); exit(1); } np->nt_machine = host; np++; np->nt_machine = domain; if (debug) fprintf(stderr, "host '%s', domain '%s'\n", host, domain); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1998 by Sun Microsystems, Inc. * All rights reserved. */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * A bunch of global variable declarations lie herein. * * All global externs are declared in def.h. All variables are initialized * here! * * !!!!!IF YOU CHANGE (OR ADD) IT HERE, DO IT THERE ALSO !!!!!!!! * */ #include "def.h" #include #include #include int Fflag = 0; /* -F option (followup) */ int Hflag = 0; /* print headers and exit */ char *Tflag; /* -T temp file for netnews */ int UnUUCP = 0; /* -U flag */ char **altnames; /* List of alternate names for user */ int askme; int baud; /* Output baud rate */ char *bflag; /* Bcc given from non tty */ char *binmsg = "*** Message content is not printable: pipe to command or save to a file ***"; char *cflag; /* Cc given from non tty */ int cond; /* Current state of conditional exc. */ NODE *curptr = NOFP; int debug; /* Debug flag set */ struct message *dot; /* Pointer to current message */ int edit; /* Indicates editing a file */ char *editfile; /* Name of file being edited */ int exitflg = 0; /* -e for mail test */ NODE *fplist = NOFP; struct grouphead *groups[HSHSIZE];/* Pointer to active groups */ int hflag; /* Sequence number for network -h */ char homedir[PATHSIZE]; /* Name of home directory */ struct ignore *ignore[HSHSIZE];/* Pointer to ignored fields */ int image; /* File descriptor for image of msg */ FILE *input; /* Current command input file */ int intty; /* True if standard input a tty */ int issysmbox; /* mailname is a system mailbox */ FILE *itf; /* Input temp file buffer */ int lexnumber; /* Number of TNUMBER from scan() */ char lexstring[STRINGLEN]; /* String from TSTRING, scan() */ int loading; /* Loading user definitions */ char *lockname; /* named used for locking in /var/mail */ #ifdef USR_SPOOL_MAIL char *maildir = "/usr/spool/mail/"; /* directory for mail files */ #else # ifdef preSVr4 char *maildir = "/usr/mail/"; /* directory for mail files */ # else char *maildir = "/var/mail/"; /* directory for mail files */ # endif #endif char mailname[PATHSIZE]; /* Name of /var/mail system mailbox */ off_t mailsize; /* Size of system mailbox */ int maxfiles; /* Maximum number of open files */ struct message *message; /* The actual message structure */ int msgCount; /* Count of messages read in */ gid_t myegid; uid_t myeuid; char myname[PATHSIZE]; /* My login id */ pid_t mypid; /* Current process id */ gid_t myrgid; uid_t myruid; int newsflg = 0; /* -I option for netnews */ char noheader; /* Suprress initial header listing */ int noreset; /* String resets suspended */ char nosrc; /* Don't source /etc/mail/mailx.rc */ int nretained; /* Number of retained fields */ int numberstack[REGDEP]; /* Stack of regretted numbers */ char origname[PATHSIZE]; /* Name of mailfile before expansion */ FILE *otf; /* Output temp file buffer */ int outtty; /* True if standard output a tty */ FILE *pipef; /* Pipe file we have opened */ char *progname; /* program name (argv[0]) */ char *prompt = NOSTR; /* prompt string */ int rcvmode; /* True if receiving mail */ int readonly; /* Will be unable to rewrite file */ int regretp; /* Pointer to TOS of regret tokens */ int regretstack[REGDEP]; /* Stack of regretted tokens */ struct ignore *retain[HSHSIZE];/* Pointer to retained fields */ char *rflag; /* -r address for network */ int rmail; /* Being called as rmail */ int sawcom; /* Set after first command */ int selfsent; /* User sent self something */ int senderr; /* An error while checking */ int rpterr; /* An error msg was sent to stderr */ char *sflag; /* Subject given from non tty */ int sourcing; /* Currently reading variant file */ int space; /* Current maximum number of messages */ jmp_buf srbuf; int tflag; /* Read headers from text */ /* * The pointers for the string allocation routines, * there are NSPACE independent areas. * The first holds STRINGSIZE bytes, the next * twice as much, and so on. */ struct strings stringdope[NSPACE]; char *stringstack[REGDEP]; /* Stack of regretted strings */ char tempEdit[TMPSIZ]; char tempMail[TMPSIZ]; char tempMesg[TMPSIZ]; char tempQuit[TMPSIZ]; char tempResid[PATHSIZE]; /* temp file in :saved */ char tempZedit[TMPSIZ]; uid_t uid; /* The invoker's user id */ static struct utimbuf utimeb; struct utimbuf *utimep = &utimeb; struct var *variables[HSHSIZE]; /* Pointer to active var list */ int receipt_flg = 0; /* Flag for return receipt */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Lexical processing of commands. */ #ifdef SIGCONT static void contin(int); #endif static int isprefix(char *as1, char *as2); static const struct cmd *lex(char word[]); static int Passeren(void); static void setmsize(int sz); /* * Set up editing on the given file name. * If isedit is true, we are considered to be editing the file, * otherwise we are reading our mail which has signficance for * mbox and so forth. */ int setfile(char *name, int isedit) { FILE *ibuf; int i; static int shudclob; static char efile[PATHSIZE]; char fortest[128]; struct stat stbuf; int exrc = 1; int rc = -1; int fd = -1; if (!isedit && issysmbox) lockmail(); if (stat(name, &stbuf) < 0 && errno == EOVERFLOW) { fprintf(stderr, gettext("mailbox %s is too large to" " accept incoming mail\n"), name); goto doret; } if ((ibuf = fopen(name, "r")) == NULL) { extern int errno; int sverrno = errno; int filethere = (access(name, 0) == 0); errno = sverrno; if (exitflg) goto doexit; /* no mail, return error */ if (isedit || filethere) perror(name); else if (!Hflag) { char *f = strrchr(name, '/'); if (f == NOSTR) fprintf(stderr, gettext("No mail.\n")); else fprintf(stderr, gettext("No mail for %s\n"), f+1); } goto doret; } fstat(fileno(ibuf), &stbuf); if (stbuf.st_size == 0L || (stbuf.st_mode&S_IFMT) != S_IFREG) { if (exitflg) goto doexit; /* no mail, return error */ if (isedit) if (stbuf.st_size == 0L) fprintf(stderr, gettext("%s: empty file\n"), name); else fprintf(stderr, gettext("%s: not a regular file\n"), name); else if (!Hflag) { if (strrchr(name, '/') == NOSTR) fprintf(stderr, gettext("No mail.\n")); else fprintf(stderr, gettext("No mail for %s\n"), strrchr(name, '/') + 1); } fclose(ibuf); goto doret; } if (fgets(fortest, sizeof (fortest), ibuf) == NULL) { perror(gettext("mailx: Unable to read from mail file")); goto doexit; } fseek(ibuf, (long)(BUFSIZ+1), 0); /* flush input buffer */ fseek(ibuf, 0L, 0); if (strncmp(fortest, "Forward to ", 11) == 0) { if (exitflg) goto doexit; /* no mail, return error */ fprintf(stderr, gettext("Your mail is being forwarded to %s"), fortest+11); fclose(ibuf); goto doret; } if (exitflg) { exrc = 0; goto doexit; /* there is mail, return success */ } /* * Looks like all will be well. Must hold signals * while we are reading the new file, else we will ruin * the message[] data structure. * Copy the messages into /tmp and set pointers. */ holdsigs(); if (shudclob) { /* * Now that we know we can switch to the new file * it's safe to close out the current file. * * If we're switching to the file we are currently * editing, don't allow it to be removed as a side * effect of closing it out. */ if (edit) edstop(strcmp(editfile, name) == 0); else { quit(strcmp(editfile, name) == 0); if (issysmbox) Verhogen(); } fflush(stdout); fclose(itf); fclose(otf); free(message); space = 0; } readonly = 0; if (!isedit && issysmbox && !Hflag) readonly = Passeren() == -1; lock(ibuf, "r", 1); fstat(fileno(ibuf), &stbuf); utimep->actime = stbuf.st_atime; utimep->modtime = stbuf.st_mtime; if (!readonly) if ((i = open(name, O_WRONLY)) < 0) readonly++; else close(i); shudclob = 1; edit = isedit; nstrcpy(efile, PATHSIZE, name); editfile = efile; #ifdef notdef if (name != mailname) nstrcpy(mailname, PATHSIZE, name); #endif mailsize = fsize(ibuf); if ((fd = open(tempMesg, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || (otf = fdopen(fd, "w")) == NULL) { perror(tempMesg); if (!edit && issysmbox) Verhogen(); goto doexit; } if ((itf = fopen(tempMesg, "r")) == NULL) { perror(tempMesg); if (!edit && issysmbox) Verhogen(); goto doexit; } removefile(tempMesg); setptr(ibuf); setmsize(msgCount); fclose(ibuf); relsesigs(); sawcom = 0; rc = 0; doret: if (!isedit && issysmbox) unlockmail(); return (rc); doexit: if (!isedit && issysmbox) unlockmail(); exit(exrc ? exrc : rpterr); /* NOTREACHED */ } /* global to semaphores */ static char semfn[128]; static FILE *semfp = NULL; /* * return -1 if file is already being read, 0 otherwise */ static int Passeren(void) { char *home; if ((home = getenv("HOME")) == NULL) return (0); snprintf(semfn, sizeof (semfn), "%s%s", home, "/.Maillock"); if ((semfp = fopen(semfn, "w")) == NULL) { fprintf(stderr, gettext("WARNING: Can't open mail lock file (%s).\n"), semfn); fprintf(stderr, gettext("\t Assuming you are not already reading mail.\n")); return (0); } if (lock(semfp, "w", 0) < 0) { if (errno == ENOLCK) { fprintf(stderr, gettext("WARNING: Unable to acquire mail lock, no record locks available.\n")); fprintf(stderr, gettext("\t Assuming you are not already reading mail.\n")); return (0); } fprintf(stderr, gettext("WARNING: You are already reading mail.\n")); fprintf(stderr, gettext("\t This instance of mail is read only.\n")); fclose(semfp); semfp = NULL; return (-1); } return (0); } void Verhogen(void) { if (semfp != NULL) { unlink(semfn); fclose(semfp); } } /* * Interpret user commands one by one. If standard input is not a tty, * print no prompt. */ static int *msgvec; static int shudprompt; void commands(void) { int eofloop; int n; char linebuf[LINESIZE]; char line[LINESIZE]; struct stat minfo; FILE *ibuf; #ifdef SIGCONT sigset(SIGCONT, SIG_DFL); #endif if (rcvmode && !sourcing) { if (sigset(SIGINT, SIG_IGN) != SIG_IGN) sigset(SIGINT, stop); if (sigset(SIGHUP, SIG_IGN) != SIG_IGN) sigset(SIGHUP, hangup); } for (;;) { setjmp(srbuf); /* * Print the prompt, if needed. Clear out * string space, and flush the output. */ if (!rcvmode && !sourcing) return; eofloop = 0; top: if ((shudprompt = (intty && !sourcing)) != 0) { if (prompt == NOSTR) { if ((int)value("bsdcompat")) prompt = "& "; else prompt = ""; } #ifdef SIGCONT sigset(SIGCONT, contin); #endif if (intty && value("autoinc") && stat(editfile, &minfo) >= 0 && minfo.st_size > mailsize) { int OmsgCount, i; OmsgCount = msgCount; fseek(otf, 0L, 2); holdsigs(); if (!edit && issysmbox) lockmail(); if ((ibuf = fopen(editfile, "r")) == NULL) { fprintf(stderr, gettext("Can't reopen %s\n"), editfile); if (!edit && issysmbox) unlockmail(); exit(1); /* NOTREACHED */ } if (edit || !issysmbox) lock(ibuf, "r", 1); fseek(ibuf, mailsize, 0); mailsize = fsize(ibuf); setptr(ibuf); setmsize(msgCount); fclose(ibuf); if (!edit && issysmbox) unlockmail(); if (msgCount-OmsgCount > 0) { printf(gettext( "New mail has arrived.\n")); if (msgCount - OmsgCount == 1) printf(gettext( "Loaded 1 new message\n")); else printf(gettext( "Loaded %d new messages\n"), msgCount-OmsgCount); if (value("header") != NOSTR) for (i = OmsgCount+1; i <= msgCount; i++) { printhead(i); sreset(); } } relsesigs(); } printf("%s", prompt); } flush(); sreset(); /* * Read a line of commands from the current input * and handle end of file specially. */ n = 0; linebuf[0] = '\0'; for (;;) { if (readline(input, line) <= 0) { if (n != 0) break; if (loading) return; if (sourcing) { unstack(); goto more; } if (value("ignoreeof") != NOSTR && shudprompt) { if (++eofloop < 25) { printf(gettext( "Use \"quit\" to quit.\n")); goto top; } } return; } if ((n = strlen(line)) == 0) break; n--; if (line[n] != '\\') break; line[n++] = ' '; if (n > LINESIZE - (int)strlen(linebuf) - 1) break; strcat(linebuf, line); } n = LINESIZE - strlen(linebuf) - 1; if ((int)strlen(line) > n) { printf(gettext( "Line plus continuation line too long:\n\t%s\n\nplus\n\t%s\n"), linebuf, line); if (loading) return; if (sourcing) { unstack(); goto more; } return; } strncat(linebuf, line, n); #ifdef SIGCONT sigset(SIGCONT, SIG_DFL); #endif if (execute(linebuf, 0)) return; more:; } } /* * Execute a single command. If the command executed * is "quit," then return non-zero so that the caller * will know to return back to main, if it cares. * Contxt is non-zero if called while composing mail. */ int execute(char linebuf[], int contxt) { char word[LINESIZE]; char *arglist[MAXARGC]; const struct cmd *com; char *cp, *cp2; int c, e; int muvec[2]; /* * Strip the white space away from the beginning * of the command, then scan out a word, which * consists of anything except digits and white space. * * Handle |, ! and # differently to get the correct * lexical conventions. */ cp = linebuf; while (any(*cp, " \t")) cp++; cp2 = word; if (any(*cp, "!|#")) *cp2++ = *cp++; else while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\"")) *cp2++ = *cp++; *cp2 = '\0'; /* * Look up the command; if not found, complain. * Normally, a blank command would map to the * first command in the table; while sourcing, * however, we ignore blank lines to eliminate * confusion. */ if (sourcing && equal(word, "")) return (0); com = lex(word); if (com == NONE) { fprintf(stderr, gettext("Unknown command: \"%s\"\n"), word); if (loading) { cond = CANY; return (1); } if (sourcing) { cond = CANY; unstack(); } return (0); } /* * See if we should execute the command -- if a conditional * we always execute it, otherwise, check the state of cond. */ if ((com->c_argtype & F) == 0) if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode || cond == CTTY && !intty || cond == CNOTTY && intty) return (0); /* * Special case so that quit causes a return to * main, who will call the quit code directly. * If we are in a source file, just unstack. */ if ((uintptr_t)com->c_func == (uintptr_t)edstop) { if (sourcing) { if (loading) return (1); unstack(); return (0); } return (1); } /* * Process the arguments to the command, depending * on the type it expects. Default to an error. * If we are sourcing an interactive command, it's * an error. */ if (!rcvmode && (com->c_argtype & M) == 0) { fprintf(stderr, gettext("May not execute \"%s\" while sending\n"), com->c_name); if (loading) return (1); if (sourcing) unstack(); return (0); } if (sourcing && com->c_argtype & I) { fprintf(stderr, gettext("May not execute \"%s\" while sourcing\n"), com->c_name); rpterr = 1; if (loading) return (1); unstack(); return (0); } if (readonly && com->c_argtype & W) { fprintf(stderr, gettext( "May not execute \"%s\" -- message file is read only\n"), com->c_name); if (loading) return (1); if (sourcing) unstack(); return (0); } if (contxt && com->c_argtype & R) { fprintf(stderr, gettext("Cannot recursively invoke \"%s\"\n"), com->c_name); return (0); } e = 1; switch (com->c_argtype & ~(F|P|I|M|T|W|R)) { case MSGLIST: /* * A message list defaulting to nearest forward * legal message. */ if (msgvec == 0) { fprintf(stderr, gettext("Illegal use of \"message list\"\n")); return (-1); } if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) break; if (c == 0) if (msgCount == 0) *msgvec = 0; else { *msgvec = first(com->c_msgflag, com->c_msgmask); msgvec[1] = 0; } if (*msgvec == 0) { fprintf(stderr, gettext("No applicable messages\n")); break; } e = (*com->c_func)(msgvec); break; case NDMLIST: /* * A message operand with no defaults, but no error * if none exists. There will be an error if the * msgvec pointer is of zero value. */ if (msgvec == 0) { fprintf(stderr, gettext("Illegal use of \"message operand\"\n")); return (-1); } if (getmessage(cp, msgvec, com->c_msgflag) < 0) break; e = (*com->c_func)(msgvec); break; case STRLIST: /* * Just the straight string, with * leading blanks removed. */ while (any(*cp, " \t")) cp++; e = (*com->c_func)(cp); break; case RAWLIST: /* * A vector of strings, in shell style. */ if ((c = getrawlist(cp, arglist, sizeof (arglist) / sizeof (*arglist))) < 0) break; if (c < com->c_minargs) { fprintf(stderr, gettext("%s requires at least %d arg(s)\n"), com->c_name, com->c_minargs); break; } if (c > com->c_maxargs) { fprintf(stderr, gettext("%s takes no more than %d arg(s)\n"), com->c_name, com->c_maxargs); break; } e = (*com->c_func)(arglist); break; case NOLIST: /* * Just the constant zero, for exiting, * eg. */ e = (*com->c_func)(0); break; default: panic("Unknown argtype"); } /* * Exit the current source file on * error. */ if (e && loading) return (1); if (e && sourcing) unstack(); if ((uintptr_t)com->c_func == (uintptr_t)edstop) return (1); if (value("autoprint") != NOSTR && com->c_argtype & P) if ((dot->m_flag & MDELETED) == 0) { muvec[0] = dot - &message[0] + 1; muvec[1] = 0; type(muvec); } if (!sourcing && (com->c_argtype & T) == 0) sawcom = 1; return (0); } #ifdef SIGCONT /* * When we wake up after ^Z, reprint the prompt. */ static void contin(int s __unused) { if (shudprompt) printf("%s", prompt); fflush(stdout); } #endif /* * Branch here on hangup signal and simulate quit. */ void hangup(int s __unused) { holdsigs(); #ifdef OLD_BSD_SIGS sigignore(SIGHUP); #endif if (edit) { if (setjmp(srbuf)) exit(rpterr); edstop(0); } else { if (issysmbox) Verhogen(); if (value("exit") != NOSTR) exit(1); else quit(0); } exit(rpterr); } /* * Set the size of the message vector used to construct argument * lists to message list functions. */ static void setmsize(int sz) { if (msgvec != (int *)0) free(msgvec); if (sz < 1) sz = 1; /* need at least one cell for terminating 0 */ if ((msgvec = (int *) calloc((unsigned)(sz + 1), sizeof (*msgvec))) == NULL) panic("Failed to allocate memory for message vector"); } /* * Find the correct command in the command table corresponding * to the passed command "word" */ static const struct cmd * lex(char word[]) { const struct cmd *cp; for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++) if (isprefix(word, cp->c_name)) return (cp); return (NONE); } /* * Determine if as1 is a valid prefix of as2. */ static int isprefix(char *as1, char *as2) { char *s1, *s2; s1 = as1; s2 = as2; while (*s1++ == *s2) if (*s2++ == '\0') return (1); return (*--s1 == '\0'); } /* * The following gets called on receipt of a rubout. This is * to abort printout of a command, mainly. * Dispatching here when command() is inactive crashes rcv. * Close all open files except 0, 1, 2, and the temporary. * The special call to getuserid() is needed so it won't get * annoyed about losing its open file. * Also, unstack all source files. */ static int inithdr; /* am printing startup headers */ void stop(int s) { NODE *head; noreset = 0; if (!inithdr) sawcom++; inithdr = 0; while (sourcing) unstack(); (void) getuserid((char *)0); for (head = fplist; head != (NODE *)NULL; head = head->next) { if (head->fp == stdin || head->fp == stdout) continue; if (head->fp == itf || head->fp == otf) continue; if (head->fp == stderr) continue; if (head->fp == semfp) continue; if (head->fp == pipef) { npclose(pipef); pipef = NULL; continue; } fclose(head->fp); } if (image >= 0) { close(image); image = -1; } if (s) { fprintf(stderr, gettext("Interrupt\n")); fflush(stderr); #ifdef OLD_BSD_SIGS sigrelse(s); #endif } longjmp(srbuf, 1); } /* * Announce the presence of the current mailx version, * give the message count, and print a header listing. */ #define GREETING "%s Type ? for help.\n" void announce(void) { int vec[2], mdot; extern const char *const version; if (!Hflag && value("quiet") == NOSTR) printf(gettext(GREETING), version); mdot = newfileinfo(1); vec[0] = mdot; vec[1] = 0; dot = &message[mdot - 1]; if (msgCount > 0 && !noheader) { inithdr++; headers(vec); inithdr = 0; } } /* * Announce information about the file we are editing. * Return a likely place to set dot. */ int newfileinfo(int start) { struct message *mp; int u, n, mdot, d, s; char fname[BUFSIZ], zname[BUFSIZ], *ename; if (Hflag) return (1); /* fake it--return message 1 */ for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) if ((mp->m_flag & (MNEW|MREAD)) == MNEW) break; if (mp >= &message[msgCount]) for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) if ((mp->m_flag & MREAD) == 0) break; if (mp < &message[msgCount]) mdot = mp - &message[0] + 1; else mdot = 1; n = u = d = s = 0; for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) n++; if ((mp->m_flag & MREAD) == 0) u++; if (mp->m_flag & MDELETED) d++; if (mp->m_flag & MSAVED) s++; } ename = origname; if (getfold(fname) >= 0) { nstrcat(fname, sizeof (fname), "/"); if (strncmp(fname, editfile, strlen(fname)) == 0) { snprintf(zname, sizeof (zname), "+%s", editfile + strlen(fname)); ename = zname; } } printf("\"%s\": ", ename); if (msgCount == 1) printf(gettext("1 message")); else printf(gettext("%d messages"), msgCount); if (n > 0) printf(gettext(" %d new"), n); if (u-n > 0) printf(gettext(" %d unread"), u); if (d > 0) printf(gettext(" %d deleted"), d); if (s > 0) printf(gettext(" %d saved"), s); if (readonly) printf(gettext(" [Read only]")); printf("\n"); return (mdot); } /* * Print the current version number. */ int pversion(char *s __unused) { printf("%s\n", version); return (0); } /* * Load a file of user definitions. */ void load(char *name) { FILE *in, *oldin; if ((in = fopen(name, "r")) == NULL) return; oldin = input; input = in; loading = 1; sourcing = 1; commands(); loading = 0; sourcing = 0; input = oldin; fclose(in); } /* * Incorporate any new mail into the current session. * * XXX - Since autoinc works on "edited" files as well as the * system mailbox, this probably ought to as well. */ int inc(void) { FILE *ibuf; int mdot; struct stat stbuf; int firstnewmsg = msgCount + 1; if (edit) { fprintf(stderr, gettext("Not in system mailbox\n")); return (-1); } if (((ibuf = fopen(mailname, "r")) == NULL) || (fstat(fileno(ibuf), &stbuf) < 0) || stbuf.st_size == 0L || stbuf.st_size == mailsize || (stbuf.st_mode&S_IFMT) != S_IFREG) { if (strrchr(mailname, '/') == NOSTR) fprintf(stderr, gettext("No new mail.\n")); else fprintf(stderr, gettext("No new mail for %s\n"), strrchr(mailname, '/')+1); if (ibuf != NULL) fclose(ibuf); return (-1); } fseek(otf, 0L, 2); holdsigs(); if (issysmbox) lockmail(); fseek(ibuf, mailsize, 0); mailsize = fsize(ibuf); setptr(ibuf); setmsize(msgCount); fclose(ibuf); if (issysmbox) unlockmail(); relsesigs(); mdot = newfileinfo(firstnewmsg); dot = &message[mdot - 1]; sawcom = 0; return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #include #include #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Message list handling. */ static int check(int mesg, int f); static int evalcol(int col); static int isinteger(char *buf); static void mark(int mesg); static int markall(char buf[], int f); static int matchsubj(char *str, int mesg); static int metamess(int meta, int f); static void regret(int token); static int scan(char **sp); static void scaninit(void); static int sender(char *str, int mesg); static void unmark(int mesg); /* * Process message operand list. * Convert the user string of message numbers and * store the numbers into vector. * * Returns the count of messages picked up or -1 on error. */ int getmessage(char *buf, int *vector, int flags) { int *ip; struct message *mp; int firstmsg = -1; char delims[] = "\t- "; char *result = NULL; if (markall(buf, flags) < 0) return (-1); ip = vector; /* * Check for first message number and make sure it is * at the beginning of the vector. */ result = strtok(buf, delims); if (result != NULL && isinteger(result)) { firstmsg = atoi(result); *ip++ = firstmsg; } /* * Add marked messages to vector and skip first * message number because it is already at the * beginning of the vector */ for (mp = &message[0]; mp < &message[msgCount]; mp++) { if (firstmsg == mp - &message[0] + 1) continue; if (mp->m_flag & MMARK) *ip++ = mp - &message[0] + 1; } *ip = 0; return (ip - vector); } /* * Check to see if string is an integer * * Returns 1 if is an integer and 0 if it is not */ static int isinteger(char *buf) { int i, result = 1; /* check for empty string */ if (strcmp(buf, "") == 0) { result = 0; return (result); } i = 0; while (buf[i] != '\0') { if (!isdigit(buf[i])) { result = 0; break; } i++; } return (result); } /* * Process msglist operand list. * Convert the user string of message numbers and * store the numbers into vector. * * Returns the count of messages picked up or -1 on error. */ int getmsglist(char *buf, int *vector, int flags) { int *ip; struct message *mp; if (markall(buf, flags) < 0) return (-1); ip = vector; for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MMARK) *ip++ = mp - &message[0] + 1; *ip = 0; return (ip - vector); } /* * Mark all messages that the user wanted from the command * line in the message structure. Return 0 on success, -1 * on error. */ /* * Bit values for colon modifiers. */ #define CMNEW 01 /* New messages */ #define CMOLD 02 /* Old messages */ #define CMUNREAD 04 /* Unread messages */ #define CMDELETED 010 /* Deleted messages */ #define CMREAD 020 /* Read messages */ /* * The following table describes the letters which can follow * the colon and gives the corresponding modifier bit. */ static struct coltab { char co_char; /* What to find past : */ int co_bit; /* Associated modifier bit */ int co_mask; /* m_status bits to mask */ int co_equal; /* ... must equal this */ } coltab[] = { 'n', CMNEW, MNEW, MNEW, 'o', CMOLD, MNEW, 0, 'u', CMUNREAD, MREAD, 0, 'd', CMDELETED, MDELETED, MDELETED, 'r', CMREAD, MREAD, MREAD, 0, 0, 0, 0 }; static int lastcolmod; static int markall(char buf[], int f) { char **np; int i; struct message *mp; char *namelist[NMLSIZE], *bufp; int tok, beg, mc, star, other, colmod, colresult; colmod = 0; for (i = 1; i <= msgCount; i++) unmark(i); bufp = buf; mc = 0; np = &namelist[0]; scaninit(); tok = scan(&bufp); star = 0; other = 0; beg = 0; while (tok != TEOL) { switch (tok) { case TNUMBER: number: if (star) { printf(gettext("No numbers mixed with *\n")); return (-1); } mc++; other++; if (beg != 0) { if (check(lexnumber, f)) return (-1); for (i = beg; i <= lexnumber; i++) if ((message[i-1].m_flag&MDELETED) == f) mark(i); beg = 0; break; } beg = lexnumber; if (check(beg, f)) return (-1); tok = scan(&bufp); if (tok != TDASH) { regret(tok); mark(beg); beg = 0; } break; case TSTRING: if (beg != 0) { printf(gettext( "Non-numeric second argument\n")); return (-1); } other++; if (lexstring[0] == ':') { colresult = evalcol(lexstring[1]); if (colresult == 0) { printf(gettext( "Unknown colon modifier \"%s\"\n"), lexstring); return (-1); } colmod |= colresult; } else *np++ = savestr(lexstring); break; case TDASH: case TPLUS: case TDOLLAR: case TUP: case TDOT: lexnumber = metamess(lexstring[0], f); if (lexnumber == -1) return (-1); goto number; case TSTAR: if (other) { printf(gettext( "Can't mix \"*\" with anything\n")); return (-1); } star++; break; } tok = scan(&bufp); } lastcolmod = colmod; *np = NOSTR; mc = 0; if (star) { for (i = 0; i < msgCount; i++) if ((message[i].m_flag & MDELETED) == f) { mark(i+1); mc++; } if (mc == 0) { printf(gettext("No applicable messages\n")); return (-1); } return (0); } /* * If no numbers were given, mark all of the messages, * so that we can unmark any whose sender was not selected * if any user names were given. */ if ((np > namelist || colmod != 0) && mc == 0) for (i = 1; i <= msgCount; i++) if ((message[i-1].m_flag & MDELETED) == f) mark(i); /* * If any names were given, go through and eliminate any * messages whose senders were not requested. */ if (np > namelist) { for (i = 1; i <= msgCount; i++) { for (mc = 0, np = &namelist[0]; *np != NOSTR; np++) if (**np == '/') { if (matchsubj(*np, i)) { mc++; break; } } else { if (sender(*np, i)) { mc++; break; } } if (mc == 0) unmark(i); } /* * Make sure we got some decent messages. */ mc = 0; for (i = 1; i <= msgCount; i++) if (message[i-1].m_flag & MMARK) { mc++; break; } if (mc == 0) { printf(gettext("No applicable messages from {%s"), namelist[0]); for (np = &namelist[1]; *np != NOSTR; np++) printf(", %s", *np); printf("}\n"); return (-1); } } /* * If any colon modifiers were given, go through and * unmark any messages which do not satisfy the modifiers. */ if (colmod != 0) { for (i = 1; i <= msgCount; i++) { struct coltab *colp; mp = &message[i - 1]; for (colp = &coltab[0]; colp->co_char; colp++) if (colp->co_bit & colmod) if ((mp->m_flag & colp->co_mask) != colp->co_equal) unmark(i); } for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MMARK) break; if (mp >= &message[msgCount]) { struct coltab *colp; printf(gettext("No messages satisfy")); for (colp = &coltab[0]; colp->co_char; colp++) if (colp->co_bit & colmod) printf(" :%c", colp->co_char); printf("\n"); return (-1); } } return (0); } /* * Turn the character after a colon modifier into a bit * value. */ static int evalcol(int col) { struct coltab *colp; if (col == 0) return (lastcolmod); for (colp = &coltab[0]; colp->co_char; colp++) if (colp->co_char == col) return (colp->co_bit); return (0); } /* * Check the passed message number for legality and proper flags. */ static int check(int mesg, int f) { struct message *mp; if (mesg < 1 || mesg > msgCount) { printf(gettext("%d: Invalid message number\n"), mesg); return (-1); } mp = &message[mesg-1]; if ((mp->m_flag & MDELETED) != f) { printf(gettext("%d: Inappropriate message\n"), mesg); return (-1); } return (0); } /* * Scan out the list of string arguments, shell style * for a RAWLIST. */ int getrawlist(char line[], char **argv, int argc) { char **ap, *cp, *cp2; char linebuf[LINESIZE], quotec; char **last; ap = argv; cp = line; last = argv + argc - 1; while (*cp != '\0') { while (any(*cp, " \t")) cp++; cp2 = linebuf; quotec = 0; while (*cp != '\0') { if (quotec) { if (*cp == quotec) { quotec = 0; cp++; } else *cp2++ = *cp++; } else { if (*cp == '\\') { if (*(cp+1) != '\0') { *cp2++ = *++cp; cp++; } else { printf(gettext( "Trailing \\; ignoring\n")); break; } } if (any(*cp, " \t")) break; if (any(*cp, "'\"")) quotec = *cp++; else *cp2++ = *cp++; } } *cp2 = '\0'; if (cp2 == linebuf) break; if (ap >= last) { printf(gettext("Too many elements in the list;" " excess discarded\n")); break; } *ap++ = savestr(linebuf); } *ap = NOSTR; return (ap-argv); } /* * scan out a single lexical item and return its token number, * updating the string pointer passed **p. Also, store the value * of the number or string scanned in lexnumber or lexstring as * appropriate. In any event, store the scanned `thing' in lexstring. */ static struct lex { char l_char; char l_token; } singles[] = { '$', TDOLLAR, '.', TDOT, '^', TUP, '*', TSTAR, '-', TDASH, '+', TPLUS, '(', TOPEN, ')', TCLOSE, 0, 0 }; static int scan(char **sp) { char *cp, *cp2; char c; struct lex *lp; int quotec; if (regretp >= 0) { copy(stringstack[regretp], lexstring); lexnumber = numberstack[regretp]; return (regretstack[regretp--]); } cp = *sp; cp2 = lexstring; c = *cp++; /* * strip away leading white space. */ while (any(c, " \t")) c = *cp++; /* * If no characters remain, we are at end of line, * so report that. */ if (c == '\0') { *sp = --cp; return (TEOL); } /* * If the leading character is a digit, scan * the number and convert it on the fly. * Return TNUMBER when done. */ if (isdigit(c)) { lexnumber = 0; while (isdigit(c)) { lexnumber = lexnumber*10 + c - '0'; *cp2++ = c; c = *cp++; } *cp2 = '\0'; *sp = --cp; return (TNUMBER); } /* * Check for single character tokens; return such * if found. */ for (lp = &singles[0]; lp->l_char != 0; lp++) if (c == lp->l_char) { lexstring[0] = c; lexstring[1] = '\0'; *sp = cp; return (lp->l_token); } /* * We've got a string! Copy all the characters * of the string into lexstring, until we see * a null, space, or tab. * If the lead character is a " or ', save it * and scan until you get another. */ quotec = 0; if (any(c, "'\"")) { quotec = c; c = *cp++; } while (c != '\0') { if (quotec == 0 && c == '\\') { if (*cp != '\0') { c = *cp++; } else { fprintf(stderr, gettext("Trailing \\; " "ignoring\n")); } } if (c == quotec) { cp++; break; } if (quotec == 0 && any(c, " \t")) break; if (cp2 - lexstring < STRINGLEN-1) *cp2++ = c; c = *cp++; } if (quotec && c == 0) fprintf(stderr, gettext("Missing %c\n"), quotec); *sp = --cp; *cp2 = '\0'; return (TSTRING); } /* * Unscan the named token by pushing it onto the regret stack. */ static void regret(int token) { if (++regretp >= REGDEP) panic("Too many regrets"); regretstack[regretp] = token; lexstring[STRINGLEN-1] = '\0'; stringstack[regretp] = savestr(lexstring); numberstack[regretp] = lexnumber; } /* * Reset all the scanner global variables. */ static void scaninit(void) { regretp = -1; } /* * Find the first message whose flags & m == f and return * its message number. */ int first(int f, int m) { int mesg; struct message *mp; mesg = dot - &message[0] + 1; f &= MDELETED; m &= MDELETED; for (mp = dot; mp < &message[msgCount]; mp++) { if ((mp->m_flag & m) == f) return (mesg); mesg++; } mesg = dot - &message[0]; for (mp = dot-1; mp >= &message[0]; mp--) { if ((mp->m_flag & m) == f) return (mesg); mesg--; } return (0); } /* * See if the passed name sent the passed message number. Return true * if so. */ static int sender(char *str, int mesg) { return (samebody(str, skin(nameof(&message[mesg-1])), TRUE)); } /* * See if the given string matches inside the subject field of the * given message. For the purpose of the scan, we ignore case differences. * If it does, return true. The string search argument is assumed to * have the form "/search-string." If it is of the form "/," we use the * previous search string. */ static char lastscan[128]; static int matchsubj(char *str, int mesg) { struct message *mp; char *cp, *cp2, *backup; str++; if (strlen(str) == 0) str = lastscan; else nstrcpy(lastscan, sizeof (lastscan), str); mp = &message[mesg-1]; /* * Now look, ignoring case, for the word in the string. */ cp = str; cp2 = hfield("subject", mp, addone); if (cp2 == NOSTR) return (0); backup = cp2; while (*cp2) { if (*cp == 0) return (1); if (toupper(*cp++) != toupper(*cp2++)) { cp2 = ++backup; cp = str; } } return (*cp == 0); } /* * Mark the named message by setting its mark bit. */ static void mark(int mesg) { int i; i = mesg; if (i < 1 || i > msgCount) panic("Bad message number to mark"); message[i-1].m_flag |= MMARK; } /* * Unmark the named message. */ static void unmark(int mesg) { int i; i = mesg; if (i < 1 || i > msgCount) panic("Bad message number to unmark"); message[i-1].m_flag &= ~MMARK; } /* * Return the message number corresponding to the passed meta character. */ static int metamess(int meta, int f) { int c, m; struct message *mp; c = meta; switch (c) { case '^': /* * First 'good' message left. */ for (mp = &message[0]; mp < &message[msgCount]; mp++) if ((mp->m_flag & MDELETED) == f) return (mp - &message[0] + 1); printf(gettext("No applicable messages\n")); return (-1); case '+': /* * Next 'good' message left. */ for (mp = dot + 1; mp < &message[msgCount]; mp++) if ((mp->m_flag & MDELETED) == f) return (mp - &message[0] + 1); printf(gettext("Referencing beyond last message\n")); return (-1); case '-': /* * Previous 'good' message. */ for (mp = dot - 1; mp >= &message[0]; mp--) if ((mp->m_flag & MDELETED) == f) return (mp - &message[0] + 1); printf(gettext("Referencing before first message\n")); return (-1); case '$': /* * Last 'good message left. */ for (mp = &message[msgCount-1]; mp >= &message[0]; mp--) if ((mp->m_flag & MDELETED) == f) return (mp - &message[0] + 1); printf(gettext("No applicable messages\n")); return (-1); case '.': /* * Current message. */ m = dot - &message[0] + 1; if ((dot->m_flag & MDELETED) != f) { printf(gettext("%d: Inappropriate message\n"), m); return (-1); } return (m); default: printf(gettext("Unknown metachar (%c)\n"), c); return (-1); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * lock a file pointer */ #include #include #include int lock(FILE *fp, char *mode, int blk) { struct flock l; l.l_type = !strcmp(mode, "r") ? F_RDLCK : F_WRLCK; l.l_whence = 0; l.l_start = l.l_len = 0L; return fcntl(fileno(fp), blk ? F_SETLKW : F_SETLK, &l); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program */ /* * libpath(file) - return the full path to the library file */ #include /* for declaration of sprintf */ #include /* for declaration of access */ #include "uparm.h" #include #define PATHSIZE 1024 char * libpath(char *file) { static char buf[PATHSIZE]; snprintf(buf, sizeof (buf), "%s/%s", LIBPATH, file); return (buf); } /* * Return the path to a potentially locale-specific help file. */ char * helppath(char *file) { static char buf[PATHSIZE]; char *loc; loc = setlocale(LC_MESSAGES, NULL); if (loc != NULL) { snprintf(buf, sizeof (buf), "%s/%s/LC_MESSAGES/%s", LOCALEPATH, loc, file); if (access(buf, 0) == 0) return (buf); } snprintf(buf, sizeof (buf), "%s/%s", LIBPATH, file); return (buf); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #ifndef preSVr4 #include #endif /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Startup -- interface with user. */ static void hdrstop(int); static jmp_buf hdrjmp; const char *const version = "mailx version 5.0"; /* * Find out who the user is, copy their mail file (if exists) into * /tmp/Rxxxxx and set up the message pointers. Then, print out the * message headers and read user commands. * * Command line syntax: * mailx [ -i ] [ -r address ] [ -h number ] [ -f [ name ] ] * or: * mailx [ -i ] [ -r address ] [ -h number ] people ... * * and a bunch of other options. */ int main(int argc, char **argv) { register char *ef; register int argp; int mustsend, f, goerr = 0; void (*prevint)(int); int loaded = 0; struct termio tbuf; struct termios tbufs; int c; char *cwd, *mf; /* * Set up a reasonable environment. * Figure out whether we are being run interactively, set up * all the temporary files, buffer standard output, and so forth. */ #ifndef preSVr4 (void)setlocale(LC_ALL, ""); #endif #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); #ifdef SIGCONT sigset(SIGCONT, SIG_DFL); #endif rpterr = 0; /* initialize; set when we output to stderr */ progname = argv[0]; if (progname[strlen(progname) - 1] != 'x') { assign("bsdcompat", ""); } myegid = getegid(); myrgid = getgid(); myeuid = geteuid(); myruid = getuid(); mypid = getpid(); setgid(myrgid); setuid(myruid); inithost(); intty = isatty(0); if (ioctl(1, TCGETS, &tbufs) < 0) { if (ioctl(1, TCGETA, &tbuf)==0) { outtty = 1; baud = tbuf.c_cflag & CBAUD; } else baud = B9600; } else { outtty = 1; baud = cfgetospeed(&tbufs); } image = -1; /* * Now, determine how we are being used. * We successively pick off instances of -r, -h, -f, and -i. * If called as "rmail" we note this fact for letter sending. * If there is anything left, it is the base of the list * of users to mail to. Argp will be set to point to the * first of these users. */ ef = NOSTR; argp = -1; mustsend = 0; if (argc > 0 && **argv == 'r') rmail++; while ((c = getopt(argc, argv, "b:Bc:defFh:HiInNr:s:u:UtT:vV~")) != EOF) switch (c) { case 'e': /* * exit status only */ exitflg++; break; case 'r': /* * Next argument is address to be sent along * to the mailer. */ mustsend++; rflag = optarg; break; case 'T': /* * Next argument is temp file to write which * articles have been read/deleted for netnews. */ Tflag = optarg; if ((f = creat(Tflag, TEMPPERM)) < 0) { perror(Tflag); exit(1); } close(f); /* fall through for -I too */ /* FALLTHROUGH */ case 'I': /* * print newsgroup in header summary */ newsflg++; break; case 'u': /* * Next argument is person's mailbox to use. * Treated the same as "-f /var/mail/user". */ { static char u[PATHSIZE]; snprintf(u, sizeof (u), "%s%s", maildir, optarg); ef = u; break; } case 'i': /* * User wants to ignore interrupts. * Set the variable "ignore" */ assign("ignore", ""); break; case 'U': UnUUCP++; break; case 'd': assign("debug", ""); break; case 'h': /* * Specified sequence number for network. * This is the number of "hops" made so * far (count of times message has been * forwarded) to help avoid infinite mail loops. */ mustsend++; hflag = atoi(optarg); if (hflag == 0) { fprintf(stderr, gettext("-h needs non-zero number\n")); goerr++; } break; case 's': /* * Give a subject field for sending from * non terminal */ mustsend++; sflag = optarg; break; case 'c': /* Cc: from command line */ mustsend++; cflag = optarg; break; case 'b': /* Bcc: from command line */ mustsend++; bflag = optarg; break; case 'f': /* * User is specifying file to "edit" with mailx, * as opposed to reading system mailbox. * If no argument is given after -f, we read their * $MBOX file or mbox in their home directory. */ ef = (argc == optind || *argv[optind] == '-') ? "" : argv[optind++]; if (*ef && *ef != '/' && *ef != '+') cwd = getcwd(NOSTR, PATHSIZE); break; case 'F': Fflag++; mustsend++; break; case 'n': /* * User doesn't want to source * /etc/mail/mailx.rc */ nosrc++; break; case 'N': /* * Avoid initial header printing. */ noheader++; break; case 'H': /* * Print headers and exit */ Hflag++; break; case 'V': puts(version); return 0; case '~': /* * Permit tildas no matter where * the input is coming from. */ assign("escapeok", ""); break; case 'v': /* * Send mailer verbose flag */ assign("verbose", ""); break; case 'B': /* * Don't buffer output * (Line buffered is good enough) */ setvbuf(stdout, NULL, _IOLBF, BUFSIZ); setvbuf(stderr, NULL, _IOLBF, BUFSIZ); break; case 't': /* * Like sendmail -t, read headers from text */ tflag++; mustsend++; break; case '?': default: goerr++; break; } if (optind != argc) argp = optind; /* * Check for inconsistent arguments. */ if (newsflg && ef==NOSTR) { fprintf(stderr, gettext("Need -f with -I flag\n")); goerr++; } if (ef != NOSTR && argp != -1) { fprintf(stderr, gettext("Cannot give -f and people to send to.\n")); goerr++; } if (exitflg && (mustsend || argp != -1)) exit(1); /* nonsense flags involving -e simply exit */ if (tflag && argp != -1) { fprintf(stderr, gettext("Ignoring recipients on command line with -t\n")); argp = -1; } else if (!tflag && mustsend && argp == -1) { fprintf(stderr, gettext("The flags you gave are used only when sending mail.\n")); goerr++; } if (goerr) { fprintf(stderr, gettext("Usage: %s -eiIUdFntBNHvV~ -T FILE -u USER -h hops -r address\n"), progname); fprintf(stderr, gettext("\t\t-s SUBJECT -f FILE users\n")); exit(1); } tinit(); input = stdin; rcvmode = !tflag && argp == -1; if (!nosrc) load(MASTER); if (!rcvmode) { load(Getf("MAILRC")); if (tflag) tmail(); else mail(&argv[argp]); exit(senderr ? senderr : rpterr); } /* * Ok, we are reading mail. * Decide whether we are editing a mailbox or reading * the system mailbox, and open up the right stuff. * * Do this before sourcing the MAILRC, because there might be * a 'chdir' there that breaks the -f option. But if the * file specified with -f is a folder name, go ahead and * source the MAILRC anyway so that "folder" will be defined. */ nstrcpy(origname, PATHSIZE, mailname); editfile = mailname; if (ef != NOSTR) { if (ef == NOSTR || *ef == '\0' || *ef == '+') { load(Getf("MAILRC")); loaded++; } ef = *ef ? safeexpand(ef) : Getf("MBOX"); nstrcpy(origname, PATHSIZE, ef); if (ef[0] != '/') { if (cwd == NOSTR) cwd = getcwd(NOSTR, PATHSIZE); nstrcat(cwd, PATHSIZE, "/"); nstrcat(cwd, PATHSIZE, ef); ef = cwd; } editfile = ef; edit++; } if (setfile(editfile, edit) < 0) exit(1); if (!loaded) load(Getf("MAILRC")); if (msgCount > 0 && !noheader && value("header") != NOSTR) { if (setjmp(hdrjmp) == 0) { if ((prevint = sigset(SIGINT, SIG_IGN)) != SIG_IGN) sigset(SIGINT, hdrstop); announce(); fflush(stdout); sigset(SIGINT, prevint); } } if (Hflag || (!edit && msgCount == 0)) { if (!Hflag) { fprintf(stderr, gettext("No mail for %s\n"), myname); Verhogen(); } fflush(stdout); exit(rpterr); } commands(); sigset(SIGHUP, SIG_IGN); sigset(SIGINT, SIG_IGN); sigset(SIGQUIT, SIG_IGN); if (!outtty) sigset(SIGPIPE, SIG_IGN); if (edit) edstop(0); else { quit(0); Verhogen(); } return (rpterr); } /* * Interrupt printing of the headers. */ static void #ifdef __cplusplus hdrstop(int) #else /* ARGSUSED */ hdrstop(int s) #endif { fflush(stdout); fprintf(stderr, gettext("\nInterrupt\n")); # ifdef OLD_BSD_SIGS sigrelse(SIGINT); # endif longjmp(hdrjmp, 1); } # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2000 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # BSD compatible Mail defaults. # like mailx... source /etc/mail/mailx.rc # except... unset showname autoinc set escapeok # ident "%Z%%M% %I% %E% SMI" mailx commands alias,group user ... declare alias for user names alternates user declare alternate names for your login cd,chdir [directory] chdir to directory or home if none given !command shell escape copy [msglist] file save messages to file without marking as saved delete [msglist] delete messages discard,ignore header discard header field when printing message dp,dt [msglist] delete messages and type next message echo string print the string edit [msglist] edit messages folder,file filename change mailboxes to filename folders list files in directory of current folder followup [msglist] reply to authors of messages and save copy Followup [message] reply to all recipients of message and save copy from [msglist] give header lines of messages header [message] print page of active message headers help,? print this help message hold,preserve [msglist] hold messages in mailbox inc incorporate new messages into current session list list all commands (no explanations) mail user mail to specific user Mail mail to specific user, saving copy mbox [msglist] messages will go to mbox when quitting next [message] goto and type next message pipe,| [msglist] shell-cmd pipe the messages to the shell command print,type [msglist] print messages Print,Type [msglist] print messages with all headers quit quit, preserving unread messages reply,respond [msglist] reply to authors (only) of the messages Reply,Respond [message] reply to the author and recipients of the msg save [msglist] file save (appending) messages to file Save [msglist] save messages to file named after author set variable[=value] set variable to value size [msglist] print size of messages source file read commands from file top [msglist] print top 5 lines of messages touch [msglist] force the messages to be saved when quitting undelete [msglist] restore deleted messages undiscard,unignore header add header field back to list printed unread,new [msglist] mark messages unread version print version visual [msglist] edit list with $VISUAL editor write [msglist] file write messages without headers xit,exit quit, preserving all messages z [+/-] display next [last] page of 10 headers [msglist] is optional and specifies messages by number, author, subject or type. The default is the current message. # ident "%Z%%M% %I% %E% SMI" -------------------- ~ ESCAPES ---------------------------- ~~ Quote a single tilde ~a,~A Autograph (insert 'sign','Sign' variable) ~b users Add users to Bcc list ~c users Add users to Cc list ~d Read in dead.letter file ~e Edit the message buffer ~f messages Read in messages, do not right-shift ~h Prompt for Subject and To, Cc and Bcc lists ~i variable Insert variable into message (~a := ~i sign) ~m messages Read in messages, right-shifted by a tab ~p Print the message buffer ~q,~Q Quit, save letter in $HOME/dead.letter ~r,~< file Read a file into the message buffer ~r,~< !command Read output from command into message ~R Mark message for return receipt ~s subject Set subject ~t users Add users to To list ~v Invoke display editor on message ~w file Write message onto file (no header) ~x Quit, do not save letter ~!command Run a shell command ~|,~^ command Pipe the message through the command ~:,~_ command Execute regular mailx command ~. end of input ~? print this help message ----------------------------------------------------------- # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2000 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # The following 3 settings produce the familiar Solaris behavior. set appenddeadletter unset pipeignore unset replyall set append dot showname if t # mailx-only defaults here # # automatically incorporate new messages as they arrive. # would be nice if mailtool supported this as well. set autoinc else # mailtool-only defaults here # # this should probably not be mailtool-only, but for # compatibility we only put it here. set hold # following so hierarchical menu is the default set LISTER="ls -LF" # default is to allow editing of message windows set editmessagewindow endif # most mail headers are uninteresting to most people. here we ignore all # the uninteresting headers that are at all common. a better approach # might've been to retain only the interesting headers... # ignore uninteresting headers ignore apparently-to default-options errors-to in-reply-to precedence priority ignore received reply-to return-path return-receipt-to sender status via # ignore headers from news ignore approved company confirmed-by distribution expires followup-to keywords ignore lines message-id newsgroups organization path posted-date posting-number ignore references submitted-by summary # ignore strange headers from other mail systems ignore auto-forward-count auto-forwarded-from email-version ignore end-of-header end-of-protocol message-protocol message-service ignore message-type message-version mts-message-id report-version sent-by ignore transport-options ua-content-id ua-message-id # ignore content headers, but they'll still be saved ignore content-length content-type content-charset # ignore common non-standard headers ignore x-lines x-mailer x-postmark x-sequence x-zippy ignore x-sun-charset /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1994 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #undef fopen #undef fclose /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Local version of fopen() and fclose(). These maintain a list of * file pointers which can be run down when we need to close * all files, such as before executing external commands. */ static NODE *append(); static NODE *del1(); static NODE *getnode(); static NODE *search(); static NODE * getnode(FILE *fp) { NODE *newnode; if ((newnode = (NODE *)malloc(sizeof(NODE))) == (NODE *)NULL) { (void) fputs("Cannot allocate node space\n", stderr); exit(3); } newnode->fp = fp; newnode->next = (NODE *)NULL; return(newnode); } static NODE * search(FILE *fp) { register NODE *tmp; for (tmp = fplist; tmp != (NODE *)NULL; tmp = tmp->next) if (tmp->fp == fp) break; return( tmp != (NODE *)NULL ? tmp : NOFP); } static NODE * append(FILE *fp) { register NODE *newnode; if ((newnode = getnode(fp)) == (NODE *)NULL) return(NOFP); if (fplist == NOFP) { fplist = newnode; } else { newnode->next = curptr->next; curptr->next = newnode; } return(newnode); } static NODE * del1(NODE *oldcur) { register NODE *cur, *prev; for (prev = cur = fplist; cur != (NODE *)NULL; cur = cur->next) { if (cur == oldcur) { if (cur == fplist) { cur = fplist = cur->next; } else { prev->next = cur->next; cur = prev->next ? prev->next : prev; } if (curptr == oldcur) curptr = prev; free(oldcur); break; } prev = cur; } return(cur); } FILE * my_fopen(char *file, char *mode) { FILE *fp; if ((fp = fopen(file, mode)) == (FILE *)NULL) { fplist = NOFP; return(fp); } else { curptr = append(fp); } return(fp); } int my_fclose(register FILE *iop) { register NODE *cur; int ret = fclose(iop); if (fplist != NOFP) { cur = search(iop); cur = del1(cur); } return ret; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2001 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Handle name lists. */ #include "rcv.h" #include static struct name *nalloc(char str[]); static int isfileaddr(char *name); static int lengthof(struct name *name); static struct name *gexpand(struct name *nlist, struct grouphead *gh, int metoo, int arg_ntype); static char *norm(register char *user, register char *ubuf, int nbangs); static struct name *put(struct name *list, struct name *node); /* * Allocate a single element of a name list, * initialize its name field to the passed * name and return it. */ static struct name * nalloc(char str[]) { register struct name *np; np = (struct name *)salloc(sizeof (*np)); np->n_flink = NIL; np->n_blink = NIL; np->n_type = -1; np->n_full = savestr(str); np->n_name = skin(np->n_full); return (np); } /* * Find the tail of a list and return it. */ struct name * tailof(struct name *name) { register struct name *np; np = name; if (np == NIL) return (NIL); while (np->n_flink != NIL) np = np->n_flink; return (np); } /* * Extract a list of names from a line, * and make a list of names from it. * Return the list or NIL if none found. */ struct name * extract(char line[], int arg_ntype) { short ntype = (short)arg_ntype; register char *cp; register struct name *top, *np, *t; char nbuf[BUFSIZ], abuf[BUFSIZ]; int comma; if (line == NOSTR || strlen(line) == 0) return (NIL); comma = docomma(line); top = NIL; np = NIL; cp = line; while ((cp = yankword(cp, nbuf, sizeof (nbuf), comma)) != NOSTR) { if (np != NIL && equal(nbuf, "at")) { nstrcpy(abuf, sizeof (abuf), nbuf); if ((cp = yankword(cp, nbuf, sizeof (nbuf), comma)) == NOSTR) { nstrcpy(nbuf, sizeof (nbuf), abuf); goto normal; } snprintf(abuf, sizeof (abuf), "%s@%s", np->n_name, nbuf); np->n_name = savestr(abuf); continue; } normal: t = nalloc(nbuf); t->n_type = ntype; if (top == NIL) top = t; else np->n_flink = t; t->n_blink = np; np = t; } return (top); } /* * Turn a list of names into a string of the same names. */ char * detract(register struct name *np, int ntype) { register int s; register char *cp, *top; register struct name *p; if (np == NIL) return (NOSTR); s = 0; for (p = np; p != NIL; p = p->n_flink) { if ((ntype && (p->n_type & GMASK) != ntype) || (p->n_type & GDEL)) continue; s += strlen(p->n_full) + 2; } if (s == 0) return (NOSTR); top = (char *)salloc((unsigned)(++s)); cp = top; for (p = np; p != NIL; p = p->n_flink) { if ((ntype && (p->n_type & GMASK) != ntype) || (p->n_type & GDEL)) continue; cp = copy(p->n_full, cp); *cp++ = ','; *cp++ = ' '; } *cp = 0; return (top); } struct name * outpre(struct name *to) { register struct name *np; for (np = to; np; np = np->n_flink) if (isfileaddr(np->n_name)) np->n_type |= GDEL; return (to); } /* * For each recipient in the passed name list with a / * in the name, append the message to the end of the named file * and remove them from the recipient list. * * Recipients whose name begins with | are piped through the given * program and removed. */ int outof(struct name *names, FILE *fo) { register int c; register struct name *np; time_t now; char *date, *fname, *shell; FILE *fout, *fin; int ispipe; int nout = 0; int fd = 0; #ifdef preSVr4 char line[BUFSIZ]; #endif if (value("expandaddr") == NOSTR) return (nout); for (np = names; np != NIL; np = np->n_flink) { if (!isfileaddr(np->n_name) && np->n_name[0] != '|') continue; nout++; ispipe = np->n_name[0] == '|'; if (ispipe) fname = np->n_name+1; else fname = safeexpand(np->n_name); /* * See if we have copied the complete message out yet. * If not, do so. */ if (image < 0) { fd = open(tempEdit, O_CREAT|O_EXCL|O_APPEND|O_WRONLY, 0600); if ((fd < 0) && (errno == EEXIST)) { if ((fd = open(tempEdit, O_APPEND|O_WRONLY, 0600)) < 0) { perror(tempEdit); senderr++; goto cant; } } if ((fout = fdopen(fd, "a")) == NULL) { perror(tempEdit); senderr++; goto cant; } image = open(tempEdit, O_RDWR); unlink(tempEdit); if (image < 0) { perror(tempEdit); senderr++; goto cant; } else { rewind(fo); time(&now); date = ctime(&now); fprintf(fout, "From %s %s", myname, date); while ((c = getc(fo)) != EOF) putc(c, fout); rewind(fo); fflush(fout); if (fferror(fout)) perror(tempEdit); fclose(fout); } } /* * Now either copy "image" to the desired file * or give it as the standard input to the desired * program as appropriate. */ if (ispipe) { wait((int *)NULL); switch (fork()) { case 0: sigchild(); sigset(SIGHUP, SIG_IGN); sigset(SIGINT, SIG_IGN); sigset(SIGQUIT, SIG_IGN); close(0); dup(image); close(image); lseek(0, 0L, 0); if ((shell = value("SHELL")) == NOSTR || *shell == '\0') shell = SHELL; (void) execlp(shell, shell, "-c", fname, (char *)0); perror(shell); exit(1); break; case (pid_t)-1: perror("fork"); senderr++; goto cant; } } else { if ((fout = fopen(fname, "a")) == NULL) { perror(fname); senderr++; goto cant; } fin = Fdopen(image, "r"); if (fin == NULL) { fprintf(stderr, gettext("Can't reopen image\n")); fclose(fout); senderr++; goto cant; } rewind(fin); #ifdef preSVr4 putc(getc(fin), fout); while (fgets(line, sizeof (line), fin)) { if (strncmp(line, "From ", 5) == 0) putc('>', fout); fputs(line, fout); } #else while ((c = getc(fin)) != EOF) putc(c, fout); #endif putc('\n', fout); fflush(fout); if (fferror(fout)) senderr++, perror(fname); fclose(fout); fclose(fin); } cant: /* * In days of old we removed the entry from the * the list; now for sake of header expansion * we leave it in and mark it as deleted. */ #ifdef CRAZYWOW { register struct name *t, *x; if (np == top) { top = np->n_flink; if (top != NIL) top->n_blink = NIL; np = top; continue; } x = np->n_blink; t = np->n_flink; x->n_flink = t; if (t != NIL) t->n_blink = x; np = t; } #endif np->n_type |= GDEL; } if (image >= 0) { close(image); image = -1; } return (nout); } /* * Determine if the passed address is a local "send to file" address. * If any of the network metacharacters precedes any slashes, it can't * be a filename. We cheat with .'s to allow path names like ./... * If "fcc" has been unset, then short-circuit those tests, but not * the +... test. */ static int isfileaddr(char *name) { register char *cp; char *fcc = value("fcc"); if (any('@', name)) return (0); if (*name == '+') return (1); if (fcc == NOSTR) return (0); for (cp = name; *cp; cp++) { if (*cp == '.') continue; if (any(*cp, metanet)) return (0); if (*cp == '/') return (1); } return (0); } /* * Map all of the aliased users in the invoker's mailrc * file and insert them into the list. * Changed after all these months of service to recursively * expand names (2/14/80). */ struct name * usermap(struct name *names) { register struct name *newnames, *np, *cp; struct grouphead *gh; register int metoo; newnames = NIL; np = names; metoo = (value("metoo") != NOSTR); while (np != NIL) { if (np->n_name[0] == '\\') { cp = np->n_flink; newnames = put(newnames, np); np = cp; continue; } gh = findgroup(np->n_name); cp = np->n_flink; if (gh != NOGRP) newnames = gexpand(newnames, gh, metoo, np->n_type); else newnames = put(newnames, np); np = cp; } return (newnames); } /* * Recursively expand a group name. We limit the expansion to some * fixed level to keep things from going haywire. * Direct recursion is not expanded for convenience. */ static struct name * gexpand(struct name *nlist, struct grouphead *gh, int metoo, int arg_ntype) { short ntype = (short)arg_ntype; struct mgroup *gp; struct grouphead *ngh; struct name *np; static int depth; register char *cp; if (depth > MAXEXP) { printf(gettext("Expanding alias to depth larger than %d\n"), MAXEXP); return (nlist); } depth++; for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) { cp = gp->ge_name; if (*cp == '\\') goto quote; if (strcmp(cp, gh->g_name) == 0) goto quote; if ((ngh = findgroup(cp)) != NOGRP) { nlist = gexpand(nlist, ngh, metoo, ntype); continue; } quote: np = nalloc(cp); np->n_type = ntype; /* * At this point should allow to expand * to self if only person in group */ if (gp == gh->g_list && gp->ge_link == NOGE) goto skip; if (!metoo && samebody(myname, gp->ge_name, FALSE)) np->n_type |= GDEL; skip: nlist = put(nlist, np); } depth--; return (nlist); } /* * Normalize a network name for comparison purposes. */ static char * norm(register char *user, register char *ubuf, int nbangs) { register char *cp; int inubuf = 0; while (*user++ == '!') ; user--; if (!strchr(user, '!')) { snprintf(ubuf, BUFSIZ, "%s!%s", host, user); user = ubuf; inubuf++; } if (nbangs) { cp = user + strlen(user); while (nbangs--) while (cp > user && *--cp != '!') ; user = (cp > user) ? ++cp : cp; /* * Now strip off all Internet-type * hosts. */ if ((cp = strchr(user, '%')) == NOSTR) cp = strchr(user, '@'); if (cp != NOSTR) { if (!inubuf) { strncpy(ubuf, user, cp - user); ubuf[cp - user] = '\0'; user = ubuf; } else *cp = '\0'; } } return (user); } /* * Implement allnet options. */ int samebody(register char *user, register char *addr, int fuzzy) { char ubuf[BUFSIZ], abuf[BUFSIZ]; char *allnet = value("allnet"); int nbangs = allnet ? (strcmp(allnet, "uucp") == 0) ? 2 : 1 : 0; if (fuzzy && value("fuzzymatch")) { int i; (void) strlcpy(ubuf, user, BUFSIZ); for (i = 0; ubuf[i]; i++) ubuf[i] = tolower(ubuf[i]); (void) strlcpy(abuf, addr, BUFSIZ); for (i = 0; abuf[i]; i++) abuf[i] = tolower(abuf[i]); return (strstr(abuf, ubuf) != NOSTR); } user = norm(user, ubuf, nbangs); addr = norm(addr, abuf, nbangs); return (strcmp(user, addr) == 0); } /* * Compute the length of the passed name list and * return it. */ static int lengthof(struct name *name) { register struct name *np; register int c; for (c = 0, np = name; np != NIL; c++, np = np->n_flink) ; return (c); } /* * Concatenate the two passed name lists, return the result. */ struct name * cat(struct name *n1, struct name *n2) { register struct name *tail; if (n1 == NIL) return (n2); if (n2 == NIL) return (n1); tail = tailof(n1); tail->n_flink = n2; n2->n_blink = tail; return (n1); } /* * Unpack the name list onto a vector of strings. * Return an error if the name list won't fit. */ char ** unpack(struct name *np) { register char **ap, **top; register struct name *n; char hbuf[10]; int t, extra, metoo, verbose; n = np; if ((t = lengthof(n)) == 0) panic("No names to unpack"); /* * Compute the number of extra arguments we will need. We need at least * 3 extra -- one for "mail", one for a terminating -- to stop sendmail * option processing, and one for the terminating 0 pointer. * * Additional spots may be needed to pass along -r and -f to the host * mailer. */ extra = 3; if (rflag != NOSTR) extra += 2; extra++; metoo = value("metoo") != NOSTR; if (metoo) extra++; verbose = value("verbose") != NOSTR; if (verbose) extra++; if (hflag) extra += 2; top = (char **)salloc((t + extra) * sizeof (char *)); ap = top; *ap++ = "sendmail"; if (rflag != NOSTR) { *ap++ = "-r"; *ap++ = rflag; } *ap++ = "-i"; if (metoo) *ap++ = "-m"; if (verbose) *ap++ = "-v"; if (hflag) { *ap++ = "-h"; snprintf(hbuf, sizeof (hbuf), "%d", hflag); *ap++ = savestr(hbuf); } *ap++ = "--"; while (n != NIL) { if (n->n_type & GDEL) { n = n->n_flink; continue; } *ap++ = n->n_name; n = n->n_flink; } *ap = NOSTR; return (top); } /* * See if the user named themself as a destination * for outgoing mail. If so, set the global flag * selfsent so that we avoid removing their mailbox. */ void mechk(struct name *names) { register struct name *np; for (np = names; np != NIL; np = np->n_flink) if ((np->n_type & GDEL) == 0 && samebody(np->n_name, myname, FALSE)) { selfsent++; return; } } /* * Remove all of the duplicates from the passed name list by * insertion sorting them, then checking for dups. * Return the head of the new list. */ struct name * elide(struct name *names) { register struct name *np, *t, *newnames; struct name *x; if (names == NIL) return (NIL); newnames = names; np = names; np = np->n_flink; if (np != NIL) np->n_blink = NIL; newnames->n_flink = NIL; while (np != NIL) { t = newnames; while (strcmp(t->n_name, np->n_name) < 0) { if (t->n_flink == NIL) break; t = t->n_flink; } /* * If we ran out of t's, put the new entry after * the current value of t. */ if (strcmp(t->n_name, np->n_name) < 0) { t->n_flink = np; np->n_blink = t; t = np; np = np->n_flink; t->n_flink = NIL; continue; } /* * Otherwise, put the new entry in front of the * current t. If at the front of the list, * the new guy becomes the new head of the list. */ if (t == newnames) { t = np; np = np->n_flink; t->n_flink = newnames; newnames->n_blink = t; t->n_blink = NIL; newnames = t; continue; } /* * The normal case -- we are inserting into the * middle of the list. */ x = np; np = np->n_flink; x->n_flink = t; x->n_blink = t->n_blink; t->n_blink->n_flink = x; t->n_blink = x; } /* * Now the list headed up by new is sorted. * Go through it and remove duplicates. * Remember the best "type" among all the * duplicates of a name. */ np = newnames; while (np != NIL) { int type; t = np; type = np->n_type; while (t->n_flink != NIL && strcmp(np->n_name, t->n_flink->n_name) == 0) { t = t->n_flink; /* "To" before "Cc" before "Bcc" */ if (t->n_type < type) type = t->n_type; } if (t == np || t == NIL) { np = np->n_flink; continue; } /* * Now t points to the last entry with the same name * as np. Make np point beyond t. */ np->n_flink = t->n_flink; if (t->n_flink != NIL) t->n_flink->n_blink = np; np->n_type = type; np = np->n_flink; } return (newnames); } /* * Put another node onto a list of names and return * the list. */ static struct name * put(struct name *list, struct name *node) { node->n_flink = list; node->n_blink = NIL; if (list != NIL) list->n_blink = node; return (node); } /* * Delete the given name from a namelist. */ struct name * delname(register struct name *np, char name[]) { register struct name *p; for (p = np; p != NIL; p = p->n_flink) if (samebody(name, p->n_name, FALSE)) { if (p->n_blink == NIL) { if (p->n_flink != NIL) p->n_flink->n_blink = NIL; np = p->n_flink; continue; } if (p->n_flink == NIL) { if (p->n_blink != NIL) p->n_blink->n_flink = NIL; continue; } p->n_blink->n_flink = p->n_flink; p->n_flink->n_blink = p->n_blink; } return (np); } /* * Call the given routine on each element of the name * list, replacing said value if need be. */ void mapf(register struct name *np, char *from) { register struct name *p; if (debug) fprintf(stderr, "mapf %lx, %s\n", (long)np, from); for (p = np; p != NIL; p = p->n_flink) if ((p->n_type & GDEL) == 0) { p->n_name = netmap(p->n_name, from); p->n_full = splice(p->n_name, p->n_full); } if (debug) fprintf(stderr, "mapf %s done\n", from); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1998 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Network name modification routines. */ #include "rcv.h" #include "configdefs.h" #include static char *arpafix(char name[], char from[]); static char *lasthost(char *addr); static char *makeremote(char name[], char from[]); static int mstash(char name[], int attnet); static int mtype(int mid); static int netlook(char machine[], int attnet); static int nettype(int mid); static int ntype(register int nc); static void stradd(register char *str, int n, register int c); static char *tackon(char *sys, char *rest); static struct xtrahash *xlocate(char name[]); #ifdef OPTIM static char best(int src, int dest); static char *mlook(int mid); static int netkind(register int nt); static void optiboth(char net[]); static void optim(char net[], char name[]); static void optim1(char netstr[], char name[]); static int optimex(char net[], char name[]); static int optimimp(char net[], char name[]); static void prefer(char name[]); static char *rpair(char str[], int mach); #endif /* * Map a name into the correct network "view" of the * name. This is done by prepending the name with the * network address of the sender, then optimizing away * nonsense. */ char * netmap(char name[], char from[]) { char nbuf[BUFSIZ], ret[BUFSIZ]; register char *cp, *oname; if (debug) fprintf(stderr, "netmap(name '%s', from '%s')\n", name, from); if (strlen(from) == 0) return(name); /* "from" is empty - can't do anything */ if (strcmp(from, name) == 0) return(name); /* "from" and "name" are the same, do nothing */ /* * If the name contains an "@" or a "%", remove it and the host * following it if that host is "known". */ if (any('@', name) || any('%', name)) return(arpafix(name, from)); /* * If the sender contains a "@" or a "%", make "name" into an * address on that host, on the presumption that it should * really have read "name@from" when we received the message * rather than just "name". */ if (any('@', from) || any('%', from)) return(unuucp(makeremote(name, from))); if (value("onehop") && (cp = strchr(name, '!')) && cp > name) { /* * "onehop" is set, meaning all machines are one UUCP * hop away (fat chance, in this day and age), and "name" * is a UUCP path rather than just a name. Leave it alone. */ nstrcpy(nbuf, sizeof (nbuf), name); } else { from = tackon(host, from); *strrchr(from, '!') = 0; name = tackon(lasthost(from), name); while (((cp = lasthost(from)) != 0) && ishost(cp, name)) { oname = name; name = strchr(name, '!') + 1; if (cp == from) { from[strlen(from)] = '!'; if (value("mustbang") && !strchr(name, '!')) name = oname; return(unuucp(name)); } *--cp = 0; } from[strlen(from)] = '!'; from = strchr(from, '!') + 1; snprintf(nbuf, sizeof (nbuf), "%s!%s", from, name); } if (debug) fprintf(stderr, "before optim, nbuf '%s'\n", name); #ifdef OPTIM if ((cp = value("conv"))==NOSTR || strcmp(cp, "optimize") != 0) nstrcpy(ret, sizeof (ret), nbuf); else optim(nbuf, ret); #else nstrcpy(ret, sizeof (ret), nbuf); #endif /* OPTIM */ if (debug) fprintf(stderr, "after optim, nbuf '%s', ret '%s'\n", nbuf, ret); cp = ret; if (debug) fprintf(stderr, "wind up with '%s'\n", name); if (!icequal(name, cp)) return(unuucp((char *) savestr(cp))); return(unuucp(name)); } /* * Stick a host on the beginning of a uucp * address if it isn't there already. */ static char * tackon(char *sys, char *rest) { while (*rest == '!') rest++; if (!ishost(sys, rest)) { char *r = (char *)salloc(strlen(sys) + strlen(rest) + 2); sprintf(r, "%s!%s", sys, rest); rest = r; } return rest; } /* * Check equality of the first host in a uucp address. */ int ishost(char *sys, char *rest) { while (*sys && *sys == *rest) sys++, rest++; return(*sys == 0 && *rest == '!'); } /* * Return last host in a uucp address. */ static char * lasthost(char *addr) { char *r = strrchr(addr, '!'); return r ? ++r : addr; } /* * Optionally translate an old format uucp name into a new one, e.g. * "mach1!mach2!user" becomes "user@mach2.UUCP". This optional because * some information is necessarily lost (e.g. the route it got here * via) and if we don't have the host in our routing tables, we lose. * XXX THIS IS NO LONGER VALID WITH THE NEW UUCP PROJECT PLANS TO * REGISTER UUCP HOSTS IN THE STANDARD INTERNET NAMESPACE, E.G. * ihnp4 BECOMES "ihnp4.att.com". */ char * unuucp(char *name) { register char *np, *hp, *cp; char result[100]; char tname[300]; if (UnUUCP==0 && ((cp = value("conv"))==NOSTR || strcmp(cp, "internet"))) return name; if (debug) fprintf(stderr, "unuucp(%s)\n", name); nstrcpy(tname, sizeof (tname), name); np = strrchr(tname, '!'); if (np == NOSTR) return name; *np++ = 0; hp = strrchr(tname, '!'); if (hp == NOSTR) hp = tname; else *hp++ = 0; cp = strchr(np, '@'); if (cp == NOSTR) cp = strchr(np, '%'); if (cp) *cp = 0; if (debug) fprintf(stderr, "host %s, name %s\n", hp, np); snprintf(result, sizeof (result), "%s@%s.UUCP", np, hp); if (debug) fprintf(stderr, "unuucp returns %s\n", result); return savestr(result); } /* * Turn a network machine name into a unique character */ static int netlook(char machine[], int attnet) { register struct netmach *np; register char *cp, *cp2; char nbuf[BUFSIZ]; /* * Make into lower case. */ for (cp = machine, cp2 = nbuf; *cp && cp2 < &nbuf[BUFSIZ-1]; *cp2++ = tolower(*cp++)) /*nothing*/; *cp2 = 0; /* * If a single letter machine, look through those first. */ if (strlen(nbuf) == 1) for (np = netmach; np->nt_mid != 0; np++) if (np->nt_mid == nbuf[0]) return(nbuf[0]); /* * Look for usual name */ for (np = netmach; np->nt_mid != 0; np++) if (strcmp(np->nt_machine, nbuf) == 0) return(np->nt_mid); /* * Look in side hash table. */ return(mstash(nbuf, attnet)); } #ifdef OPTIM /* * Turn a network unique character identifier into a network name. */ static char * netname(int mid) { register struct netmach *np; if (mid & 0200) return(mlook(mid)); for (np = netmach; np->nt_mid != 0; np++) if (np->nt_mid == mid) return(np->nt_machine); return(NOSTR); } #endif /* * Deal with arpa net addresses. The way this is done is strange. * name contains an "@" or "%". Look up the machine after it in * the hash table. If it isn't found, return name unmolested. * If ???, return name unmolested. * Otherwise, delete the "@" or "%" and the machine after it from * name, and return the new string. */ static char * arpafix(char name[], char from[]) { register char *cp; register int arpamach; char newname[BUFSIZ]; if (debug) { fprintf(stderr, "arpafix(%s, %s)\n", name, from); } cp = strrchr(name, '@'); if (cp == NOSTR) cp = strrchr(name, '%'); if (cp == NOSTR) { fprintf(stderr, gettext("Something's amiss -- no @ or %% in arpafix\n")); return(name); } cp++; arpamach = netlook(cp, '@'); if (debug) fprintf(stderr, "cp '%s', arpamach %o, nettypes arpamach %o LOCAL %o\n", cp, arpamach, nettype(arpamach), nettype(LOCAL)); if (arpamach == 0) { if (debug) fprintf(stderr, "machine %s unknown, uses: %s\n", cp, name); return(name); } if (((nettype(arpamach) & nettype(LOCAL)) & ~AN) == 0) { if (debug) fprintf(stderr, "machine %s known but remote, uses: %s\n", cp, name); return(name); } nstrcpy(newname, sizeof (newname), name); cp = strrchr(newname, '@'); if (cp == NOSTR) cp = strrchr(newname, '%'); *cp = 0; if (debug) fprintf(stderr, "local address, return '%s'\n", newname); return(savestr(newname)); } /* * We have name with no @'s in it, and from with @'s. * Assume that name is meaningful only on the site in from, * and return "name@site_in_from". */ static char * makeremote(char name[], char from[]) { register char *cp; char rbuf[BUFSIZ]; if (!value("makeremote")) return(name); if (debug) fprintf(stderr, "makeremote(%s, %s) returns ", name, from); cp = strrchr(from, '@'); if (cp == NOSTR) cp = strrchr(from, '%'); snprintf(rbuf, sizeof (rbuf), "%s%s", name, cp); if (debug) fprintf(stderr, "%s\n", rbuf); return(savestr(rbuf)); } /* * Take a network machine descriptor and find the types of connected * nets and return it. */ static int nettype(int mid) { register struct netmach *np; if (mid & 0200) return(mtype(mid)); for (np = netmach; np->nt_mid != 0; np++) if (np->nt_mid == mid) return(np->nt_type); return(0); } /* * Hashing routines to salt away machines seen scanning * networks paths that we don't know about. */ #define XHSIZE 97 /* Size of extra hash table */ #define NXMID (XHSIZE*3/4) /* Max extra machines */ struct xtrahash { char *xh_name; /* Name of machine */ short xh_mid; /* Machine ID */ short xh_attnet; /* Attached networks */ } xtrahash[XHSIZE]; static struct xtrahash *xtab[XHSIZE]; /* F: mid-->machine name */ static short midfree; /* Next free machine id */ /* * Initialize the extra host hash table. * Called by sreset. */ void minit(void) { register struct xtrahash *xp, **tp; midfree = 0; tp = &xtab[0]; for (xp = &xtrahash[0]; xp < &xtrahash[XHSIZE]; xp++) { xp->xh_name = NOSTR; xp->xh_mid = 0; xp->xh_attnet = 0; *tp++ = (struct xtrahash *) 0; } } /* * Stash a net name in the extra host hash table. * If a new entry is put in the hash table, deduce what * net the machine is attached to from the net character. * * If the machine is already known, add the given attached * net to those already known. */ static int mstash(char name[], int attnet) { register struct xtrahash *xp; int x; xp = xlocate(name); if (xp == (struct xtrahash *) 0) { printf(gettext("Ran out of machine id spots\n")); return(0); } if (xp->xh_name == NOSTR) { if (midfree >= XHSIZE) { printf(gettext("Out of machine ids\n")); return(0); } xtab[midfree] = xp; xp->xh_name = savestr(name); xp->xh_mid = 0200 + midfree++; } x = ntype(attnet); if (x == 0) xp->xh_attnet |= AN; else xp->xh_attnet |= x; return(xp->xh_mid); } /* * Search for the given name in the hash table * and return the pointer to it if found, or to the first * empty slot if not found. * * If no free slots can be found, return 0. */ static struct xtrahash * xlocate(char name[]) { register int h, q, i; register char *cp; register struct xtrahash *xp; for (h = 0, cp = name; *cp; h = (h << 2) + *cp++) ; if (h < 0 && (h = -h) < 0) h = 0; h = h % XHSIZE; cp = name; for (i = 0, q = 0; q < XHSIZE; i++, q = i * i) { xp = &xtrahash[(h + q) % XHSIZE]; if (xp->xh_name == NOSTR) return(xp); if (strcmp(cp, xp->xh_name) == 0) return(xp); if (h - q < 0) h += XHSIZE; xp = &xtrahash[(h - q) % XHSIZE]; if (xp->xh_name == NOSTR) return(xp); if (strcmp(cp, xp->xh_name) == 0) return(xp); } return((struct xtrahash *) 0); } #ifdef OPTIM /* * Return the name from the extra host hash table corresponding * to the passed machine id. */ static char * mlook(int mid) { register int m; if ((mid & 0200) == 0) return(NOSTR); m = mid & 0177; if (m >= midfree) { printf(gettext("Use made of undefined machine id\n")); return(NOSTR); } return(xtab[m]->xh_name); } #endif /* * Return the bit mask of net's that the given extra host machine * id has so far. */ static int mtype(int mid) { register int m; if ((mid & 0200) == 0) return(0); m = mid & 0177; if (m >= midfree) { printf(gettext("Use made of undefined machine id\n")); return(0); } return(xtab[m]->xh_attnet); } #ifdef OPTIM /* * Take a network name and optimize it. This gloriously messy * operation takes place as follows: the name with machine names * in it is tokenized by mapping each machine name into a single * character machine id (netlook). The separator characters (network * metacharacters) are left intact. The last component of the network * name is stripped off and assumed to be the destination user name -- * it does not participate in the optimization. As an example, the * name "res!vax!res!uvax!bill" becomes, tokenized, * "r!x!r!v!" and "bill" A low level routine, optim1, fixes up the * network part (eg, "r!x!r!v!"), then we convert back to network * machine names and tack the user name on the end. * * The result of this is copied into the parameter "name" */ static void optim(char net[], char name[]) { char netcomp[BUFSIZ], netstr[STSIZ], xfstr[STSIZ]; register char *cp, *cp2; register int c; if (debug) fprintf(stderr, "optim(%s, %s) called\n", net, name); *netstr = '\0'; cp = net; for (;;) { /* * Rip off next path component into netcomp */ cp2 = netcomp; while (*cp && !any(*cp, metanet)) *cp2++ = *cp++; *cp2 = 0; /* * If we hit null byte, then we just scanned * the destination user name. Go off and optimize * if its so. */ if (*cp == 0) break; if ((c = netlook(netcomp, *cp)) == 0) { printf(gettext("No host named \"%s\"\n"), netcomp); err: nstrcpy(name, BUFSIZ, net); return; } stradd(name, BUFSIZ, c); stradd(name, BUFSIZ, *cp++); /* * If multiple network separators given, * throw away the extras. */ while (any(*cp, metanet)) cp++; } if (strlen(netcomp) == 0) { printf(gettext("net name syntax\n")); goto err; } if (debug) fprintf(stderr, "optim1(%s,%s) called\n", netstr, xfstr); optim1(netstr, xfstr); if (debug) fprintf(stderr, "optim1(%s,%s) returns\n", netstr, xfstr); /* * Convert back to machine names. */ cp = xfstr; *name = '\0'; while (*cp) { if ((cp2 = netname(*cp++)) == NOSTR) { printf(gettext("Made up bad net name\n")); printf(gettext("Machine code %c (0%o)\n"), cp[-1], cp[-1]); printf(gettext("Sorry.\n")); goto err; } nstrcat(name, BUFSIZ, cp2); stradd(name, BUFSIZ, *cp++); } nstrcat(name, BUFSIZ, netcomp); if (debug) fprintf(stderr, "optim returns %s in name\n", name); } /* * Take a string of network machine id's and separators and * optimize them. We process these by pulling off maximal * leading strings of the same type, passing these to the appropriate * optimizer and concatenating the results. */ static void optim1(char netstr[], char name[]) { char path[STSIZ], rpath[STSIZ]; register char *cp, *cp2; register int tp, nc; cp = netstr; prefer(cp); *name = '\0'; /* * If the address ultimately points back to us, * just return a null network path. */ if ((int)strlen(cp) > 1 && cp[strlen(cp) - 2] == LOCAL) return; while (*cp != 0) { *path = '\0'; tp = ntype(cp[1]); nc = cp[1]; while (*cp && tp == ntype(cp[1])) { stradd(path, sizeof (path), *cp++); cp++; } switch (netkind(tp)) { default: nstrcpy(rpath, sizeof (rpath), path); break; case IMPLICIT: optimimp(path, rpath); break; case EXPLICIT: optimex(path, rpath); break; } for (cp2 = rpath; *cp2 != 0; cp2++) { stradd(name, BUFSIZ, *cp2); stradd(name, BUFSIZ, nc); } } optiboth(name); prefer(name); } #endif /* OPTIM */ /* * Return the network of the separator -- * AN for arpa net * BN for Bell labs net (e.g. UUCP, NOT Berknet) * SN for Schmidt net (Berknet) * 0 if we don't know. */ static int ntype(register int nc) { register struct ntypetab *np; for (np = ntypetab; np->nt_char != 0; np++) if (np->nt_char == nc) return(np->nt_bcode); return(0); } #ifdef OPTIM /* * Return the kind of routing used for the particular net * EXPLICIT means explicitly routed * IMPLICIT means implicitly routed * 0 means don't know */ static int netkind(register int nt) { register struct nkindtab *np; for (np = nkindtab; np->nk_type != 0; np++) if (np->nk_type == nt) return(np->nk_kind); return(0); } /* * Do name optimization for an explicitly routed network (eg uucp). */ static int optimex(char net[], char name[]) { register char *cp, *rp; register int m; nstrcpy(name, STSIZ, net); cp = name; if (strlen(cp) == 0) return(-1); if (cp[strlen(cp)-1] == LOCAL) { name[0] = 0; return(0); } for (cp = name; *cp; cp++) { m = *cp; rp = strrchr(cp+1, m); if (rp != NOSTR) strcpy(cp, rp); } return(0); } /* * Do name optimization for implicitly routed network (eg, arpanet). */ static int optimimp(char net[], char name[]) { register char *cp; register char m; cp = net; if (strlen(cp) == 0) return(-1); m = cp[strlen(cp) - 1]; if (m == LOCAL) { *name = '\0'; return(0); } name[0] = m; name[1] = 0; return(0); } /* * Perform global optimization on the given network path. * The trick here is to look ahead to see if there are any loops * in the path and remove them. The interpretation of loops is * more strict here than in optimex since both the machine and net * type must match. */ static void optiboth(char net[]) { register char *cp, *cp2; cp = net; if (strlen(cp) == 0) return; if (((int)strlen(cp) % 2) != 0) { printf(gettext("Strange arg to optiboth\n")); return; } while (*cp) { cp2 = rpair(cp+2, *cp); if (cp2 != NOSTR) strcpy(cp, cp2); cp += 2; } } /* * Find the rightmost instance of the given (machine, type) pair. */ static char * rpair(char str[], int mach) { register char *cp, *last; cp = str; last = NOSTR; while (*cp) { if (*cp == mach) last = cp; cp += 2; } return(last); } /* * Change the network separators in the given network path * to the preferred network transmission means. */ static void prefer(char name[]) { register char *cp, n; register int state; state = LOCAL; for (cp = name; *cp; cp += 2) { n = best(state, *cp); if (n) cp[1] = n; state = *cp; } } /* * Return the best network separator for the given machine pair. */ static char best(int src, int dest) { register int dtype, stype; register struct netorder *np; stype = nettype(src); dtype = nettype(dest); fflush(stdout); if (stype == 0 || dtype == 0) { printf(gettext("ERROR: unknown internal machine id\n")); return(0); } if ((stype & dtype) == 0) return(0); np = &netorder[0]; while ((np->no_stat & stype & dtype) == 0) np++; return(np->no_char); } #endif /* OPTIM */ #ifdef notdef /* * Code to twist around arpa net names. */ #define WORD 257 /* Token for a string */ static char netbuf[256]; static char *yylval; /* * Reverse all of the arpa net addresses in the given name to * be of the form "host @ user" instead of "user @ host" * This function is its own inverse. */ char * revarpa(char str[]) { if (yyinit(str) < 0) return(NOSTR); if (name()) return(NOSTR); if (strcmp(str, netbuf) == 0) return(str); return(savestr(netbuf)); } /* * Parse (by recursive descent) network names, using the following grammar: * name: * term {':' term} * term {'^' term} * term {'!' term} * term '@' name * term '%' name * * term: * string of characters. */ static int name(void) { register int t; register char *cp; for (;;) { t = yylex(); if (t != WORD) return(-1); cp = yylval; t = yylex(); switch (t) { case 0: nstrcat(netbuf, sizeof (netbuf), cp); return(0); case '@': case '%': if (name()) return(-1); stradd(netbuf, sizeof (netbuf), '@'); nstrcat(netbuf, sizeof (netbuf), cp); return(0); case WORD: return(-1); default: nstrcat(netbuf, sizeof (netbuf), cp); stradd(netbuf, sizeof (netbuf), t); } } } /* * Scanner for network names. */ static char *charp; /* Current input pointer */ static int nexttok; /* Salted away next token */ /* * Initialize the network name scanner. */ int yyinit(char str[]) { static char lexbuf[BUFSIZ]; netbuf[0] = 0; if (strlen(str) >= sizeof lexbuf - 1) return(-1); nexttok = 0; nstrcpy(lexbuf, sizeof (lexbuf), str); charp = lexbuf; return(0); } /* * Scan and return a single token. * yylval is set to point to a scanned string. */ int yylex(void) { register char *cp, *dotp; register int s; if (nexttok) { s = nexttok; nexttok = 0; return(s); } cp = charp; while (*cp && isspace(*cp)) cp++; if (*cp == 0) return(0); if (any(*cp, metanet)) { charp = cp+1; return(*cp); } dotp = cp; while (*cp && !any(*cp, metanet) && !any(*cp, " \t")) cp++; if (any(*cp, metanet)) nexttok = *cp; if (*cp == 0) charp = cp; else charp = cp+1; *cp = 0; yylval = dotp; return(WORD); } #endif /* * Add a single character onto a string. Here dstsize is the size of the * destnation buffer. */ static void stradd(register char *dst, int dstsize, register int c) { while (*dst != '\0') { dst++; dstsize--; } if (--dstsize > 0) *dst++ = (char)c; *dst = '\0'; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * npopen() and npclose() * * stolen from C library, modified to use SHELL variable */ #include #include #include #define tst(a,b) (*mode == 'r'? (b) : (a)) #define RDR 0 #define WTR 1 #ifdef VMUNIX #define sigset signal #else extern void (*sigset())(); #endif #ifdef preSVr4 extern FILE *fdopen(); extern int execlp(), fork(), pipe(), close(), fcntl(); #ifndef sun typedef int pid_t; #endif #else # include # include #endif static pid_t popen_pid[20]; FILE * npopen(char *cmd, char *mode) { int p[2]; register pid_t pid; register int myside, yourside; char *Shell, *value(char *); if ((Shell = value("SHELL")) == NULL || *Shell=='\0') #ifdef preSVr4 Shell = "/bin/sh"; #else Shell = "/usr/bin/sh"; #endif if(pipe(p) < 0) return(NULL); myside = tst(p[WTR], p[RDR]); yourside = tst(p[RDR], p[WTR]); if((pid = fork()) == 0) { /* myside and yourside reverse roles in child */ int stdio; stdio = tst(0, 1); (void) close(myside); (void) close(stdio); (void) fcntl(yourside, 0, stdio); (void) close(yourside); (void) execlp(Shell, Shell, "-c", cmd, (char *)0); perror(Shell); _exit(1); } if(pid == (pid_t)-1) return(NULL); popen_pid[myside] = pid; (void) close(yourside); return(fdopen(myside, mode)); } int npclose(FILE *ptr) { register int f; register pid_t r; int status; void (*istat)(int), (*qstat)(int); f = fileno(ptr); (void) fclose(ptr); istat = sigset(SIGINT, SIG_IGN); qstat = sigset(SIGQUIT, SIG_IGN); while((r = wait(&status)) != popen_pid[f] && r != (pid_t)-1) ; if(r == (pid_t)-1) status = -1; (void) sigset(SIGINT, istat); (void) sigset(SIGQUIT, qstat); return(status); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 2016 by Delphix. All rights reserved. */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Rcv -- receive mail rationally. * * Termination processing. */ static void writeback(int noremove); #define PRIV(x) setgid(myegid), (x), setgid(myrgid); /* * Save all of the undetermined messages at the top of "mbox" * Save all untouched messages back in the system mailbox. * Remove the system mailbox, if none saved there. */ void quit( int noremove /* don't remove system mailbox, trunc it instead */ ) { int mcount, p, modify, autohold, anystat, holdbit, nohold, fd; FILE *ibuf, *obuf, *fbuf, *readstat; register struct message *mp; register int c; char *id; int appending; char *mbox = Getf("MBOX"); /* * If we are read only, we can't do anything, * so just return quickly. */ mcount = 0; if (readonly) return; /* * See if there any messages to save in mbox. If no, we * can save copying mbox to /tmp and back. * * Check also to see if any files need to be preserved. * Delete all untouched messages to keep them out of mbox. * If all the messages are to be preserved, just exit with * a message. * * If the luser has sent mail to himself, refuse to do * anything with the mailbox, unless mail locking works. */ #ifndef CANLOCK if (selfsent) { printf(gettext("You have new mail.\n")); return; } #endif /* * Adjust the message flags in each message. */ anystat = 0; autohold = value("hold") != NOSTR; appending = value("append") != NOSTR; holdbit = autohold ? MPRESERVE : MBOX; nohold = MBOXED|MBOX|MSAVED|MDELETED|MPRESERVE; if (value("keepsave") != NOSTR) nohold &= ~MSAVED; for (mp = &message[0]; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) { receipt(mp); mp->m_flag &= ~MNEW; mp->m_flag |= MSTATUS; } if (mp->m_flag & MSTATUS) anystat++; if ((mp->m_flag & MTOUCH) == 0) mp->m_flag |= MPRESERVE; if ((mp->m_flag & nohold) == 0) mp->m_flag |= holdbit; } modify = 0; if (Tflag != NOSTR) { if ((readstat = fopen(Tflag, "w")) == NULL) Tflag = NOSTR; } for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) { if (mp->m_flag & MBOX) c++; if (mp->m_flag & MPRESERVE) p++; if (mp->m_flag & MODIFY) modify++; if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) { id = hfield("message-id", mp, addone); if (id != NOSTR) fprintf(readstat, "%s\n", id); else { id = hfield("article-id", mp, addone); if (id != NOSTR) fprintf(readstat, "%s\n", id); } } } if (Tflag != NOSTR) fclose(readstat); if (p == msgCount && !modify && !anystat) { if (p == 1) printf(gettext("Held 1 message in %s\n"), mailname); else printf(gettext("Held %d messages in %s\n"), p, mailname); return; } if (c == 0) { writeback(noremove); return; } /* * Create another temporary file and copy user's mbox file * therein. If there is no mbox, copy nothing. * If they have specified "append" don't copy the mailbox, * just copy saveable entries at the end. */ mcount = c; if (!appending) { if ((fd = open(tempQuit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || (obuf = fdopen(fd, "w")) == NULL) { perror(tempQuit); return; } if ((ibuf = fopen(tempQuit, "r")) == NULL) { perror(tempQuit); removefile(tempQuit); fclose(obuf); return; } removefile(tempQuit); if ((fbuf = fopen(mbox, "r")) != NULL) { while ((c = getc(fbuf)) != EOF) putc(c, obuf); fclose(fbuf); } fflush(obuf); if (fferror(obuf)) { perror(tempQuit); fclose(ibuf); fclose(obuf); return; } fclose(obuf); if ((fd = open(mbox, O_RDWR|O_CREAT|O_TRUNC, MBOXPERM)) < 0 || (obuf = fdopen(fd, "r+")) == NULL) { perror(mbox); fclose(ibuf); return; } if (issysmbox) touchlock(); } else { /* we are appending */ if ((fd = open(mbox, O_RDWR|O_CREAT, MBOXPERM)) < 0 || (obuf = fdopen(fd, "a")) == NULL) { perror(mbox); return; } } for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MBOX) { if (msend(mp, obuf, (int)value("alwaysignore") ? M_IGNORE|M_SAVING : M_SAVING, fputs) < 0) { perror(mbox); if (!appending) fclose(ibuf); fclose(obuf); return; } mp->m_flag &= ~MBOX; mp->m_flag |= MBOXED; if (issysmbox) touchlock(); } /* * Copy the user's old mbox contents back * to the end of the stuff we just saved. * If we are appending, this is unnecessary. */ if (!appending) { rewind(ibuf); c = getc(ibuf); while (c != EOF) { putc(c, obuf); if (ferror(obuf)) break; c = getc(ibuf); } fclose(ibuf); fflush(obuf); } trunc(obuf); if (fferror(obuf)) { perror(mbox); fclose(obuf); return; } fclose(obuf); if (mcount == 1) printf(gettext("Saved 1 message in %s\n"), mbox); else printf(gettext("Saved %d messages in %s\n"), mcount, mbox); /* * Now we are ready to copy back preserved files to * the system mailbox, if any were requested. */ writeback(noremove); } /* * Preserve all the appropriate messages back in the system * mailbox, and print a nice message indicating how many were * saved. Incorporate any new mail that we found. */ static void writeback(int noremove) { register struct message *mp; register int p, c; struct stat st; FILE *obuf = 0, *fbuf = 0, *rbuf = 0; void (*fhup)(int), (*fint)(int), (*fquit)(int); int fd = -1; fhup = sigset(SIGHUP, SIG_IGN); fint = sigset(SIGINT, SIG_IGN); fquit = sigset(SIGQUIT, SIG_IGN); if (issysmbox) lockmail(); if ((fbuf = fopen(mailname, "r+")) == NULL) { perror(mailname); goto die; } if (!issysmbox) lock(fbuf, "r+", 1); fstat(fileno(fbuf), &st); if (st.st_size > mailsize) { printf(gettext("New mail has arrived.\n")); snprintf(tempResid, PATHSIZE, "%s/:saved/%s", maildir, myname); PRIV(rbuf = fopen(tempResid, "w+")); if (rbuf == NULL) { snprintf(tempResid, PATHSIZE, "/tmp/Rq%-ld", mypid); fd = open(tempResid,O_RDWR|O_CREAT|O_EXCL, 0600); PRIV(rbuf = fdopen(fd, "w+")); if (rbuf == NULL) { snprintf(tempResid, PATHSIZE, "%s/:saved/%s", maildir, myname); perror(tempResid); fclose(fbuf); goto die; } } #ifdef APPEND fseek(fbuf, mailsize, 0); while ((c = getc(fbuf)) != EOF) putc(c, rbuf); #else p = st.st_size - mailsize; while (p-- > 0) { c = getc(fbuf); if (c == EOF) { perror(mailname); fclose(fbuf); goto die; } putc(c, rbuf); } #endif fclose(fbuf); fseek(rbuf, 0L, 0); if (issysmbox) touchlock(); } if ((obuf = fopen(mailname, "r+")) == NULL) { perror(mailname); goto die; } #ifndef APPEND if (rbuf != NULL) while ((c = getc(rbuf)) != EOF) putc(c, obuf); #endif p = 0; for (mp = &message[0]; mp < &message[msgCount]; mp++) if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) { p++; if (msend(mp, obuf, 0, fputs) < 0) { perror(mailname); goto die; } if (issysmbox) touchlock(); } #ifdef APPEND if (rbuf != NULL) while ((c = getc(rbuf)) != EOF) putc(c, obuf); #endif fflush(obuf); trunc(obuf); if (fferror(obuf)) { perror(mailname); goto die; } alter(mailname); if (p) { if (p == 1) printf(gettext("Held 1 message in %s\n"), mailname); else printf(gettext("Held %d messages in %s\n"), p, mailname); } if (!noremove && (fsize(obuf) == 0) && (value("keep") == NOSTR)) { if (stat(mailname, &st) >= 0) PRIV(delempty(st.st_mode, mailname)); } die: if (rbuf) { fclose(rbuf); PRIV(removefile(tempResid)); } if (obuf) fclose(obuf); if (issysmbox) unlockmail(); sigset(SIGHUP, fhup); sigset(SIGINT, fint); sigset(SIGQUIT, fquit); } void lockmail(void) { PRIV(maillock(lockname,10)); } void unlockmail(void) { PRIV(mailunlock()); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2014 Joyent, Inc. */ /* * Copyright 1998 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include #include "rcv.h" static int icsubstr(char *s1, char *s2); void receipt(struct message *mp) { char head[LINESIZE]; char buf[BUFSIZ]; FILE *pp, *fp; char *mail, *s; if ((mail = value("sendmail")) == 0) #ifdef SENDMAIL mail = SENDMAIL; #else mail = MAIL; #endif if (icsubstr(hfield("default-options", mp, addone), "/receipt") || icsubstr(hfield(">to", mp, addto), "/receipt")) { snprintf(buf, sizeof (buf), "%s %s", mail, skin(nameof(mp))); if (pp = npopen(buf, "w")) { headline_t *hl; if (headline_alloc(&hl) != 0) { err(1, "could not allocate memory"); } fp = setinput(mp); readline(fp, head); if (parse_headline(head, hl) != 0) { headline_reset(hl); } if (custr_len(hl->hl_date) > 0) { fprintf(pp, "Original-Date: %s\n", custr_cstr(hl->hl_date)); } if (s = hfield("message-id", mp, addone)) fprintf(pp, "Original-Message-ID: %s\n", s); s = hfield("subject", mp, addone); fprintf(pp, "Subject: RR: %s\n", s ? s : "(none)"); npclose(pp); headline_free(hl); } } } static int icsubstr(char *s1, char *s2) { char buf[LINESIZE]; if (s1 && s2) { istrcpy(buf, sizeof (buf), s1); return substr(buf, s2) != -1; } else return 0; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1985, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Mail to others. */ static void fmt(register char *str, register FILE *fo); static FILE *infix(struct header *hp, FILE *fi); static void statusput(register struct message *mp, register FILE *obuf, int doign, int (*fp)(const char *, FILE *)); static int savemail(char name[], struct header *hp, FILE *fi); static int sendmail(char *str); static int Sendmail(char *str); static off_t textpos; /* * Send message described by the passed pointer to the * passed output buffer. Return -1 on error, but normally * the number of lines written. Adjust the status: field * if need be. If doign is set, suppress ignored header fields. * Call (*fp)(line, obuf) to print the line. */ long msend( struct message *mailp, FILE *obuf, int flag, int (*fp)(const char *, FILE *)) { register struct message *mp; long clen, n, c; FILE *ibuf; char line[LINESIZE], field[BUFSIZ]; int ishead, infld, fline, dostat, doclen, nread, unused; char *cp, *cp2; int doign = flag & M_IGNORE; int oldign = 0; /* previous line was ignored */ long lc; mp = mailp; if (mp->m_clen == 0) setclen(mp); ibuf = setinput(mp); c = mp->m_size; ishead = 1; dostat = 1; doclen = 1; infld = 0; fline = 1; lc = 0; clearerr(obuf); while (c > 0L) { nread = getaline(line, LINESIZE, ibuf, &unused); c -= nread; lc++; if (ishead) { /* * First line is the From line, so no headers * there to worry about */ if (fline) { fline = 0; goto writeit; } /* * If line is blank, we've reached end of * headers, so force out status: field * and note that we are no longer in header * fields. Also force out Content-Length: field. */ if (line[0] == '\n') { if (dostat) { statusput(mailp, obuf, doign, fp); dostat = 0; } if (doclen && !isign("content-length", flag&M_SAVING)) { snprintf(field, sizeof (field), "Content-Length: %ld\n", mp->m_clen - 1); (*fp)(field, obuf); if (ferror(obuf)) return(-1); doclen = 0; } ishead = 0; goto writeit; } /* * If this line is a continuation * of a previous header field, just echo it. */ if (isspace(line[0]) && infld) if (oldign) continue; else goto writeit; infld = 0; /* * If we are no longer looking at real * header lines, force out status: * This happens in uucp style mail where * there are no headers at all. */ if (!headerp(line)) { if (dostat) { statusput(mailp, obuf, doign, fp); dostat = 0; } (*fp)("\n", obuf); ishead = 0; goto writeit; } infld++; /* * Pick up the header field. * If it is an ignored field and * we care about such things, skip it. */ cp = line; cp2 = field; while (*cp && *cp != ':' && !isspace(*cp)) *cp2++ = *cp++; *cp2 = 0; oldign = doign && isign(field, flag&M_SAVING); if (oldign) continue; /* * If the field is "status," go compute and print the * real Status: field */ if (icequal(field, "status")) { if (dostat) { statusput(mailp, obuf, doign, fp); dostat = 0; } continue; } if (icequal(field, "content-length")) { if (doclen) { snprintf(line, sizeof (line), "Content-Length: %ld\n", mp->m_clen - 1); (*fp)(line, obuf); if (ferror(obuf)) return(-1); doclen = 0; } continue; } } writeit: if (!ishead && !mp->m_text && mp->m_clen != 0) { if (line[0] == '\n') putc('\n', obuf); clen = mp->m_clen-1; for (;;) { n = clen < sizeof line ? clen : sizeof line; if ((n = fread(line, 1, (int)n, ibuf)) <= 0) { fprintf(stderr, gettext( "\t(Unexpected end-of-file).\n")); clen = 0; } else { if (fwrite(line, 1, (int)n, obuf) != n) { fprintf(stderr, gettext( "\tError writing to the new file.\n")); fflush(obuf); if (fferror(obuf)) return (-1); } } clen -= n; if (clen <= 0) { break; } } c = 0L; } else { (*fp)(line, obuf); if (ferror(obuf)) return(-1); } } fflush(obuf); if (ferror(obuf)) return(-1); if (ishead && (mailp->m_flag & MSTATUS)) printf(gettext("failed to fix up status field\n")); return(lc); } /* * Test if the passed line is a header line, RFC 822 style. */ int headerp(register char *line) { register char *cp = line; while (*cp && *cp != ' ' && *cp != '\t' && *cp != ':') cp++; return(*cp == ':'); } /* * Output a reasonable looking status field. * But if "status" is ignored and doign, forget it. */ static void statusput( register struct message *mp, register FILE *obuf, int doign, int (*fp)(const char *, FILE *)) { char statout[12]; if (doign && isign("status", 0)) return; if ((mp->m_flag & (MNEW|MREAD)) == MNEW) return; strcpy(statout, "Status: "); if (mp->m_flag & MREAD) strcat(statout, "R"); if ((mp->m_flag & MNEW) == 0) strcat(statout, "O"); strcat(statout, "\n"); (*fp)(statout, obuf); } /* * Interface between the argument list and the mail1 routine * which does all the dirty work. */ int mail(char **people) { register char *cp2, *cp3; register int s; char *buf, **ap; struct header head; for (s = 0, ap = people; *ap; ap++) s += strlen(*ap) + 2; buf = (char *)salloc((unsigned)(s+1)); cp2 = buf; for (ap = people; *ap; ap++) { for (cp3 = *ap; *cp3; ) { if (*cp3 == ' ' || *cp3 == '\t') { *cp3++ = ','; while (*cp3 == ' ' || *cp3 == '\t') cp3++; } else cp3++; } cp2 = copy(*ap, cp2); *cp2++ = ','; *cp2++ = ' '; } *cp2 = '\0'; head.h_to = buf; head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR; head.h_others = NOSTRPTR; head.h_seq = 0; mail1(&head, Fflag, NOSTR); return(0); } int sendm(char *str) { if (value("flipm") != NOSTR) return(Sendmail(str)); else return(sendmail(str)); } int Sendm(char *str) { if (value("flipm") != NOSTR) return(sendmail(str)); else return(Sendmail(str)); } /* * Interface to the mail1 routine for the -t flag * (read headers from text). */ int tmail(void) { struct header head; head.h_to = NOSTR; head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR; head.h_others = NOSTRPTR; head.h_seq = 0; mail1(&head, Fflag, NOSTR); return(0); } /* * Send mail to a bunch of user names. The interface is through * the mail routine below. */ static int sendmail(char *str) { struct header head; if (blankline(str)) head.h_to = NOSTR; else head.h_to = addto(NOSTR, str); head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR; head.h_others = NOSTRPTR; head.h_seq = 0; mail1(&head, 0, NOSTR); return(0); } /* * Send mail to a bunch of user names. The interface is through * the mail routine below. * save a copy of the letter */ static int Sendmail(char *str) { struct header head; if (blankline(str)) head.h_to = NOSTR; else head.h_to = addto(NOSTR, str); head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR; head.h_others = NOSTRPTR; head.h_seq = 0; mail1(&head, 1, NOSTR); return(0); } /* * Walk the list of fds, closing all but one. */ static int closefd_walk(void *special_fd, int fd) { if (fd > STDERR_FILENO && fd != *(int *)special_fd) (void) close(fd); return (0); } /* * Mail a message on standard input to the people indicated * in the passed header. (Internal interface). */ void mail1(struct header *hp, int use_to, char *orig_to) { pid_t p, pid; int i, s, gotcha; char **namelist, *deliver; struct name *to, *np; FILE *mtf, *fp; int remote = rflag != NOSTR || rmail; char **t; char *deadletter; char recfile[PATHSIZE]; /* * Collect user's mail from standard input. * Get the result as mtf. */ pid = (pid_t)-1; if ((mtf = collect(hp)) == NULL) return; hp->h_seq = 1; if (hp->h_subject == NOSTR) hp->h_subject = sflag; if (fsize(mtf) == 0 && hp->h_subject == NOSTR) { printf(gettext("No message !?!\n")); goto out; } if (intty) { printf(gettext("EOT\n")); flush(); } /* * If we need to use the To: line to determine the record * file, save a copy of it before it's sorted below. */ if (use_to && orig_to == NOSTR && hp->h_to != NOSTR) orig_to = strcpy((char *)salloc(strlen(hp->h_to)+1), hp->h_to); else if (orig_to == NOSTR) orig_to = ""; /* * Now, take the user names from the combined * to and cc lists and do all the alias * processing. */ senderr = 0; to = cat(extract(hp->h_bcc, GBCC), cat(extract(hp->h_to, GTO), extract(hp->h_cc, GCC))); to = translate(outpre(elide(usermap(to)))); if (!senderr) mapf(to, myname); mechk(to); for (gotcha = 0, np = to; np != NIL; np = np->n_flink) if ((np->n_type & GDEL) == 0) gotcha++; hp->h_to = detract(to, GTO); hp->h_cc = detract(to, GCC); hp->h_bcc = detract(to, GBCC); if ((mtf = infix(hp, mtf)) == NULL) { fprintf(stderr, gettext(". . . message lost, sorry.\n")); return; } rewind(mtf); if (askme && isatty(0)) { char ans[64]; puthead(hp, stdout, GTO|GCC|GBCC, 0); printf(gettext("Send? ")); printf("[yes] "); if (fgets(ans, sizeof(ans), stdin) && ans[0] && (tolower(ans[0]) != 'y' && ans[0] != '\n')) goto dead; } if (senderr) goto dead; /* * Look through the recipient list for names with /'s * in them which we write to as files directly. */ i = outof(to, mtf); rewind(mtf); if (!gotcha && !i) { printf(gettext("No recipients specified\n")); goto dead; } if (senderr) goto dead; getrecf(orig_to, recfile, use_to, sizeof (recfile)); if (recfile != NOSTR && *recfile) savemail(safeexpand(recfile), hp, mtf); if (!gotcha) goto out; namelist = unpack(to); if (debug) { fprintf(stderr, "Recipients of message:\n"); for (t = namelist; *t != NOSTR; t++) fprintf(stderr, " \"%s\"", *t); fprintf(stderr, "\n"); return; } /* * Wait, to absorb a potential zombie, then * fork, set up the temporary mail file as standard * input for "mail" and exec with the user list we generated * far above. Return the process id to caller in case it * wants to await the completion of mail. */ #ifdef VMUNIX while (wait3((int *)0, WNOHANG, (struct rusage *)0) > 0) ; #else #ifdef preSVr4 wait((int *)0); #else while (waitpid((pid_t)-1, (int *)0, WNOHANG) > 0) ; #endif #endif rewind(mtf); pid = fork(); if (pid == (pid_t)-1) { perror("fork"); dead: deadletter = Getf("DEAD"); if (fp = fopen(deadletter, value("appenddeadletter") == NOSTR ? "w" : "a")) { chmod(deadletter, DEADPERM); puthead(hp, fp, GMASK|GCLEN, fsize(mtf) - textpos); fseek(mtf, textpos, 0); lcwrite(deadletter, mtf, fp, value("appenddeadletter") != NOSTR); fclose(fp); } else perror(deadletter); goto out; } if (pid == 0) { sigchild(); #ifdef SIGTSTP if (remote == 0) { sigset(SIGTSTP, SIG_IGN); sigset(SIGTTIN, SIG_IGN); sigset(SIGTTOU, SIG_IGN); } #endif sigset(SIGHUP, SIG_IGN); sigset(SIGINT, SIG_IGN); sigset(SIGQUIT, SIG_IGN); s = fileno(mtf); (void) fdwalk(closefd_walk, &s); close(0); dup(s); close(s); #ifdef CC submit(getpid()); #endif /* CC */ if ((deliver = value("sendmail")) == NOSTR) #ifdef SENDMAIL deliver = SENDMAIL; #else deliver = MAIL; #endif execvp(safeexpand(deliver), namelist); perror(deliver); exit(1); } if (value("sendwait")!=NOSTR) remote++; out: if (remote) { while ((p = wait(&s)) != pid && p != (pid_t)-1) ; if (s != 0) senderr++; pid = 0; } fclose(mtf); return; } /* * Prepend a header in front of the collected stuff * and return the new file. */ static FILE * infix(struct header *hp, FILE *fi) { register FILE *nfo, *nfi; register int c; char *postmark, *returnaddr; int fd = -1; rewind(fi); if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || (nfo = fdopen(fd, "w")) == NULL) { perror(tempMail); return(fi); } if ((nfi = fopen(tempMail, "r")) == NULL) { perror(tempMail); fclose(nfo); return(fi); } removefile(tempMail); postmark = value("postmark"); returnaddr = value("returnaddr"); if ((postmark != NOSTR) || (returnaddr != NOSTR)) { if (returnaddr && *returnaddr) fprintf(nfo, "From: %s", returnaddr); else fprintf(nfo, "From: %s@%s", myname, host); if (postmark && *postmark) fprintf(nfo, " (%s)", postmark); putc('\n', nfo); } puthead(hp, nfo, (GMASK & ~GBCC) | GCLEN, fsize(fi)); textpos = ftell(nfo); while ((c = getc(fi)) != EOF) putc(c, nfo); if (ferror(fi)) { perror("read"); return(fi); } fflush(nfo); if (fferror(nfo)) { perror(tempMail); fclose(nfo); fclose(nfi); return(fi); } fclose(nfo); fclose(fi); rewind(nfi); return(nfi); } /* * Dump the message header on the * passed file buffer. */ int puthead(struct header *hp, FILE *fo, int w, long clen) { register int gotcha; gotcha = 0; if (hp->h_to != NOSTR && (w & GTO)) fprintf(fo, "To: "), fmt(hp->h_to, fo), gotcha++; if ((w & GSUBJECT) && (int)value("bsdcompat")) if (hp->h_subject != NOSTR && *hp->h_subject) fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; else if (sflag && *sflag) fprintf(fo, "Subject: %s\n", sflag), gotcha++; if (hp->h_cc != NOSTR && (w & GCC)) fprintf(fo, "Cc: "), fmt(hp->h_cc, fo), gotcha++; if (hp->h_bcc != NOSTR && (w & GBCC)) fprintf(fo, "Bcc: "), fmt(hp->h_bcc, fo), gotcha++; if (hp->h_defopt != NOSTR && (w & GDEFOPT)) if (receipt_flg) fprintf(fo, "Return-Receipt-To: %s\n", hp->h_defopt), gotcha++; else fprintf(fo, "Default-Options: %s\n", hp->h_defopt), gotcha++; if ((w & GSUBJECT) && !(int)value("bsdcompat")) if (hp->h_subject != NOSTR && *hp->h_subject) fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; else if (sflag && *sflag) fprintf(fo, "Subject: %s\n", sflag), gotcha++; if (hp->h_others != NOSTRPTR && (w & GOTHER)) { char **p; for (p = hp->h_others; *p; p++) fprintf(fo, "%s\n", *p); gotcha++; } #ifndef preSVr4 if (w & GCLEN) fprintf(fo, "Content-Length: %ld\n", clen), gotcha++; #endif if (gotcha && (w & GNL)) putc('\n', fo); return(0); } /* * Format the given text to not exceed 78 characters. */ static void fmt(register char *str, register FILE *fo) { register int col = 4; char name[256]; int len; str = strcpy((char *)salloc(strlen(str)+1), str); while (str = yankword(str, name, sizeof (name), 1)) { len = strlen(name); if (col > 4) { if (col + len > 76) { fputs(",\n ", fo); col = 4; } else { fputs(", ", fo); col += 2; } } fputs(name, fo); col += len; } putc('\n', fo); } /* * Save the outgoing mail on the passed file. */ static int savemail(char name[], struct header *hp, FILE *fi) { register FILE *fo; time_t now; char *n; #ifdef preSVr4 char line[BUFSIZ]; #else int c; #endif if (debug) fprintf(stderr, gettext("save in '%s'\n"), name); if ((fo = fopen(name, "a")) == NULL) { perror(name); return(-1); } time(&now); n = rflag; if (n == NOSTR) n = myname; fprintf(fo, "From %s %s", n, ctime(&now)); puthead(hp, fo, GMASK|GCLEN, fsize(fi) - textpos); fseek(fi, textpos, 0); #ifdef preSVr4 while (fgets(line, sizeof line, fi)) { if (!strncmp(line, "From ", 5)) putc('>', fo); fputs(line, fo); } #else while ((c = getc(fi)) != EOF) putc(c, fo); #endif putc('\n', fo); fflush(fo); if (fferror(fo)) perror(name); fclose(fo); return(0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program */ /* * This code is only compiled in if SIG_HOLD is not defined! * * Retrofit new signal interface to old signal primitives. * Supported routines: * sigsys(sig, func) * sigset(sig, func) * sighold(sig) * sigrelse(sig) * sigignore(sig) * sigpause(sig) * Also, * sigchild() * to set all held signals to ignored signals in the * child process after fork(2) */ #include #ifndef SIG_HOLD # include # include # include extern int errno; typedef void (*sigtype)(); #define SIG_HOLD ((sigtype) 2) #ifndef SIG_ERR # define SIG_ERR ((sigtype) -1) #endif sigtype sigdisp(), sighold(), sigignore(); void _Sigtramp(); /* * The following helps us keep the extended signal semantics together. * We remember for each signal the address of the function we're * supposed to call. s_func is SIG_DFL / SIG_IGN if appropriate. */ static struct sigtable { sigtype s_func; /* What to call */ int s_flag; /* Signal flags; see below */ } sigtable[NSIG + 1]; /* * Signal flag values. */ #define SHELD 1 /* Signal is being held */ #define SDEFER 2 /* Signal occured while held */ #define SSET 4 /* s_func is believable */ #define SPAUSE 8 /* are pausing, waiting for sig */ jmp_buf _pause; /* For doing sigpause() */ /* * Approximate sigsys() system call * This is almost useless since one only calls sigsys() * in the child of a vfork(). If you have vfork(), you have new signals * anyway. The real sigsys() does all the stuff needed to support * the real sigset() library. We don't bother here, assuming that * you are either ignoring or defaulting a signal in the child. */ sigtype sigsys(int sig, sigtype func) { sigtype old; old = sigdisp(sig); signal(sig, func); return(old); } /* * Set the (permanent) disposition of a signal. * If the signal is subsequently (or even now) held, * the action you set here can be enabled using sigrelse(). */ sigtype sigset(int sig, sigtype func) { sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return(SIG_ERR); } old = sigdisp(sig); /* * Does anyone actually call sigset with SIG_HOLD!? */ if (func == SIG_HOLD) { sighold(sig); return(old); } sigtable[sig].s_flag |= SSET; sigtable[sig].s_func = func; if (func == SIG_DFL) { /* * If signal has been held, must retain * the catch so that we can note occurrance * of signal. */ if ((sigtable[sig].s_flag & SHELD) == 0) signal(sig, SIG_DFL); else signal(sig, _Sigtramp); return(old); } if (func == SIG_IGN) { /* * Clear pending signal */ signal(sig, SIG_IGN); sigtable[sig].s_flag &= ~SDEFER; return(old); } signal(sig, _Sigtramp); return(old); } /* * Hold a signal. * This CAN be tricky if the signal's disposition is SIG_DFL. * In that case, we still catch the signal so we can note it */ sigtype sighold(int sig) { sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return(SIG_ERR); } old = sigdisp(sig); if (sigtable[sig].s_flag & SHELD) return(old); /* * When the default action is required, we have to * set up to catch the signal to note signal's occurrance. */ if (old == SIG_DFL) { sigtable[sig].s_flag |= SSET; signal(sig, _Sigtramp); } sigtable[sig].s_flag |= SHELD; return(old); } /* * Release a signal * If the signal occurred while we had it held, cause the signal. */ sigtype sigrelse(int sig) { sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return(SIG_ERR); } old = sigdisp(sig); if ((sigtable[sig].s_flag & SHELD) == 0) return(old); sigtable[sig].s_flag &= ~SHELD; if (sigtable[sig].s_flag & SDEFER) _Sigtramp(sig); /* * If disposition was the default, then we can unset the * catch to _Sigtramp() and let the system do the work. */ if (sigtable[sig].s_func == SIG_DFL) signal(sig, SIG_DFL); return(old); } /* * Ignore a signal. */ sigtype sigignore(int sig) { return(sigset(sig, SIG_IGN)); } /* * Pause, waiting for sig to occur. * We assume LUSER called us with the signal held. * When we got the signal, mark the signal as having * occurred. It will actually cause something when * the signal is released. */ int sigpause(int sig) { if (sig < 1 || sig > NSIG) { errno = EINVAL; return; } sigtable[sig].s_flag |= SHELD|SPAUSE; if (setjmp(_pause) == 0) pause(); sigtable[sig].s_flag &= ~SPAUSE; sigtable[sig].s_flag |= SDEFER; } /* * In the child process after fork(2), set the disposition of all held * signals to SIG_IGN. This is a new procedure not in the real sigset() * package, provided for retrofitting purposes. */ int sigchild(void) { register int i; for (i = 1; i <= NSIG; i++) if (sigtable[i].s_flag & SHELD) signal(i, SIG_IGN); } /* * Return the current disposition of a signal * If we have not set this signal before, we have to * ask the system */ sigtype sigdisp(int sig) { sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return(SIG_ERR); } /* * If we have no knowledge of this signal, * ask the system, then save the result for later. */ if ((sigtable[sig].s_flag & SSET) == 0) { old = signal(sig, SIG_IGN); sigtable[sig].s_func = old; sigtable[sig].s_flag |= SSET; signal(sig, old); return(old); } /* * If we have set this signal before, then sigset() * will have been careful to leave something meaningful * in s_func. */ return(sigtable[sig].s_func); } /* * The following routine gets called for any signal * that is to be trapped to a user function. */ void _Sigtramp(int sig) { sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return; } top: old = signal(sig, SIG_IGN); /* * If signal being paused on, wakeup sigpause() */ if (sigtable[sig].s_flag & SPAUSE) longjmp(_pause, 1); /* * If signal is being held, mark its table entry * so we can trigger it when signal is released. * Then just return. */ if (sigtable[sig].s_flag & SHELD) { sigtable[sig].s_flag |= SDEFER; signal(sig, _Sigtramp); return; } /* * If the signal is being ignored, just return. * This would make SIGCONT more normal, but of course * any system with SIGCONT also has the new signal pkg, so... */ if (sigtable[sig].s_func == SIG_IGN) return; /* * If the signal is SIG_DFL, then we probably got here * by holding the signal, having it happen, then releasing * the signal. */ if (sigtable[sig].s_func == SIG_DFL) { signal(sig, SIG_DFL); kill(getpid(), sig); /* Will we get back here? */ return; } /* * Looks like we should just cause the signal... * We hold the signal for the duration of the user's * code with the signal re-enabled. If the signal * happens again while in user code, we will recursively * trap here and mark that we had another occurance * and return to the user's trap code. When we return * from there, we can cause the signal again. */ sigtable[sig].s_flag &= ~SDEFER; sigtable[sig].s_flag |= SHELD; signal(sig, _Sigtramp); (*sigtable[sig].s_func)(sig); /* * If the signal re-occurred while in the user's routine, * just go try it again... */ sigtable[sig].s_flag &= ~SHELD; if (sigtable[sig].s_flag & SDEFER) goto top; } #endif /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1997 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Memory allocation routines. * Memory handed out here are reclaimed at the top of the command * loop each time, so they need not be freed. */ #include "rcv.h" #include static void *lastptr; /* addr of last buffer allocated */ static struct strings *lastsp; /* last string space allocated from */ /* * Allocate size more bytes of space and return the address of the * first byte to the caller. An even number of bytes are always * allocated so that the space will always be on a word boundary. * The string spaces are of exponentially increasing size, to satisfy * the occasional user with enormous string size requests. */ void * salloc(unsigned size) { register char *t; register unsigned s; register struct strings *sp; int index; s = size; #if defined(u3b) || defined(sparc) s += 3; /* needs alignment on quad boundary */ s &= ~03; #elif defined(i386) || defined(__amd64) s++; s &= ~01; #else #error Unknown architecture! #endif index = 0; for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) { if (sp->s_topFree == NOSTR && (STRINGSIZE << index) >= s) break; if (sp->s_nleft >= s) break; index++; } if (sp >= &stringdope[NSPACE]) panic("String too large"); if (sp->s_topFree == NOSTR) { index = sp - &stringdope[0]; sp->s_topFree = (char *) calloc(STRINGSIZE << index, (unsigned) 1); if (sp->s_topFree == NOSTR) { fprintf(stderr, gettext("No room for space %d\n"), index); panic("Internal error"); } sp->s_nextFree = sp->s_topFree; sp->s_nleft = STRINGSIZE << index; } sp->s_nleft -= s; t = sp->s_nextFree; sp->s_nextFree += s; lastptr = t; lastsp = sp; return(t); } /* * Reallocate size bytes of space and return the address of the * first byte to the caller. The old data is copied into the new area. */ void * srealloc(void *optr, unsigned size) { void *nptr; /* if we just want to expand the last allocation, that's easy */ if (optr == lastptr) { register unsigned s, delta; register struct strings *sp = lastsp; s = size; #if defined(u3b) || defined(sparc) s += 3; /* needs alignment on quad boundary */ s &= ~03; #elif defined(i386) || defined(__amd64) s++; s &= ~01; #else #error Unknown architecture! #endif /* defined(u3b) || defined(sparc) */ delta = s - (sp->s_nextFree - (char *)optr); if (delta <= sp->s_nleft) { sp->s_nextFree += delta; sp->s_nleft -= delta; return (optr); } } nptr = salloc(size); if (nptr) memcpy(nptr, optr, size); /* XXX - copying too much */ return nptr; } /* * Reset the string area to be empty. * Called to free all strings allocated * since last reset. */ void sreset(void) { register struct strings *sp; register int index; if (noreset) return; minit(); index = 0; for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) { if (sp->s_topFree == NOSTR) continue; sp->s_nextFree = sp->s_topFree; sp->s_nleft = STRINGSIZE << index; index++; } lastptr = NULL; lastsp = NULL; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1985-2001 by Sun Microsystems, Inc. * All rights reserved. */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #include #include #ifdef preSVr4 extern struct passwd *getpwnam(); extern struct passwd *getpwuid(); #endif /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Give names to all the temporary files that we will need. */ void tinit(void) { register pid_t pid = mypid; struct passwd *pwd; snprintf(tempMail, TMPSIZ, "/tmp/Rs%-ld", pid); snprintf(tempQuit, TMPSIZ, "/tmp/Rm%-ld", pid); snprintf(tempEdit, TMPSIZ, "/tmp/Re%-ld", pid); snprintf(tempMesg, TMPSIZ, "/tmp/Rx%-ld", pid); snprintf(tempZedit, TMPSIZ, "/tmp/Rz%-ld", pid); /* get the name associated with this uid */ pwd = getpwuid(uid = myruid); if (!pwd) { printf(gettext("Error looking up username for uid=%d\n"), uid); exit(1); } else copy(pwd->pw_name, myname); endpwent(); nstrcpy(homedir, PATHSIZE, Getf("HOME")); findmail(NULL); assign("MBOX", Getf("MBOX")); assign("MAILRC", Getf("MAILRC")); assign("DEAD", Getf("DEAD")); assign("save", ""); assign("asksub", ""); assign("header", ""); assign("prompt", "? "); assign("pipeignore", ""); assign("replyall", ""); assign("from", ""); assign("fcc", ""); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" struct name * translate(struct name *np) { struct name *n, *t, *x; void (*sigint)(int), (*sigquit)(int); char *xl = value("translate"); char line[LINESIZE]; char postmark[256]; char *cmd; FILE *pp; int i; if (!xl) return np; askme = 0; postmark[0] = 0; i = strlen(xl) + 1; for (n = np; n; n = n->n_flink) if (! (n->n_type & GDEL)) i += strlen(n->n_name) + 3; cmd = (char *)salloc((unsigned)i); strcpy(cmd, xl); for (n = np; n; n = n->n_flink) if (! (n->n_type & GDEL)) { strcat(cmd, " \""); strcat(cmd, n->n_name); strcat(cmd, "\""); } if ((pp = npopen(cmd, "r")) == NULL) { perror(xl); senderr++; return np; } sigint = sigset(SIGINT, SIG_IGN); sigquit = sigset(SIGQUIT, SIG_IGN); fgets(postmark, sizeof postmark, pp); if (postmark[0]) { postmark[strlen(postmark)-1] = 0; assign("postmark", postmark); } for (n = np; n; n = n->n_flink) { if (n->n_type & GDEL) continue; if (fgets(line, sizeof line, pp) == NULL) break; line[strlen(line)-1] = 0; if (!strcmp(line, n->n_name)) continue; x = extract(line, n->n_type); n->n_type |= GDEL; n->n_name = ""; if (x && !x->n_flink && strpbrk(n->n_full, "(<")) x->n_full = splice(x->n_name, n->n_full); if (x) { t = tailof(x); cat(t, n->n_flink); n->n_flink = NULL; cat(n, x); n = t; } } if (getc(pp) == 'y') askme++; if (npclose(pp) != 0 || n) senderr++; sigset(SIGINT, sigint); sigset(SIGQUIT, sigquit); return np; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Generally useful tty stuff. */ #include "rcv.h" #include #ifdef USG_TTY static char *readtty(char pr[], char src[]); static int savetty(void); static void ttycont(int); static int c_erase; /* Current erase char */ static int c_kill; /* Current kill char */ static int c_intr; /* interrupt char */ static int c_quit; /* quit character */ static struct termio savtty; static char canonb[LINESIZE]; /* canonical buffer for input */ /* processing */ #ifndef TIOCSTI static void Echo(int cc); static int countcol(void); static void outstr(register char *s); static void resetty(void); static void rubout(register char *cp); static int setty(void); static int c_word; /* Current word erase char */ static int Col; /* current output column */ static int Pcol; /* end column of prompt string */ static int Out; /* file descriptor of stdout */ static int erasing; /* we are erasing characters */ static struct termio ttybuf; #else static jmp_buf rewrite; /* Place to go when continued */ #endif #ifdef SIGCONT # ifdef preSVr4 typedef int sig_atomic_t; # endif static sig_atomic_t hadcont; /* Saw continue signal */ /*ARGSUSED*/ static void #ifdef __cplusplus ttycont(int) #else /* ARGSUSED */ ttycont(int s) #endif { hadcont++; longjmp(rewrite, 1); } #ifndef TIOCSTI /*ARGSUSED*/ static void ttystop(int s) { resetty(); kill(mypid, SIGSTOP); } #endif #endif /* * Read all relevant header fields. */ int grabh(register struct header *hp, int gflags, int subjtop) { #ifdef SIGCONT void (*savecont)(int); #ifndef TIOCSTI void (*savestop)(int); #endif #endif if (savetty()) return -1; #ifdef SIGCONT savecont = sigset(SIGCONT, ttycont); #ifndef TIOCSTI savestop = sigset(SIGTSTP, ttystop); #endif #endif if (gflags & GTO) { hp->h_to = addto(NOSTR, readtty("To: ", hp->h_to)); if (hp->h_to != NOSTR) hp->h_seq++; } if (gflags & GSUBJECT && subjtop) { hp->h_subject = readtty("Subject: ", hp->h_subject); if (hp->h_subject != NOSTR) hp->h_seq++; } if (gflags & GCC) { hp->h_cc = addto(NOSTR, readtty("Cc: ", hp->h_cc)); if (hp->h_cc != NOSTR) hp->h_seq++; } if (gflags & GBCC) { hp->h_bcc = addto(NOSTR, readtty("Bcc: ", hp->h_bcc)); if (hp->h_bcc != NOSTR) hp->h_seq++; } if (gflags & GSUBJECT && !subjtop) { hp->h_subject = readtty("Subject: ", hp->h_subject); if (hp->h_subject != NOSTR) hp->h_seq++; } #ifdef SIGCONT (void) sigset(SIGCONT, savecont); #ifndef TIOCSTI (void) sigset(SIGTSTP, savestop); #endif #endif return(0); } /* * Read up a header from standard input. * The source string has the preliminary contents to * be read. * */ static char * readtty(char pr[], char src[]) { int c; register char *cp; #ifndef TIOCSTI register char *cp2; erasing = 0; Col = 0; outstr(pr); Pcol = Col; #else fputs(pr, stdout); #endif fflush(stdout); if (src != NOSTR && (int)strlen(src) > LINESIZE - 2) { printf(gettext("too long to edit\n")); return(src); } #ifndef TIOCSTI if (setty()) return(src); cp2 = src==NOSTR ? "" : src; for (cp=canonb; *cp2; cp++, cp2++) *cp = *cp2; *cp = '\0'; outstr(canonb); #else cp = src == NOSTR ? "" : src; while (c = *cp++) { char ch; if (c == c_erase || c == c_kill) { ch = '\\'; ioctl(0, TIOCSTI, &ch); } ch = c; ioctl(0, TIOCSTI, &ch); } cp = canonb; *cp = 0; if (setjmp(rewrite)) goto redo; #endif for (;;) { fflush(stdout); #ifdef SIGCONT hadcont = 0; #endif c = getc(stdin); #ifndef TIOCSTI if (c==c_erase) { if (cp > canonb) if (cp[-1]=='\\' && !erasing) { *cp++ = (char)c; Echo(c); } else { rubout(--cp); } } else if (c==c_kill) { if (cp > canonb && cp[-1]=='\\') { *cp++ = (char)c; Echo(c); } else while (cp > canonb) { rubout(--cp); } } else if (c==c_word) { if (cp > canonb) if (cp[-1]=='\\' && !erasing) { *cp++ = (char)c; Echo(c); } else { while (--cp >= canonb) if (!isspace(*cp)) break; else rubout(cp); while (cp >= canonb) if (!isspace(*cp)) rubout(cp--); else break; if (cp < canonb) cp = canonb; else if (*cp) cp++; } } else #endif if (c==EOF || ferror(stdin) || c==c_intr || c==c_quit) { #ifdef SIGCONT if (hadcont) { #ifndef TIOCSTI (void) setty(); outstr("(continue)\n"); Col = 0; outstr(pr); *cp = '\0'; outstr(canonb); clearerr(stdin); continue; #else redo: hadcont = 0; cp = canonb[0] != 0 ? canonb : src; clearerr(stdin); return(readtty(pr, cp)); #endif } #endif #ifndef TIOCSTI resetty(); #endif savedead(c==c_quit? SIGQUIT: SIGINT); } else switch (c) { case '\n': case '\r': #ifndef TIOCSTI resetty(); putchar('\n'); fflush(stdout); #endif if (canonb[0]=='\0') return(NOSTR); return(savestr(canonb)); default: *cp++ = (char)c; *cp = '\0'; #ifndef TIOCSTI erasing = 0; Echo(c); #endif } } } static int savetty(void) { if (ioctl(fileno(stdout), TCGETA, &savtty) < 0) { perror("ioctl"); return(-1); } c_erase = savtty.c_cc[VERASE]; c_kill = savtty.c_cc[VKILL]; c_intr = savtty.c_cc[VINTR]; c_quit = savtty.c_cc[VQUIT]; #ifndef TIOCSTI c_word = 'W' & 037; /* erase word character */ Out = fileno(stdout); ttybuf = savtty; #ifdef u370 ttybuf.c_cflag &= ~PARENB; /* disable parity */ ttybuf.c_cflag |= CS8; /* character size = 8 */ #endif /* u370 */ ttybuf.c_cc[VTIME] = 0; ttybuf.c_cc[VMIN] = 1; ttybuf.c_iflag &= ~(BRKINT); ttybuf.c_lflag &= ~(ICANON|ISIG|ECHO); #endif return 0; } #ifndef TIOCSTI static int setty(void) { if (ioctl(Out, TCSETAW, &ttybuf) < 0) { perror("ioctl"); return(-1); } return(0); } static void resetty(void) { if (ioctl(Out, TCSETAW, &savtty) < 0) perror("ioctl"); } static void outstr(register char *s) { while (*s) Echo(*s++); } static void rubout(register char *cp) { register int oldcol; register int c = *cp; erasing = 1; *cp = '\0'; switch (c) { case '\t': oldcol = countcol(); do putchar('\b'); while (--Col > oldcol); break; case '\b': if (isprint(cp[-1])) putchar(*(cp-1)); else putchar(' '); Col++; break; default: if (isprint(c)) { fputs("\b \b", stdout); Col--; } } } static int countcol(void) { register int col; register char *s; for (col=Pcol, s=canonb; *s; s++) switch (*s) { case '\t': while (++col % 8) ; break; case '\b': col--; break; default: if (isprint(*s)) col++; } return(col); } static void Echo(int cc) { char c = (char)cc; switch (c) { case '\t': do putchar(' '); while (++Col % 8); break; case '\b': if (Col > 0) { putchar('\b'); Col--; } break; case '\r': case '\n': Col = 0; fputs("\r\n", stdout); break; default: if (isprint(c)) { Col++; putchar(c); } } } #endif #else #ifdef SIGCONT static void signull(int); #endif static int c_erase; /* Current erase char */ static int c_kill; /* Current kill char */ static int hadcont; /* Saw continue signal */ static jmp_buf rewrite; /* Place to go when continued */ #ifndef TIOCSTI static int ttyset; /* We must now do erase/kill */ #endif /* * Read all relevant header fields. */ int grabh(struct header *hp, int gflags, int subjtop) { struct sgttyb ttybuf; void (*savecont)(int); register int s; int errs; #ifndef TIOCSTI void (*savesigs[2])(int); #endif #ifdef SIGCONT savecont = sigset(SIGCONT, signull); #endif errs = 0; #ifndef TIOCSTI ttyset = 0; #endif if (gtty(fileno(stdin), &ttybuf) < 0) { perror("gtty"); return(-1); } c_erase = ttybuf.sg_erase; c_kill = ttybuf.sg_kill; #ifndef TIOCSTI ttybuf.sg_erase = 0; ttybuf.sg_kill = 0; for (s = SIGINT; s <= SIGQUIT; s++) if ((savesigs[s-SIGINT] = sigset(s, SIG_IGN)) == SIG_DFL) sigset(s, SIG_DFL); #endif if (gflags & GTO) { #ifndef TIOCSTI if (!ttyset && hp->h_to != NOSTR) ttyset++, stty(fileno(stdin), &ttybuf); #endif hp->h_to = addto(NOSTR, readtty("To: ", hp->h_to)); if (hp->h_to != NOSTR) hp->h_seq++; } if (gflags & GSUBJECT && subjtop) { #ifndef TIOCSTI if (!ttyset && hp->h_subject != NOSTR) ttyset++, stty(fileno(stdin), &ttybuf); #endif hp->h_subject = readtty("Subject: ", hp->h_subject); if (hp->h_subject != NOSTR) hp->h_seq++; } if (gflags & GCC) { #ifndef TIOCSTI if (!ttyset && hp->h_cc != NOSTR) ttyset++, stty(fileno(stdin), &ttybuf); #endif hp->h_cc = addto(NOSTR, readtty("Cc: ", hp->h_cc)); if (hp->h_cc != NOSTR) hp->h_seq++; } if (gflags & GBCC) { #ifndef TIOCSTI if (!ttyset && hp->h_bcc != NOSTR) ttyset++, stty(fileno(stdin), &ttybuf); #endif hp->h_bcc = addto(NOSTR, readtty("Bcc: ", hp->h_bcc)); if (hp->h_bcc != NOSTR) hp->h_seq++; } if (gflags & GSUBJECT && !subjtop) { #ifndef TIOCSTI if (!ttyset && hp->h_subject != NOSTR) ttyset++, stty(fileno(stdin), &ttybuf); #endif hp->h_subject = readtty("Subject: ", hp->h_subject); if (hp->h_subject != NOSTR) hp->h_seq++; } #ifdef SIGCONT sigset(SIGCONT, savecont); #endif #ifndef TIOCSTI ttybuf.sg_erase = c_erase; ttybuf.sg_kill = c_kill; if (ttyset) stty(fileno(stdin), &ttybuf); for (s = SIGINT; s <= SIGQUIT; s++) sigset(s, savesigs[s-SIGINT]); #endif return(errs); } /* * Read up a header from standard input. * The source string has the preliminary contents to * be read. * */ char * readtty(char pr[], char src[]) { char ch, canonb[LINESIZE]; int c; register char *cp, *cp2; fputs(pr, stdout); fflush(stdout); if (src != NOSTR && strlen(src) > LINESIZE - 2) { printf(gettext("too long to edit\n")); return(src); } #ifndef TIOCSTI if (src != NOSTR) cp = copy(src, canonb); else cp = copy("", canonb); fputs(canonb, stdout); fflush(stdout); #else cp = src == NOSTR ? "" : src; while (c = *cp++) { if (c == c_erase || c == c_kill) { ch = '\\'; ioctl(0, TIOCSTI, &ch); } ch = c; ioctl(0, TIOCSTI, &ch); } cp = canonb; *cp = 0; #endif cp2 = cp; while (cp2 < canonb + LINESIZE) *cp2++ = 0; cp2 = cp; if (setjmp(rewrite)) goto redo; #ifdef SIGCONT sigset(SIGCONT, ttycont); #endif clearerr(stdin); while (cp2 < canonb + LINESIZE) { c = getc(stdin); if (c == EOF || c == '\n') break; *cp2++ = c; } *cp2 = 0; #ifdef SIGCONT sigset(SIGCONT, signull); #endif if (c == EOF && ferror(stdin) && hadcont) { redo: hadcont = 0; cp = strlen(canonb) > 0 ? canonb : NOSTR; clearerr(stdin); return(readtty(pr, cp)); } clearerr(stdin); #ifndef TIOCSTI if (cp == NOSTR || *cp == '\0') return(src); cp2 = cp; if (!ttyset) return(strlen(canonb) > 0 ? savestr(canonb) : NOSTR); while (*cp != '\0') { c = *cp++; if (c == c_erase) { if (cp2 == canonb) continue; if (cp2[-1] == '\\') { cp2[-1] = c; continue; } cp2--; continue; } if (c == c_kill) { if (cp2 == canonb) continue; if (cp2[-1] == '\\') { cp2[-1] = c; continue; } cp2 = canonb; continue; } *cp2++ = c; } *cp2 = '\0'; #endif if (equal("", canonb)) return(NOSTR); return(savestr(canonb)); } #ifdef SIGCONT /* * Receipt continuation. */ /*ARGSUSED*/ void ttycont(int) { hadcont++; longjmp(rewrite, 1); } /* * Null routine to allow us to hold SIGCONT */ /*ARGSUSED*/ static void signull(int) {} #endif #endif /* USG_TTY */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1998 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Local routines that are installation dependent. */ #include "rcv.h" static int ismailbox(char *file); /* * Locate the user's mailbox file (ie, the place where new, unread * mail is queued). In SVr4 UNIX, it is in /var/mail/name. * In preSVr4 UNIX, it is in either /usr/mail/name or /usr/spool/mail/name. */ void findmail(char *name) { register char *cp; if (name != NOSTR) { copy(name, copy(maildir, mailname)); issysmbox = 1; /* it's a system mailbox */ } else if ((cp = getenv("MAIL")) != NULL) { /* if $MAIL is set, use it */ nstrcpy(mailname, PATHSIZE, cp); issysmbox = ismailbox(mailname); /* XXX - should warn that there's no locking? */ } else { copy(myname, copy(maildir, mailname)); issysmbox = 1; } if (issysmbox) lockname = strrchr(mailname, '/') + 1; } /* * Make sure file matches (/usr|/var)(/spool)?/mail/. * If is does, it's a "system mailbox", return true. */ static int ismailbox(char *file) { #ifdef preSVr4 return (strncmp(file, maildir, strlen(maildir)) == 0); #else if (strncmp(file, "/var", 4) != 0 && strncmp(file, "/usr", 4) != 0 ) return (0); file += 4; if (strncmp(file, "/spool", 6) == 0) file += 6; return (strncmp(file, "/mail/", 6) == 0); #endif } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 1985-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Auxiliary functions. */ static char *phrase(char *name, int token, int comma); static char *ripoff(register char *buf); /* * Return a pointer to a dynamic copy of the argument. */ char * savestr(char *str) { register char *cp, *cp2, *top; for (cp = str; *cp; cp++) ; top = (char *)salloc((unsigned)(cp-str + 1)); if (top == NOSTR) return(NOSTR); for (cp = str, cp2 = top; *cp; cp++) *cp2++ = *cp; *cp2 = 0; return(top); } /* * Announce a fatal error and die. */ void panic(char *str) { fprintf(stderr, gettext("mailx: Panic - %s\n"), str); exit(1); /* NOTREACHED */ } /* * Touch the named message by setting its MTOUCH flag. * Touched messages have the effect of not being sent * back to the system mailbox on exit. */ void touch(int mesg) { register struct message *mp; if (mesg < 1 || mesg > msgCount) return; mp = &message[mesg-1]; mp->m_flag |= MTOUCH; if ((mp->m_flag & MREAD) == 0) mp->m_flag |= MREAD|MSTATUS; } /* * Test to see if the passed file name is a directory. * Return true if it is. */ int isdir(char name[]) { struct stat sbuf; if (stat(name, &sbuf) < 0) return(0); return((sbuf.st_mode & S_IFMT) == S_IFDIR); } /* * Count the number of arguments in the given string raw list. */ int argcount(char **argv) { register char **ap; for (ap = argv; *ap != NOSTR; ap++) ; return(ap-argv); } /* * Return the desired header line from the passed message * pointer (or NOSTR if the desired header field is not available). * Read all the header lines and concatenate multiple instances of * the requested header. */ char * hfield(char field[], struct message *mp, char *(*add)(char *, char *)) { register FILE *ibuf; char linebuf[LINESIZE]; register long lc; char *r = NOSTR; ibuf = setinput(mp); if ((lc = mp->m_lines) <= 0) return(NOSTR); if (readline(ibuf, linebuf) < 0) return(NOSTR); lc--; while ((lc = gethfield(ibuf, linebuf, lc)) >= 0) if (ishfield(linebuf, field)) r = (*add)(r, hcontents(linebuf)); return r; } /* * Return the next header field found in the given message. * Return > 0 if something found, <= 0 elsewise. * Must deal with \ continuations & other such fraud. */ int gethfield( register FILE *f, char linebuf[], register long rem) { char line2[LINESIZE]; register char *cp, *cp2; register int c; for (;;) { if (rem <= 0) return(-1); if (readline(f, linebuf) < 0) return(-1); rem--; if (strlen(linebuf) == 0) return(-1); if (isspace(linebuf[0])) continue; if (!headerp(linebuf)) return(-1); /* * I guess we got a headline. * Handle wraparounding */ for (;;) { if (rem <= 0) break; c = getc(f); ungetc(c, f); if (!isspace(c) || c == '\n') break; if (readline(f, line2) < 0) break; rem--; cp2 = line2; for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) ; if (strlen(linebuf) + strlen(cp2) >= (unsigned)LINESIZE-2) break; cp = &linebuf[strlen(linebuf)]; while (cp > linebuf && (isspace(cp[-1]) || cp[-1] == '\\')) cp--; *cp++ = ' '; for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) ; nstrcpy(cp, LINESIZE - (cp - linebuf), cp2); } if ((c = strlen(linebuf)) > 0) { cp = &linebuf[c-1]; while (cp > linebuf && isspace(*cp)) cp--; *++cp = 0; } return(rem); } /* NOTREACHED */ } /* * Check whether the passed line is a header line of * the desired breed. */ int ishfield(char linebuf[], char field[]) { register char *cp; if ((cp = strchr(linebuf, ':')) == NOSTR) return(0); if (cp == linebuf) return(0); *cp = 0; if (icequal(linebuf, field)) { *cp = ':'; return(1); } *cp = ':'; return(0); } /* * Extract the non label information from the given header field * and return it. */ char * hcontents(char hfield[]) { register char *cp; if ((cp = strchr(hfield, ':')) == NOSTR) return(NOSTR); cp++; while (*cp && isspace(*cp)) cp++; return(cp); } /* * Compare two strings, ignoring case. */ int icequal(register char *s1, register char *s2) { while (toupper(*s1++) == toupper(*s2)) if (*s2++ == 0) return(1); return(0); } /* * Copy a string, lowercasing it as we go. Here dstsize is the size of * the destination buffer dst. */ void istrcpy(char *dst, int dstsize, char *src) { register char *cp, *cp2; cp2 = dst; cp = src; while (--dstsize > 0 && *cp != '\0') *cp2++ = tolower(*cp++); *cp2 = '\0'; } /* * The following code deals with input stacking to do source * commands. All but the current file pointer are saved on * the stack. */ static int ssp = -1; /* Top of file stack */ static struct sstack { FILE *s_file; /* File we were in. */ int s_cond; /* Saved state of conditionals */ int s_loading; /* Loading .mailrc, etc. */ } *sstack; /* * Pushdown current input file and switch to a new one. * Set the global flag "sourcing" so that others will realize * that they are no longer reading from a tty (in all probability). */ int source(char name[]) { register FILE *fi; register char *cp; if ((cp = expand(name)) == NOSTR) return(1); if ((fi = fopen(cp, "r")) == NULL) { printf(gettext("Unable to open %s\n"), cp); return(1); } if (!maxfiles) { if ((maxfiles = (int)ulimit(4, 0)) < 0) #ifndef _NFILE # define _NFILE 20 #endif maxfiles = _NFILE; sstack = (struct sstack *)calloc(maxfiles, sizeof(struct sstack)); if (sstack == NULL) { printf(gettext( "Couldn't allocate memory for sourcing stack\n")); fclose(fi); return(1); } } sstack[++ssp].s_file = input; sstack[ssp].s_cond = cond; sstack[ssp].s_loading = loading; loading = 0; cond = CANY; input = fi; sourcing++; return(0); } /* * Pop the current input back to the previous level. * Update the "sourcing" flag as appropriate. */ int unstack(void) { if (ssp < 0) { printf(gettext("\"Source\" stack over-pop.\n")); sourcing = 0; return(1); } fclose(input); if (cond != CANY) printf(gettext("Unmatched \"if\"\n")); cond = sstack[ssp].s_cond; loading = sstack[ssp].s_loading; input = sstack[ssp--].s_file; if (ssp < 0) sourcing = loading; return(0); } /* * Touch the indicated file. * This is nifty for the shell. * If we have the utime() system call, this is better served * by using that, since it will work for empty files. * On non-utime systems, we must sleep a second, then read. */ void alter(char name[]) { int rc = utime(name, utimep); extern int errno; if (rc != 0) { fprintf(stderr, gettext("Cannot utime %s in aux:alter\n"), name); fprintf(stderr, gettext("Errno: %d\n"), errno); } } /* * Examine the passed line buffer and * return true if it is all blanks and tabs. */ int blankline(const char linebuf[]) { register const char *cp; for (cp = linebuf; *cp; cp++) if (!any(*cp, " \t")) return(0); return(1); } /* * Skin an arpa net address according to the RFC 822 interpretation * of "host-phrase." */ static char * phrase(char *name, int token, int comma) { register char c; register char *cp, *cp2; char *bufend, *nbufp; int gotlt, lastsp, didq; char nbuf[LINESIZE]; int nesting; if (name == NOSTR) return(NOSTR); if (strlen(name) >= (unsigned)LINESIZE) nbufp = (char *)salloc(strlen(name)); else nbufp = nbuf; gotlt = 0; lastsp = 0; bufend = nbufp; for (cp = name, cp2 = bufend; (c = *cp++) != 0;) { switch (c) { case '(': /* Start of a comment, ignore it. */ nesting = 1; while ((c = *cp) != 0) { cp++; switch(c) { case '\\': if (*cp == 0) goto outcm; cp++; break; case '(': nesting++; break; case ')': --nesting; break; } if (nesting <= 0) break; } outcm: lastsp = 0; break; case '"': /* Start a quoted string. Copy it in its entirety. */ didq = 0; while ((c = *cp) != 0) { cp++; switch (c) { case '\\': if ((c = *cp) == 0) goto outqs; cp++; break; case '"': goto outqs; } if (gotlt == 0 || gotlt == '<') { if (lastsp) { lastsp = 0; *cp2++ = ' '; } if (!didq) { *cp2++ = '"'; didq++; } *cp2++ = c; } } outqs: if (didq) *cp2++ = '"'; lastsp = 0; break; case ' ': case '\t': case '\n': if (token && (!comma || c == '\n')) { done: cp[-1] = 0; return cp; } lastsp = 1; break; case ',': *cp2++ = c; if (gotlt != '<') { if (token) goto done; bufend = cp2; gotlt = 0; } break; case '<': cp2 = bufend; gotlt = c; lastsp = 0; break; case '>': if (gotlt == '<') { gotlt = c; break; } /* FALLTHROUGH . . . */ default: if (gotlt == 0 || gotlt == '<') { if (lastsp) { lastsp = 0; *cp2++ = ' '; } *cp2++ = c; } break; } } *cp2 = 0; return (token ? --cp : equal(name, nbufp) ? name : nbufp == nbuf ? savestr(nbuf) : nbufp); } char * skin(char *name) { return phrase(name, 0, 0); } /* * Here sz is the buffer size of word. */ char * yankword(char *name, char *word, int sz, int comma) { char *cp; if (name == 0) return 0; while (isspace(*name)) name++; if (*name == 0) return 0; cp = phrase(name, 1, comma); nstrcpy(word, sz, name); return cp; } int docomma(char *s) { return s && strpbrk(s, "(<,"); } /* * Fetch the sender's name from the passed message. */ char * nameof(register struct message *mp) { char namebuf[LINESIZE]; char linebuf[LINESIZE]; register char *cp, *cp2; register FILE *ibuf; int first = 1, wint = 0; char *tmp; if (value("from") && (cp = hfield("from", mp, addto)) != NOSTR) return ripoff(cp); ibuf = setinput(mp); copy("", namebuf); if (readline(ibuf, linebuf) <= 0) return(savestr(namebuf)); newname: for (cp = linebuf; *cp != ' '; cp++) ; while (any(*cp, " \t")) cp++; for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") && cp2-namebuf < LINESIZE-1; *cp2++ = *cp++) ; *cp2 = '\0'; for (;;) { if (readline(ibuf, linebuf) <= 0) break; if (substr(linebuf,"forwarded by ") != -1) continue; if (linebuf[0] == 'F') cp = linebuf; else if (linebuf[0] == '>') cp = linebuf + 1; else break; if (strncmp(cp, "From ", 5) != 0) break; if ((wint = substr(cp, "remote from ")) != -1) { cp += wint + 12; if (first) { copy(cp, namebuf); first = 0; } else { tmp = strrchr(namebuf, '!') + 1; nstrcpy(tmp, sizeof (namebuf) - (tmp - namebuf), cp); } nstrcat(namebuf, sizeof (namebuf), "!"); goto newname; } else break; } for (cp = namebuf; *cp == '!'; cp++); while (ishost(host, cp)) cp = strchr(cp, '!') + 1; if (value("mustbang") && !strchr(cp, '!')) { snprintf(linebuf, sizeof (linebuf), "%s!%s", host, cp); cp = linebuf; } if (cp2 = hfield("from", mp, addto)) return(splice(cp, cp2)); else return(savestr(cp)); } /* * Splice an address into a commented recipient header. */ char * splice(char *addr, char *hdr) { char buf[LINESIZE]; char *cp, *cp2; if (cp = strchr(hdr, '<')) { cp2 = strchr(cp, '>'); if (cp2 == NULL) { nstrcpy(buf, sizeof (buf), addr); } else { snprintf(buf, sizeof (buf), "%.*s%s%s", cp - hdr + 1, hdr, addr, cp2); } } else if (cp = strchr(hdr, '(')) { snprintf(buf, sizeof (buf), "%s %s", addr, cp); } else nstrcpy(buf, sizeof (buf), addr); return savestr(ripoff(buf)); } static char * ripoff(register char *buf) { register char *cp; cp = buf + strlen(buf); while (--cp >= buf && isspace(*cp)); if (cp >= buf && *cp == ',') cp--; *++cp = 0; return buf; } /* * Are any of the characters in the two strings the same? */ int anyof(register char *s1, register char *s2) { register int c; while ((c = *s1++) != 0) if (any(c, s2)) return(1); return(0); } /* * See if the given header field is supposed to be ignored. * Fields of the form "Content-*" can't be ignored when saving. */ int isign(char *field, int saving) { char realfld[BUFSIZ]; /* * Lower-case the string, so that "Status" and "status" * will hash to the same place. */ istrcpy(realfld, sizeof (realfld), field); if (saving && strncmp(realfld, "content-", 8) == 0) return (0); if (nretained > 0) return (!member(realfld, retain)); else return (member(realfld, ignore)); } int member(register char *realfield, register struct ignore **table) { register struct ignore *igp; for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link) if (equal(igp->i_field, realfield)) return (1); return (0); } /* * This routine looks for string2 in string1. * If found, it returns the position string2 is found at, * otherwise it returns a -1. */ int substr(char *string1, char *string2) { int i, j, len1, len2; len1 = strlen(string1); len2 = strlen(string2); for (i = 0; i < len1 - len2 + 1; i++) { for (j = 0; j < len2 && string1[i+j] == string2[j]; j++) ; if (j == len2) return(i); } return(-1); } /* * Copies src to the dstsize buffer at dst. The copy will never * overflow the destination buffer and the buffer will always be null * terminated. */ char * nstrcpy(char *dst, int dstsize, char *src) { char *cp, *cp2; cp2 = dst; cp = src; while (--dstsize > 0 && *cp != '\0') *cp2++ = *cp++; *cp2 = '\0'; return(dst); } /* * Appends src to the dstsize buffer at dst. The append will never * overflow the destination buffer and the buffer will always be null * terminated. */ char * nstrcat(char *dst, int dstsize, char *src) { char *cp, *cp2; cp2 = dst; cp = src; while (*cp2 != '\0') { cp2++; dstsize--; } while (--dstsize > 0 && *cp != '\0') *cp2++ = *cp++; *cp2 = '\0'; return(dst); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "rcv.h" #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Variable handling stuff. */ static struct var *lookup(char name[]); /* * Assign a value to a variable. */ void assign(char name[], char value[]) { register struct var *vp; register int h; if (name[0]=='-') deassign(name+1); else if (name[0]=='n' && name[1]=='o') deassign(name+2); else { h = hash(name); vp = lookup(name); if (vp == NOVAR) { if ((vp = (struct var *) calloc(sizeof (*vp), 1)) == NULL) panic("Out of memory"); vp->v_name = vcopy(name); vp->v_link = variables[h]; variables[h] = vp; } else vfree(vp->v_value); vp->v_value = vcopy(value); /* * for efficiency, intercept certain assignments here */ if (strcmp(name, "prompt")==0) prompt = vp->v_value; else if (strcmp(name, "debug")==0) debug = 1; if (debug) fprintf(stderr, "assign(%s)=%s\n", vp->v_name, vp->v_value); } } int deassign(register char *s) { register struct var *vp, *vp2; register int h; if ((vp2 = lookup(s)) == NOVAR) { if (!sourcing) { printf(gettext("\"%s\": undefined variable\n"), s); return(1); } return(0); } if (debug) fprintf(stderr, "deassign(%s)\n", s); if (strcmp(s, "prompt")==0) prompt = NOSTR; else if (strcmp(s, "debug")==0) debug = 0; h = hash(s); if (vp2 == variables[h]) { variables[h] = variables[h]->v_link; vfree(vp2->v_name); vfree(vp2->v_value); free(vp2); return(0); } for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link) ; vp->v_link = vp2->v_link; vfree(vp2->v_name); vfree(vp2->v_value); free(vp2); return(0); } /* * Free up a variable string. We do not bother to allocate * strings whose value is "" since they are expected to be frequent. * Thus, we cannot free same! */ void vfree(register char *cp) { if (!equal(cp, "")) free(cp); } /* * Copy a variable value into permanent (ie, not collected after each * command) space. Do not bother to alloc space for "" */ char * vcopy(char str[]) { register char *top, *cp, *cp2; if (equal(str, "")) return(""); if ((top = (char *)calloc(strlen(str)+1, 1)) == NULL) panic("Out of memory"); cp = top; cp2 = str; while (*cp++ = *cp2++) ; return(top); } /* * Get the value of a variable and return it. * Look in the environment if its not available locally. */ char * value(char name[]) { register struct var *vp; register char *cp; if ((vp = lookup(name)) == NOVAR) cp = getenv(name); else cp = vp->v_value; if (debug) fprintf(stderr, "value(%s)=%s\n", name, (cp)?cp:""); return(cp); } /* * Locate a variable and return its variable * node. */ static struct var * lookup(char name[]) { register struct var *vp; register int h; h = hash(name); for (vp = variables[h]; vp != NOVAR; vp = vp->v_link) if (equal(vp->v_name, name)) return(vp); return(NOVAR); } /* * Locate a group name and return it. */ struct grouphead * findgroup(char name[]) { register struct grouphead *gh; register int h; h = hash(name); for (gh = groups[h]; gh != NOGRP; gh = gh->g_link) if (equal(gh->g_name, name)) return(gh); return(NOGRP); } /* * Print a group out on stdout */ void printgroup(char name[]) { register struct grouphead *gh; register struct mgroup *gp; if ((gh = findgroup(name)) == NOGRP) { printf(gettext("\"%s\": not a group\n"), name); return; } printf("%s\t", gh->g_name); for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) printf(" %s", gp->ge_name); printf("\n"); } /* * Hash the passed string and return an index into * the variable or group hash table. */ int hash(char name[]) { register unsigned h; register char *cp; for (cp = name, h = 0; *cp; h = (h << 2) + *cp++) ; return(h % HSHSIZE); }