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