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 2008 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
40 #include "rcv.h"
41 #include <locale.h>
42
43 /*
44 * mailx -- a modified version of a University of California at Berkeley
45 * mail program
46 *
47 * More user commands.
48 */
49
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);
59
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 */
65
66 /*
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.
70 */
71
72 int
next(int * msgvec)73 next(int *msgvec)
74 {
75 register struct message *mp;
76 int list[2];
77
78 if (*msgvec != NULL) {
79 if (*msgvec < 0) {
80 printf((gettext("Negative message given\n")));
81 return (1);
82 }
83 mp = &message[*msgvec - 1];
84 if ((mp->m_flag & MDELETED) == 0) {
85 dot = mp;
86 goto hitit;
87 }
88 printf(gettext("No applicable message\n"));
89 return (1);
90 }
91
92 /*
93 * If this is the first command, select message 1.
94 * Note that this must exist for us to get here at all.
95 */
96 if (!sawcom)
97 goto hitit;
98
99 /*
100 * Just find the next good message after dot, no
101 * wraparound.
102 */
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);
109 }
110 dot = mp;
111 hitit:
112 /*
113 * Print dot.
114 */
115 list[0] = dot - &message[0] + 1;
116 list[1] = NULL;
117 return (type(list));
118 }
119
120 /*
121 * Save a message in a file. Mark the message as saved
122 * so we can discard when the user quits.
123 */
124 int
save(char str[])125 save(char str[])
126 {
127 return (save1(str, S_MARK));
128 }
129
130 /*
131 * Copy a message to a file without affected its saved-ness
132 */
133 int
copycmd(char str[])134 copycmd(char str[])
135 {
136 return (save1(str, 0));
137 }
138
139 /*
140 * Save/copy the indicated messages at the end of the passed file name.
141 * If mark is true, mark the message "saved."
142 */
143 static int
save1(char str[],int mark)144 save1(char str[], int mark)
145 {
146 char *file, *cmd;
147 int f, *msgvec;
148
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 == NULL) {
158 printf(gettext("No messages to %s.\n"), cmd);
159 return (1);
160 }
161 msgvec[1] = NULL;
162 }
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);
169 }
170
171 int
Save(int * msgvec)172 Save(int *msgvec)
173 {
174 return (Save1(msgvec, S_MARK));
175 }
176
177 int
Copy(int * msgvec)178 Copy(int *msgvec)
179 {
180 return (Save1(msgvec, 0));
181 }
182
183 /*
184 * save/copy the indicated messages at the end of a file named
185 * by the sender of the first message in the msglist.
186 */
187 static int
Save1(int * msgvec,int mark)188 Save1(int *msgvec, int mark)
189 {
190 register char *from;
191 char recfile[BUFSIZ];
192
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);
202 }
203
204 int
sput(char str[])205 sput(char str[])
206 {
207 return (put1(str, 0));
208 }
209
210 int
Sput(char str[])211 Sput(char str[])
212 {
213 return (put1(str, S_NOIGNORE));
214 }
215
216 /*
217 * Put the indicated messages at the end of the passed file name.
218 */
219 static int
put1(char str[],int doign)220 put1(char str[], int doign)
221 {
222 char *file;
223 int f, *msgvec;
224
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 == NULL) {
233 printf(gettext("No messages to put.\n"));
234 return (1);
235 }
236 msgvec[1] = NULL;
237 }
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);
244 }
245
246 /*
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.
251 */
252
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 */
258
259 static void
savemsglist(char * file,int * msgvec,int flag)260 savemsglist(char *file, int *msgvec, int flag)
261 {
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;
269
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;
279 }
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;
293 }
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);
303 }
304 if (t < 0) {
305 perror(file);
306 fclose(obuf);
307 return;
308 }
309 touch(mesg);
310 dot = mp;
311 lc += wr_linecount;
312 cc += wr_charcount;
313 if (flag & S_MARK)
314 mp->m_flag |= MSAVED;
315 }
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);
324 }
325 }
326
327 static int
svputs(const char * line,FILE * obuf)328 svputs(const char *line, FILE *obuf)
329 {
330 wr_linecount++;
331 wr_charcount += strlen(line);
332 return (fputs(line, obuf));
333 }
334
335 static int
wrputs(const char * line,FILE * obuf)336 wrputs(const char *line, FILE *obuf)
337 {
338 /*
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.
343 */
344 wr_inlines++;
345 if (wr_inhead) {
346 if (strcmp(line, "\n") == 0)
347 wr_inhead = 0;
348 return (0);
349 }
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));
355 }
356
357 /*
358 * Write the indicated messages at the end of the passed
359 * file name, minus header and trailing blank line.
360 */
361
362 int
swrite(char str[])363 swrite(char str[])
364 {
365 register char *file;
366 int f, *msgvec;
367
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 == NULL) {
378 printf(gettext("No messages to write.\n"));
379 return (1);
380 }
381 msgvec[1] = NULL;
382 }
383 if (f && getmsglist(str, msgvec, 0) < 0)
384 return (1);
385 savemsglist(file, msgvec, S_MARK|S_NOHEADER);
386 return (0);
387 }
388
389 /*
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.
396 */
397
398 /*
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.
402 */
403
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, "...") */
407
408 char *
snarf(char linebuf[],int * flag,int erf)409 snarf(char linebuf[], int *flag, int erf)
410 {
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 */
418
419 /*
420 * Skip leading whitespace.
421 */
422 for (line_beg = linebuf;
423 *line_beg && any(*line_beg, " \t");
424 line_beg++) {
425 /* empty body */
426 }
427 if (!*line_beg) {
428 if (erf) {
429 printf(gettext("No file specified.\n"));
430 }
431 *flag = 0;
432 return (NOSTR);
433 }
434 /*
435 * Process line from left-to-right, 1 char at a time.
436 */
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;
445 }
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;
455 }
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++;
462 }
463 }
464 if (*p == '\0') {
465 printf(gettext("Syntax error: missing "
466 "%c.\n"), qc);
467 *flag = -1;
468 return (NOSTR);
469 }
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;
479 }
480 } else {
481 printf(gettext("improper quotes"
482 " at \"%s\".\n"), p);
483 *flag = -1;
484 return (NOSTR);
485 }
486 if (*p == '\\' && *++p == '\0') {
487 printf(gettext("\'\\\' at "
488 "end of line.\n"));
489 *flag = -1;
490 return (NOSTR);
491 }
492 pc_type = SN_TOKEN;
493 p++;
494 }
495 }
496 if (pc_type == SN_TOKEN) {
497 tok_end = p - 1;
498 }
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;
505 }
506 tok_end[1] = '\0';
507 return (tok_beg);
508 } else {
509 if (erf) {
510 printf(gettext("No file specified.\n"));
511 }
512 *flag = 0;
513 return (NOSTR);
514 }
515 }
516
517 /*
518 * Delete messages, then type the new dot.
519 */
520
521 int
deltype(int msgvec[])522 deltype(int msgvec[])
523 {
524 int list[2];
525 int lastdot;
526
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] = NULL;
534 return (type(list));
535 }
536 printf(gettext("At EOF\n"));
537 return (0);
538 } else {
539 printf(gettext("No more messages\n"));
540 return (0);
541 }
542 }
543
544 /*
545 * Delete the indicated messages.
546 * Set dot to some nice place afterwards.
547 */
548 int
delm(int * msgvec)549 delm(int *msgvec)
550 {
551 register struct message *mp;
552 int *ip, mesg;
553 int last;
554
555 last = NULL;
556 for (ip = msgvec; *ip != NULL; 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;
563 }
564 if (last != NULL) {
565 dot = &message[last-1];
566 last = first(0, MDELETED);
567 if (last != NULL) {
568 dot = &message[last-1];
569 return (0);
570 } else {
571 dot = &message[0];
572 return (-1);
573 }
574 }
575
576 /*
577 * Following can't happen -- it keeps lint happy
578 */
579
580 return (-1);
581 }
582
583 /*
584 * Undelete the indicated messages.
585 */
586 int
undelete(int * msgvec)587 undelete(int *msgvec)
588 {
589 register struct message *mp;
590 int *ip, mesg;
591
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;
600 }
601 return (0);
602 }
603
604 /*
605 * Add the given header fields to the retained list.
606 * If no arguments, print the current list of retained fields.
607 */
608 int
retfield(char * list[])609 retfield(char *list[])
610 {
611 char field[BUFSIZ];
612 register int h;
613 register struct ignore *igp;
614 char **ap;
615
616 if (argcount(list) == 0)
617 return (retshow());
618 for (ap = list; *ap != 0; ap++) {
619 istrcpy(field, sizeof (field), *ap);
620
621 if (member(field, retain))
622 continue;
623
624 h = hash(field);
625 if ((igp = (struct ignore *)
626 calloc(1, sizeof (struct ignore))) == NULL) {
627 panic("Couldn't allocate memory");
628 }
629 if ((igp->i_field = (char *)
630 calloc(strlen(field) + 1, sizeof (char))) == NULL) {
631 panic("Couldn't allocate memory");
632 }
633 strcpy(igp->i_field, field);
634 igp->i_link = retain[h];
635 retain[h] = igp;
636 nretained++;
637 }
638 return (0);
639 }
640
641 /*
642 * Print out all currently retained fields.
643 */
644 static int
retshow(void)645 retshow(void)
646 {
647 register int h, count;
648 struct ignore *igp;
649 char **ap, **ring;
650
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);
658 }
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);
669 }
670
671 /*
672 * Remove a list of fields from the retain list.
673 */
674 int
unretfield(char * list[])675 unretfield(char *list[])
676 {
677 char **ap, field[BUFSIZ];
678 register int h, count = 0;
679 register struct ignore *ig1, *ig2;
680
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++;
690 }
691 retain[h] = NULL;
692 }
693 if (count == 0)
694 printf(gettext(
695 "No fields currently being retained.\n"));
696 nretained = 0;
697 return (0);
698 }
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;
712 }
713 }
714 return (0);
715 }
716
717 /*
718 * Add the given header fields to the ignored list.
719 * If no arguments, print the current list of ignored fields.
720 */
721 int
igfield(char * list[])722 igfield(char *list[])
723 {
724 char field[BUFSIZ];
725 register int h;
726 register struct ignore *igp;
727 char **ap;
728
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");
739 }
740 if ((igp->i_field = (char *)
741 calloc((unsigned)strlen(field) + 1,
742 sizeof (char))) == NULL) {
743 panic("Couldn't allocate memory");
744 }
745 strcpy(igp->i_field, field);
746 igp->i_link = ignore[h];
747 ignore[h] = igp;
748 }
749 return (0);
750 }
751
752 /*
753 * Print out all currently ignored fields.
754 */
755 static int
igshow(void)756 igshow(void)
757 {
758 register int h, count;
759 struct ignore *igp;
760 char **ap, **ring;
761
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);
769 }
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);
780 }
781
782 /*
783 * Compare two names for sorting ignored field list.
784 */
785 static int
igcomp(const void * l,const void * r)786 igcomp(const void *l, const void *r)
787 {
788 return (strcmp(*(char **)l, *(char **)r));
789 }
790
791 /*
792 * Remove a list of fields from the ignore list.
793 */
794 int
unigfield(char * list[])795 unigfield(char *list[])
796 {
797 char **ap, field[BUFSIZ];
798 register int h, count = 0;
799 register struct ignore *ig1, *ig2;
800
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++;
810 }
811 ignore[h] = NULL;
812 }
813 if (count == 0)
814 printf(gettext("No fields currently being ignored.\n"));
815 return (0);
816 }
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;
829 }
830 }
831 return (0);
832 }
833