+/* Copyright (c) 1979 Regents of the University of California */
+#
+
+#include "rcv.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Message list handling.
+ */
+
+/*
+ * Convert the user string of message numbers and
+ * store the numbers into vector.
+ *
+ * Returns the count of messages picked up or -1 on error.
+ */
+
+getmsglist(buf, vector, flags)
+ char *buf;
+ int *vector;
+{
+ 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.
+ */
+
+markall(buf, f)
+ char buf[];
+{
+ register char **np;
+ register int i;
+ char *namelist[NMLSIZE], *bufp;
+ int tok, beg, mc, star, other;
+
+ 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("No numbers mixed with *\n");
+ return(-1);
+ }
+ mc++;
+ other++;
+ if (beg != 0) {
+ if (check(lexnumber, f))
+ return(-1);
+ for (i = beg; i <= lexnumber; i++)
+ mark(i);
+ beg = 0;
+ break;
+ }
+ beg = lexnumber;
+ if (check(beg, f))
+ return(-1);
+ tok = scan(&bufp);
+ regret(tok);
+ if (tok != TDASH) {
+ mark(beg);
+ beg = 0;
+ }
+ break;
+
+ case TDASH:
+ if (beg == 0) {
+ printf("Unexpected leading dash\n");
+ return(-1);
+ }
+ break;
+
+ case TSTRING:
+ if (beg != 0) {
+ printf("Non-numeric second argument\n");
+ return(-1);
+ }
+ other++;
+ *np++ = savestr(lexstring);
+ break;
+
+ case TDOLLAR:
+ case TUP:
+ case TDOT:
+ lexnumber = metamess(lexstring[0], f);
+ if (lexnumber == -1)
+ return(-1);
+ goto number;
+
+ case TSTAR:
+ if (other) {
+ printf("Can't mix \"*\" with anything\n");
+ return(-1);
+ }
+ star++;
+ break;
+ }
+ tok = scan(&bufp);
+ }
+ *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("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 && mc == 0)
+ for (i = 1; i <= msgCount; i++)
+ if ((message[i-1].m_flag & (MSAVED|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 (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("No applicable messages from {%s",
+ namelist[0]);
+ for (np = &namelist[1]; *np != NOSTR; np++)
+ printf(", %s", *np);
+ printf("}\n");
+ return(-1);
+ }
+ }
+ return(0);
+}
+
+/*
+ * Check the passed message number for legality and proper flags.
+ */
+
+check(mesg, f)
+{
+ register struct message *mp;
+
+ if (mesg < 1 || mesg > msgCount) {
+ printf("%d: Invalid message number\n", mesg);
+ return(-1);
+ }
+ mp = &message[mesg-1];
+ if ((mp->m_flag & MDELETED) != f) {
+ printf("%d: Inappropriate message\n", mesg);
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * Scan out the list of string arguments, shell style
+ * for a RAWLIST.
+ */
+
+getrawlist(line, argv)
+ char line[];
+ char **argv;
+{
+ register char **ap, *cp, *cp2;
+ char linebuf[BUFSIZ], quotec;
+
+ ap = argv;
+ cp = line;
+ while (*cp != '\0') {
+ while (any(*cp, " \t"))
+ cp++;
+ cp2 = linebuf;
+ quotec = 0;
+ if (any(*cp, "'\""))
+ quotec = *cp++;
+ if (quotec == 0)
+ while (*cp != '\0' && !any(*cp, " \t"))
+ *cp2++ = *cp++;
+ else {
+ while (*cp != '\0' && *cp != quotec)
+ *cp2++ = *cp++;
+ if (*cp != '\0')
+ cp++;
+ }
+ *cp2 = '\0';
+ if (cp2 == linebuf)
+ 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.
+ */
+
+struct lex {
+ char l_char;
+ char l_token;
+} singles[] = {
+ '$', TDOLLAR,
+ '.', TDOT,
+ '^', TUP,
+ '*', TSTAR,
+ '-', TDASH,
+ '(', TOPEN,
+ ')', TCLOSE,
+ 0, 0
+};
+
+scan(sp)
+ char **sp;
+{
+ register char *cp, *cp2;
+ register int 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 (c == quotec)
+ break;
+ if (quotec == 0 && any(c, " \t"))
+ break;
+ if (cp2 - lexstring < STRINGLEN-1)
+ *cp2++ = c;
+ c = *cp++;
+ }
+ if (quotec && c == 0)
+ fprintf(stderr, "Missing %c\n", quotec);
+ *sp = --cp;
+ *cp2 = '\0';
+ return(TSTRING);
+}
+
+/*
+ * Unscan the named token by pushing it onto the regret stack.
+ */
+
+regret(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.
+ */
+
+scaninit()
+{
+ regretp = -1;
+}
+
+/*
+ * Find the first message whose flags & m == f and return
+ * its message number.
+ */
+
+first(f, m)
+{
+ register int mesg;
+ register struct message *mp;
+
+ mesg = dot - &message[0];
+ mesg++;
+ 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.
+ */
+
+sender(str, mesg)
+ char *str;
+{
+ register struct message *mp;
+
+ mp = &message[mesg-1];
+ return(!strcmp(nameof(mp), str));
+}
+
+/*
+ * Mark the named message by setting its mark bit.
+ */
+
+mark(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.
+ */
+
+unmark(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.
+ */
+
+metamess(meta, 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("No applicable messages\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("No applicable messages\n");
+ return(-1);
+
+ case '.':
+ /*
+ * Current message.
+ */
+ m = dot - &message[0] + 1;
+ if ((dot->m_flag & MDELETED) != f) {
+ printf("%d: Inappropriate message\n", m);
+ return(-1);
+ }
+ return(m);
+
+ default:
+ printf("Unknown metachar (%c)\n", c);
+ return(-1);
+ }
+}