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