add imagen
[unix-history] / usr / src / usr.bin / mail / list.c
CommitLineData
9552e6b8
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
2ae9f53f 7#ifndef lint
a965cce5 8static char *sccsid = "@(#)list.c 5.3 (Berkeley) %G%";
9552e6b8 9#endif not lint
b3149556
KS
10
11#include "rcv.h"
12#include <ctype.h>
13
14/*
15 * Mail -- a mail program
16 *
17 * Message list handling.
18 */
19
b3149556
KS
20/*
21 * Convert the user string of message numbers and
22 * store the numbers into vector.
23 *
24 * Returns the count of messages picked up or -1 on error.
25 */
26
27getmsglist(buf, vector, flags)
28 char *buf;
29 int *vector;
30{
31 register int *ip;
32 register struct message *mp;
33
34 if (markall(buf, flags) < 0)
35 return(-1);
36 ip = vector;
37 for (mp = &message[0]; mp < &message[msgCount]; mp++)
38 if (mp->m_flag & MMARK)
39 *ip++ = mp - &message[0] + 1;
40 *ip = NULL;
41 return(ip - vector);
42}
43
44/*
45 * Mark all messages that the user wanted from the command
46 * line in the message structure. Return 0 on success, -1
47 * on error.
48 */
49
2919b83d
KS
50/*
51 * Bit values for colon modifiers.
52 */
53
54#define CMNEW 01 /* New messages */
55#define CMOLD 02 /* Old messages */
56#define CMUNREAD 04 /* Unread messages */
57#define CMDELETED 010 /* Deleted messages */
58#define CMREAD 020 /* Read messages */
59
60/*
61 * The following table describes the letters which can follow
62 * the colon and gives the corresponding modifier bit.
63 */
64
65struct coltab {
66 char co_char; /* What to find past : */
67 int co_bit; /* Associated modifier bit */
68 int co_mask; /* m_status bits to mask */
69 int co_equal; /* ... must equal this */
70} coltab[] = {
71 'n', CMNEW, MNEW, MNEW,
72 'o', CMOLD, MNEW, 0,
73 'u', CMUNREAD, MREAD, 0,
74 'd', CMDELETED, MDELETED, MDELETED,
75 'r', CMREAD, MREAD, MREAD,
76 0, 0, 0, 0
77};
78
79static int lastcolmod;
80
b3149556
KS
81markall(buf, f)
82 char buf[];
83{
84 register char **np;
85 register int i;
2919b83d 86 register struct message *mp;
b3149556 87 char *namelist[NMLSIZE], *bufp;
2919b83d 88 int tok, beg, mc, star, other, valdot, colmod, colresult;
b3149556
KS
89
90 valdot = dot - &message[0] + 1;
2919b83d 91 colmod = 0;
b3149556
KS
92 for (i = 1; i <= msgCount; i++)
93 unmark(i);
94 bufp = buf;
95 mc = 0;
96 np = &namelist[0];
97 scaninit();
98 tok = scan(&bufp);
99 star = 0;
100 other = 0;
101 beg = 0;
102 while (tok != TEOL) {
103 switch (tok) {
104 case TNUMBER:
105number:
106 if (star) {
107 printf("No numbers mixed with *\n");
108 return(-1);
109 }
110 mc++;
111 other++;
112 if (beg != 0) {
113 if (check(lexnumber, f))
114 return(-1);
115 for (i = beg; i <= lexnumber; i++)
c9c93837
CL
116 if ((message[i - 1].m_flag & MDELETED) == f)
117 mark(i);
b3149556
KS
118 beg = 0;
119 break;
120 }
121 beg = lexnumber;
122 if (check(beg, f))
123 return(-1);
124 tok = scan(&bufp);
125 regret(tok);
126 if (tok != TDASH) {
127 mark(beg);
128 beg = 0;
129 }
130 break;
131
132 case TPLUS:
133 if (beg != 0) {
134 printf("Non-numeric second argument\n");
135 return(-1);
136 }
137 if (valdot < msgCount)
138 mark(valdot+1);
139 else {
140 printf("Referencing beyond EOF\n");
141 return(-1);
142 }
143 break;
144
145 case TDASH:
146 if (beg == 0) {
147 if (valdot > 1)
148 mark(valdot-1);
149 else {
150 printf("Referencing before 1\n");
151 return(-1);
152 }
153 }
154 break;
155
156 case TSTRING:
157 if (beg != 0) {
158 printf("Non-numeric second argument\n");
159 return(-1);
160 }
161 other++;
2919b83d
KS
162 if (lexstring[0] == ':') {
163 colresult = evalcol(lexstring[1]);
164 if (colresult == 0) {
165 printf("Unknown colon modifier \"%s\"\n",
166 lexstring);
167 return(-1);
168 }
169 colmod |= colresult;
170 }
171 else
172 *np++ = savestr(lexstring);
b3149556
KS
173 break;
174
175 case TDOLLAR:
176 case TUP:
177 case TDOT:
178 lexnumber = metamess(lexstring[0], f);
179 if (lexnumber == -1)
180 return(-1);
181 goto number;
182
183 case TSTAR:
184 if (other) {
185 printf("Can't mix \"*\" with anything\n");
186 return(-1);
187 }
188 star++;
189 break;
190 }
191 tok = scan(&bufp);
192 }
2919b83d 193 lastcolmod = colmod;
b3149556
KS
194 *np = NOSTR;
195 mc = 0;
196 if (star) {
197 for (i = 0; i < msgCount; i++)
198 if ((message[i].m_flag & MDELETED) == f) {
199 mark(i+1);
200 mc++;
201 }
202 if (mc == 0) {
203 printf("No applicable messages.\n");
204 return(-1);
205 }
206 return(0);
207 }
208
209 /*
210 * If no numbers were given, mark all of the messages,
211 * so that we can unmark any whose sender was not selected
212 * if any user names were given.
213 */
214
2919b83d 215 if ((np > namelist || colmod != 0) && mc == 0)
b3149556
KS
216 for (i = 1; i <= msgCount; i++)
217 if ((message[i-1].m_flag & (MSAVED|MDELETED)) == f)
218 mark(i);
219
220 /*
221 * If any names were given, go through and eliminate any
222 * messages whose senders were not requested.
223 */
224
225 if (np > namelist) {
226 for (i = 1; i <= msgCount; i++) {
227 for (mc = 0, np = &namelist[0]; *np != NOSTR; np++)
f7a4f91c
KS
228 if (**np == '/') {
229 if (matchsubj(*np, i)) {
230 mc++;
231 break;
232 }
233 }
234 else {
235 if (sender(*np, i)) {
236 mc++;
237 break;
238 }
b3149556
KS
239 }
240 if (mc == 0)
241 unmark(i);
242 }
243
244 /*
245 * Make sure we got some decent messages.
246 */
247
248 mc = 0;
249 for (i = 1; i <= msgCount; i++)
250 if (message[i-1].m_flag & MMARK) {
251 mc++;
252 break;
253 }
254 if (mc == 0) {
255 printf("No applicable messages from {%s",
256 namelist[0]);
257 for (np = &namelist[1]; *np != NOSTR; np++)
258 printf(", %s", *np);
259 printf("}\n");
260 return(-1);
261 }
262 }
2919b83d
KS
263
264 /*
265 * If any colon modifiers were given, go through and
266 * unmark any messages which do not satisfy the modifiers.
267 */
268
269 if (colmod != 0) {
270 for (i = 1; i <= msgCount; i++) {
271 register struct coltab *colp;
272
273 mp = &message[i - 1];
274 for (colp = &coltab[0]; colp->co_char; colp++)
275 if (colp->co_bit & colmod)
276 if ((mp->m_flag & colp->co_mask)
277 != colp->co_equal)
278 unmark(i);
279
280 }
281 for (mp = &message[0]; mp < &message[msgCount]; mp++)
282 if (mp->m_flag & MMARK)
283 break;
284 if (mp >= &message[msgCount]) {
285 register struct coltab *colp;
286
287 printf("No messages satisfy");
288 for (colp = &coltab[0]; colp->co_char; colp++)
289 if (colp->co_bit & colmod)
290 printf(" :%c", colp->co_char);
291 printf("\n");
292 return(-1);
293 }
294 }
295 return(0);
296}
297
298/*
299 * Turn the character after a colon modifier into a bit
300 * value.
301 */
302evalcol(col)
303{
304 register struct coltab *colp;
305
306 if (col == 0)
307 return(lastcolmod);
308 for (colp = &coltab[0]; colp->co_char; colp++)
309 if (colp->co_char == col)
310 return(colp->co_bit);
b3149556
KS
311 return(0);
312}
313
314/*
315 * Check the passed message number for legality and proper flags.
316 */
317
318check(mesg, f)
319{
320 register struct message *mp;
321
322 if (mesg < 1 || mesg > msgCount) {
323 printf("%d: Invalid message number\n", mesg);
324 return(-1);
325 }
326 mp = &message[mesg-1];
327 if ((mp->m_flag & MDELETED) != f) {
328 printf("%d: Inappropriate message\n", mesg);
329 return(-1);
330 }
331 return(0);
332}
333
334/*
335 * Scan out the list of string arguments, shell style
336 * for a RAWLIST.
337 */
338
339getrawlist(line, argv)
340 char line[];
341 char **argv;
342{
343 register char **ap, *cp, *cp2;
344 char linebuf[BUFSIZ], quotec;
345
346 ap = argv;
347 cp = line;
348 while (*cp != '\0') {
349 while (any(*cp, " \t"))
350 cp++;
351 cp2 = linebuf;
352 quotec = 0;
353 if (any(*cp, "'\""))
354 quotec = *cp++;
355 if (quotec == 0)
356 while (*cp != '\0' && !any(*cp, " \t"))
357 *cp2++ = *cp++;
358 else {
359 while (*cp != '\0' && *cp != quotec)
360 *cp2++ = *cp++;
361 if (*cp != '\0')
362 cp++;
363 }
364 *cp2 = '\0';
365 if (cp2 == linebuf)
366 break;
367 *ap++ = savestr(linebuf);
368 }
369 *ap = NOSTR;
370 return(ap-argv);
371}
372
373/*
374 * scan out a single lexical item and return its token number,
375 * updating the string pointer passed **p. Also, store the value
376 * of the number or string scanned in lexnumber or lexstring as
377 * appropriate. In any event, store the scanned `thing' in lexstring.
378 */
379
380struct lex {
381 char l_char;
382 char l_token;
383} singles[] = {
384 '$', TDOLLAR,
385 '.', TDOT,
386 '^', TUP,
387 '*', TSTAR,
388 '-', TDASH,
389 '+', TPLUS,
390 '(', TOPEN,
391 ')', TCLOSE,
392 0, 0
393};
394
395scan(sp)
396 char **sp;
397{
398 register char *cp, *cp2;
399 register int 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)
483 fprintf(stderr, "Missing %c\n", quotec);
484 *sp = --cp;
485 *cp2 = '\0';
486 return(TSTRING);
487}
488
489/*
490 * Unscan the named token by pushing it onto the regret stack.
491 */
492
493regret(token)
494{
495 if (++regretp >= REGDEP)
496 panic("Too many regrets");
497 regretstack[regretp] = token;
498 lexstring[STRINGLEN-1] = '\0';
499 stringstack[regretp] = savestr(lexstring);
500 numberstack[regretp] = lexnumber;
501}
502
503/*
504 * Reset all the scanner global variables.
505 */
506
507scaninit()
508{
509 regretp = -1;
510}
511
512/*
513 * Find the first message whose flags & m == f and return
514 * its message number.
515 */
516
517first(f, m)
518{
519 register int mesg;
520 register struct message *mp;
521
522 mesg = dot - &message[0] + 1;
523 f &= MDELETED;
524 m &= MDELETED;
525 for (mp = dot; mp < &message[msgCount]; mp++) {
526 if ((mp->m_flag & m) == f)
527 return(mesg);
528 mesg++;
529 }
530 mesg = dot - &message[0];
531 for (mp = dot-1; mp >= &message[0]; mp--) {
532 if ((mp->m_flag & m) == f)
533 return(mesg);
534 mesg--;
535 }
536 return(NULL);
537}
538
539/*
540 * See if the passed name sent the passed message number. Return true
541 * if so.
542 */
543
544sender(str, mesg)
545 char *str;
546{
547 register struct message *mp;
548 register char *cp;
549
550 mp = &message[mesg-1];
58e24fe2 551 cp = nameof(mp, 0);
b3149556
KS
552 return(icequal(cp, str));
553}
554
f7a4f91c
KS
555/*
556 * See if the given string matches inside the subject field of the
557 * given message. For the purpose of the scan, we ignore case differences.
558 * If it does, return true. The string search argument is assumed to
559 * have the form "/search-string." If it is of the form "/," we use the
560 * previous search string.
561 */
562
563char lastscan[128];
564
565matchsubj(str, mesg)
566 char *str;
567{
568 register struct message *mp;
312bdff6 569 register char *cp, *cp2, *backup;
f7a4f91c
KS
570
571 str++;
572 if (strlen(str) == 0)
573 str = lastscan;
574 else
575 strcpy(lastscan, str);
576 mp = &message[mesg-1];
577
578 /*
579 * Now look, ignoring case, for the word in the string.
580 */
581
582 cp = str;
583 cp2 = hfield("subject", mp);
584 if (cp2 == NOSTR)
585 return(0);
312bdff6 586 backup = cp2;
f7a4f91c
KS
587 while (*cp2) {
588 if (*cp == 0)
589 return(1);
312bdff6
KS
590 if (raise(*cp++) != raise(*cp2++)) {
591 cp2 = ++backup;
f7a4f91c 592 cp = str;
312bdff6 593 }
f7a4f91c
KS
594 }
595 return(*cp == 0);
596}
597
b3149556
KS
598/*
599 * Mark the named message by setting its mark bit.
600 */
601
602mark(mesg)
603{
604 register int i;
605
606 i = mesg;
607 if (i < 1 || i > msgCount)
608 panic("Bad message number to mark");
609 message[i-1].m_flag |= MMARK;
610}
611
612/*
613 * Unmark the named message.
614 */
615
616unmark(mesg)
617{
618 register int i;
619
620 i = mesg;
621 if (i < 1 || i > msgCount)
622 panic("Bad message number to unmark");
623 message[i-1].m_flag &= ~MMARK;
624}
625
626/*
627 * Return the message number corresponding to the passed meta character.
628 */
629
630metamess(meta, f)
631{
632 register int c, m;
633 register struct message *mp;
634
635 c = meta;
636 switch (c) {
637 case '^':
638 /*
639 * First 'good' message left.
640 */
641 for (mp = &message[0]; mp < &message[msgCount]; mp++)
642 if ((mp->m_flag & MDELETED) == f)
643 return(mp - &message[0] + 1);
644 printf("No applicable messages\n");
645 return(-1);
646
647 case '$':
648 /*
649 * Last 'good message left.
650 */
651 for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
652 if ((mp->m_flag & MDELETED) == f)
653 return(mp - &message[0] + 1);
654 printf("No applicable messages\n");
655 return(-1);
656
657 case '.':
658 /*
659 * Current message.
660 */
661 m = dot - &message[0] + 1;
662 if ((dot->m_flag & MDELETED) != f) {
663 printf("%d: Inappropriate message\n", m);
664 return(-1);
665 }
666 return(m);
667
668 default:
669 printf("Unknown metachar (%c)\n", c);
670 return(-1);
671 }
672}