dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / mailx / list.c
blob6e8d9df358e7de2ad2d66840bcc1c15dd8889265
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
39 #include "rcv.h"
40 #include <locale.h>
41 #include <stdlib.h>
42 #include <string.h>
45 * mailx -- a modified version of a University of California at Berkeley
46 * mail program
48 * Message list handling.
51 static int check(int mesg, int f);
52 static int evalcol(int col);
53 static int isinteger(char *buf);
54 static void mark(int mesg);
55 static int markall(char buf[], int f);
56 static int matchsubj(char *str, int mesg);
57 static int metamess(int meta, int f);
58 static void regret(int token);
59 static int scan(char **sp);
60 static void scaninit(void);
61 static int sender(char *str, int mesg);
62 static void unmark(int mesg);
65 * Process message operand list.
66 * Convert the user string of message numbers and
67 * store the numbers into vector.
69 * Returns the count of messages picked up or -1 on error.
71 int
72 getmessage(char *buf, int *vector, int flags)
74 register int *ip;
75 register struct message *mp;
76 int firstmsg = -1;
77 char delims[] = "\t- ";
78 char *result = NULL;
80 if (markall(buf, flags) < 0)
81 return (-1);
82 ip = vector;
85 * Check for first message number and make sure it is
86 * at the beginning of the vector.
88 result = strtok(buf, delims);
89 if (result != NULL && isinteger(result)) {
90 firstmsg = atoi(result);
91 *ip++ = firstmsg;
95 * Add marked messages to vector and skip first
96 * message number because it is already at the
97 * beginning of the vector
99 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
100 if (firstmsg == mp - &message[0] + 1)
101 continue;
102 if (mp->m_flag & MMARK)
103 *ip++ = mp - &message[0] + 1;
105 *ip = 0;
106 return (ip - vector);
110 * Check to see if string is an integer
112 * Returns 1 if is an integer and 0 if it is not
114 static int
115 isinteger(char *buf)
117 int i, result = 1;
119 /* check for empty string */
120 if (strcmp(buf, "") == 0) {
121 result = 0;
122 return (result);
125 i = 0;
126 while (buf[i] != '\0') {
127 if (!isdigit(buf[i])) {
128 result = 0;
129 break;
131 i++;
133 return (result);
137 * Process msglist operand list.
138 * Convert the user string of message numbers and
139 * store the numbers into vector.
141 * Returns the count of messages picked up or -1 on error.
145 getmsglist(char *buf, int *vector, int flags)
147 register int *ip;
148 register struct message *mp;
150 if (markall(buf, flags) < 0)
151 return (-1);
152 ip = vector;
153 for (mp = &message[0]; mp < &message[msgCount]; mp++)
154 if (mp->m_flag & MMARK)
155 *ip++ = mp - &message[0] + 1;
156 *ip = 0;
157 return (ip - vector);
162 * Mark all messages that the user wanted from the command
163 * line in the message structure. Return 0 on success, -1
164 * on error.
168 * Bit values for colon modifiers.
171 #define CMNEW 01 /* New messages */
172 #define CMOLD 02 /* Old messages */
173 #define CMUNREAD 04 /* Unread messages */
174 #define CMDELETED 010 /* Deleted messages */
175 #define CMREAD 020 /* Read messages */
178 * The following table describes the letters which can follow
179 * the colon and gives the corresponding modifier bit.
182 static struct coltab {
183 char co_char; /* What to find past : */
184 int co_bit; /* Associated modifier bit */
185 int co_mask; /* m_status bits to mask */
186 int co_equal; /* ... must equal this */
187 } coltab[] = {
188 'n', CMNEW, MNEW, MNEW,
189 'o', CMOLD, MNEW, 0,
190 'u', CMUNREAD, MREAD, 0,
191 'd', CMDELETED, MDELETED, MDELETED,
192 'r', CMREAD, MREAD, MREAD,
193 0, 0, 0, 0
196 static int lastcolmod;
198 static int
199 markall(char buf[], int f)
201 register char **np;
202 register int i;
203 register struct message *mp;
204 char *namelist[NMLSIZE], *bufp;
205 int tok, beg, mc, star, other, colmod, colresult;
207 colmod = 0;
208 for (i = 1; i <= msgCount; i++)
209 unmark(i);
210 bufp = buf;
211 mc = 0;
212 np = &namelist[0];
213 scaninit();
214 tok = scan(&bufp);
215 star = 0;
216 other = 0;
217 beg = 0;
218 while (tok != TEOL) {
219 switch (tok) {
220 case TNUMBER:
221 number:
222 if (star) {
223 printf(gettext("No numbers mixed with *\n"));
224 return (-1);
226 mc++;
227 other++;
228 if (beg != 0) {
229 if (check(lexnumber, f))
230 return (-1);
231 for (i = beg; i <= lexnumber; i++)
232 if ((message[i-1].m_flag&MDELETED) == f)
233 mark(i);
234 beg = 0;
235 break;
237 beg = lexnumber;
238 if (check(beg, f))
239 return (-1);
240 tok = scan(&bufp);
241 if (tok != TDASH) {
242 regret(tok);
243 mark(beg);
244 beg = 0;
246 break;
248 case TSTRING:
249 if (beg != 0) {
250 printf(gettext(
251 "Non-numeric second argument\n"));
252 return (-1);
254 other++;
255 if (lexstring[0] == ':') {
256 colresult = evalcol(lexstring[1]);
257 if (colresult == 0) {
258 printf(gettext(
259 "Unknown colon modifier \"%s\"\n"),
260 lexstring);
261 return (-1);
263 colmod |= colresult;
265 else
266 *np++ = savestr(lexstring);
267 break;
269 case TDASH:
270 case TPLUS:
271 case TDOLLAR:
272 case TUP:
273 case TDOT:
274 lexnumber = metamess(lexstring[0], f);
275 if (lexnumber == -1)
276 return (-1);
277 goto number;
279 case TSTAR:
280 if (other) {
281 printf(gettext(
282 "Can't mix \"*\" with anything\n"));
283 return (-1);
285 star++;
286 break;
288 tok = scan(&bufp);
290 lastcolmod = colmod;
291 *np = NOSTR;
292 mc = 0;
293 if (star) {
294 for (i = 0; i < msgCount; i++)
295 if ((message[i].m_flag & MDELETED) == f) {
296 mark(i+1);
297 mc++;
299 if (mc == 0) {
300 printf(gettext("No applicable messages\n"));
301 return (-1);
303 return (0);
307 * If no numbers were given, mark all of the messages,
308 * so that we can unmark any whose sender was not selected
309 * if any user names were given.
312 if ((np > namelist || colmod != 0) && mc == 0)
313 for (i = 1; i <= msgCount; i++)
314 if ((message[i-1].m_flag & MDELETED) == f)
315 mark(i);
318 * If any names were given, go through and eliminate any
319 * messages whose senders were not requested.
322 if (np > namelist) {
323 for (i = 1; i <= msgCount; i++) {
324 for (mc = 0, np = &namelist[0]; *np != NOSTR; np++)
325 if (**np == '/') {
326 if (matchsubj(*np, i)) {
327 mc++;
328 break;
330 } else {
331 if (sender(*np, i)) {
332 mc++;
333 break;
336 if (mc == 0)
337 unmark(i);
341 * Make sure we got some decent messages.
344 mc = 0;
345 for (i = 1; i <= msgCount; i++)
346 if (message[i-1].m_flag & MMARK) {
347 mc++;
348 break;
350 if (mc == 0) {
351 printf(gettext("No applicable messages from {%s"),
352 namelist[0]);
353 for (np = &namelist[1]; *np != NOSTR; np++)
354 printf(", %s", *np);
355 printf("}\n");
356 return (-1);
361 * If any colon modifiers were given, go through and
362 * unmark any messages which do not satisfy the modifiers.
365 if (colmod != 0) {
366 for (i = 1; i <= msgCount; i++) {
367 register struct coltab *colp;
369 mp = &message[i - 1];
370 for (colp = &coltab[0]; colp->co_char; colp++)
371 if (colp->co_bit & colmod)
372 if ((mp->m_flag & colp->co_mask)
373 != colp->co_equal)
374 unmark(i);
377 for (mp = &message[0]; mp < &message[msgCount]; mp++)
378 if (mp->m_flag & MMARK)
379 break;
380 if (mp >= &message[msgCount]) {
381 register struct coltab *colp;
383 printf(gettext("No messages satisfy"));
384 for (colp = &coltab[0]; colp->co_char; colp++)
385 if (colp->co_bit & colmod)
386 printf(" :%c", colp->co_char);
387 printf("\n");
388 return (-1);
391 return (0);
395 * Turn the character after a colon modifier into a bit
396 * value.
398 static int
399 evalcol(int col)
401 register struct coltab *colp;
403 if (col == 0)
404 return (lastcolmod);
405 for (colp = &coltab[0]; colp->co_char; colp++)
406 if (colp->co_char == col)
407 return (colp->co_bit);
408 return (0);
412 * Check the passed message number for legality and proper flags.
414 static int
415 check(int mesg, int f)
417 register struct message *mp;
419 if (mesg < 1 || mesg > msgCount) {
420 printf(gettext("%d: Invalid message number\n"), mesg);
421 return (-1);
423 mp = &message[mesg-1];
424 if ((mp->m_flag & MDELETED) != f) {
425 printf(gettext("%d: Inappropriate message\n"), mesg);
426 return (-1);
428 return (0);
432 * Scan out the list of string arguments, shell style
433 * for a RAWLIST.
437 getrawlist(char line[], char **argv, int argc)
439 register char **ap, *cp, *cp2;
440 char linebuf[LINESIZE], quotec;
441 register char **last;
443 ap = argv;
444 cp = line;
445 last = argv + argc - 1;
446 while (*cp != '\0') {
447 while (any(*cp, " \t"))
448 cp++;
449 cp2 = linebuf;
450 quotec = 0;
451 while (*cp != '\0') {
452 if (quotec) {
453 if (*cp == quotec) {
454 quotec = 0;
455 cp++;
456 } else
457 *cp2++ = *cp++;
458 } else {
459 if (*cp == '\\') {
460 if (*(cp+1) != '\0') {
461 *cp2++ = *++cp;
462 cp++;
463 } else {
464 printf(gettext(
465 "Trailing \\; ignoring\n"));
466 break;
469 if (any(*cp, " \t"))
470 break;
471 if (any(*cp, "'\""))
472 quotec = *cp++;
473 else
474 *cp2++ = *cp++;
477 *cp2 = '\0';
478 if (cp2 == linebuf)
479 break;
480 if (ap >= last) {
481 printf(gettext("Too many elements in the list;"
482 " excess discarded\n"));
483 break;
485 *ap++ = savestr(linebuf);
487 *ap = NOSTR;
488 return (ap-argv);
492 * scan out a single lexical item and return its token number,
493 * updating the string pointer passed **p. Also, store the value
494 * of the number or string scanned in lexnumber or lexstring as
495 * appropriate. In any event, store the scanned `thing' in lexstring.
498 static struct lex {
499 char l_char;
500 char l_token;
501 } singles[] = {
502 '$', TDOLLAR,
503 '.', TDOT,
504 '^', TUP,
505 '*', TSTAR,
506 '-', TDASH,
507 '+', TPLUS,
508 '(', TOPEN,
509 ')', TCLOSE,
510 0, 0
513 static int
514 scan(char **sp)
516 register char *cp, *cp2;
517 register char c;
518 register struct lex *lp;
519 int quotec;
521 if (regretp >= 0) {
522 copy(stringstack[regretp], lexstring);
523 lexnumber = numberstack[regretp];
524 return (regretstack[regretp--]);
526 cp = *sp;
527 cp2 = lexstring;
528 c = *cp++;
531 * strip away leading white space.
534 while (any(c, " \t"))
535 c = *cp++;
538 * If no characters remain, we are at end of line,
539 * so report that.
542 if (c == '\0') {
543 *sp = --cp;
544 return (TEOL);
548 * If the leading character is a digit, scan
549 * the number and convert it on the fly.
550 * Return TNUMBER when done.
553 if (isdigit(c)) {
554 lexnumber = 0;
555 while (isdigit(c)) {
556 lexnumber = lexnumber*10 + c - '0';
557 *cp2++ = c;
558 c = *cp++;
560 *cp2 = '\0';
561 *sp = --cp;
562 return (TNUMBER);
566 * Check for single character tokens; return such
567 * if found.
570 for (lp = &singles[0]; lp->l_char != 0; lp++)
571 if (c == lp->l_char) {
572 lexstring[0] = c;
573 lexstring[1] = '\0';
574 *sp = cp;
575 return (lp->l_token);
579 * We've got a string! Copy all the characters
580 * of the string into lexstring, until we see
581 * a null, space, or tab.
582 * If the lead character is a " or ', save it
583 * and scan until you get another.
586 quotec = 0;
587 if (any(c, "'\"")) {
588 quotec = c;
589 c = *cp++;
591 while (c != '\0') {
592 if (quotec == 0 && c == '\\') {
593 if (*cp != '\0') {
594 c = *cp++;
595 } else {
596 fprintf(stderr, gettext("Trailing \\; "
597 "ignoring\n"));
600 if (c == quotec) {
601 cp++;
602 break;
604 if (quotec == 0 && any(c, " \t"))
605 break;
606 if (cp2 - lexstring < STRINGLEN-1)
607 *cp2++ = c;
608 c = *cp++;
610 if (quotec && c == 0)
611 fprintf(stderr, gettext("Missing %c\n"), quotec);
612 *sp = --cp;
613 *cp2 = '\0';
614 return (TSTRING);
618 * Unscan the named token by pushing it onto the regret stack.
621 static void
622 regret(int token)
624 if (++regretp >= REGDEP)
625 panic("Too many regrets");
626 regretstack[regretp] = token;
627 lexstring[STRINGLEN-1] = '\0';
628 stringstack[regretp] = savestr(lexstring);
629 numberstack[regretp] = lexnumber;
633 * Reset all the scanner global variables.
636 static void
637 scaninit(void)
639 regretp = -1;
643 * Find the first message whose flags & m == f and return
644 * its message number.
648 first(int f, int m)
650 register int mesg;
651 register struct message *mp;
653 mesg = dot - &message[0] + 1;
654 f &= MDELETED;
655 m &= MDELETED;
656 for (mp = dot; mp < &message[msgCount]; mp++) {
657 if ((mp->m_flag & m) == f)
658 return (mesg);
659 mesg++;
661 mesg = dot - &message[0];
662 for (mp = dot-1; mp >= &message[0]; mp--) {
663 if ((mp->m_flag & m) == f)
664 return (mesg);
665 mesg--;
667 return (0);
671 * See if the passed name sent the passed message number. Return true
672 * if so.
674 static int
675 sender(char *str, int mesg)
677 return (samebody(str, skin(nameof(&message[mesg-1])), TRUE));
681 * See if the given string matches inside the subject field of the
682 * given message. For the purpose of the scan, we ignore case differences.
683 * If it does, return true. The string search argument is assumed to
684 * have the form "/search-string." If it is of the form "/," we use the
685 * previous search string.
688 static char lastscan[128];
690 static int
691 matchsubj(char *str, int mesg)
693 register struct message *mp;
694 register char *cp, *cp2, *backup;
696 str++;
697 if (strlen(str) == 0)
698 str = lastscan;
699 else
700 nstrcpy(lastscan, sizeof (lastscan), str);
701 mp = &message[mesg-1];
704 * Now look, ignoring case, for the word in the string.
707 cp = str;
708 cp2 = hfield("subject", mp, addone);
709 if (cp2 == NOSTR)
710 return (0);
711 backup = cp2;
712 while (*cp2) {
713 if (*cp == 0)
714 return (1);
715 if (toupper(*cp++) != toupper(*cp2++)) {
716 cp2 = ++backup;
717 cp = str;
720 return (*cp == 0);
724 * Mark the named message by setting its mark bit.
727 static void
728 mark(int mesg)
730 register int i;
732 i = mesg;
733 if (i < 1 || i > msgCount)
734 panic("Bad message number to mark");
735 message[i-1].m_flag |= MMARK;
739 * Unmark the named message.
742 static void
743 unmark(int mesg)
745 register int i;
747 i = mesg;
748 if (i < 1 || i > msgCount)
749 panic("Bad message number to unmark");
750 message[i-1].m_flag &= ~MMARK;
754 * Return the message number corresponding to the passed meta character.
756 static int
757 metamess(int meta, int f)
759 register int c, m;
760 register struct message *mp;
762 c = meta;
763 switch (c) {
764 case '^':
766 * First 'good' message left.
768 for (mp = &message[0]; mp < &message[msgCount]; mp++)
769 if ((mp->m_flag & MDELETED) == f)
770 return (mp - &message[0] + 1);
771 printf(gettext("No applicable messages\n"));
772 return (-1);
774 case '+':
776 * Next 'good' message left.
778 for (mp = dot + 1; mp < &message[msgCount]; mp++)
779 if ((mp->m_flag & MDELETED) == f)
780 return (mp - &message[0] + 1);
781 printf(gettext("Referencing beyond last message\n"));
782 return (-1);
784 case '-':
786 * Previous 'good' message.
788 for (mp = dot - 1; mp >= &message[0]; mp--)
789 if ((mp->m_flag & MDELETED) == f)
790 return (mp - &message[0] + 1);
791 printf(gettext("Referencing before first message\n"));
792 return (-1);
794 case '$':
796 * Last 'good message left.
798 for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
799 if ((mp->m_flag & MDELETED) == f)
800 return (mp - &message[0] + 1);
801 printf(gettext("No applicable messages\n"));
802 return (-1);
804 case '.':
806 * Current message.
808 m = dot - &message[0] + 1;
809 if ((dot->m_flag & MDELETED) != f) {
810 printf(gettext("%d: Inappropriate message\n"), m);
811 return (-1);
813 return (m);
815 default:
816 printf(gettext("Unknown metachar (%c)\n"), c);
817 return (-1);