1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Still more user commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 /* Modify subject we reply to to begin with Re: if it does not already */
49 static char * _reedit(char *subj
);
51 /* "set" command: show all option settings */
52 static int _set_show_all(void);
54 static int bangexp(char **str
, size_t *size
);
55 static void make_ref_and_cs(struct message
*mp
, struct header
*head
);
56 static int (* respond_or_Respond(int c
))(int *, int);
57 static int respond_internal(int *msgvec
, int recipient_record
);
58 static char * fwdedit(char *subj
);
59 static void asort(char **list
);
60 static int diction(const void *a
, const void *b
);
61 static int file1(char const *name
);
62 static int Respond_internal(int *msgvec
, int recipient_record
);
63 static int resend1(void *v
, int add_resent
);
64 static void list_shortcuts(void);
65 static enum okay
delete_shortcut(const char *str
);
73 if (subj
== NULL
|| *subj
== '\0')
78 mime_fromhdr(&in
, &out
, TD_ISPR
|TD_ICONV
);
80 if ((out
.s
[0] == 'r' || out
.s
[0] == 'R') &&
81 (out
.s
[1] == 'e' || out
.s
[1] == 'E') &&
83 newsubj
= savestr(out
.s
);
86 newsubj
= salloc(out
.l
+ 5);
87 sstpcpy(sstpcpy(newsubj
, "Re: "), out
.s
);
99 char *cp
, **vacp
, **p
;
102 union {size_t j
; char const *fmt
;} u
;
104 if ((fp
= Ftemp(&cp
, "Ra", "w+", 0600, 1)) == NULL
) {
111 for (u
.j
= 1, i
= 0; i
< HSHSIZE
; ++i
)
112 for (vp
= variables
[i
]; vp
!= NULL
; vp
= vp
->v_link
)
114 vacp
= (char**)salloc(u
.j
* sizeof(*vacp
));
115 for (p
= vacp
, i
= 0; i
< HSHSIZE
; ++i
)
116 for (vp
= variables
[i
]; vp
!= NULL
; vp
= vp
->v_link
)
122 i
= (value("bsdcompat") != NULL
|| value("bsdset") != NULL
);
123 u
.fmt
= i
? "%s\t%s\n" : "%s=\"%s\"\n";
124 for (p
= vacp
; *p
!= NULL
; ++p
) {
125 char const *x
= value(*p
);
129 fprintf(fp
, u
.fmt
, *p
, x
);
131 fprintf(fp
, "%s\n", *p
);
134 page_or_print(fp
, (size_t)(p
- vacp
));
142 * Process a shell escape by saving signals, ignoring signals,
143 * and forking a sh -c
151 sighandler_type sigint
= safe_signal(SIGINT
, SIG_IGN
);
153 cmd
= smalloc(cmdsize
= strlen(str
) + 1);
154 memcpy(cmd
, str
, cmdsize
);
155 if (bangexp(&cmd
, &cmdsize
) < 0)
157 if ((shell
= value("SHELL")) == NULL
)
159 run_command(shell
, 0, -1, -1, "-c", cmd
, NULL
);
160 safe_signal(SIGINT
, sigint
);
167 * Fork an interactive shell.
173 sighandler_type sigint
= safe_signal(SIGINT
, SIG_IGN
);
177 if ((shell
= value("SHELL")) == NULL
)
179 run_command(shell
, 0, -1, -1, NULL
, NULL
, NULL
);
180 safe_signal(SIGINT
, sigint
);
186 * Expand the shell escape by expanding unescaped !'s into the
187 * last issued command where possible.
190 static char *lastbang
;
191 static size_t lastbangsize
;
194 bangexp(char **str
, size_t *size
)
198 int dobang
= value("bang") != NULL
;
199 size_t sz
, i
, j
, bangbufsize
;
201 bangbuf
= smalloc(bangbufsize
= *size
);
205 if ((*str
)[i
] == '!') {
206 sz
= strlen(lastbang
);
207 bangbuf
= srealloc(bangbuf
, bangbufsize
+= sz
);
209 memcpy(bangbuf
+ j
, lastbang
, sz
+ 1);
215 if ((*str
)[i
] == '\\' && (*str
)[i
+ 1] == '!') {
220 bangbuf
[j
++] = (*str
)[i
++];
224 printf("!%s\n", bangbuf
);
229 *str
= srealloc(*str
, *size
= sz
);
230 memcpy(*str
, bangbuf
, sz
);
231 if (sz
> lastbangsize
)
232 lastbang
= srealloc(lastbang
, lastbangsize
= sz
);
233 memcpy(lastbang
, bangbuf
, sz
);
243 char *arg
= *(char**)v
;
246 #ifdef HAVE_DOCSTRINGS
247 extern struct cmd
const cmdtab
[];
248 struct cmd
const *cp
;
249 for (cp
= cmdtab
; cp
->c_name
!= NULL
; ++cp
) {
250 if (cp
->c_func
== &ccmdnotsupp
)
252 if (strcmp(arg
, cp
->c_name
) == 0)
253 printf("%s: %s\n", arg
,
254 tr(cp
->c_docid
, cp
->c_doc
));
255 else if (is_prefix(arg
, cp
->c_name
))
256 printf("%s (%s): %s\n", arg
, cp
->c_name
,
257 tr(cp
->c_docid
, cp
->c_doc
));
262 fprintf(stderr
, tr(91, "Unknown command: \"%s\"\n"), arg
);
265 ret
= ccmdnotsupp(NULL
);
270 /* Very ugly, but take care for compiler supported string lengths :( */
271 printf(tr(295, "%s commands:\n"), progname
);
273 "type <message list> type messages\n"
274 "next goto and type next message\n"
275 "from <message list> give head lines of messages\n"
276 "headers print out active message headers\n"
277 "delete <message list> delete messages\n"
278 "undelete <message list> undelete messages\n"));
280 "save <message list> folder append messages to folder and mark as saved\n"
281 "copy <message list> folder append messages to folder without marking them\n"
282 "write <message list> file append message texts to file, save attachments\n"
283 "preserve <message list> keep incoming messages in mailbox even if saved\n"
284 "Reply <message list> reply to message senders\n"
285 "reply <message list> reply to message senders and all recipients\n"));
287 "mail addresses mail to specific recipients\n"
288 "file folder change to another folder\n"
289 "quit quit and apply changes to folder\n"
290 "xit quit and discard changes made to folder\n"
292 "cd <directory> chdir to directory or home if none given\n"
293 "list list names of all available commands\n"));
294 fprintf(stdout
, tr(299,
295 "\nA <message list> consists of integers, ranges of same, or other criteria\n"
296 "separated by spaces. If omitted, %s uses the last message typed.\n"),
304 * Change user's working directory.
312 if (*arglist
== NULL
)
314 else if ((cp
= file_expand(*arglist
)) == NULL
)
325 make_ref_and_cs(struct message
*mp
, struct header
*head
)
327 char *oldref
, *oldmsgid
, *newref
, *cp
;
328 size_t oldreflen
= 0, oldmsgidlen
= 0, reflen
;
332 oldref
= hfield1("references", mp
);
333 oldmsgid
= hfield1("message-id", mp
);
334 if (oldmsgid
== NULL
|| *oldmsgid
== '\0') {
340 oldreflen
= strlen(oldref
);
341 reflen
+= oldreflen
+ 2;
344 oldmsgidlen
= strlen(oldmsgid
);
345 reflen
+= oldmsgidlen
;
348 newref
= ac_alloc(reflen
);
349 if (oldref
!= NULL
) {
350 memcpy(newref
, oldref
, oldreflen
+ 1);
351 if (oldmsgid
!= NULL
) {
352 newref
[oldreflen
++] = ',';
353 newref
[oldreflen
++] = ' ';
354 memcpy(newref
+ oldreflen
, oldmsgid
, oldmsgidlen
+ 1);
357 memcpy(newref
, oldmsgid
, oldmsgidlen
+ 1);
358 n
= extract(newref
, GREF
);
362 * Limit the references to 21 entries.
364 while (n
->n_flink
!= NULL
)
366 for (i
= 1; i
< 21; i
++) {
367 if (n
->n_blink
!= NULL
)
374 if (value("reply-in-same-charset") != NULL
&&
375 (cp
= hfield1("content-type", mp
)) != NULL
)
376 head
->h_charset
= mime_getparam("charset", cp
);
380 (*respond_or_Respond(int c
))(int *, int)
384 opt
+= (value("Replyall") != NULL
);
385 opt
+= (value("flipr") != NULL
);
386 return ((opt
== 1) ^ (c
== 'R')) ? Respond_internal
: respond_internal
;
392 return (respond_or_Respond('r'))((int *)v
, 0);
398 return respond_internal((int *)v
, 0);
402 respondsender(void *v
)
404 return Respond_internal((int *)v
, 0);
410 return (respond_or_Respond('r'))((int *)v
, 1);
416 return respond_internal((int *)v
, 1);
420 followupsender(void *v
)
422 return Respond_internal((int *)v
, 1);
426 * Reply to a single message. Extract each name from the
427 * message header and send them off to mail1()
430 respond_internal(int *msgvec
, int recipient_record
)
435 struct name
*np
= NULL
;
436 enum gfield gf
= boption("fullnames") ? GFULL
: GSKIN
;
438 if (msgvec
[1] != 0) {
439 fprintf(stderr
, tr(37,
440 "Sorry, can't reply to multiple messages at once\n"));
443 mp
= &message
[msgvec
[0] - 1];
447 if ((rcv
= hfield1("reply-to", mp
)) == NULL
)
448 if ((rcv
= hfield1("from", mp
)) == NULL
)
451 np
= lextract(rcv
, GTO
|gf
);
452 if (! boption("recipients-in-cc") && (cp
= hfield1("to", mp
)) != NULL
)
453 np
= cat(np
, lextract(cp
, GTO
| gf
));
455 * Delete my name from the reply list,
456 * and with it, all my alternate names.
458 np
= elide(delete_alternates(np
));
460 np
= lextract(rcv
, GTO
| gf
);
462 memset(&head
, 0, sizeof head
);
464 head
.h_subject
= hfield1("subject", mp
);
465 head
.h_subject
= _reedit(head
.h_subject
);
468 if (boption("recipients-in-cc") && (cp
= hfield1("to", mp
)) != NULL
)
469 np
= lextract(cp
, GCC
| gf
);
470 if ((cp
= hfield1("cc", mp
)) != NULL
)
471 np
= cat(np
, lextract(cp
, GCC
| gf
));
473 head
.h_cc
= elide(delete_alternates(np
));
474 make_ref_and_cs(mp
, &head
);
476 if (boption("quote-as-attachment")) {
477 head
.h_attach
= csalloc(1, sizeof *head
.h_attach
);
478 head
.h_attach
->a_msgno
= *msgvec
;
479 head
.h_attach
->a_content_description
= tr(512,
480 "Original message content");
483 if (mail1(&head
, 1, mp
, NULL
, recipient_record
, 0) == OKAY
&&
484 boption("markanswered") &&
485 (mp
->m_flag
& MANSWERED
) == 0)
486 mp
->m_flag
|= MANSWER
| MANSWERED
;
491 * Forward a message to a new recipient, in the sense of RFC 2822.
494 forward1(char *str
, int recipient_record
)
500 bool_t forward_as_attachment
;
502 forward_as_attachment
= boption("forward-as-attachment");
503 msgvec
= salloc((msgCount
+ 2) * sizeof *msgvec
);
504 if ((recipient
= laststring(str
, &f
, 0)) == NULL
) {
505 puts(catgets(catd
, CATSET
, 47, "No recipient specified."));
509 *msgvec
= first(0, MMNORM
);
513 printf("No messages to forward.\n");
518 if (f
&& getmsglist(str
, msgvec
, 0) < 0)
523 printf("No applicable messages.\n");
526 if (msgvec
[1] != 0) {
527 printf("Cannot forward multiple messages at once\n");
530 memset(&head
, 0, sizeof head
);
531 if ((head
.h_to
= lextract(recipient
,
532 GTO
| (value("fullnames") ? GFULL
: GSKIN
))) == NULL
)
534 mp
= &message
[*msgvec
- 1];
535 if (forward_as_attachment
) {
536 head
.h_attach
= csalloc(1, sizeof *head
.h_attach
);
537 head
.h_attach
->a_msgno
= *msgvec
;
538 head
.h_attach
->a_content_description
= "Forwarded message";
543 head
.h_subject
= hfield1("subject", mp
);
544 head
.h_subject
= fwdedit(head
.h_subject
);
545 mail1(&head
, 1, (forward_as_attachment
? NULL
: mp
),
546 NULL
, recipient_record
, 1);
551 * Modify the subject we are replying to to begin with Fwd:.
559 if (subj
== NULL
|| *subj
== '\0')
563 mime_fromhdr(&in
, &out
, TD_ISPR
|TD_ICONV
);
565 newsubj
= salloc(out
.l
+ 6);
566 memcpy(newsubj
, "Fwd: ", 5);
567 memcpy(newsubj
+ 5, out
.s
, out
.l
+ 1);
573 * The 'forward' command.
578 return forward1(v
, 0);
582 * Similar to forward, saving the message in a file named after the
588 return forward1(v
, 1);
592 * Preserve the named messages, so that they will be sent
593 * back to the system mailbox.
603 printf(catgets(catd
, CATSET
, 39,
604 "Cannot \"preserve\" in edit mode\n"));
607 for (ip
= msgvec
; *ip
!= 0; ip
++) {
609 mp
= &message
[mesg
-1];
610 mp
->m_flag
|= MPRESERVE
;
614 * This is now Austin Group Request XCU #20.
616 did_print_dot
= TRU1
;
622 * Mark all given messages as unread.
630 for (ip
= msgvec
; *ip
!= 0; ip
++) {
631 setdot(&message
[*ip
-1]);
632 dot
->m_flag
&= ~(MREAD
|MTOUCH
);
633 dot
->m_flag
|= MSTATUS
;
635 if (mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
)
636 imap_unread(&message
[*ip
-1], *ip
); /* TODO return? */
639 * The "unread" command is not part of POSIX mailx.
641 did_print_dot
= TRU1
;
647 * Mark all given messages as read.
655 for (ip
= msgvec
; *ip
; ip
++) {
656 setdot(&message
[*ip
-1]);
657 touch(&message
[*ip
-1]);
663 * Print the size of each message.
672 for (ip
= msgvec
; *ip
!= 0; ip
++) {
674 mp
= &message
[mesg
-1];
675 printf("%d: ", mesg
);
676 if (mp
->m_xlines
> 0)
677 printf("%ld", mp
->m_xlines
);
680 printf("/%lu\n", (unsigned long)mp
->m_xsize
);
686 * Quit quickly. If we are sourcing, just pop the input level
687 * by returning an error.
703 char **ap
= v
, *cp
, *cp2
, *varbuf
, c
;
711 for (; *ap
!= NULL
; ++ap
) {
713 cp2
= varbuf
= ac_alloc(strlen(cp
) + 1);
714 for (; (c
= *cp
) != '=' && c
!= '\0'; ++cp
)
723 tr(41, "Non-null variable name required\n"));
727 if (varbuf
[0] == 'n' && varbuf
[1] == 'o')
728 errs
+= unset_internal(&varbuf
[2]);
731 jnext
: ac_free(varbuf
);
738 * Unset a bunch of variable values.
747 for (ap
= (char **)v
; *ap
!= NULL
; ap
++)
748 errs
+= unset_internal(*ap
);
753 * Put add users to a group.
759 struct grouphead
*gh
;
763 char **ap
, *gname
, **p
;
766 for (h
= 0, s
= 1; h
< HSHSIZE
; h
++)
767 for (gh
= groups
[h
]; gh
!= NULL
; gh
= gh
->g_link
)
770 ap
= (char **)salloc(s
* sizeof *ap
);
771 for (h
= 0, p
= ap
; h
< HSHSIZE
; h
++)
772 for (gh
= groups
[h
]; gh
!= NULL
; gh
= gh
->g_link
)
776 for (p
= ap
; *p
!= NULL
; p
++)
780 if (argv
[1] == NULL
) {
786 if ((gh
= findgroup(gname
)) == NULL
) {
787 gh
= (struct grouphead
*)scalloc(1, sizeof *gh
);
788 gh
->g_name
= sstrdup(gname
);
790 gh
->g_link
= groups
[h
];
795 * Insert names from the command list into the group.
796 * Who cares if there are duplicates? They get tossed
800 for (ap
= argv
+1; *ap
!= NULL
; ap
++) {
801 gp
= (struct group
*)scalloc(1, sizeof *gp
);
802 gp
->ge_name
= sstrdup(*ap
);
803 gp
->ge_link
= gh
->g_list
;
810 * Delete the passed groups.
818 fprintf(stderr
, tr(209, "Must specify alias to remove\n"));
823 while (*++argv
!= NULL
);
828 * Sort the passed string vecotor into ascending dictionary
836 for (ap
= list
; *ap
!= NULL
; ap
++)
840 qsort(list
, ap
-list
, sizeof(*list
), diction
);
844 * Do a dictionary order comparison of the arguments from
848 diction(const void *a
, const void *b
)
850 return(strcmp(*(char**)UNCONST(a
), *(char**)UNCONST(b
)));
854 * Change to another file. With no argument, print information about
861 #if 1 /* TODO this & expansion is completely redundant! */
864 if (argv
[0] == NULL
) {
869 if ((exp
= expand("&")) == NULL
)
871 strncpy(mboxname
, exp
, sizeof mboxname
)[sizeof mboxname
- 1] = '\0';
877 file1(char const *name
)
882 fprintf(stderr
, "Cannot change folder from within a hook.\n");
885 i
= setfile(name
, 0);
888 callhook(mailname
, 0);
889 if (i
> 0 && value("emptystart") == NULL
)
891 announce(value("bsdcompat") != NULL
|| value("bsdannounce") != NULL
);
897 * Expand file names like echo
902 char const **argv
= v
, **ap
, *cp
;
905 for (ap
= argv
; *ap
!= NULL
; ++ap
) {
907 if ((cp
= fexpand(cp
, FEXP_NSHORTCUT
)) != NULL
) {
910 while (*cp
!= '\0' &&
911 (c
= expand_shell_escape(&cp
)) > 0)
913 /* \c ends overall processing */
926 return (respond_or_Respond('R'))((int *)v
, 0);
932 return (respond_or_Respond('R'))((int *)v
, 1);
936 * Reply to a series of messages by simply mailing to the senders
937 * and not messing around with the To: and Cc: lists as in normal
941 Respond_internal(int *msgvec
, int recipient_record
)
947 enum gfield gf
= boption("fullnames") ? GFULL
: GSKIN
;
949 memset(&head
, 0, sizeof head
);
951 for (ap
= msgvec
; *ap
!= 0; ap
++) {
952 mp
= &message
[*ap
- 1];
955 if ((cp
= hfield1("reply-to", mp
)) == NULL
)
956 if ((cp
= hfield1("from", mp
)) == NULL
)
958 head
.h_to
= cat(head
.h_to
, lextract(cp
, GTO
| gf
));
960 if (head
.h_to
== NULL
)
963 mp
= &message
[msgvec
[0] - 1];
964 head
.h_subject
= hfield1("subject", mp
);
965 head
.h_subject
= _reedit(head
.h_subject
);
966 make_ref_and_cs(mp
, &head
);
968 if (boption("quote-as-attachment")) {
969 head
.h_attach
= csalloc(1, sizeof *head
.h_attach
);
970 head
.h_attach
->a_msgno
= *msgvec
;
971 head
.h_attach
->a_content_description
= tr(512,
972 "Original message content");
975 if (mail1(&head
, 1, mp
, NULL
, recipient_record
, 0) == OKAY
&&
976 value("markanswered") && (mp
->m_flag
& MANSWERED
) == 0)
977 mp
->m_flag
|= MANSWER
| MANSWERED
;
982 * Conditional commands. These allow one to parameterize one's
983 * .mailrc and do some things if sending, others if receiving.
992 printf(catgets(catd
, CATSET
, 42, "Illegal nested \"if\"\n"));
1011 printf(catgets(catd
, CATSET
, 43,
1012 "Unrecognized if-keyword: \"%s\"\n"), cp
);
1019 * Implement 'else'. This is pretty simple -- we just
1020 * flip over the conditional flag.
1030 printf(catgets(catd
, CATSET
, 44,
1031 "\"Else\" without matching \"if\"\n"));
1047 printf(catgets(catd
, CATSET
, 45,
1048 "Mail's idea of conditions is screwed up\n"));
1056 * End of if statement. Just set cond back to anything.
1065 printf(catgets(catd
, CATSET
, 46,
1066 "\"Endif\" without matching \"if\"\n"));
1074 * Set the list of alternate names.
1080 char **namelist
= v
, **ap
, **ap2
, *cp
;
1082 l
= argcount(namelist
) + 1;
1085 if (altnames
== NULL
)
1087 for (ap
= altnames
; *ap
!= NULL
; ++ap
)
1093 if (altnames
!= NULL
) {
1094 for (ap
= altnames
; *ap
!= NULL
; ++ap
)
1098 altnames
= smalloc(l
* sizeof(char*));
1099 for (ap
= namelist
, ap2
= altnames
; *ap
; ++ap
, ++ap2
) {
1100 l
= strlen(*ap
) + 1;
1111 * Do the real work of resending.
1114 resend1(void *v
, int add_resent
)
1119 int f
, *ip
, *msgvec
;
1123 msgvec
= (int *)salloc((msgCount
+ 2) * sizeof *msgvec
);
1124 name
= laststring(str
, &f
, 1);
1126 puts(catgets(catd
, CATSET
, 47, "No recipient specified."));
1130 *msgvec
= first(0, MMNORM
);
1134 puts(catgets(catd
, CATSET
, 48,
1135 "No applicable messages."));
1139 } else if (getmsglist(str
, msgvec
, 0) < 0)
1144 printf("No applicable messages.\n");
1147 sn
= nalloc(name
, GTO
);
1148 to
= usermap(sn
, FAL0
);
1149 for (ip
= msgvec
; *ip
&& ip
- msgvec
< msgCount
; ip
++) {
1150 if (resend_msg(&message
[*ip
- 1], to
, add_resent
) != OKAY
)
1157 * Resend a message list to a third person.
1162 return resend1(v
, 1);
1166 * Resend a message list to a third person without adding headers.
1171 return resend1(v
, 0);
1175 * 'newmail' or 'inc' command: Check for new mail without writing old
1187 (mb
.mb_type
!= MB_IMAP
|| imap_newmail(1)) &&
1189 (val
= setfile(mailname
, 1)) == 0) {
1191 setdot(&message
[mdot
- 1]);
1197 list_shortcuts(void)
1201 for (s
= shortcuts
; s
; s
= s
->sh_next
)
1202 printf("%s=%s\n", s
->sh_short
, s
->sh_long
);
1208 char **args
= (char **)v
;
1211 if (args
[0] == NULL
) {
1215 if (args
[1] == NULL
) {
1216 fprintf(stderr
, catgets(catd
, CATSET
, 220,
1217 "expansion name for shortcut missing\n"));
1220 if (args
[2] != NULL
) {
1221 fprintf(stderr
, catgets(catd
, CATSET
, 221,
1222 "too many arguments\n"));
1225 if ((s
= get_shortcut(args
[0])) != NULL
) {
1227 s
->sh_long
= sstrdup(args
[1]);
1229 s
= scalloc(1, sizeof *s
);
1230 s
->sh_short
= sstrdup(args
[0]);
1231 s
->sh_long
= sstrdup(args
[1]);
1232 s
->sh_next
= shortcuts
;
1239 get_shortcut(const char *str
)
1243 for (s
= shortcuts
; s
; s
= s
->sh_next
)
1244 if (strcmp(str
, s
->sh_short
) == 0)
1250 delete_shortcut(const char *str
)
1252 struct shortcut
*sp
, *sq
;
1254 for (sp
= shortcuts
, sq
= NULL
; sp
; sq
= sp
, sp
= sp
->sh_next
) {
1255 if (strcmp(sp
->sh_short
, str
) == 0) {
1259 sq
->sh_next
= sp
->sh_next
;
1260 if (sp
== shortcuts
)
1261 shortcuts
= sp
->sh_next
;
1272 char **args
= (char **)v
;
1275 if (args
[0] == NULL
) {
1276 fprintf(stderr
, catgets(catd
, CATSET
, 222,
1277 "need shortcut names to remove\n"));
1280 while (*args
!= NULL
) {
1281 if (delete_shortcut(*args
) != OKAY
) {
1283 fprintf(stderr
, catgets(catd
, CATSET
, 223,
1284 "%s: no such shortcut\n"), *args
);
1292 struct oldaccount
*ac_next
; /* next account in list */
1293 char *ac_name
; /* name of account */
1294 char **ac_vars
; /* variables to set */
1297 static struct oldaccount
*oldaccounts
;
1300 get_oldaccount(const char *name
)
1302 struct oldaccount
*a
;
1304 for (a
= oldaccounts
; a
; a
= a
->ac_next
)
1305 if (a
->ac_name
&& strcmp(name
, a
->ac_name
) == 0)
1313 char **args
= (char **)v
;
1314 struct oldaccount
*a
;
1316 int i
, mc
, oqf
, nqf
;
1318 if (args
[0] == NULL
) {
1320 if ((fp
= Ftemp(&cp
, "Ra", "w+", 0600, 1)) == NULL
) {
1326 mc
= listaccounts(fp
);
1327 for (a
= oldaccounts
; a
; a
= a
->ac_next
)
1331 fprintf(fp
, "%s:\n", a
->ac_name
);
1332 for (i
= 0; a
->ac_vars
[i
]; i
++)
1333 fprintf(fp
, "\t%s\n", a
->ac_vars
[i
]);
1340 if (args
[1] && args
[1][0] == '{' && args
[1][1] == '\0') {
1341 if (args
[2] != NULL
) {
1342 fprintf(stderr
, "Syntax is: account <name> {\n");
1345 if ((a
= get_oldaccount(args
[0])) != NULL
)
1347 return define1(args
[0], 1);
1350 if ((cp
= expand("&")) == NULL
)
1352 strncpy(mboxname
, cp
, sizeof mboxname
)[sizeof mboxname
- 1] = '\0';
1354 oqf
= savequitflags();
1355 if ((a
= get_oldaccount(args
[0])) == NULL
) {
1357 a
= scalloc(1, sizeof *a
);
1358 a
->ac_next
= oldaccounts
;
1361 if ((i
= callaccount(args
[0])) != CBAD
)
1363 printf("Account %s does not exist.\n", args
[0]);
1368 delaccount(args
[0]);
1369 a
->ac_name
= sstrdup(args
[0]);
1370 for (i
= 1; args
[i
]; i
++);
1371 a
->ac_vars
= scalloc(i
, sizeof *a
->ac_vars
);
1372 for (i
= 0; args
[i
+1]; i
++)
1373 a
->ac_vars
[i
] = sstrdup(args
[i
+1]);
1375 unset_allow_undefined
= TRU1
;
1377 unset_allow_undefined
= FAL0
;
1378 setf
: if (!starting
) {
1379 nqf
= savequitflags();
1380 restorequitflags(oqf
);
1382 restorequitflags(nqf
);
1396 for (ip
= msgvec
; *ip
!= 0; ip
++) {
1397 m
= &message
[*ip
-1];
1399 if ((m
->m_flag
& (MFLAG
|MFLAGGED
)) == 0)
1400 m
->m_flag
|= MFLAG
|MFLAGGED
;
1412 for (ip
= msgvec
; *ip
!= 0; ip
++) {
1413 m
= &message
[*ip
-1];
1415 if (m
->m_flag
& (MFLAG
|MFLAGGED
)) {
1416 m
->m_flag
&= ~(MFLAG
|MFLAGGED
);
1417 m
->m_flag
|= MUNFLAG
;
1430 for (ip
= msgvec
; *ip
!= 0; ip
++) {
1431 m
= &message
[*ip
-1];
1433 if ((m
->m_flag
& (MANSWER
|MANSWERED
)) == 0)
1434 m
->m_flag
|= MANSWER
|MANSWERED
;
1440 cunanswered(void *v
)
1446 for (ip
= msgvec
; *ip
!= 0; ip
++) {
1447 m
= &message
[*ip
-1];
1449 if (m
->m_flag
& (MANSWER
|MANSWERED
)) {
1450 m
->m_flag
&= ~(MANSWER
|MANSWERED
);
1451 m
->m_flag
|= MUNANSWER
;
1464 for (ip
= msgvec
; *ip
!= 0; ip
++) {
1465 m
= &message
[*ip
-1];
1467 if ((m
->m_flag
& (MDRAFT
|MDRAFTED
)) == 0)
1468 m
->m_flag
|= MDRAFT
|MDRAFTED
;
1480 for (ip
= msgvec
; *ip
!= 0; ip
++) {
1481 m
= &message
[*ip
-1];
1483 if (m
->m_flag
& (MDRAFT
|MDRAFTED
)) {
1484 m
->m_flag
&= ~(MDRAFT
|MDRAFTED
);
1485 m
->m_flag
|= MUNDRAFT
;
1497 switch (mb
.mb_type
) {
1503 return (ccmdnotsupp(NULL
));
1510 return (ccmdnotsupp(NULL
));
1526 if (*args
== NULL
) {
1527 fprintf(stderr
, tr(290, "Syntax is: remove mailbox ...\n"));
1531 if ((name
= expand(*args
)) == NULL
)
1533 if (strcmp(name
, mailname
) == 0) {
1534 fprintf(stderr
, tr(286,
1535 "Cannot remove current mailbox \"%s\".\n"),
1540 snprintf(vb
, sizeof vb
, tr(287, "Remove \"%s\" (y/n) ? "),
1544 switch (which_protocol(name
)) {
1546 if (unlink(name
) < 0) { /* do not handle .gz .bz2 */
1552 fprintf(stderr
, tr(288,
1553 "Cannot remove POP3 mailbox \"%s\".\n"),
1559 if (imap_remove(name
) != OKAY
)
1564 if (maildir_remove(name
) != OKAY
)
1568 fprintf(stderr
, tr(289,
1569 "Unknown protocol in \"%s\". Not removed.\n"),
1580 char **args
= v
, *old
, *new;
1581 enum protocol oldp
, newp
;
1584 if (args
[0] == NULL
|| args
[1] == NULL
|| args
[2] != NULL
) {
1585 fprintf(stderr
, "Syntax: rename old new\n");
1589 if ((old
= expand(args
[0])) == NULL
)
1591 oldp
= which_protocol(old
);
1592 if ((new = expand(args
[1])) == NULL
)
1594 newp
= which_protocol(new);
1596 if (strcmp(old
, mailname
) == 0 || strcmp(new, mailname
) == 0) {
1597 fprintf(stderr
, tr(291,
1598 "Cannot rename current mailbox \"%s\".\n"), old
);
1601 if ((oldp
== PROTO_IMAP
|| newp
== PROTO_IMAP
) && oldp
!= newp
) {
1602 fprintf(stderr
, tr(292,
1603 "Can only rename folders of same type.\n"));
1606 if (newp
== PROTO_POP3
)
1610 if (link(old
, new) < 0) {
1624 } else if (unlink(old
) < 0) {
1630 if (rename(old
, new) < 0) {
1636 nopop3
: fprintf(stderr
, tr(293, "Cannot rename POP3 mailboxes.\n"));
1641 if (imap_rename(old
, new) != OKAY
)
1647 fprintf(stderr
, tr(294,
1648 "Unknown protocol in \"%s\" and \"%s\". "
1649 "Not renamed.\n"), old
, new);