xref: /original-bsd/usr.bin/mail/list.c (revision ba72ef4c)
1 #
2 
3 #include "rcv.h"
4 #include <ctype.h>
5 
6 /*
7  * Mail -- a mail program
8  *
9  * Message list handling.
10  */
11 
12 static char *SccsId = "@(#)list.c	1.3 10/10/80";
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 
21 getmsglist(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 
44 markall(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:
66 number:
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 (**np == '/') {
178 					if (matchsubj(*np, i)) {
179 						mc++;
180 						break;
181 					}
182 				}
183 				else {
184 					if (sender(*np, i)) {
185 						mc++;
186 						break;
187 					}
188 				}
189 			if (mc == 0)
190 				unmark(i);
191 		}
192 
193 		/*
194 		 * Make sure we got some decent messages.
195 		 */
196 
197 		mc = 0;
198 		for (i = 1; i <= msgCount; i++)
199 			if (message[i-1].m_flag & MMARK) {
200 				mc++;
201 				break;
202 			}
203 		if (mc == 0) {
204 			printf("No applicable messages from {%s",
205 				namelist[0]);
206 			for (np = &namelist[1]; *np != NOSTR; np++)
207 				printf(", %s", *np);
208 			printf("}\n");
209 			return(-1);
210 		}
211 	}
212 	return(0);
213 }
214 
215 /*
216  * Check the passed message number for legality and proper flags.
217  */
218 
219 check(mesg, f)
220 {
221 	register struct message *mp;
222 
223 	if (mesg < 1 || mesg > msgCount) {
224 		printf("%d: Invalid message number\n", mesg);
225 		return(-1);
226 	}
227 	mp = &message[mesg-1];
228 	if ((mp->m_flag & MDELETED) != f) {
229 		printf("%d: Inappropriate message\n", mesg);
230 		return(-1);
231 	}
232 	return(0);
233 }
234 
235 /*
236  * Scan out the list of string arguments, shell style
237  * for a RAWLIST.
238  */
239 
240 getrawlist(line, argv)
241 	char line[];
242 	char **argv;
243 {
244 	register char **ap, *cp, *cp2;
245 	char linebuf[BUFSIZ], quotec;
246 
247 	ap = argv;
248 	cp = line;
249 	while (*cp != '\0') {
250 		while (any(*cp, " \t"))
251 			cp++;
252 		cp2 = linebuf;
253 		quotec = 0;
254 		if (any(*cp, "'\""))
255 			quotec = *cp++;
256 		if (quotec == 0)
257 			while (*cp != '\0' && !any(*cp, " \t"))
258 				*cp2++ = *cp++;
259 		else {
260 			while (*cp != '\0' && *cp != quotec)
261 				*cp2++ = *cp++;
262 			if (*cp != '\0')
263 				cp++;
264 		}
265 		*cp2 = '\0';
266 		if (cp2 == linebuf)
267 			break;
268 		*ap++ = savestr(linebuf);
269 	}
270 	*ap = NOSTR;
271 	return(ap-argv);
272 }
273 
274 /*
275  * scan out a single lexical item and return its token number,
276  * updating the string pointer passed **p.  Also, store the value
277  * of the number or string scanned in lexnumber or lexstring as
278  * appropriate.  In any event, store the scanned `thing' in lexstring.
279  */
280 
281 struct lex {
282 	char	l_char;
283 	char	l_token;
284 } singles[] = {
285 	'$',	TDOLLAR,
286 	'.',	TDOT,
287 	'^',	TUP,
288 	'*',	TSTAR,
289 	'-',	TDASH,
290 	'+',	TPLUS,
291 	'(',	TOPEN,
292 	')',	TCLOSE,
293 	0,	0
294 };
295 
296 scan(sp)
297 	char **sp;
298 {
299 	register char *cp, *cp2;
300 	register int c;
301 	register struct lex *lp;
302 	int quotec;
303 
304 	if (regretp >= 0) {
305 		copy(stringstack[regretp], lexstring);
306 		lexnumber = numberstack[regretp];
307 		return(regretstack[regretp--]);
308 	}
309 	cp = *sp;
310 	cp2 = lexstring;
311 	c = *cp++;
312 
313 	/*
314 	 * strip away leading white space.
315 	 */
316 
317 	while (any(c, " \t"))
318 		c = *cp++;
319 
320 	/*
321 	 * If no characters remain, we are at end of line,
322 	 * so report that.
323 	 */
324 
325 	if (c == '\0') {
326 		*sp = --cp;
327 		return(TEOL);
328 	}
329 
330 	/*
331 	 * If the leading character is a digit, scan
332 	 * the number and convert it on the fly.
333 	 * Return TNUMBER when done.
334 	 */
335 
336 	if (isdigit(c)) {
337 		lexnumber = 0;
338 		while (isdigit(c)) {
339 			lexnumber = lexnumber*10 + c - '0';
340 			*cp2++ = c;
341 			c = *cp++;
342 		}
343 		*cp2 = '\0';
344 		*sp = --cp;
345 		return(TNUMBER);
346 	}
347 
348 	/*
349 	 * Check for single character tokens; return such
350 	 * if found.
351 	 */
352 
353 	for (lp = &singles[0]; lp->l_char != 0; lp++)
354 		if (c == lp->l_char) {
355 			lexstring[0] = c;
356 			lexstring[1] = '\0';
357 			*sp = cp;
358 			return(lp->l_token);
359 		}
360 
361 	/*
362 	 * We've got a string!  Copy all the characters
363 	 * of the string into lexstring, until we see
364 	 * a null, space, or tab.
365 	 * If the lead character is a " or ', save it
366 	 * and scan until you get another.
367 	 */
368 
369 	quotec = 0;
370 	if (any(c, "'\"")) {
371 		quotec = c;
372 		c = *cp++;
373 	}
374 	while (c != '\0') {
375 		if (c == quotec)
376 			break;
377 		if (quotec == 0 && any(c, " \t"))
378 			break;
379 		if (cp2 - lexstring < STRINGLEN-1)
380 			*cp2++ = c;
381 		c = *cp++;
382 	}
383 	if (quotec && c == 0)
384 		fprintf(stderr, "Missing %c\n", quotec);
385 	*sp = --cp;
386 	*cp2 = '\0';
387 	return(TSTRING);
388 }
389 
390 /*
391  * Unscan the named token by pushing it onto the regret stack.
392  */
393 
394 regret(token)
395 {
396 	if (++regretp >= REGDEP)
397 		panic("Too many regrets");
398 	regretstack[regretp] = token;
399 	lexstring[STRINGLEN-1] = '\0';
400 	stringstack[regretp] = savestr(lexstring);
401 	numberstack[regretp] = lexnumber;
402 }
403 
404 /*
405  * Reset all the scanner global variables.
406  */
407 
408 scaninit()
409 {
410 	regretp = -1;
411 }
412 
413 /*
414  * Find the first message whose flags & m == f  and return
415  * its message number.
416  */
417 
418 first(f, m)
419 {
420 	register int mesg;
421 	register struct message *mp;
422 
423 	mesg = dot - &message[0] + 1;
424 	f &= MDELETED;
425 	m &= MDELETED;
426 	for (mp = dot; mp < &message[msgCount]; mp++) {
427 		if ((mp->m_flag & m) == f)
428 			return(mesg);
429 		mesg++;
430 	}
431 	mesg = dot - &message[0];
432 	for (mp = dot-1; mp >= &message[0]; mp--) {
433 		if ((mp->m_flag & m) == f)
434 			return(mesg);
435 		mesg--;
436 	}
437 	return(NULL);
438 }
439 
440 /*
441  * See if the passed name sent the passed message number.  Return true
442  * if so.
443  */
444 
445 sender(str, mesg)
446 	char *str;
447 {
448 	register struct message *mp;
449 	register char *cp;
450 
451 	mp = &message[mesg-1];
452 	cp = nameof(mp);
453 	return(icequal(cp, str));
454 }
455 
456 /*
457  * See if the given string matches inside the subject field of the
458  * given message.  For the purpose of the scan, we ignore case differences.
459  * If it does, return true.  The string search argument is assumed to
460  * have the form "/search-string."  If it is of the form "/," we use the
461  * previous search string.
462  */
463 
464 char lastscan[128];
465 
466 matchsubj(str, mesg)
467 	char *str;
468 {
469 	register struct message *mp;
470 	register char *cp, *cp2, *backup;
471 
472 	str++;
473 	if (strlen(str) == 0)
474 		str = lastscan;
475 	else
476 		strcpy(lastscan, str);
477 	mp = &message[mesg-1];
478 
479 	/*
480 	 * Now look, ignoring case, for the word in the string.
481 	 */
482 
483 	cp = str;
484 	cp2 = hfield("subject", mp);
485 	if (cp2 == NOSTR)
486 		return(0);
487 	backup = cp2;
488 	while (*cp2) {
489 		if (*cp == 0)
490 			return(1);
491 		if (raise(*cp++) != raise(*cp2++)) {
492 			cp2 = ++backup;
493 			cp = str;
494 		}
495 	}
496 	return(*cp == 0);
497 }
498 
499 /*
500  * Mark the named message by setting its mark bit.
501  */
502 
503 mark(mesg)
504 {
505 	register int i;
506 
507 	i = mesg;
508 	if (i < 1 || i > msgCount)
509 		panic("Bad message number to mark");
510 	message[i-1].m_flag |= MMARK;
511 }
512 
513 /*
514  * Unmark the named message.
515  */
516 
517 unmark(mesg)
518 {
519 	register int i;
520 
521 	i = mesg;
522 	if (i < 1 || i > msgCount)
523 		panic("Bad message number to unmark");
524 	message[i-1].m_flag &= ~MMARK;
525 }
526 
527 /*
528  * Return the message number corresponding to the passed meta character.
529  */
530 
531 metamess(meta, f)
532 {
533 	register int c, m;
534 	register struct message *mp;
535 
536 	c = meta;
537 	switch (c) {
538 	case '^':
539 		/*
540 		 * First 'good' message left.
541 		 */
542 		for (mp = &message[0]; mp < &message[msgCount]; mp++)
543 			if ((mp->m_flag & MDELETED) == f)
544 				return(mp - &message[0] + 1);
545 		printf("No applicable messages\n");
546 		return(-1);
547 
548 	case '$':
549 		/*
550 		 * Last 'good message left.
551 		 */
552 		for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
553 			if ((mp->m_flag & MDELETED) == f)
554 				return(mp - &message[0] + 1);
555 		printf("No applicable messages\n");
556 		return(-1);
557 
558 	case '.':
559 		/*
560 		 * Current message.
561 		 */
562 		m = dot - &message[0] + 1;
563 		if ((dot->m_flag & MDELETED) != f) {
564 			printf("%d: Inappropriate message\n", m);
565 			return(-1);
566 		}
567 		return(m);
568 
569 	default:
570 		printf("Unknown metachar (%c)\n", c);
571 		return(-1);
572 	}
573 }
574