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