2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 static char sccsid
[] = "@(#)cmd2.c 2.47 (gritter) 5/9/10";
52 * Mail -- a mail program
57 static int save1(char *str
, int mark
, char *cmd
, struct ignoretab
*ignore
,
58 int convert
, int sender_record
, int domove
);
59 static char *snarf(char *linebuf
, int *flag
, int usembox
);
60 static int delm(int *msgvec
);
61 static int ignore1(char **list
, struct ignoretab
*tab
, char *which
);
62 static int igshow(struct ignoretab
*tab
, char *which
);
63 static int igcomp(const void *l
, const void *r
);
64 static void unignore_one(const char *name
, struct ignoretab
*tab
);
65 static int unignore1(char **list
, struct ignoretab
*tab
, char *which
);
68 * If any arguments were given, go to the next applicable argument
69 * following dot, otherwise, go to the next applicable message.
70 * If given as first command with no arguments, print first message.
83 * If some messages were supplied, find the
84 * first applicable one following dot using
88 mdot
= dot
- &message
[0] + 1;
91 * Find the first message in the supplied
92 * message list which follows dot.
95 for (ip
= msgvec
; *ip
!= 0; ip
++) {
98 * Work around an optimizer bug in Cray Standard C Version 4.0.3 (057126).
99 * Otherwise, SIGFPE is received when mb.mb_threaded != 0.
101 #pragma _CRI suppress ip
103 if (mb
.mb_threaded
? message
[*ip
-1].m_threadpos
>
112 mp
= &message
[*ip2
- 1];
113 if ((mp
->m_flag
& (MDELETED
|MHIDDEN
)) == 0) {
122 printf(catgets(catd
, CATSET
, 21, "No messages applicable\n"));
127 * If this is the first command, select message 1.
128 * Note that this must exist for us to get here at all.
138 * Just find the next good message after dot, no
142 if (mb
.mb_threaded
== 0) {
143 for (mp
= dot
+ did_print_dot
; mp
< &message
[msgCount
]; mp
++)
144 if ((mp
->m_flag
& (MDELETED
|MSAVED
|MHIDDEN
|MKILL
)) == 0)
149 mp
= next_in_thread(mp
);
150 while (mp
&& mp
->m_flag
& (MDELETED
|MSAVED
|MHIDDEN
|MKILL
))
151 mp
= next_in_thread(mp
);
153 if (mp
== NULL
|| mp
>= &message
[msgCount
]) {
155 printf(catgets(catd
, CATSET
, 22, "At EOF\n"));
164 list
[0] = dot
- &message
[0] + 1;
170 * Save a message in a file. Mark the message as saved
171 * so we can discard when the user quits.
178 return save1(str
, 1, "save", saveignore
, SEND_MBOX
, 0, 0);
186 return save1(str
, 1, "save", saveignore
, SEND_MBOX
, 1, 0);
190 * Copy a message to a file without affected its saved-ness
197 return save1(str
, 0, "copy", saveignore
, SEND_MBOX
, 0, 0);
205 return save1(str
, 0, "copy", saveignore
, SEND_MBOX
, 1, 0);
209 * Move a message to a file.
216 return save1(str
, 0, "move", saveignore
, SEND_MBOX
, 0, 1);
224 return save1(str
, 0, "move", saveignore
, SEND_MBOX
, 1, 1);
228 * Decrypt and copy a message to a file.
235 return save1(str
, 0, "decrypt", saveignore
, SEND_DECRYPT
, 0, 0);
243 return save1(str
, 0, "decrypt", saveignore
, SEND_DECRYPT
, 1, 0);
247 * Save/copy the indicated messages at the end of the passed file name.
248 * If mark is true, mark the message "saved."
251 save1(char *str
, int mark
, char *cmd
, struct ignoretab
*ignore
,
252 int convert
, int sender_record
, int domove
)
257 char *file
= NULL
, *disp
= "";
262 off_t mstats
[2], tstats
[2];
265 int success
= 1, last
= 0;
268 msgvec
= (int *)salloc((msgCount
+ 2) * sizeof *msgvec
);
270 for (cp
= str
; *cp
&& blankchar(*cp
& 0377); cp
++);
273 if ((file
= snarf(str
, &f
, convert
!= SEND_TOFILE
)) == NULL
)
277 *msgvec
= first(0, MMNORM
);
281 printf(catgets(catd
, CATSET
, 23,
282 "No messages to %s.\n"), cmd
);
287 if (f
&& getmsglist(str
, msgvec
, 0) < 0)
292 printf("No applicable messages.\n");
296 if ((cp
= nameof(&message
[*msgvec
- 1], 0)) == NULL
) {
297 printf(catgets(catd
, CATSET
, 24,
298 "Cannot determine message sender to %s.\n"),
302 for (cq
= cp
; *cq
&& *cq
!= '@'; cq
++);
304 if (value("outfolder")) {
305 file
= salloc(strlen(cp
) + 2);
307 strcpy(&file
[1], cp
);
311 if ((file
= expand(file
)) == NULL
)
313 prot
= which_protocol(file
);
314 if (prot
!= PROTO_IMAP
) {
315 if (access(file
, 0) >= 0) {
317 disp
= catgets(catd
, CATSET
, 25, "[Appended]");
320 disp
= catgets(catd
, CATSET
, 26, "[New file]");
323 if ((obuf
= convert
== SEND_TOFILE
? Fopen(file
, "a+") :
324 Zopen(file
, "a+", &compressed
)) == NULL
) {
325 if ((obuf
= convert
== SEND_TOFILE
? Fopen(file
, "wx") :
326 Zopen(file
, "wx", &compressed
)) == NULL
) {
333 disp
= catgets(catd
, CATSET
, 25, "[Appended]");
335 if (!newfile
&& fstat(fileno(obuf
), &st
) &&
336 S_ISREG(st
.st_mode
) &&
337 fseek(obuf
, -2L, SEEK_END
) == 0) {
341 switch (fread(buf
, sizeof *buf
, 2, obuf
)) {
343 if (buf
[1] != '\n') {
366 tstats
[0] = tstats
[1] = 0;
367 imap_created_mailbox
= 0;
368 for (ip
= msgvec
; *ip
&& ip
-msgvec
< msgCount
; ip
++) {
369 mp
= &message
[*ip
- 1];
370 if (prot
== PROTO_IMAP
&&
371 ignore
[0].i_count
== 0 &&
372 ignore
[1].i_count
== 0 &&
373 imap_thisaccount(file
)) {
374 if (imap_copy(mp
, *ip
, file
) == STOP
)
377 mstats
[1] = mp
->m_xsize
;
378 } else if (send(mp
, obuf
, ignore
, NULL
,
379 convert
, mstats
) < 0) {
385 mp
->m_flag
|= MSAVED
;
387 mp
->m_flag
|= MDELETED
|MSAVED
;
390 tstats
[0] += mstats
[0];
391 tstats
[1] += mstats
[1];
398 if (Fclose(obuf
) != 0)
401 if (prot
== PROTO_IMAP
|| prot
== PROTO_MAILDIR
)
402 disp
= prot
== PROTO_IMAP
&& disconnected(file
) ?
403 "[Queued]" : imap_created_mailbox
?
404 "[New file]" : "[Appended]";
405 printf("\"%s\" %s ", file
, disp
);
407 printf("%lu", (long)tstats
[0]);
409 printf(catgets(catd
, CATSET
, 27, "binary"));
410 printf("/%lu\n", (long)tstats
[1]);
412 for (ip
= msgvec
; *ip
&& ip
-msgvec
< msgCount
; ip
++) {
413 mp
= &message
[*ip
- 1];
414 mp
->m_flag
&= ~MSAVED
;
417 for (ip
= msgvec
; *ip
&& ip
-msgvec
< msgCount
; ip
++) {
418 mp
= &message
[*ip
- 1];
419 mp
->m_flag
&= ~(MSAVED
|MDELETED
);
422 if (domove
&& last
&& success
) {
423 setdot(&message
[last
-1]);
424 last
= first(0, MDELETED
);
425 setdot(&message
[last
? last
-1 : 0]);
427 return(success
== 0);
431 * Write the indicated messages at the end of the passed
432 * file name, minus header and trailing blank line.
433 * This is the MIME save function.
440 return save1(str
, 0, "write", allignore
, SEND_TOFILE
, 0, 0);
444 * Snarf the file from the end of the command line and
445 * return a pointer to it. If there is no file attached,
446 * return the mbox file. Put a null in front of the file
447 * name so that the message list processing won't see it,
448 * unless the file name is the only thing on the line, in
449 * which case, return 0 in the reference flag variable.
453 snarf(char *linebuf
, int *flag
, int usembox
)
458 if ((cp
= laststring(linebuf
, flag
, 0)) == NULL
) {
463 printf(catgets(catd
, CATSET
, 28,
464 "No file specified.\n"));
483 * Delete messages, then type the new dot.
492 lastdot
= dot
- &message
[0] + 1;
493 if (delm(msgvec
) >= 0) {
494 list
[0] = dot
- &message
[0] + 1;
495 if (list
[0] > lastdot
) {
500 printf(catgets(catd
, CATSET
, 29, "At EOF\n"));
502 printf(catgets(catd
, CATSET
, 30, "No more messages\n"));
507 * Delete the indicated messages.
508 * Set dot to some nice place afterwards.
509 * Internal interface.
519 for (ip
= msgvec
; *ip
!= 0; ip
++) {
520 mp
= &message
[*ip
- 1];
522 mp
->m_flag
|= MDELETED
|MTOUCH
;
523 mp
->m_flag
&= ~(MPRESERVE
|MSAVED
|MBOX
);
527 setdot(&message
[last
-1]);
528 last
= first(0, MDELETED
);
530 setdot(&message
[last
-1]);
540 * Following can't happen -- it keeps lint happy
547 * Undelete the indicated messages.
556 for (ip
= msgvec
; *ip
&& ip
-msgvec
< msgCount
; ip
++) {
557 mp
= &message
[*ip
- 1];
560 if (mp
->m_flag
& (MDELETED
|MSAVED
))
561 mp
->m_flag
&= ~(MDELETED
|MSAVED
);
563 mp
->m_flag
&= ~MDELETED
;
564 if (mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
)
565 imap_undelete(mp
, *ip
);
570 #ifdef DEBUG_COMMANDS
572 * Interactively dump core on "core"
580 extern int wait_status
;
583 switch (pid
= fork()) {
591 printf(catgets(catd
, CATSET
, 31, "Okie dokie"));
595 if (WCOREDUMP(wait_status
))
596 printf(catgets(catd
, CATSET
, 32, " -- Core dumped.\n"));
598 printf(catgets(catd
, CATSET
, 33, " -- Can't dump core.\n"));
604 * Clobber as many bytes of stack as the user requests.
615 times
= (atoi(argv
[0]) + 511) / 512;
631 for (cp
= buf
; cp
< &buf
[512]; *cp
++ = (char)0xFF)
635 #endif /* DEBUG_COMMANDS */
638 * Add the given header fields to the retained list.
639 * If no arguments, print the current list of retained fields.
646 return ignore1(list
, ignore
+ 1, "retained");
650 * Add the given header fields to the ignored list.
651 * If no arguments, print the current list of ignored fields.
658 return ignore1(list
, ignore
, "ignored");
662 saveretfield(void *v
)
666 return ignore1(list
, saveignore
+ 1, "retained");
674 return ignore1(list
, saveignore
, "ignored");
682 return ignore1(list
, fwdignore
+ 1, "retained");
690 return ignore1(list
, fwdignore
, "ignored");
694 ignore1(char **list
, struct ignoretab
*tab
, char *which
)
701 return igshow(tab
, which
);
702 for (ap
= list
; *ap
!= 0; ap
++) {
707 field
= ac_alloc(sz
+ 1);
708 i_strcpy(field
, *ap
, sz
+ 1);
710 if (member(field
, tab
)) {
715 igp
= (struct ignore
*)scalloc(1, sizeof (struct ignore
));
716 igp
->i_field
= smalloc(strlen(field
) + 1);
717 strcpy(igp
->i_field
, field
);
718 igp
->i_link
= tab
->i_head
[h
];
719 tab
->i_head
[h
] = igp
;
727 * Print out all currently retained fields.
730 igshow(struct ignoretab
*tab
, char *which
)
736 if (tab
->i_count
== 0) {
737 printf(catgets(catd
, CATSET
, 34,
738 "No fields currently being %s.\n"), which
);
742 ring
= (char **)salloc((tab
->i_count
+ 1) * sizeof (char *));
744 for (h
= 0; h
< HSHSIZE
; h
++)
745 for (igp
= tab
->i_head
[h
]; igp
!= 0; igp
= igp
->i_link
)
746 *ap
++ = igp
->i_field
;
748 qsort(ring
, tab
->i_count
, sizeof (char *), igcomp
);
749 for (ap
= ring
; *ap
!= 0; ap
++)
755 * Compare two names for sorting ignored field list.
758 igcomp(const void *l
, const void *r
)
760 return (strcmp(*(char **)l
, *(char **)r
));
766 return unignore1((char **)v
, ignore
, "ignored");
772 return unignore1((char **)v
, ignore
+ 1, "retained");
776 unsaveignore(void *v
)
778 return unignore1((char **)v
, saveignore
, "ignored");
782 unsaveretain(void *v
)
784 return unignore1((char **)v
, saveignore
+ 1, "retained");
790 return unignore1((char **)v
, fwdignore
, "ignored");
796 return unignore1((char **)v
, fwdignore
+ 1, "retained");
800 unignore_one(const char *name
, struct ignoretab
*tab
)
802 struct ignore
*ip
, *iq
= NULL
;
805 for (ip
= tab
->i_head
[h
]; ip
; ip
= ip
->i_link
) {
806 if (asccasecmp(ip
->i_field
, name
) == 0) {
809 iq
->i_link
= ip
->i_link
;
811 tab
->i_head
[h
] = ip
->i_link
;
821 unignore1(char **list
, struct ignoretab
*tab
, char *which
)
823 if (tab
->i_count
== 0) {
824 printf(catgets(catd
, CATSET
, 34,
825 "No fields currently being %s.\n"), which
);
829 unignore_one(*list
++, tab
);