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