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