dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / mailx / cmd2.c
blobae8e44a90e5d828ca528cc39d064d61660031291
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 2008 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.
40 #include "rcv.h"
41 #include <locale.h>
44 * mailx -- a modified version of a University of California at Berkeley
45 * mail program
47 * More user commands.
50 static int igshow(void);
51 static int igcomp(const void *l, const void *r);
52 static int save1(char str[], int mark);
53 static int Save1(int *msgvec, int mark);
54 static void savemsglist(char *file, int *msgvec, int flag);
55 static int put1(char str[], int doign);
56 static int svputs(const char *line, FILE *obuf);
57 static int wrputs(const char *line, FILE *obuf);
58 static int retshow(void);
60 /* flags for savemsglist() */
61 #define S_MARK 1 /* mark the message as saved */
62 #define S_NOHEADER 2 /* don't write out the header */
63 #define S_SAVING 4 /* doing save/copy */
64 #define S_NOIGNORE 8 /* don't do ignore processing */
67 * If any arguments were given, print the first message
68 * identified by the first argument. If no arguments are given,
69 * print the next applicable message after dot.
72 int
73 next(int *msgvec)
75 register struct message *mp;
76 int list[2];
78 if (*msgvec != 0) {
79 if (*msgvec < 0) {
80 printf((gettext("Negative message given\n")));
81 return (1);
83 mp = &message[*msgvec - 1];
84 if ((mp->m_flag & MDELETED) == 0) {
85 dot = mp;
86 goto hitit;
88 printf(gettext("No applicable message\n"));
89 return (1);
93 * If this is the first command, select message 1.
94 * Note that this must exist for us to get here at all.
96 if (!sawcom)
97 goto hitit;
100 * Just find the next good message after dot, no
101 * wraparound.
103 for (mp = dot+1; mp < &message[msgCount]; mp++)
104 if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
105 break;
106 if (mp >= &message[msgCount]) {
107 printf(gettext("At EOF\n"));
108 return (0);
110 dot = mp;
111 hitit:
113 * Print dot.
115 list[0] = dot - &message[0] + 1;
116 list[1] = 0;
117 return (type(list));
121 * Save a message in a file. Mark the message as saved
122 * so we can discard when the user quits.
125 save(char str[])
127 return (save1(str, S_MARK));
131 * Copy a message to a file without affected its saved-ness
134 copycmd(char str[])
136 return (save1(str, 0));
140 * Save/copy the indicated messages at the end of the passed file name.
141 * If mark is true, mark the message "saved."
143 static int
144 save1(char str[], int mark)
146 char *file, *cmd;
147 int f, *msgvec;
149 cmd = mark ? "save" : "copy";
150 msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec));
151 if ((file = snarf(str, &f, 0)) == NOSTR)
152 file = Getf("MBOX");
153 if (f == -1)
154 return (1);
155 if (!f) {
156 *msgvec = first(0, MMNORM);
157 if (*msgvec == 0) {
158 printf(gettext("No messages to %s.\n"), cmd);
159 return (1);
161 msgvec[1] = 0;
163 if (f && getmsglist(str, msgvec, 0) < 0)
164 return (1);
165 if ((file = expand(file)) == NOSTR)
166 return (1);
167 savemsglist(file, msgvec, mark | S_SAVING);
168 return (0);
172 Save(int *msgvec)
174 return (Save1(msgvec, S_MARK));
178 Copy(int *msgvec)
180 return (Save1(msgvec, 0));
184 * save/copy the indicated messages at the end of a file named
185 * by the sender of the first message in the msglist.
187 static int
188 Save1(int *msgvec, int mark)
190 register char *from;
191 char recfile[BUFSIZ];
193 #ifdef notdef
194 from = striphosts(nameof(&message[*msgvec-1], 0));
195 #else
196 from = nameof(&message[*msgvec-1]);
197 #endif
198 getrecf(from, recfile, 1, sizeof (recfile));
199 if (*recfile != '\0')
200 savemsglist(safeexpand(recfile), msgvec, mark | S_SAVING);
201 return (0);
205 sput(char str[])
207 return (put1(str, 0));
211 Sput(char str[])
213 return (put1(str, S_NOIGNORE));
217 * Put the indicated messages at the end of the passed file name.
219 static int
220 put1(char str[], int doign)
222 char *file;
223 int f, *msgvec;
225 msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec));
226 if ((file = snarf(str, &f, 0)) == NOSTR)
227 file = Getf("MBOX");
228 if (f == -1)
229 return (1);
230 if (!f) {
231 *msgvec = first(0, MMNORM);
232 if (*msgvec == 0) {
233 printf(gettext("No messages to put.\n"));
234 return (1);
236 msgvec[1] = 0;
238 if (f && getmsglist(str, msgvec, 0) < 0)
239 return (1);
240 if ((file = expand(file)) == NOSTR)
241 return (1);
242 savemsglist(file, msgvec, doign);
243 return (0);
247 * save a message list in a file.
248 * if wr set, doing "write" instead
249 * of "save" or "copy" so don't put
250 * out header.
253 static int wr_linecount; /* count of lines written */
254 static int wr_charcount; /* char count of lines written */
255 static int wr_inlines; /* count of lines read */
256 static long wr_maxlines; /* total lines in message */
257 static int wr_inhead; /* in header of message */
259 static void
260 savemsglist(char *file, int *msgvec, int flag)
262 register int *ip, mesg;
263 register struct message *mp;
264 char *disp;
265 FILE *obuf;
266 struct stat statb;
267 long lc, cc, t;
268 int bnry, mflag;
270 printf("\"%s\" ", file);
271 flush();
272 if (stat(file, &statb) >= 0)
273 disp = "[Appended]";
274 else
275 disp = "[New file]";
276 if ((obuf = fopen(file, "a")) == NULL) {
277 perror("");
278 return;
280 lc = cc = 0;
281 bnry = 0;
282 if (flag & S_SAVING)
283 mflag = (int)value("alwaysignore")?(M_IGNORE|M_SAVING):M_SAVING;
284 else if (flag & S_NOIGNORE)
285 mflag = 0;
286 else
287 mflag = M_IGNORE;
288 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
289 mesg = *ip;
290 mp = &message[mesg-1];
291 if (!mp->m_text) {
292 bnry = 1;
294 wr_linecount = 0;
295 wr_charcount = 0;
296 if (flag & S_NOHEADER) {
297 wr_inhead = 1;
298 wr_maxlines = mp->m_lines;
299 wr_inlines = 0;
300 t = msend(mp, obuf, 0, wrputs);
301 } else {
302 t = msend(mp, obuf, mflag, svputs);
304 if (t < 0) {
305 perror(file);
306 fclose(obuf);
307 return;
309 touch(mesg);
310 dot = mp;
311 lc += wr_linecount;
312 cc += wr_charcount;
313 if (flag & S_MARK)
314 mp->m_flag |= MSAVED;
316 fflush(obuf);
317 if (fferror(obuf))
318 perror(file);
319 fclose(obuf);
320 if (!bnry) {
321 printf("%s %ld/%ld\n", disp, lc, cc);
322 } else {
323 printf("%s binary/%ld\n", disp, cc);
327 static int
328 svputs(const char *line, FILE *obuf)
330 wr_linecount++;
331 wr_charcount += strlen(line);
332 return (fputs(line, obuf));
335 static int
336 wrputs(const char *line, FILE *obuf)
339 * If this is a header line or
340 * the last line, don't write it out. Since we may add a
341 * "Status" line the line count may be off by one so insist
342 * that the last line is blank before we skip it.
344 wr_inlines++;
345 if (wr_inhead) {
346 if (strcmp(line, "\n") == 0)
347 wr_inhead = 0;
348 return (0);
350 if (wr_inlines >= wr_maxlines && strcmp(line, "\n") == 0)
351 return (0);
352 wr_linecount++;
353 wr_charcount += strlen(line);
354 return (fputs(line, obuf));
358 * Write the indicated messages at the end of the passed
359 * file name, minus header and trailing blank line.
363 swrite(char str[])
365 register char *file;
366 int f, *msgvec;
368 msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec));
369 if ((file = snarf(str, &f, 1)) == NOSTR)
370 return (1);
371 if (f == -1)
372 return (1);
373 if ((file = expand(file)) == NOSTR)
374 return (1);
375 if (!f) {
376 *msgvec = first(0, MMNORM);
377 if (*msgvec == 0) {
378 printf(gettext("No messages to write.\n"));
379 return (1);
381 msgvec[1] = 0;
383 if (f && getmsglist(str, msgvec, 0) < 0)
384 return (1);
385 savemsglist(file, msgvec, S_MARK|S_NOHEADER);
386 return (0);
390 * Snarf the file from the end of the command line and
391 * return a pointer to it. If there is no file attached,
392 * just return NOSTR. Put a null in front of the file
393 * name so that the message list processing won't see it,
394 * unless the file name is the only thing on the line, in
395 * which case, return 0 in the reference flag variable.
399 * The following definitions are used to characterize the syntactic
400 * category of the preceding character in the following parse procedure.
401 * The variable pc_type assumes these values.
404 #define SN_DELIM 1 /* Delimiter (<blank> or line beginning) */
405 #define SN_TOKEN 2 /* A part of a token */
406 #define SN_QUOTE 4 /* An entire quoted string (ie, "...") */
408 char *
409 snarf(char linebuf[], int *flag, int erf)
411 register char *p; /* utility pointer */
412 register char qc; /* quotation character to match */
413 register unsigned int pc_type; /* preceding character type */
414 register char *tok_beg; /* beginning of last token */
415 register char *tok_end; /* end of last token */
416 char *line_beg; /* beginning of line, after */
417 /* leading whitespace */
420 * Skip leading whitespace.
422 for (line_beg = linebuf;
423 *line_beg && any(*line_beg, " \t");
424 line_beg++) {
425 /* empty body */
427 if (!*line_beg) {
428 if (erf) {
429 printf(gettext("No file specified.\n"));
431 *flag = 0;
432 return (NOSTR);
435 * Process line from left-to-right, 1 char at a time.
437 pc_type = SN_DELIM;
438 tok_beg = tok_end = NOSTR;
439 p = line_beg;
440 while (*p != '\0') {
441 if (any(*p, " \t")) {
442 /* This character is a DELIMITER */
443 if (pc_type & (SN_TOKEN|SN_QUOTE)) {
444 tok_end = p - 1;
446 pc_type = SN_DELIM;
447 p++;
448 } else if ((qc = *p) == '"' || qc == '\'') {
449 /* This character is a QUOTE character */
450 if (pc_type == SN_TOKEN) {
451 /* embedded quotation symbols are simply */
452 /* token characters. */
453 p++;
454 continue;
456 /* Search for the matching QUOTE character */
457 for (tok_beg = p, tok_end = NOSTR, p++;
458 *p != '\0' && *p != qc;
459 p++) {
460 if (*p == '\\' && *(p+1) == qc) {
461 p++;
464 if (*p == '\0') {
465 printf(gettext("Syntax error: missing "
466 "%c.\n"), qc);
467 *flag = -1;
468 return (NOSTR);
470 tok_end = p;
471 pc_type = SN_QUOTE;
472 p++;
473 } else {
474 /* This character should be a TOKEN character */
475 if (pc_type & (SN_DELIM|SN_TOKEN)) {
476 if (pc_type & SN_DELIM) {
477 tok_beg = p;
478 tok_end = NOSTR;
480 } else {
481 printf(gettext("improper quotes"
482 " at \"%s\".\n"), p);
483 *flag = -1;
484 return (NOSTR);
486 if (*p == '\\' && *++p == '\0') {
487 printf(gettext("\'\\\' at "
488 "end of line.\n"));
489 *flag = -1;
490 return (NOSTR);
492 pc_type = SN_TOKEN;
493 p++;
496 if (pc_type == SN_TOKEN) {
497 tok_end = p - 1;
499 if (tok_beg != NOSTR && tok_end != NOSTR) {
500 if (tok_beg == line_beg) {
501 *flag = 0;
502 } else {
503 tok_beg[-1] = '\0';
504 *flag = 1;
506 tok_end[1] = '\0';
507 return (tok_beg);
508 } else {
509 if (erf) {
510 printf(gettext("No file specified.\n"));
512 *flag = 0;
513 return (NOSTR);
518 * Delete messages, then type the new dot.
522 deltype(int msgvec[])
524 int list[2];
525 int lastdot;
527 lastdot = dot - &message[0] + 1;
528 if (delm(msgvec) >= 0) {
529 list[0] = dot - &message[0];
530 list[0]++;
531 if (list[0] > lastdot) {
532 touch(list[0]);
533 list[1] = 0;
534 return (type(list));
536 printf(gettext("At EOF\n"));
537 return (0);
538 } else {
539 printf(gettext("No more messages\n"));
540 return (0);
545 * Delete the indicated messages.
546 * Set dot to some nice place afterwards.
549 delm(int *msgvec)
551 register struct message *mp;
552 int *ip, mesg;
553 int last;
555 last = 0;
556 for (ip = msgvec; *ip != 0; ip++) {
557 mesg = *ip;
558 touch(mesg);
559 mp = &message[mesg-1];
560 mp->m_flag |= MDELETED|MTOUCH;
561 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
562 last = mesg;
564 if (last != 0) {
565 dot = &message[last-1];
566 last = first(0, MDELETED);
567 if (last != 0) {
568 dot = &message[last-1];
569 return (0);
570 } else {
571 dot = &message[0];
572 return (-1);
577 * Following can't happen -- it keeps lint happy
580 return (-1);
584 * Undelete the indicated messages.
587 undelete(int *msgvec)
589 register struct message *mp;
590 int *ip, mesg;
592 for (ip = msgvec; ip-msgvec < msgCount; ip++) {
593 mesg = *ip;
594 if (mesg == 0)
595 return (0);
596 touch(mesg);
597 mp = &message[mesg-1];
598 dot = mp;
599 mp->m_flag &= ~MDELETED;
601 return (0);
605 * Add the given header fields to the retained list.
606 * If no arguments, print the current list of retained fields.
609 retfield(char *list[])
611 char field[BUFSIZ];
612 register int h;
613 register struct ignore *igp;
614 char **ap;
616 if (argcount(list) == 0)
617 return (retshow());
618 for (ap = list; *ap != 0; ap++) {
619 istrcpy(field, sizeof (field), *ap);
621 if (member(field, retain))
622 continue;
624 h = hash(field);
625 if ((igp = (struct ignore *)
626 calloc(1, sizeof (struct ignore))) == NULL) {
627 panic("Couldn't allocate memory");
629 if ((igp->i_field = (char *)
630 calloc(strlen(field) + 1, sizeof (char))) == NULL) {
631 panic("Couldn't allocate memory");
633 strcpy(igp->i_field, field);
634 igp->i_link = retain[h];
635 retain[h] = igp;
636 nretained++;
638 return (0);
642 * Print out all currently retained fields.
644 static int
645 retshow(void)
647 register int h, count;
648 struct ignore *igp;
649 char **ap, **ring;
651 count = 0;
652 for (h = 0; h < HSHSIZE; h++)
653 for (igp = retain[h]; igp != 0; igp = igp->i_link)
654 count++;
655 if (count == 0) {
656 printf(gettext("No fields currently being retained.\n"));
657 return (0);
659 ring = (char **)salloc((count + 1) * sizeof (char *));
660 ap = ring;
661 for (h = 0; h < HSHSIZE; h++)
662 for (igp = retain[h]; igp != 0; igp = igp->i_link)
663 *ap++ = igp->i_field;
664 *ap = 0;
665 qsort(ring, count, sizeof (char *), igcomp);
666 for (ap = ring; *ap != 0; ap++)
667 printf("%s\n", *ap);
668 return (0);
672 * Remove a list of fields from the retain list.
675 unretfield(char *list[])
677 char **ap, field[BUFSIZ];
678 register int h, count = 0;
679 register struct ignore *ig1, *ig2;
681 if (argcount(list) == 0) {
682 for (h = 0; h < HSHSIZE; h++) {
683 ig1 = retain[h];
684 while (ig1) {
685 free(ig1->i_field);
686 ig2 = ig1->i_link;
687 free((char *)ig1);
688 ig1 = ig2;
689 count++;
691 retain[h] = NULL;
693 if (count == 0)
694 printf(gettext(
695 "No fields currently being retained.\n"));
696 nretained = 0;
697 return (0);
699 for (ap = list; *ap; ap++) {
700 istrcpy(field, sizeof (field), *ap);
701 h = hash(field);
702 for (ig1 = retain[h]; ig1; ig2 = ig1, ig1 = ig1->i_link)
703 if (strcmp(ig1->i_field, field) == 0) {
704 if (ig1 == retain[h])
705 retain[h] = ig1->i_link;
706 else
707 ig2->i_link = ig1->i_link;
708 free(ig1->i_field);
709 free((char *)ig1);
710 nretained--;
711 break;
714 return (0);
718 * Add the given header fields to the ignored list.
719 * If no arguments, print the current list of ignored fields.
722 igfield(char *list[])
724 char field[BUFSIZ];
725 register int h;
726 register struct ignore *igp;
727 char **ap;
729 if (argcount(list) == 0)
730 return (igshow());
731 for (ap = list; *ap != 0; ap++) {
732 if (isign(*ap, 0))
733 continue;
734 istrcpy(field, sizeof (field), *ap);
735 h = hash(field);
736 if ((igp = (struct ignore *)
737 calloc(1, sizeof (struct ignore))) == NULL) {
738 panic("Couldn't allocate memory");
740 if ((igp->i_field = (char *)
741 calloc((unsigned)strlen(field) + 1,
742 sizeof (char))) == NULL) {
743 panic("Couldn't allocate memory");
745 strcpy(igp->i_field, field);
746 igp->i_link = ignore[h];
747 ignore[h] = igp;
749 return (0);
753 * Print out all currently ignored fields.
755 static int
756 igshow(void)
758 register int h, count;
759 struct ignore *igp;
760 char **ap, **ring;
762 count = 0;
763 for (h = 0; h < HSHSIZE; h++)
764 for (igp = ignore[h]; igp != 0; igp = igp->i_link)
765 count++;
766 if (count == 0) {
767 printf(gettext("No fields currently being ignored.\n"));
768 return (0);
770 ring = (char **)salloc((count + 1) * sizeof (char *));
771 ap = ring;
772 for (h = 0; h < HSHSIZE; h++)
773 for (igp = ignore[h]; igp != 0; igp = igp->i_link)
774 *ap++ = igp->i_field;
775 *ap = 0;
776 qsort((char *)ring, (unsigned)count, sizeof (char *), igcomp);
777 for (ap = ring; *ap != 0; ap++)
778 printf("%s\n", *ap);
779 return (0);
783 * Compare two names for sorting ignored field list.
785 static int
786 igcomp(const void *l, const void *r)
788 return (strcmp(*(char **)l, *(char **)r));
792 * Remove a list of fields from the ignore list.
795 unigfield(char *list[])
797 char **ap, field[BUFSIZ];
798 register int h, count = 0;
799 register struct ignore *ig1, *ig2;
801 if (argcount(list) == 0) {
802 for (h = 0; h < HSHSIZE; h++) {
803 ig1 = ignore[h];
804 while (ig1) {
805 free(ig1->i_field);
806 ig2 = ig1->i_link;
807 free((char *)ig1);
808 ig1 = ig2;
809 count++;
811 ignore[h] = NULL;
813 if (count == 0)
814 printf(gettext("No fields currently being ignored.\n"));
815 return (0);
817 for (ap = list; *ap; ap++) {
818 istrcpy(field, sizeof (field), *ap);
819 h = hash(field);
820 for (ig1 = ignore[h]; ig1; ig2 = ig1, ig1 = ig1->i_link)
821 if (strcmp(ig1->i_field, field) == 0) {
822 if (ig1 == ignore[h])
823 ignore[h] = ig1->i_link;
824 else
825 ig2->i_link = ig1->i_link;
826 free(ig1->i_field);
827 free((char *)ig1);
828 break;
831 return (0);