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