/* * 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); } }