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