8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / mailx / list.c
blob892fdb4641df706544189b40460266967a97ee6f
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 #pragma ident "%Z%%M% %I% %E% SMI"
41 #include "rcv.h"
42 #include <locale.h>
43 #include <stdlib.h>
44 #include <string.h>
47 * mailx -- a modified version of a University of California at Berkeley
48 * mail program
50 * Message list handling.
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);
67 * Process message operand list.
68 * Convert the user string of message numbers and
69 * store the numbers into vector.
71 * Returns the count of messages picked up or -1 on error.
73 int
74 getmessage(char *buf, int *vector, int flags)
76 register int *ip;
77 register struct message *mp;
78 int firstmsg = -1;
79 char delims[] = "\t- ";
80 char *result = NULL;
82 if (markall(buf, flags) < 0)
83 return (-1);
84 ip = vector;
87 * Check for first message number and make sure it is
88 * at the beginning of the vector.
90 result = strtok(buf, delims);
91 if (result != NULL && isinteger(result)) {
92 firstmsg = atoi(result);
93 *ip++ = firstmsg;
97 * Add marked messages to vector and skip first
98 * message number because it is already at the
99 * beginning of the vector
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;
107 *ip = NULL;
108 return (ip - vector);
112 * Check to see if string is an integer
114 * Returns 1 if is an integer and 0 if it is not
116 static int
117 isinteger(char *buf)
119 int i, result = 1;
121 /* check for empty string */
122 if (strcmp(buf, "") == 0) {
123 result = 0;
124 return (result);
127 i = 0;
128 while (buf[i] != '\0') {
129 if (!isdigit(buf[i])) {
130 result = 0;
131 break;
133 i++;
135 return (result);
139 * Process msglist operand list.
140 * Convert the user string of message numbers and
141 * store the numbers into vector.
143 * Returns the count of messages picked up or -1 on error.
147 getmsglist(char *buf, int *vector, int flags)
149 register int *ip;
150 register struct message *mp;
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);
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.
170 * Bit values for colon modifiers.
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 */
180 * The following table describes the letters which can follow
181 * the colon and gives the corresponding modifier bit.
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
198 static int lastcolmod;
200 static int
201 markall(char buf[], int f)
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;
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);
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;
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;
248 break;
250 case TSTRING:
251 if (beg != 0) {
252 printf(gettext(
253 "Non-numeric second argument\n"));
254 return (-1);
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);
265 colmod |= colresult;
267 else
268 *np++ = savestr(lexstring);
269 break;
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;
281 case TSTAR:
282 if (other) {
283 printf(gettext(
284 "Can't mix \"*\" with anything\n"));
285 return (-1);
287 star++;
288 break;
290 tok = scan(&bufp);
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++;
301 if (mc == 0) {
302 printf(gettext("No applicable messages\n"));
303 return (-1);
305 return (0);
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.
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);
320 * If any names were given, go through and eliminate any
321 * messages whose senders were not requested.
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;
332 } else {
333 if (sender(*np, i)) {
334 mc++;
335 break;
338 if (mc == 0)
339 unmark(i);
343 * Make sure we got some decent messages.
346 mc = 0;
347 for (i = 1; i <= msgCount; i++)
348 if (message[i-1].m_flag & MMARK) {
349 mc++;
350 break;
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);
363 * If any colon modifiers were given, go through and
364 * unmark any messages which do not satisfy the modifiers.
367 if (colmod != 0) {
368 for (i = 1; i <= msgCount; i++) {
369 register struct coltab *colp;
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);
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;
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);
393 return (0);
397 * Turn the character after a colon modifier into a bit
398 * value.
400 static int
401 evalcol(int col)
403 register struct coltab *colp;
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);
414 * Check the passed message number for legality and proper flags.
416 static int
417 check(int mesg, int f)
419 register struct message *mp;
421 if (mesg < 1 || mesg > msgCount) {
422 printf(gettext("%d: Invalid message number\n"), mesg);
423 return (-1);
425 mp = &message[mesg-1];
426 if ((mp->m_flag & MDELETED) != f) {
427 printf(gettext("%d: Inappropriate message\n"), mesg);
428 return (-1);
430 return (0);
434 * Scan out the list of string arguments, shell style
435 * for a RAWLIST.
439 getrawlist(char line[], char **argv, int argc)
441 register char **ap, *cp, *cp2;
442 char linebuf[LINESIZE], quotec;
443 register char **last;
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;
471 if (any(*cp, " \t"))
472 break;
473 if (any(*cp, "'\""))
474 quotec = *cp++;
475 else
476 *cp2++ = *cp++;
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;
487 *ap++ = savestr(linebuf);
489 *ap = NOSTR;
490 return (ap-argv);
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.
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
515 static int
516 scan(char **sp)
518 register char *cp, *cp2;
519 register char c;
520 register struct lex *lp;
521 int quotec;
523 if (regretp >= 0) {
524 copy(stringstack[regretp], lexstring);
525 lexnumber = numberstack[regretp];
526 return (regretstack[regretp--]);
528 cp = *sp;
529 cp2 = lexstring;
530 c = *cp++;
533 * strip away leading white space.
536 while (any(c, " \t"))
537 c = *cp++;
540 * If no characters remain, we are at end of line,
541 * so report that.
544 if (c == '\0') {
545 *sp = --cp;
546 return (TEOL);
550 * If the leading character is a digit, scan
551 * the number and convert it on the fly.
552 * Return TNUMBER when done.
555 if (isdigit(c)) {
556 lexnumber = 0;
557 while (isdigit(c)) {
558 lexnumber = lexnumber*10 + c - '0';
559 *cp2++ = c;
560 c = *cp++;
562 *cp2 = '\0';
563 *sp = --cp;
564 return (TNUMBER);
568 * Check for single character tokens; return such
569 * if found.
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);
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.
588 quotec = 0;
589 if (any(c, "'\"")) {
590 quotec = c;
591 c = *cp++;
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"));
602 if (c == quotec) {
603 cp++;
604 break;
606 if (quotec == 0 && any(c, " \t"))
607 break;
608 if (cp2 - lexstring < STRINGLEN-1)
609 *cp2++ = c;
610 c = *cp++;
612 if (quotec && c == 0)
613 fprintf(stderr, gettext("Missing %c\n"), quotec);
614 *sp = --cp;
615 *cp2 = '\0';
616 return (TSTRING);
620 * Unscan the named token by pushing it onto the regret stack.
623 static void
624 regret(int token)
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;
635 * Reset all the scanner global variables.
638 static void
639 scaninit(void)
641 regretp = -1;
645 * Find the first message whose flags & m == f and return
646 * its message number.
650 first(int f, int m)
652 register int mesg;
653 register struct message *mp;
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++;
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--;
669 return (NULL);
673 * See if the passed name sent the passed message number. Return true
674 * if so.
676 static int
677 sender(char *str, int mesg)
679 return (samebody(str, skin(nameof(&message[mesg-1])), TRUE));
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.
690 static char lastscan[128];
692 static int
693 matchsubj(char *str, int mesg)
695 register struct message *mp;
696 register char *cp, *cp2, *backup;
698 str++;
699 if (strlen(str) == 0)
700 str = lastscan;
701 else
702 nstrcpy(lastscan, sizeof (lastscan), str);
703 mp = &message[mesg-1];
706 * Now look, ignoring case, for the word in the string.
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;
722 return (*cp == 0);
726 * Mark the named message by setting its mark bit.
729 static void
730 mark(int mesg)
732 register int i;
734 i = mesg;
735 if (i < 1 || i > msgCount)
736 panic("Bad message number to mark");
737 message[i-1].m_flag |= MMARK;
741 * Unmark the named message.
744 static void
745 unmark(int mesg)
747 register int i;
749 i = mesg;
750 if (i < 1 || i > msgCount)
751 panic("Bad message number to unmark");
752 message[i-1].m_flag &= ~MMARK;
756 * Return the message number corresponding to the passed meta character.
758 static int
759 metamess(int meta, int f)
761 register int c, m;
762 register struct message *mp;
764 c = meta;
765 switch (c) {
766 case '^':
768 * First 'good' message left.
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);
776 case '+':
778 * Next 'good' message left.
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);
786 case '-':
788 * Previous 'good' message.
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);
796 case '$':
798 * Last 'good message left.
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);
806 case '.':
808 * Current message.
810 m = dot - &message[0] + 1;
811 if ((dot->m_flag & MDELETED) != f) {
812 printf(gettext("%d: Inappropriate message\n"), m);
813 return (-1);
815 return (m);
817 default:
818 printf(gettext("Unknown metachar (%c)\n"), c);
819 return (-1);