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