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