|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)list.c 2.3 (Berkeley) 8/11/83";
3: #endif
4:
5: #include "rcv.h"
6: #include <ctype.h>
7:
8: /*
9: * Mail -- a mail program
10: *
11: * Message list handling.
12: */
13:
14: /*
15: * Convert the user string of message numbers and
16: * store the numbers into vector.
17: *
18: * Returns the count of messages picked up or -1 on error.
19: */
20:
21: getmsglist(buf, vector, flags)
22: char *buf;
23: int *vector;
24: {
25: register int *ip;
26: register struct message *mp;
27:
28: if (markall(buf, flags) < 0)
29: return(-1);
30: ip = vector;
31: for (mp = &message[0]; mp < &message[msgCount]; mp++)
32: if (mp->m_flag & MMARK)
33: *ip++ = mp - &message[0] + 1;
34: *ip = NULL;
35: return(ip - vector);
36: }
37:
38: /*
39: * Mark all messages that the user wanted from the command
40: * line in the message structure. Return 0 on success, -1
41: * on error.
42: */
43:
44: /*
45: * Bit values for colon modifiers.
46: */
47:
48: #define CMNEW 01 /* New messages */
49: #define CMOLD 02 /* Old messages */
50: #define CMUNREAD 04 /* Unread messages */
51: #define CMDELETED 010 /* Deleted messages */
52: #define CMREAD 020 /* Read messages */
53:
54: /*
55: * The following table describes the letters which can follow
56: * the colon and gives the corresponding modifier bit.
57: */
58:
59: struct coltab {
60: char co_char; /* What to find past : */
61: int co_bit; /* Associated modifier bit */
62: int co_mask; /* m_status bits to mask */
63: int co_equal; /* ... must equal this */
64: } coltab[] = {
65: 'n', CMNEW, MNEW, MNEW,
66: 'o', CMOLD, MNEW, 0,
67: 'u', CMUNREAD, MREAD, 0,
68: 'd', CMDELETED, MDELETED, MDELETED,
69: 'r', CMREAD, MREAD, MREAD,
70: 0, 0, 0, 0
71: };
72:
73: static int lastcolmod;
74:
75: markall(buf, f)
76: char buf[];
77: {
78: register char **np;
79: register int i;
80: register struct message *mp;
81: char *namelist[NMLSIZE], *bufp;
82: int tok, beg, mc, star, other, valdot, colmod, colresult;
83:
84: valdot = dot - &message[0] + 1;
85: colmod = 0;
86: for (i = 1; i <= msgCount; i++)
87: unmark(i);
88: bufp = buf;
89: mc = 0;
90: np = &namelist[0];
91: scaninit();
92: tok = scan(&bufp);
93: star = 0;
94: other = 0;
95: beg = 0;
96: while (tok != TEOL) {
97: switch (tok) {
98: case TNUMBER:
99: number:
100: if (star) {
101: printf("No numbers mixed with *\n");
102: return(-1);
103: }
104: mc++;
105: other++;
106: if (beg != 0) {
107: if (check(lexnumber, f))
108: return(-1);
109: for (i = beg; i <= lexnumber; i++)
110: if ((message[i - 1].m_flag & MDELETED) == f)
111: mark(i);
112: beg = 0;
113: break;
114: }
115: beg = lexnumber;
116: if (check(beg, f))
117: return(-1);
118: tok = scan(&bufp);
119: regret(tok);
120: if (tok != TDASH) {
121: mark(beg);
122: beg = 0;
123: }
124: break;
125:
126: case TPLUS:
127: if (beg != 0) {
128: printf("Non-numeric second argument\n");
129: return(-1);
130: }
131: if (valdot < msgCount)
132: mark(valdot+1);
133: else {
134: printf("Referencing beyond EOF\n");
135: return(-1);
136: }
137: break;
138:
139: case TDASH:
140: if (beg == 0) {
141: if (valdot > 1)
142: mark(valdot-1);
143: else {
144: printf("Referencing before 1\n");
145: return(-1);
146: }
147: }
148: break;
149:
150: case TSTRING:
151: if (beg != 0) {
152: printf("Non-numeric second argument\n");
153: return(-1);
154: }
155: other++;
156: if (lexstring[0] == ':') {
157: colresult = evalcol(lexstring[1]);
158: if (colresult == 0) {
159: printf("Unknown colon modifier \"%s\"\n",
160: lexstring);
161: return(-1);
162: }
163: colmod |= colresult;
164: }
165: else
166: *np++ = savestr(lexstring);
167: break;
168:
169: case TDOLLAR:
170: case TUP:
171: case TDOT:
172: lexnumber = metamess(lexstring[0], f);
173: if (lexnumber == -1)
174: return(-1);
175: goto number;
176:
177: case TSTAR:
178: if (other) {
179: printf("Can't mix \"*\" with anything\n");
180: return(-1);
181: }
182: star++;
183: break;
184: }
185: tok = scan(&bufp);
186: }
187: lastcolmod = colmod;
188: *np = NOSTR;
189: mc = 0;
190: if (star) {
191: for (i = 0; i < msgCount; i++)
192: if ((message[i].m_flag & MDELETED) == f) {
193: mark(i+1);
194: mc++;
195: }
196: if (mc == 0) {
197: printf("No applicable messages.\n");
198: return(-1);
199: }
200: return(0);
201: }
202:
203: /*
204: * If no numbers were given, mark all of the messages,
205: * so that we can unmark any whose sender was not selected
206: * if any user names were given.
207: */
208:
209: if ((np > namelist || colmod != 0) && mc == 0)
210: for (i = 1; i <= msgCount; i++)
211: if ((message[i-1].m_flag & (MSAVED|MDELETED)) == f)
212: mark(i);
213:
214: /*
215: * If any names were given, go through and eliminate any
216: * messages whose senders were not requested.
217: */
218:
219: if (np > namelist) {
220: for (i = 1; i <= msgCount; i++) {
221: for (mc = 0, np = &namelist[0]; *np != NOSTR; np++)
222: if (**np == '/') {
223: if (matchsubj(*np, i)) {
224: mc++;
225: break;
226: }
227: }
228: else {
229: if (sender(*np, i)) {
230: mc++;
231: break;
232: }
233: }
234: if (mc == 0)
235: unmark(i);
236: }
237:
238: /*
239: * Make sure we got some decent messages.
240: */
241:
242: mc = 0;
243: for (i = 1; i <= msgCount; i++)
244: if (message[i-1].m_flag & MMARK) {
245: mc++;
246: break;
247: }
248: if (mc == 0) {
249: printf("No applicable messages from {%s",
250: namelist[0]);
251: for (np = &namelist[1]; *np != NOSTR; np++)
252: printf(", %s", *np);
253: printf("}\n");
254: return(-1);
255: }
256: }
257:
258: /*
259: * If any colon modifiers were given, go through and
260: * unmark any messages which do not satisfy the modifiers.
261: */
262:
263: if (colmod != 0) {
264: for (i = 1; i <= msgCount; i++) {
265: register struct coltab *colp;
266:
267: mp = &message[i - 1];
268: for (colp = &coltab[0]; colp->co_char; colp++)
269: if (colp->co_bit & colmod)
270: if ((mp->m_flag & colp->co_mask)
271: != colp->co_equal)
272: unmark(i);
273:
274: }
275: for (mp = &message[0]; mp < &message[msgCount]; mp++)
276: if (mp->m_flag & MMARK)
277: break;
278: if (mp >= &message[msgCount]) {
279: register struct coltab *colp;
280:
281: printf("No messages satisfy");
282: for (colp = &coltab[0]; colp->co_char; colp++)
283: if (colp->co_bit & colmod)
284: printf(" :%c", colp->co_char);
285: printf("\n");
286: return(-1);
287: }
288: }
289: return(0);
290: }
291:
292: /*
293: * Turn the character after a colon modifier into a bit
294: * value.
295: */
296: evalcol(col)
297: {
298: register struct coltab *colp;
299:
300: if (col == 0)
301: return(lastcolmod);
302: for (colp = &coltab[0]; colp->co_char; colp++)
303: if (colp->co_char == col)
304: return(colp->co_bit);
305: return(0);
306: }
307:
308: /*
309: * Check the passed message number for legality and proper flags.
310: */
311:
312: check(mesg, f)
313: {
314: register struct message *mp;
315:
316: if (mesg < 1 || mesg > msgCount) {
317: printf("%d: Invalid message number\n", mesg);
318: return(-1);
319: }
320: mp = &message[mesg-1];
321: if ((mp->m_flag & MDELETED) != f) {
322: printf("%d: Inappropriate message\n", mesg);
323: return(-1);
324: }
325: return(0);
326: }
327:
328: /*
329: * Scan out the list of string arguments, shell style
330: * for a RAWLIST.
331: */
332:
333: getrawlist(line, argv)
334: char line[];
335: char **argv;
336: {
337: register char **ap, *cp, *cp2;
338: char linebuf[BUFSIZ], quotec;
339:
340: ap = argv;
341: cp = line;
342: while (*cp != '\0') {
343: while (any(*cp, " \t"))
344: cp++;
345: cp2 = linebuf;
346: quotec = 0;
347: if (any(*cp, "'\""))
348: quotec = *cp++;
349: if (quotec == 0)
350: while (*cp != '\0' && !any(*cp, " \t"))
351: *cp2++ = *cp++;
352: else {
353: while (*cp != '\0' && *cp != quotec)
354: *cp2++ = *cp++;
355: if (*cp != '\0')
356: cp++;
357: }
358: *cp2 = '\0';
359: if (cp2 == linebuf)
360: break;
361: *ap++ = savestr(linebuf);
362: }
363: *ap = NOSTR;
364: return(ap-argv);
365: }
366:
367: /*
368: * scan out a single lexical item and return its token number,
369: * updating the string pointer passed **p. Also, store the value
370: * of the number or string scanned in lexnumber or lexstring as
371: * appropriate. In any event, store the scanned `thing' in lexstring.
372: */
373:
374: struct lex {
375: char l_char;
376: char l_token;
377: } singles[] = {
378: '$', TDOLLAR,
379: '.', TDOT,
380: '^', TUP,
381: '*', TSTAR,
382: '-', TDASH,
383: '+', TPLUS,
384: '(', TOPEN,
385: ')', TCLOSE,
386: 0, 0
387: };
388:
389: scan(sp)
390: char **sp;
391: {
392: register char *cp, *cp2;
393: register int c;
394: register struct lex *lp;
395: int quotec;
396:
397: if (regretp >= 0) {
398: copy(stringstack[regretp], lexstring);
399: lexnumber = numberstack[regretp];
400: return(regretstack[regretp--]);
401: }
402: cp = *sp;
403: cp2 = lexstring;
404: c = *cp++;
405:
406: /*
407: * strip away leading white space.
408: */
409:
410: while (any(c, " \t"))
411: c = *cp++;
412:
413: /*
414: * If no characters remain, we are at end of line,
415: * so report that.
416: */
417:
418: if (c == '\0') {
419: *sp = --cp;
420: return(TEOL);
421: }
422:
423: /*
424: * If the leading character is a digit, scan
425: * the number and convert it on the fly.
426: * Return TNUMBER when done.
427: */
428:
429: if (isdigit(c)) {
430: lexnumber = 0;
431: while (isdigit(c)) {
432: lexnumber = lexnumber*10 + c - '0';
433: *cp2++ = c;
434: c = *cp++;
435: }
436: *cp2 = '\0';
437: *sp = --cp;
438: return(TNUMBER);
439: }
440:
441: /*
442: * Check for single character tokens; return such
443: * if found.
444: */
445:
446: for (lp = &singles[0]; lp->l_char != 0; lp++)
447: if (c == lp->l_char) {
448: lexstring[0] = c;
449: lexstring[1] = '\0';
450: *sp = cp;
451: return(lp->l_token);
452: }
453:
454: /*
455: * We've got a string! Copy all the characters
456: * of the string into lexstring, until we see
457: * a null, space, or tab.
458: * If the lead character is a " or ', save it
459: * and scan until you get another.
460: */
461:
462: quotec = 0;
463: if (any(c, "'\"")) {
464: quotec = c;
465: c = *cp++;
466: }
467: while (c != '\0') {
468: if (c == quotec)
469: break;
470: if (quotec == 0 && any(c, " \t"))
471: break;
472: if (cp2 - lexstring < STRINGLEN-1)
473: *cp2++ = c;
474: c = *cp++;
475: }
476: if (quotec && c == 0)
477: fprintf(stderr, "Missing %c\n", quotec);
478: *sp = --cp;
479: *cp2 = '\0';
480: return(TSTRING);
481: }
482:
483: /*
484: * Unscan the named token by pushing it onto the regret stack.
485: */
486:
487: regret(token)
488: {
489: if (++regretp >= REGDEP)
490: panic("Too many regrets");
491: regretstack[regretp] = token;
492: lexstring[STRINGLEN-1] = '\0';
493: stringstack[regretp] = savestr(lexstring);
494: numberstack[regretp] = lexnumber;
495: }
496:
497: /*
498: * Reset all the scanner global variables.
499: */
500:
501: scaninit()
502: {
503: regretp = -1;
504: }
505:
506: /*
507: * Find the first message whose flags & m == f and return
508: * its message number.
509: */
510:
511: first(f, m)
512: {
513: register int mesg;
514: register struct message *mp;
515:
516: mesg = dot - &message[0] + 1;
517: f &= MDELETED;
518: m &= MDELETED;
519: for (mp = dot; mp < &message[msgCount]; mp++) {
520: if ((mp->m_flag & m) == f)
521: return(mesg);
522: mesg++;
523: }
524: mesg = dot - &message[0];
525: for (mp = dot-1; mp >= &message[0]; mp--) {
526: if ((mp->m_flag & m) == f)
527: return(mesg);
528: mesg--;
529: }
530: return(NULL);
531: }
532:
533: /*
534: * See if the passed name sent the passed message number. Return true
535: * if so.
536: */
537:
538: sender(str, mesg)
539: char *str;
540: {
541: register struct message *mp;
542: register char *cp;
543:
544: mp = &message[mesg-1];
545: cp = nameof(mp, 0);
546: return(icequal(cp, str));
547: }
548:
549: /*
550: * See if the given string matches inside the subject field of the
551: * given message. For the purpose of the scan, we ignore case differences.
552: * If it does, return true. The string search argument is assumed to
553: * have the form "/search-string." If it is of the form "/," we use the
554: * previous search string.
555: */
556:
557: char lastscan[128];
558:
559: matchsubj(str, mesg)
560: char *str;
561: {
562: register struct message *mp;
563: register char *cp, *cp2, *backup;
564:
565: str++;
566: if (strlen(str) == 0)
567: str = lastscan;
568: else
569: strcpy(lastscan, str);
570: mp = &message[mesg-1];
571:
572: /*
573: * Now look, ignoring case, for the word in the string.
574: */
575:
576: cp = str;
577: cp2 = hfield("subject", mp);
578: if (cp2 == NOSTR)
579: return(0);
580: backup = cp2;
581: while (*cp2) {
582: if (*cp == 0)
583: return(1);
584: if (raise(*cp++) != raise(*cp2++)) {
585: cp2 = ++backup;
586: cp = str;
587: }
588: }
589: return(*cp == 0);
590: }
591:
592: /*
593: * Mark the named message by setting its mark bit.
594: */
595:
596: mark(mesg)
597: {
598: register int i;
599:
600: i = mesg;
601: if (i < 1 || i > msgCount)
602: panic("Bad message number to mark");
603: message[i-1].m_flag |= MMARK;
604: }
605:
606: /*
607: * Unmark the named message.
608: */
609:
610: unmark(mesg)
611: {
612: register int i;
613:
614: i = mesg;
615: if (i < 1 || i > msgCount)
616: panic("Bad message number to unmark");
617: message[i-1].m_flag &= ~MMARK;
618: }
619:
620: /*
621: * Return the message number corresponding to the passed meta character.
622: */
623:
624: metamess(meta, f)
625: {
626: register int c, m;
627: register struct message *mp;
628:
629: c = meta;
630: switch (c) {
631: case '^':
632: /*
633: * First 'good' message left.
634: */
635: for (mp = &message[0]; mp < &message[msgCount]; mp++)
636: if ((mp->m_flag & MDELETED) == f)
637: return(mp - &message[0] + 1);
638: printf("No applicable messages\n");
639: return(-1);
640:
641: case '$':
642: /*
643: * Last 'good message left.
644: */
645: for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
646: if ((mp->m_flag & MDELETED) == f)
647: return(mp - &message[0] + 1);
648: printf("No applicable messages\n");
649: return(-1);
650:
651: case '.':
652: /*
653: * Current message.
654: */
655: m = dot - &message[0] + 1;
656: if ((dot->m_flag & MDELETED) != f) {
657: printf("%d: Inappropriate message\n", m);
658: return(-1);
659: }
660: return(m);
661:
662: default:
663: printf("Unknown metachar (%c)\n", c);
664: return(-1);
665: }
666: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.