date and time created 80/10/08 09:51:29 by kas
[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
12static char *SccsId = "@(#)list.c 1.1 %G%";
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
44markall(buf, f)
45 char buf[];
46{
47 register char **np;
48 register int i;
49 char *namelist[NMLSIZE], *bufp;
50 int tok, beg, mc, star, other, valdot;
51
52 valdot = dot - &message[0] + 1;
53 for (i = 1; i <= msgCount; i++)
54 unmark(i);
55 bufp = buf;
56 mc = 0;
57 np = &namelist[0];
58 scaninit();
59 tok = scan(&bufp);
60 star = 0;
61 other = 0;
62 beg = 0;
63 while (tok != TEOL) {
64 switch (tok) {
65 case TNUMBER:
66number:
67 if (star) {
68 printf("No numbers mixed with *\n");
69 return(-1);
70 }
71 mc++;
72 other++;
73 if (beg != 0) {
74 if (check(lexnumber, f))
75 return(-1);
76 for (i = beg; i <= lexnumber; i++)
77 mark(i);
78 beg = 0;
79 break;
80 }
81 beg = lexnumber;
82 if (check(beg, f))
83 return(-1);
84 tok = scan(&bufp);
85 regret(tok);
86 if (tok != TDASH) {
87 mark(beg);
88 beg = 0;
89 }
90 break;
91
92 case TPLUS:
93 if (beg != 0) {
94 printf("Non-numeric second argument\n");
95 return(-1);
96 }
97 if (valdot < msgCount)
98 mark(valdot+1);
99 else {
100 printf("Referencing beyond EOF\n");
101 return(-1);
102 }
103 break;
104
105 case TDASH:
106 if (beg == 0) {
107 if (valdot > 1)
108 mark(valdot-1);
109 else {
110 printf("Referencing before 1\n");
111 return(-1);
112 }
113 }
114 break;
115
116 case TSTRING:
117 if (beg != 0) {
118 printf("Non-numeric second argument\n");
119 return(-1);
120 }
121 other++;
122 *np++ = savestr(lexstring);
123 break;
124
125 case TDOLLAR:
126 case TUP:
127 case TDOT:
128 lexnumber = metamess(lexstring[0], f);
129 if (lexnumber == -1)
130 return(-1);
131 goto number;
132
133 case TSTAR:
134 if (other) {
135 printf("Can't mix \"*\" with anything\n");
136 return(-1);
137 }
138 star++;
139 break;
140 }
141 tok = scan(&bufp);
142 }
143 *np = NOSTR;
144 mc = 0;
145 if (star) {
146 for (i = 0; i < msgCount; i++)
147 if ((message[i].m_flag & MDELETED) == f) {
148 mark(i+1);
149 mc++;
150 }
151 if (mc == 0) {
152 printf("No applicable messages.\n");
153 return(-1);
154 }
155 return(0);
156 }
157
158 /*
159 * If no numbers were given, mark all of the messages,
160 * so that we can unmark any whose sender was not selected
161 * if any user names were given.
162 */
163
164 if (np > namelist && mc == 0)
165 for (i = 1; i <= msgCount; i++)
166 if ((message[i-1].m_flag & (MSAVED|MDELETED)) == f)
167 mark(i);
168
169 /*
170 * If any names were given, go through and eliminate any
171 * messages whose senders were not requested.
172 */
173
174 if (np > namelist) {
175 for (i = 1; i <= msgCount; i++) {
176 for (mc = 0, np = &namelist[0]; *np != NOSTR; np++)
177 if (sender(*np, i)) {
178 mc++;
179 break;
180 }
181 if (mc == 0)
182 unmark(i);
183 }
184
185 /*
186 * Make sure we got some decent messages.
187 */
188
189 mc = 0;
190 for (i = 1; i <= msgCount; i++)
191 if (message[i-1].m_flag & MMARK) {
192 mc++;
193 break;
194 }
195 if (mc == 0) {
196 printf("No applicable messages from {%s",
197 namelist[0]);
198 for (np = &namelist[1]; *np != NOSTR; np++)
199 printf(", %s", *np);
200 printf("}\n");
201 return(-1);
202 }
203 }
204 return(0);
205}
206
207/*
208 * Check the passed message number for legality and proper flags.
209 */
210
211check(mesg, f)
212{
213 register struct message *mp;
214
215 if (mesg < 1 || mesg > msgCount) {
216 printf("%d: Invalid message number\n", mesg);
217 return(-1);
218 }
219 mp = &message[mesg-1];
220 if ((mp->m_flag & MDELETED) != f) {
221 printf("%d: Inappropriate message\n", mesg);
222 return(-1);
223 }
224 return(0);
225}
226
227/*
228 * Scan out the list of string arguments, shell style
229 * for a RAWLIST.
230 */
231
232getrawlist(line, argv)
233 char line[];
234 char **argv;
235{
236 register char **ap, *cp, *cp2;
237 char linebuf[BUFSIZ], quotec;
238
239 ap = argv;
240 cp = line;
241 while (*cp != '\0') {
242 while (any(*cp, " \t"))
243 cp++;
244 cp2 = linebuf;
245 quotec = 0;
246 if (any(*cp, "'\""))
247 quotec = *cp++;
248 if (quotec == 0)
249 while (*cp != '\0' && !any(*cp, " \t"))
250 *cp2++ = *cp++;
251 else {
252 while (*cp != '\0' && *cp != quotec)
253 *cp2++ = *cp++;
254 if (*cp != '\0')
255 cp++;
256 }
257 *cp2 = '\0';
258 if (cp2 == linebuf)
259 break;
260 *ap++ = savestr(linebuf);
261 }
262 *ap = NOSTR;
263 return(ap-argv);
264}
265
266/*
267 * scan out a single lexical item and return its token number,
268 * updating the string pointer passed **p. Also, store the value
269 * of the number or string scanned in lexnumber or lexstring as
270 * appropriate. In any event, store the scanned `thing' in lexstring.
271 */
272
273struct lex {
274 char l_char;
275 char l_token;
276} singles[] = {
277 '$', TDOLLAR,
278 '.', TDOT,
279 '^', TUP,
280 '*', TSTAR,
281 '-', TDASH,
282 '+', TPLUS,
283 '(', TOPEN,
284 ')', TCLOSE,
285 0, 0
286};
287
288scan(sp)
289 char **sp;
290{
291 register char *cp, *cp2;
292 register int c;
293 register struct lex *lp;
294 int quotec;
295
296 if (regretp >= 0) {
297 copy(stringstack[regretp], lexstring);
298 lexnumber = numberstack[regretp];
299 return(regretstack[regretp--]);
300 }
301 cp = *sp;
302 cp2 = lexstring;
303 c = *cp++;
304
305 /*
306 * strip away leading white space.
307 */
308
309 while (any(c, " \t"))
310 c = *cp++;
311
312 /*
313 * If no characters remain, we are at end of line,
314 * so report that.
315 */
316
317 if (c == '\0') {
318 *sp = --cp;
319 return(TEOL);
320 }
321
322 /*
323 * If the leading character is a digit, scan
324 * the number and convert it on the fly.
325 * Return TNUMBER when done.
326 */
327
328 if (isdigit(c)) {
329 lexnumber = 0;
330 while (isdigit(c)) {
331 lexnumber = lexnumber*10 + c - '0';
332 *cp2++ = c;
333 c = *cp++;
334 }
335 *cp2 = '\0';
336 *sp = --cp;
337 return(TNUMBER);
338 }
339
340 /*
341 * Check for single character tokens; return such
342 * if found.
343 */
344
345 for (lp = &singles[0]; lp->l_char != 0; lp++)
346 if (c == lp->l_char) {
347 lexstring[0] = c;
348 lexstring[1] = '\0';
349 *sp = cp;
350 return(lp->l_token);
351 }
352
353 /*
354 * We've got a string! Copy all the characters
355 * of the string into lexstring, until we see
356 * a null, space, or tab.
357 * If the lead character is a " or ', save it
358 * and scan until you get another.
359 */
360
361 quotec = 0;
362 if (any(c, "'\"")) {
363 quotec = c;
364 c = *cp++;
365 }
366 while (c != '\0') {
367 if (c == quotec)
368 break;
369 if (quotec == 0 && any(c, " \t"))
370 break;
371 if (cp2 - lexstring < STRINGLEN-1)
372 *cp2++ = c;
373 c = *cp++;
374 }
375 if (quotec && c == 0)
376 fprintf(stderr, "Missing %c\n", quotec);
377 *sp = --cp;
378 *cp2 = '\0';
379 return(TSTRING);
380}
381
382/*
383 * Unscan the named token by pushing it onto the regret stack.
384 */
385
386regret(token)
387{
388 if (++regretp >= REGDEP)
389 panic("Too many regrets");
390 regretstack[regretp] = token;
391 lexstring[STRINGLEN-1] = '\0';
392 stringstack[regretp] = savestr(lexstring);
393 numberstack[regretp] = lexnumber;
394}
395
396/*
397 * Reset all the scanner global variables.
398 */
399
400scaninit()
401{
402 regretp = -1;
403}
404
405/*
406 * Find the first message whose flags & m == f and return
407 * its message number.
408 */
409
410first(f, m)
411{
412 register int mesg;
413 register struct message *mp;
414
415 mesg = dot - &message[0] + 1;
416 f &= MDELETED;
417 m &= MDELETED;
418 for (mp = dot; mp < &message[msgCount]; mp++) {
419 if ((mp->m_flag & m) == f)
420 return(mesg);
421 mesg++;
422 }
423 mesg = dot - &message[0];
424 for (mp = dot-1; mp >= &message[0]; mp--) {
425 if ((mp->m_flag & m) == f)
426 return(mesg);
427 mesg--;
428 }
429 return(NULL);
430}
431
432/*
433 * See if the passed name sent the passed message number. Return true
434 * if so.
435 */
436
437sender(str, mesg)
438 char *str;
439{
440 register struct message *mp;
441 register char *cp;
442
443 mp = &message[mesg-1];
444 cp = nameof(mp);
445 return(icequal(cp, str));
446}
447
448/*
449 * Mark the named message by setting its mark bit.
450 */
451
452mark(mesg)
453{
454 register int i;
455
456 i = mesg;
457 if (i < 1 || i > msgCount)
458 panic("Bad message number to mark");
459 message[i-1].m_flag |= MMARK;
460}
461
462/*
463 * Unmark the named message.
464 */
465
466unmark(mesg)
467{
468 register int i;
469
470 i = mesg;
471 if (i < 1 || i > msgCount)
472 panic("Bad message number to unmark");
473 message[i-1].m_flag &= ~MMARK;
474}
475
476/*
477 * Return the message number corresponding to the passed meta character.
478 */
479
480metamess(meta, f)
481{
482 register int c, m;
483 register struct message *mp;
484
485 c = meta;
486 switch (c) {
487 case '^':
488 /*
489 * First 'good' message left.
490 */
491 for (mp = &message[0]; mp < &message[msgCount]; mp++)
492 if ((mp->m_flag & MDELETED) == f)
493 return(mp - &message[0] + 1);
494 printf("No applicable messages\n");
495 return(-1);
496
497 case '$':
498 /*
499 * Last 'good message left.
500 */
501 for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
502 if ((mp->m_flag & MDELETED) == f)
503 return(mp - &message[0] + 1);
504 printf("No applicable messages\n");
505 return(-1);
506
507 case '.':
508 /*
509 * Current message.
510 */
511 m = dot - &message[0] + 1;
512 if ((dot->m_flag & MDELETED) != f) {
513 printf("%d: Inappropriate message\n", m);
514 return(-1);
515 }
516 return(m);
517
518 default:
519 printf("Unknown metachar (%c)\n", c);
520 return(-1);
521 }
522}