changed to make use of signal holding facility
[unix-history] / usr / src / usr.bin / mail / list.c
CommitLineData
b3149556
KS
1#
2
3#include "rcv.h"
4#include <ctype.h>
5
6/*
7 * Mail -- a mail program
8 *
9 * Message list handling.
10 */
11
58e24fe2 12static char *SccsId = "@(#)list.c 1.5 %G%";
b3149556
KS
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
21getmsglist(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
2919b83d
KS
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
59struct 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
73static int lastcolmod;
74
b3149556
KS
75markall(buf, f)
76 char buf[];
77{
78 register char **np;
79 register int i;
2919b83d 80 register struct message *mp;
b3149556 81 char *namelist[NMLSIZE], *bufp;
2919b83d 82 int tok, beg, mc, star, other, valdot, colmod, colresult;
b3149556
KS
83
84 valdot = dot - &message[0] + 1;
2919b83d 85 colmod = 0;
b3149556
KS
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:
99number:
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 mark(i);
111 beg = 0;
112 break;
113 }
114 beg = lexnumber;
115 if (check(beg, f))
116 return(-1);
117 tok = scan(&bufp);
118 regret(tok);
119 if (tok != TDASH) {
120 mark(beg);
121 beg = 0;
122 }
123 break;
124
125 case TPLUS:
126 if (beg != 0) {
127 printf("Non-numeric second argument\n");
128 return(-1);
129 }
130 if (valdot < msgCount)
131 mark(valdot+1);
132 else {
133 printf("Referencing beyond EOF\n");
134 return(-1);
135 }
136 break;
137
138 case TDASH:
139 if (beg == 0) {
140 if (valdot > 1)
141 mark(valdot-1);
142 else {
143 printf("Referencing before 1\n");
144 return(-1);
145 }
146 }
147 break;
148
149 case TSTRING:
150 if (beg != 0) {
151 printf("Non-numeric second argument\n");
152 return(-1);
153 }
154 other++;
2919b83d
KS
155 if (lexstring[0] == ':') {
156 colresult = evalcol(lexstring[1]);
157 if (colresult == 0) {
158 printf("Unknown colon modifier \"%s\"\n",
159 lexstring);
160 return(-1);
161 }
162 colmod |= colresult;
163 }
164 else
165 *np++ = savestr(lexstring);
b3149556
KS
166 break;
167
168 case TDOLLAR:
169 case TUP:
170 case TDOT:
171 lexnumber = metamess(lexstring[0], f);
172 if (lexnumber == -1)
173 return(-1);
174 goto number;
175
176 case TSTAR:
177 if (other) {
178 printf("Can't mix \"*\" with anything\n");
179 return(-1);
180 }
181 star++;
182 break;
183 }
184 tok = scan(&bufp);
185 }
2919b83d 186 lastcolmod = colmod;
b3149556
KS
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
2919b83d 208 if ((np > namelist || colmod != 0) && mc == 0)
b3149556
KS
209 for (i = 1; i <= msgCount; i++)
210 if ((message[i-1].m_flag & (MSAVED|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++)
f7a4f91c
KS
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 }
b3149556
KS
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 }
2919b83d
KS
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 */
295evalcol(col)
296{
297 register struct coltab *colp;
298
299 if (col == 0)
300 return(lastcolmod);
301 for (colp = &coltab[0]; colp->co_char; colp++)
302 if (colp->co_char == col)
303 return(colp->co_bit);
b3149556
KS
304 return(0);
305}
306
307/*
308 * Check the passed message number for legality and proper flags.
309 */
310
311check(mesg, f)
312{
313 register struct message *mp;
314
315 if (mesg < 1 || mesg > msgCount) {
316 printf("%d: Invalid message number\n", mesg);
317 return(-1);
318 }
319 mp = &message[mesg-1];
320 if ((mp->m_flag & MDELETED) != f) {
321 printf("%d: Inappropriate message\n", mesg);
322 return(-1);
323 }
324 return(0);
325}
326
327/*
328 * Scan out the list of string arguments, shell style
329 * for a RAWLIST.
330 */
331
332getrawlist(line, argv)
333 char line[];
334 char **argv;
335{
336 register char **ap, *cp, *cp2;
337 char linebuf[BUFSIZ], quotec;
338
339 ap = argv;
340 cp = line;
341 while (*cp != '\0') {
342 while (any(*cp, " \t"))
343 cp++;
344 cp2 = linebuf;
345 quotec = 0;
346 if (any(*cp, "'\""))
347 quotec = *cp++;
348 if (quotec == 0)
349 while (*cp != '\0' && !any(*cp, " \t"))
350 *cp2++ = *cp++;
351 else {
352 while (*cp != '\0' && *cp != quotec)
353 *cp2++ = *cp++;
354 if (*cp != '\0')
355 cp++;
356 }
357 *cp2 = '\0';
358 if (cp2 == linebuf)
359 break;
360 *ap++ = savestr(linebuf);
361 }
362 *ap = NOSTR;
363 return(ap-argv);
364}
365
366/*
367 * scan out a single lexical item and return its token number,
368 * updating the string pointer passed **p. Also, store the value
369 * of the number or string scanned in lexnumber or lexstring as
370 * appropriate. In any event, store the scanned `thing' in lexstring.
371 */
372
373struct lex {
374 char l_char;
375 char l_token;
376} singles[] = {
377 '$', TDOLLAR,
378 '.', TDOT,
379 '^', TUP,
380 '*', TSTAR,
381 '-', TDASH,
382 '+', TPLUS,
383 '(', TOPEN,
384 ')', TCLOSE,
385 0, 0
386};
387
388scan(sp)
389 char **sp;
390{
391 register char *cp, *cp2;
392 register int c;
393 register struct lex *lp;
394 int quotec;
395
396 if (regretp >= 0) {
397 copy(stringstack[regretp], lexstring);
398 lexnumber = numberstack[regretp];
399 return(regretstack[regretp--]);
400 }
401 cp = *sp;
402 cp2 = lexstring;
403 c = *cp++;
404
405 /*
406 * strip away leading white space.
407 */
408
409 while (any(c, " \t"))
410 c = *cp++;
411
412 /*
413 * If no characters remain, we are at end of line,
414 * so report that.
415 */
416
417 if (c == '\0') {
418 *sp = --cp;
419 return(TEOL);
420 }
421
422 /*
423 * If the leading character is a digit, scan
424 * the number and convert it on the fly.
425 * Return TNUMBER when done.
426 */
427
428 if (isdigit(c)) {
429 lexnumber = 0;
430 while (isdigit(c)) {
431 lexnumber = lexnumber*10 + c - '0';
432 *cp2++ = c;
433 c = *cp++;
434 }
435 *cp2 = '\0';
436 *sp = --cp;
437 return(TNUMBER);
438 }
439
440 /*
441 * Check for single character tokens; return such
442 * if found.
443 */
444
445 for (lp = &singles[0]; lp->l_char != 0; lp++)
446 if (c == lp->l_char) {
447 lexstring[0] = c;
448 lexstring[1] = '\0';
449 *sp = cp;
450 return(lp->l_token);
451 }
452
453 /*
454 * We've got a string! Copy all the characters
455 * of the string into lexstring, until we see
456 * a null, space, or tab.
457 * If the lead character is a " or ', save it
458 * and scan until you get another.
459 */
460
461 quotec = 0;
462 if (any(c, "'\"")) {
463 quotec = c;
464 c = *cp++;
465 }
466 while (c != '\0') {
467 if (c == quotec)
468 break;
469 if (quotec == 0 && any(c, " \t"))
470 break;
471 if (cp2 - lexstring < STRINGLEN-1)
472 *cp2++ = c;
473 c = *cp++;
474 }
475 if (quotec && c == 0)
476 fprintf(stderr, "Missing %c\n", quotec);
477 *sp = --cp;
478 *cp2 = '\0';
479 return(TSTRING);
480}
481
482/*
483 * Unscan the named token by pushing it onto the regret stack.
484 */
485
486regret(token)
487{
488 if (++regretp >= REGDEP)
489 panic("Too many regrets");
490 regretstack[regretp] = token;
491 lexstring[STRINGLEN-1] = '\0';
492 stringstack[regretp] = savestr(lexstring);
493 numberstack[regretp] = lexnumber;
494}
495
496/*
497 * Reset all the scanner global variables.
498 */
499
500scaninit()
501{
502 regretp = -1;
503}
504
505/*
506 * Find the first message whose flags & m == f and return
507 * its message number.
508 */
509
510first(f, m)
511{
512 register int mesg;
513 register struct message *mp;
514
515 mesg = dot - &message[0] + 1;
516 f &= MDELETED;
517 m &= MDELETED;
518 for (mp = dot; mp < &message[msgCount]; mp++) {
519 if ((mp->m_flag & m) == f)
520 return(mesg);
521 mesg++;
522 }
523 mesg = dot - &message[0];
524 for (mp = dot-1; mp >= &message[0]; mp--) {
525 if ((mp->m_flag & m) == f)
526 return(mesg);
527 mesg--;
528 }
529 return(NULL);
530}
531
532/*
533 * See if the passed name sent the passed message number. Return true
534 * if so.
535 */
536
537sender(str, mesg)
538 char *str;
539{
540 register struct message *mp;
541 register char *cp;
542
543 mp = &message[mesg-1];
58e24fe2 544 cp = nameof(mp, 0);
b3149556
KS
545 return(icequal(cp, str));
546}
547
f7a4f91c
KS
548/*
549 * See if the given string matches inside the subject field of the
550 * given message. For the purpose of the scan, we ignore case differences.
551 * If it does, return true. The string search argument is assumed to
552 * have the form "/search-string." If it is of the form "/," we use the
553 * previous search string.
554 */
555
556char lastscan[128];
557
558matchsubj(str, mesg)
559 char *str;
560{
561 register struct message *mp;
312bdff6 562 register char *cp, *cp2, *backup;
f7a4f91c
KS
563
564 str++;
565 if (strlen(str) == 0)
566 str = lastscan;
567 else
568 strcpy(lastscan, str);
569 mp = &message[mesg-1];
570
571 /*
572 * Now look, ignoring case, for the word in the string.
573 */
574
575 cp = str;
576 cp2 = hfield("subject", mp);
577 if (cp2 == NOSTR)
578 return(0);
312bdff6 579 backup = cp2;
f7a4f91c
KS
580 while (*cp2) {
581 if (*cp == 0)
582 return(1);
312bdff6
KS
583 if (raise(*cp++) != raise(*cp2++)) {
584 cp2 = ++backup;
f7a4f91c 585 cp = str;
312bdff6 586 }
f7a4f91c
KS
587 }
588 return(*cp == 0);
589}
590
b3149556
KS
591/*
592 * Mark the named message by setting its mark bit.
593 */
594
595mark(mesg)
596{
597 register int i;
598
599 i = mesg;
600 if (i < 1 || i > msgCount)
601 panic("Bad message number to mark");
602 message[i-1].m_flag |= MMARK;
603}
604
605/*
606 * Unmark the named message.
607 */
608
609unmark(mesg)
610{
611 register int i;
612
613 i = mesg;
614 if (i < 1 || i > msgCount)
615 panic("Bad message number to unmark");
616 message[i-1].m_flag &= ~MMARK;
617}
618
619/*
620 * Return the message number corresponding to the passed meta character.
621 */
622
623metamess(meta, f)
624{
625 register int c, m;
626 register struct message *mp;
627
628 c = meta;
629 switch (c) {
630 case '^':
631 /*
632 * First 'good' message left.
633 */
634 for (mp = &message[0]; mp < &message[msgCount]; mp++)
635 if ((mp->m_flag & MDELETED) == f)
636 return(mp - &message[0] + 1);
637 printf("No applicable messages\n");
638 return(-1);
639
640 case '$':
641 /*
642 * Last 'good message left.
643 */
644 for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
645 if ((mp->m_flag & MDELETED) == f)
646 return(mp - &message[0] + 1);
647 printf("No applicable messages\n");
648 return(-1);
649
650 case '.':
651 /*
652 * Current message.
653 */
654 m = dot - &message[0] + 1;
655 if ((dot->m_flag & MDELETED) != f) {
656 printf("%d: Inappropriate message\n", m);
657 return(-1);
658 }
659 return(m);
660
661 default:
662 printf("Unknown metachar (%c)\n", c);
663 return(-1);
664 }
665}