/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "rcv.h"
#include <locale.h>
#include <stdlib.h>
#include <string.h>
/*
* 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)
{
register int *ip;
register 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 = NULL;
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)
{
register int *ip;
register 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 = NULL;
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)
{
register char **np;
register int i;
register 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++) {
register 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]) {
register 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)
{
register 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)
{
register 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)
{
register char **ap, *cp, *cp2;
char linebuf[LINESIZE], quotec;
register 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)
{
register char *cp, *cp2;
register char c;
register 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)
{
register int mesg;
register 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 (NULL);
}
/*
* 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)
{
register struct message *mp;
register 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)
{
register 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)
{
register 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)
{
register int c, m;
register 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);
}
}