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