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