xref: /original-bsd/usr.bin/mail/list.c (revision 552e81d8)
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.1 10/08/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 (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 
211 check(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 
232 getrawlist(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 
273 struct 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 
288 scan(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 
386 regret(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 
400 scaninit()
401 {
402 	regretp = -1;
403 }
404 
405 /*
406  * Find the first message whose flags & m == f  and return
407  * its message number.
408  */
409 
410 first(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 
437 sender(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 
452 mark(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 
466 unmark(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 
480 metamess(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 }
523