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]
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
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
44 * mailx -- a modified version of a University of California at Berkeley
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.
75 register struct message
*mp
;
78 if (*msgvec
!= NULL
) {
80 printf((gettext("Negative message given\n")));
83 mp
= &message
[*msgvec
- 1];
84 if ((mp
->m_flag
& MDELETED
) == 0) {
88 printf(gettext("No applicable message\n"));
93 * If this is the first command, select message 1.
94 * Note that this must exist for us to get here at all.
100 * Just find the next good message after dot, no
103 for (mp
= dot
+1; mp
< &message
[msgCount
]; mp
++)
104 if ((mp
->m_flag
& (MDELETED
|MSAVED
)) == 0)
106 if (mp
>= &message
[msgCount
]) {
107 printf(gettext("At EOF\n"));
115 list
[0] = dot
- &message
[0] + 1;
121 * Save a message in a file. Mark the message as saved
122 * so we can discard when the user quits.
127 return (save1(str
, S_MARK
));
131 * Copy a message to a file without affected its saved-ness
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."
144 save1(char str
[], int mark
)
149 cmd
= mark
? "save" : "copy";
150 msgvec
= (int *)salloc((msgCount
+ 2) * sizeof (*msgvec
));
151 if ((file
= snarf(str
, &f
, 0)) == NOSTR
)
156 *msgvec
= first(0, MMNORM
);
157 if (*msgvec
== NULL
) {
158 printf(gettext("No messages to %s.\n"), cmd
);
163 if (f
&& getmsglist(str
, msgvec
, 0) < 0)
165 if ((file
= expand(file
)) == NOSTR
)
167 savemsglist(file
, msgvec
, mark
| S_SAVING
);
174 return (Save1(msgvec
, S_MARK
));
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.
188 Save1(int *msgvec
, int mark
)
191 char recfile
[BUFSIZ
];
194 from
= striphosts(nameof(&message
[*msgvec
-1], 0));
196 from
= nameof(&message
[*msgvec
-1]);
198 getrecf(from
, recfile
, 1, sizeof (recfile
));
199 if (*recfile
!= '\0')
200 savemsglist(safeexpand(recfile
), msgvec
, mark
| S_SAVING
);
207 return (put1(str
, 0));
213 return (put1(str
, S_NOIGNORE
));
217 * Put the indicated messages at the end of the passed file name.
220 put1(char str
[], int doign
)
225 msgvec
= (int *)salloc((msgCount
+ 2) * sizeof (*msgvec
));
226 if ((file
= snarf(str
, &f
, 0)) == NOSTR
)
231 *msgvec
= first(0, MMNORM
);
232 if (*msgvec
== NULL
) {
233 printf(gettext("No messages to put.\n"));
238 if (f
&& getmsglist(str
, msgvec
, 0) < 0)
240 if ((file
= expand(file
)) == NOSTR
)
242 savemsglist(file
, msgvec
, doign
);
247 * save a message list in a file.
248 * if wr set, doing "write" instead
249 * of "save" or "copy" so don't put
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 */
260 savemsglist(char *file
, int *msgvec
, int flag
)
262 register int *ip
, mesg
;
263 register struct message
*mp
;
270 printf("\"%s\" ", file
);
272 if (stat(file
, &statb
) >= 0)
276 if ((obuf
= fopen(file
, "a")) == NULL
) {
283 mflag
= (int)value("alwaysignore")?(M_IGNORE
|M_SAVING
):M_SAVING
;
284 else if (flag
& S_NOIGNORE
)
288 for (ip
= msgvec
; *ip
&& ip
-msgvec
< msgCount
; ip
++) {
290 mp
= &message
[mesg
-1];
296 if (flag
& S_NOHEADER
) {
298 wr_maxlines
= mp
->m_lines
;
300 t
= msend(mp
, obuf
, 0, wrputs
);
302 t
= msend(mp
, obuf
, mflag
, svputs
);
314 mp
->m_flag
|= MSAVED
;
321 printf("%s %ld/%ld\n", disp
, lc
, cc
);
323 printf("%s binary/%ld\n", disp
, cc
);
328 svputs(const char *line
, FILE *obuf
)
331 wr_charcount
+= strlen(line
);
332 return (fputs(line
, obuf
));
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.
346 if (strcmp(line
, "\n") == 0)
350 if (wr_inlines
>= wr_maxlines
&& strcmp(line
, "\n") == 0)
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.
368 msgvec
= (int *)salloc((msgCount
+ 2) * sizeof (*msgvec
));
369 if ((file
= snarf(str
, &f
, 1)) == NOSTR
)
373 if ((file
= expand(file
)) == NOSTR
)
376 *msgvec
= first(0, MMNORM
);
377 if (*msgvec
== NULL
) {
378 printf(gettext("No messages to write.\n"));
383 if (f
&& getmsglist(str
, msgvec
, 0) < 0)
385 savemsglist(file
, msgvec
, S_MARK
|S_NOHEADER
);
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, "...") */
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");
429 printf(gettext("No file specified.\n"));
435 * Process line from left-to-right, 1 char at a time.
438 tok_beg
= tok_end
= NOSTR
;
441 if (any(*p
, " \t")) {
442 /* This character is a DELIMITER */
443 if (pc_type
& (SN_TOKEN
|SN_QUOTE
)) {
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. */
456 /* Search for the matching QUOTE character */
457 for (tok_beg
= p
, tok_end
= NOSTR
, p
++;
458 *p
!= '\0' && *p
!= qc
;
460 if (*p
== '\\' && *(p
+1) == qc
) {
465 printf(gettext("Syntax error: missing "
474 /* This character should be a TOKEN character */
475 if (pc_type
& (SN_DELIM
|SN_TOKEN
)) {
476 if (pc_type
& SN_DELIM
) {
481 printf(gettext("improper quotes"
482 " at \"%s\".\n"), p
);
486 if (*p
== '\\' && *++p
== '\0') {
487 printf(gettext("\'\\\' at "
496 if (pc_type
== SN_TOKEN
) {
499 if (tok_beg
!= NOSTR
&& tok_end
!= NOSTR
) {
500 if (tok_beg
== line_beg
) {
510 printf(gettext("No file specified.\n"));
518 * Delete messages, then type the new dot.
522 deltype(int msgvec
[])
527 lastdot
= dot
- &message
[0] + 1;
528 if (delm(msgvec
) >= 0) {
529 list
[0] = dot
- &message
[0];
531 if (list
[0] > lastdot
) {
536 printf(gettext("At EOF\n"));
539 printf(gettext("No more messages\n"));
545 * Delete the indicated messages.
546 * Set dot to some nice place afterwards.
551 register struct message
*mp
;
556 for (ip
= msgvec
; *ip
!= NULL
; ip
++) {
559 mp
= &message
[mesg
-1];
560 mp
->m_flag
|= MDELETED
|MTOUCH
;
561 mp
->m_flag
&= ~(MPRESERVE
|MSAVED
|MBOX
);
565 dot
= &message
[last
-1];
566 last
= first(0, MDELETED
);
568 dot
= &message
[last
-1];
577 * Following can't happen -- it keeps lint happy
584 * Undelete the indicated messages.
587 undelete(int *msgvec
)
589 register struct message
*mp
;
592 for (ip
= msgvec
; ip
-msgvec
< msgCount
; ip
++) {
597 mp
= &message
[mesg
-1];
599 mp
->m_flag
&= ~MDELETED
;
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
[])
613 register struct ignore
*igp
;
616 if (argcount(list
) == 0)
618 for (ap
= list
; *ap
!= 0; ap
++) {
619 istrcpy(field
, sizeof (field
), *ap
);
621 if (member(field
, retain
))
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
];
642 * Print out all currently retained fields.
647 register int h
, count
;
652 for (h
= 0; h
< HSHSIZE
; h
++)
653 for (igp
= retain
[h
]; igp
!= 0; igp
= igp
->i_link
)
656 printf(gettext("No fields currently being retained.\n"));
659 ring
= (char **)salloc((count
+ 1) * sizeof (char *));
661 for (h
= 0; h
< HSHSIZE
; h
++)
662 for (igp
= retain
[h
]; igp
!= 0; igp
= igp
->i_link
)
663 *ap
++ = igp
->i_field
;
665 qsort(ring
, count
, sizeof (char *), igcomp
);
666 for (ap
= ring
; *ap
!= 0; ap
++)
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
++) {
695 "No fields currently being retained.\n"));
699 for (ap
= list
; *ap
; ap
++) {
700 istrcpy(field
, sizeof (field
), *ap
);
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
;
707 ig2
->i_link
= ig1
->i_link
;
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
[])
726 register struct ignore
*igp
;
729 if (argcount(list
) == 0)
731 for (ap
= list
; *ap
!= 0; ap
++) {
734 istrcpy(field
, sizeof (field
), *ap
);
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
];
753 * Print out all currently ignored fields.
758 register int h
, count
;
763 for (h
= 0; h
< HSHSIZE
; h
++)
764 for (igp
= ignore
[h
]; igp
!= 0; igp
= igp
->i_link
)
767 printf(gettext("No fields currently being ignored.\n"));
770 ring
= (char **)salloc((count
+ 1) * sizeof (char *));
772 for (h
= 0; h
< HSHSIZE
; h
++)
773 for (igp
= ignore
[h
]; igp
!= 0; igp
= igp
->i_link
)
774 *ap
++ = igp
->i_field
;
776 qsort((char *)ring
, (unsigned)count
, sizeof (char *), igcomp
);
777 for (ap
= ring
; *ap
!= 0; ap
++)
783 * Compare two names for sorting ignored field list.
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
++) {
814 printf(gettext("No fields currently being ignored.\n"));
817 for (ap
= list
; *ap
; ap
++) {
818 istrcpy(field
, sizeof (field
), *ap
);
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
;
825 ig2
->i_link
= ig1
->i_link
;