1 /*
2  * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3  *
4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5  */
6 /*
7  * Copyright (c) 1980, 1993
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
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.
25  *
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
36  * SUCH DAMAGE.
37  */
38 
39 #ifndef lint
40 #ifdef	DOSCCS
41 static char sccsid[] = "@(#)cmd3.c	2.86 (gritter) 10/1/07";
42 #endif
43 #endif /* not lint */
44 
45 #include <math.h>
46 #include <float.h>
47 #include "rcv.h"
48 #include "extern.h"
49 #include <unistd.h>
50 #include <errno.h>
51 
52 /*
53  * Mail -- a mail program
54  *
55  * Still more user commands.
56  */
57 
58 static int bangexp(char **str, size_t *size);
59 static void make_ref_and_cs(struct message *mp, struct header *head);
60 static int (*respond_or_Respond(int c))(int *, int);
61 static int respond_internal(int *msgvec, int recipient_record);
62 static char *reedit(char *subj);
63 static char *fwdedit(char *subj);
64 static void onpipe(int signo);
65 static void asort(char **list);
66 static int diction(const void *a, const void *b);
67 static int file1(char *name);
68 static int shellecho(const char *cp);
69 static int Respond_internal(int *msgvec, int recipient_record);
70 static int resend1(void *v, int add_resent);
71 static void list_shortcuts(void);
72 static enum okay delete_shortcut(const char *str);
73 static float huge(void);
74 
75 /*
76  * Process a shell escape by saving signals, ignoring signals,
77  * and forking a sh -c
78  */
79 int
shell(void * v)80 shell(void *v)
81 {
82 	char *str = v;
83 	sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
84 	char *shell;
85 	char *cmd;
86 	size_t cmdsize;
87 
88 	cmd = smalloc(cmdsize = strlen(str) + 1);
89 	strcpy(cmd, str);
90 	if (bangexp(&cmd, &cmdsize) < 0)
91 		return 1;
92 	if ((shell = value("SHELL")) == NULL)
93 		shell = SHELL;
94 	run_command(shell, 0, -1, -1, "-c", cmd, NULL);
95 	safe_signal(SIGINT, sigint);
96 	printf("!\n");
97 	free(cmd);
98 	return 0;
99 }
100 
101 /*
102  * Fork an interactive shell.
103  */
104 /*ARGSUSED*/
105 int
dosh(void * v)106 dosh(void *v)
107 {
108 	sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
109 	char *shell;
110 
111 	if ((shell = value("SHELL")) == NULL)
112 		shell = SHELL;
113 	run_command(shell, 0, -1, -1, NULL, NULL, NULL);
114 	safe_signal(SIGINT, sigint);
115 	putchar('\n');
116 	return 0;
117 }
118 
119 /*
120  * Expand the shell escape by expanding unescaped !'s into the
121  * last issued command where possible.
122  */
123 
124 static char	*lastbang;
125 static size_t	lastbangsize;
126 
127 static int
bangexp(char ** str,size_t * size)128 bangexp(char **str, size_t *size)
129 {
130 	char *bangbuf;
131 	int changed = 0;
132 	int dobang = value("bang") != NULL;
133 	size_t sz, i, j, bangbufsize;
134 
135 	bangbuf = smalloc(bangbufsize = *size);
136 	i = j = 0;
137 	while ((*str)[i]) {
138 		if (dobang) {
139 			if ((*str)[i] == '!') {
140 				sz = strlen(lastbang);
141 				bangbuf = srealloc(bangbuf, bangbufsize += sz);
142 				changed++;
143 				strcpy(&bangbuf[j], lastbang);
144 				j += sz;
145 				i++;
146 				continue;
147 			}
148 		}
149 		if ((*str)[i] == '\\' && (*str)[i + 1] == '!') {
150 			bangbuf[j++] = '!';
151 			i += 2;
152 			changed++;
153 		}
154 		bangbuf[j++] = (*str)[i++];
155 	}
156 	bangbuf[j] = '\0';
157 	if (changed) {
158 		printf("!%s\n", bangbuf);
159 		fflush(stdout);
160 	}
161 	sz = j;
162 	if (sz >= *size)
163 		*str = srealloc(*str, *size = sz + 1);
164 	strcpy(*str, bangbuf);
165 	if (sz >= lastbangsize)
166 		lastbang = srealloc(lastbang, lastbangsize = sz + 1);
167 	strcpy(lastbang, bangbuf);
168 	free(bangbuf);
169 	return(0);
170 }
171 
172 /*ARGSUSED*/
173 int
help(void * v)174 help(void *v)
175 {
176 	const char *helptext =
177 "               %s commands\n\
178 type <message list>             type messages\n\
179 next                            goto and type next message\n\
180 from <message list>             give head lines of messages\n\
181 headers                         print out active message headers\n\
182 delete <message list>           delete messages\n\
183 undelete <message list>         undelete messages\n\
184 save <message list> folder      append messages to folder and mark as saved\n\
185 copy <message list> folder      append messages to folder without marking them\n\
186 write <message list> file       append message texts to file, save attachments\n\
187 preserve <message list>         keep incoming messages in mailbox even if saved\n\
188 Reply <message list>            reply to message senders\n\
189 reply <message list>            reply to message senders and all recipients\n\
190 mail addresses                  mail to specific recipients\n\
191 file folder                     change to another folder\n\
192 quit                            quit and apply changes to folder\n\
193 xit                             quit and discard changes made to folder\n\
194 !                               shell escape\n\
195 cd <directory>                  chdir to directory or home if none given\n\
196 list                            list names of all available commands\n\
197 \n\
198 A <message list> consists of integers, ranges of same, or other criteria\n\
199 separated by spaces.  If omitted, %s uses the last message typed.\n";
200 
201 	fprintf(stdout, helptext, progname, progname);
202 	return(0);
203 }
204 
205 /*
206  * Change user's working directory.
207  */
208 int
schdir(void * v)209 schdir(void *v)
210 {
211 	char **arglist = v;
212 	char *cp;
213 
214 	if (*arglist == NULL)
215 		cp = homedir;
216 	else
217 		if ((cp = expand(*arglist)) == NULL)
218 			return(1);
219 	if (chdir(cp) < 0) {
220 		perror(cp);
221 		return(1);
222 	}
223 	return 0;
224 }
225 
226 static void
make_ref_and_cs(struct message * mp,struct header * head)227 make_ref_and_cs(struct message *mp, struct header *head)
228 {
229 	char *oldref, *oldmsgid, *newref, *cp;
230 	size_t reflen;
231 	unsigned i;
232 	struct name *n;
233 
234 	oldref = hfield("references", mp);
235 	oldmsgid = hfield("message-id", mp);
236 	if (oldmsgid == NULL || *oldmsgid == '\0') {
237 		head->h_ref = NULL;
238 		return;
239 	}
240 	reflen = 1;
241 	if (oldref)
242 		reflen += strlen(oldref) + 2;
243 	if (oldmsgid)
244 		reflen += strlen(oldmsgid);
245 	newref = ac_alloc(reflen);
246 	if (oldref) {
247 		strcpy(newref, oldref);
248 		if (oldmsgid) {
249 			strcat(newref, ", ");
250 			strcat(newref, oldmsgid);
251 		}
252 	} else if (oldmsgid)
253 		strcpy(newref, oldmsgid);
254 	n = extract(newref, GREF);
255 	ac_free(newref);
256 	/*
257 	 * Limit the references to 21 entries.
258 	 */
259 	while (n->n_flink != NULL)
260 		n = n->n_flink;
261 	for (i = 1; i < 21; i++) {
262 		if (n->n_blink != NULL)
263 			n = n->n_blink;
264 		else
265 			break;
266 	}
267 	n->n_blink = NULL;
268 	head->h_ref = n;
269 	if (value("reply-in-same-charset") != NULL &&
270 			(cp = hfield("content-type", mp)) != NULL)
271 		head->h_charset = mime_getparam("charset", cp);
272 }
273 
274 static int
respond_or_Respond(int c)275 (*respond_or_Respond(int c))(int *, int)
276 {
277 	int opt = 0;
278 
279 	opt += (value("Replyall") != NULL);
280 	opt += (value("flipr") != NULL);
281 	return ((opt == 1) ^ (c == 'R')) ? Respond_internal : respond_internal;
282 }
283 
284 int
respond(void * v)285 respond(void *v)
286 {
287 	return (respond_or_Respond('r'))((int *)v, 0);
288 }
289 
290 int
respondall(void * v)291 respondall(void *v)
292 {
293 	return respond_internal((int *)v, 0);
294 }
295 
296 int
respondsender(void * v)297 respondsender(void *v)
298 {
299 	return Respond_internal((int *)v, 0);
300 }
301 
302 int
followup(void * v)303 followup(void *v)
304 {
305 	return (respond_or_Respond('r'))((int *)v, 1);
306 }
307 
308 int
followupall(void * v)309 followupall(void *v)
310 {
311 	return respond_internal((int *)v, 1);
312 }
313 
314 int
followupsender(void * v)315 followupsender(void *v)
316 {
317 	return Respond_internal((int *)v, 1);
318 }
319 
320 /*
321  * Reply to a list of messages.  Extract each name from the
322  * message header and send them off to mail1()
323  */
324 static int
respond_internal(int * msgvec,int recipient_record)325 respond_internal(int *msgvec, int recipient_record)
326 {
327 	int Eflag;
328 	struct message *mp;
329 	char *cp, *rcv;
330 	enum gfield	gf = value("fullnames") ? GFULL : GSKIN;
331 	struct name *np = NULL;
332 	struct header head;
333 
334 	memset(&head, 0, sizeof head);
335 	if (msgvec[1] != 0) {
336 		printf(catgets(catd, CATSET, 37,
337 			"Sorry, can't reply to multiple messages at once\n"));
338 		return(1);
339 	}
340 	mp = &message[msgvec[0] - 1];
341 	touch(mp);
342 	setdot(mp);
343 	if ((rcv = hfield("reply-to", mp)) == NULL)
344 		if ((rcv = hfield("from", mp)) == NULL)
345 			rcv = nameof(mp, 1);
346 	if (rcv != NULL)
347 		np = sextract(rcv, GTO|gf);
348 	if ((cp = hfield("to", mp)) != NULL)
349 		np = cat(np, sextract(cp, GTO|gf));
350 	np = elide(np);
351 	/*
352 	 * Delete my name from the reply list,
353 	 * and with it, all my alternate names.
354 	 */
355 	np = delete_alternates(np);
356 	if (np == NULL)
357 		np = sextract(rcv, GTO|gf);
358 	head.h_to = np;
359 	if ((head.h_subject = hfield("subject", mp)) == NULL)
360 		head.h_subject = hfield("subj", mp);
361 	head.h_subject = reedit(head.h_subject);
362 	if ((cp = hfield("cc", mp)) != NULL) {
363 		np = elide(sextract(cp, GCC|gf));
364 		np = delete_alternates(np);
365 		head.h_cc = np;
366 	}
367 	make_ref_and_cs(mp, &head);
368 	Eflag = value("skipemptybody") != NULL;
369 	if (mail1(&head, 1, mp, NULL, recipient_record, 0, 0, Eflag) == OKAY &&
370 			value("markanswered") && (mp->m_flag & MANSWERED) == 0)
371 		mp->m_flag |= MANSWER|MANSWERED;
372 	return(0);
373 }
374 
375 /*
376  * Modify the subject we are replying to to begin with Re: if
377  * it does not already.
378  */
379 static char *
reedit(char * subj)380 reedit(char *subj)
381 {
382 	char *newsubj;
383 	struct str in, out;
384 
385 	if (subj == NULL || *subj == '\0')
386 		return NULL;
387 	in.s = subj;
388 	in.l = strlen(subj);
389 	mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
390 	if ((out.s[0] == 'r' || out.s[0] == 'R') &&
391 	    (out.s[1] == 'e' || out.s[1] == 'E') &&
392 	    out.s[2] == ':')
393 		return out.s;
394 	newsubj = salloc(out.l + 5);
395 	strcpy(newsubj, "Re: ");
396 	strcpy(newsubj + 4, out.s);
397 	return newsubj;
398 }
399 
400 /*
401  * Forward a message to a new recipient, in the sense of RFC 2822.
402  */
403 static int
forward1(char * str,int recipient_record)404 forward1(char *str, int recipient_record)
405 {
406 	int Eflag;
407 	int	*msgvec, f;
408 	char	*recipient;
409 	struct message	*mp;
410 	struct header	head;
411 	int	forward_as_attachment;
412 
413 	forward_as_attachment = value("forward-as-attachment") != NULL;
414 	msgvec = salloc((msgCount + 2) * sizeof *msgvec);
415 	if ((recipient = laststring(str, &f, 0)) == NULL) {
416 		puts(catgets(catd, CATSET, 47, "No recipient specified."));
417 		return 1;
418 	}
419 	if (!f) {
420 		*msgvec = first(0, MMNORM);
421 		if (*msgvec == 0) {
422 			if (inhook)
423 				return 0;
424 			printf("No messages to forward.\n");
425 			return 1;
426 		}
427 		msgvec[1] = 0;
428 	}
429 	if (f && getmsglist(str, msgvec, 0) < 0)
430 		return 1;
431 	if (*msgvec == 0) {
432 		if (inhook)
433 			return 0;
434 		printf("No applicable messages.\n");
435 		return 1;
436 	}
437 	if (msgvec[1] != 0) {
438 		printf("Cannot forward multiple messages at once\n");
439 		return 1;
440 	}
441 	memset(&head, 0, sizeof head);
442 	if ((head.h_to = sextract(recipient,
443 			GTO | (value("fullnames") ? GFULL : GSKIN))) == NULL)
444 		return 1;
445 	mp = &message[*msgvec - 1];
446 	if (forward_as_attachment) {
447 		head.h_attach = csalloc(1, sizeof *head.h_attach);
448 		head.h_attach->a_msgno = *msgvec;
449 	} else {
450 		touch(mp);
451 		setdot(mp);
452 	}
453 	if ((head.h_subject = hfield("subject", mp)) == NULL)
454 		head.h_subject = hfield("subj", mp);
455 	head.h_subject = fwdedit(head.h_subject);
456 	Eflag = value("skipemptybody") != NULL;
457 	mail1(&head, 1, forward_as_attachment ? NULL : mp,
458 			NULL, recipient_record, 1, 0, Eflag);
459 	return 0;
460 }
461 
462 /*
463  * Modify the subject we are replying to to begin with Fwd:.
464  */
465 static char *
fwdedit(char * subj)466 fwdedit(char *subj)
467 {
468 	char *newsubj;
469 	struct str	in, out;
470 
471 	if (subj == NULL || *subj == '\0')
472 		return NULL;
473 	in.s = subj;
474 	in.l = strlen(subj);
475 	mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
476 	newsubj = salloc(strlen(out.s) + 6);
477 	strcpy(newsubj, "Fwd: ");
478 	strcpy(&newsubj[5], out.s);
479 	free(out.s);
480 	return newsubj;
481 }
482 
483 /*
484  * The 'forward' command.
485  */
486 int
forwardcmd(void * v)487 forwardcmd(void *v)
488 {
489 	return forward1(v, 0);
490 }
491 
492 /*
493  * Similar to forward, saving the message in a file named after the
494  * first recipient.
495  */
496 int
Forwardcmd(void * v)497 Forwardcmd(void *v)
498 {
499 	return forward1(v, 1);
500 }
501 
502 /*
503  * Preserve the named messages, so that they will be sent
504  * back to the system mailbox.
505  */
506 int
preserve(void * v)507 preserve(void *v)
508 {
509 	int *msgvec = v;
510 	struct message *mp;
511 	int *ip, mesg;
512 
513 	if (edit) {
514 		printf(catgets(catd, CATSET, 39,
515 				"Cannot \"preserve\" in edit mode\n"));
516 		return(1);
517 	}
518 	for (ip = msgvec; *ip != 0; ip++) {
519 		mesg = *ip;
520 		mp = &message[mesg-1];
521 		mp->m_flag |= MPRESERVE;
522 		mp->m_flag &= ~MBOX;
523 		setdot(mp);
524 		/*
525 		 * This is now Austin Group Request XCU #20.
526 		 */
527 		did_print_dot = 1;
528 	}
529 	return(0);
530 }
531 
532 /*
533  * Mark all given messages as unread.
534  */
535 int
unread(void * v)536 unread(void *v)
537 {
538 	int	*msgvec = v;
539 	int *ip;
540 
541 	for (ip = msgvec; *ip != 0; ip++) {
542 		setdot(&message[*ip-1]);
543 		dot->m_flag &= ~(MREAD|MTOUCH);
544 		dot->m_flag |= MSTATUS;
545 		if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
546 			imap_unread(&message[*ip-1], *ip);
547 		/*
548 		 * The "unread" command is not part of POSIX mailx.
549 		 */
550 		did_print_dot = 1;
551 	}
552 	return(0);
553 }
554 
555 /*
556  * Mark all given messages as read.
557  */
558 int
seen(void * v)559 seen(void *v)
560 {
561 	int	*msgvec = v;
562 	int	*ip;
563 
564 	for (ip = msgvec; *ip; ip++) {
565 		setdot(&message[*ip-1]);
566 		touch(&message[*ip-1]);
567 	}
568 	return 0;
569 }
570 
571 /*
572  * Print the size of each message.
573  */
574 int
messize(void * v)575 messize(void *v)
576 {
577 	int *msgvec = v;
578 	struct message *mp;
579 	int *ip, mesg;
580 
581 	for (ip = msgvec; *ip != 0; ip++) {
582 		mesg = *ip;
583 		mp = &message[mesg-1];
584 		printf("%d: ", mesg);
585 		if (mp->m_xlines > 0)
586 			printf("%ld", mp->m_xlines);
587 		else
588 			putchar(' ');
589 		printf("/%lu\n", (unsigned long)mp->m_xsize);
590 	}
591 	return(0);
592 }
593 
594 /*
595  * Quit quickly.  If we are sourcing, just pop the input level
596  * by returning an error.
597  */
598 /*ARGSUSED*/
599 int
rexit(void * v)600 rexit(void *v)
601 {
602 	if (sourcing)
603 		return(1);
604 	exit(0);
605 	/*NOTREACHED*/
606 }
607 
608 static sigjmp_buf	pipejmp;
609 
610 /*ARGSUSED*/
611 static void
onpipe(int signo)612 onpipe(int signo)
613 {
614 	siglongjmp(pipejmp, 1);
615 }
616 
617 /*
618  * Set or display a variable value.  Syntax is similar to that
619  * of sh.
620  */
621 int
set(void * v)622 set(void *v)
623 {
624 	char **arglist = v;
625 	struct var *vp;
626 	char *cp, *cp2;
627 	char **ap, **p;
628 	int errs, h, s;
629 	FILE *obuf = stdout;
630 	int bsdset = value("bsdcompat") != NULL || value("bsdset") != NULL;
631 
632 	(void)&cp;
633 	(void)&ap;
634 	(void)&obuf;
635 	(void)&bsdset;
636 	if (*arglist == NULL) {
637 		for (h = 0, s = 1; h < HSHSIZE; h++)
638 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
639 				s++;
640 		/*LINTED*/
641 		ap = (char **)salloc(s * sizeof *ap);
642 		for (h = 0, p = ap; h < HSHSIZE; h++)
643 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
644 				*p++ = vp->v_name;
645 		*p = NULL;
646 		asort(ap);
647 		if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
648 			if (s > (*cp == '\0' ? screensize() : atoi(cp)) + 3) {
649 				cp = get_pager();
650 				if (sigsetjmp(pipejmp, 1))
651 					goto endpipe;
652 				if ((obuf = Popen(cp, "w", NULL, 1)) == NULL) {
653 					perror(cp);
654 					obuf = stdout;
655 				} else
656 					safe_signal(SIGPIPE, onpipe);
657 			}
658 		}
659 		for (p = ap; *p != NULL; p++) {
660 			if (bsdset)
661 				fprintf(obuf, "%s\t%s\n", *p, value(*p));
662 			else {
663 				if ((cp = value(*p)) != NULL && *cp)
664 					fprintf(obuf, "%s=\"%s\"\n",
665 							*p, value(*p));
666 				else
667 					fprintf(obuf, "%s\n", *p);
668 			}
669 		}
670 endpipe:
671 		if (obuf != stdout) {
672 			safe_signal(SIGPIPE, SIG_IGN);
673 			Pclose(obuf);
674 			safe_signal(SIGPIPE, dflpipe);
675 		}
676 		return(0);
677 	}
678 	errs = 0;
679 	for (ap = arglist; *ap != NULL; ap++) {
680 		char *varbuf;
681 
682 		varbuf = ac_alloc(strlen(*ap) + 1);
683 		cp = *ap;
684 		cp2 = varbuf;
685 		while (*cp != '=' && *cp != '\0')
686 			*cp2++ = *cp++;
687 		*cp2 = '\0';
688 		if (*cp == '\0')
689 			cp = "";
690 		else
691 			cp++;
692 		if (equal(varbuf, "")) {
693 			printf(catgets(catd, CATSET, 41,
694 					"Non-null variable name required\n"));
695 			errs++;
696 			ac_free(varbuf);
697 			continue;
698 		}
699 		if (varbuf[0] == 'n' && varbuf[1] == 'o')
700 			errs += unset_internal(&varbuf[2]);
701 		else
702 			assign(varbuf, cp);
703 		ac_free(varbuf);
704 	}
705 	return(errs);
706 }
707 
708 /*
709  * Unset a bunch of variable values.
710  */
711 int
unset(void * v)712 unset(void *v)
713 {
714 	int errs;
715 	char **ap;
716 
717 	errs = 0;
718 	for (ap = (char **)v; *ap != NULL; ap++)
719 		errs += unset_internal(*ap);
720 	return(errs);
721 }
722 
723 /*
724  * Put add users to a group.
725  */
726 int
group(void * v)727 group(void *v)
728 {
729 	char **argv = v;
730 	struct grouphead *gh;
731 	struct group *gp;
732 	int h;
733 	int s;
734 	char **ap, *gname, **p;
735 
736 	if (*argv == NULL) {
737 		for (h = 0, s = 1; h < HSHSIZE; h++)
738 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
739 				s++;
740 		/*LINTED*/
741 		ap = (char **)salloc(s * sizeof *ap);
742 		for (h = 0, p = ap; h < HSHSIZE; h++)
743 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
744 				*p++ = gh->g_name;
745 		*p = NULL;
746 		asort(ap);
747 		for (p = ap; *p != NULL; p++)
748 			printgroup(*p);
749 		return(0);
750 	}
751 	if (argv[1] == NULL) {
752 		printgroup(*argv);
753 		return(0);
754 	}
755 	gname = *argv;
756 	h = hash(gname);
757 	if ((gh = findgroup(gname)) == NULL) {
758 		gh = (struct grouphead *)scalloc(1, sizeof *gh);
759 		gh->g_name = vcopy(gname);
760 		gh->g_list = NULL;
761 		gh->g_link = groups[h];
762 		groups[h] = gh;
763 	}
764 
765 	/*
766 	 * Insert names from the command list into the group.
767 	 * Who cares if there are duplicates?  They get tossed
768 	 * later anyway.
769 	 */
770 
771 	for (ap = argv+1; *ap != NULL; ap++) {
772 		gp = (struct group *)scalloc(1, sizeof *gp);
773 		gp->ge_name = vcopy(*ap);
774 		gp->ge_link = gh->g_list;
775 		gh->g_list = gp;
776 	}
777 	return(0);
778 }
779 
780 /*
781  * Delete the passed groups.
782  */
783 int
ungroup(void * v)784 ungroup(void *v)
785 {
786 	char **argv = v;
787 
788 	if (*argv == NULL) {
789 		printf(catgets(catd, CATSET, 209,
790 				"Must specify alias or group to remove\n"));
791 		return 1;
792 	}
793 	do
794 		remove_group(*argv);
795 	while (*++argv != NULL);
796 	return 0;
797 }
798 
799 /*
800  * Sort the passed string vecotor into ascending dictionary
801  * order.
802  */
803 static void
asort(char ** list)804 asort(char **list)
805 {
806 	char **ap;
807 
808 	for (ap = list; *ap != NULL; ap++)
809 		;
810 	if (ap-list < 2)
811 		return;
812 	qsort(list, ap-list, sizeof(*list), diction);
813 }
814 
815 /*
816  * Do a dictionary order comparison of the arguments from
817  * qsort.
818  */
819 static int
diction(const void * a,const void * b)820 diction(const void *a, const void *b)
821 {
822 	return(strcmp(*(char **)a, *(char **)b));
823 }
824 
825 /*
826  * Change to another file.  With no argument, print information about
827  * the current file.
828  */
829 int
cfile(void * v)830 cfile(void *v)
831 {
832 	char **argv = v;
833 
834 	if (argv[0] == NULL) {
835 		newfileinfo();
836 		return 0;
837 	}
838 	strncpy(mboxname, expand("&"), sizeof mboxname)[sizeof mboxname-1]='\0';
839 	return file1(*argv);
840 }
841 
842 static int
file1(char * name)843 file1(char *name)
844 {
845 	int	i;
846 
847 	if (inhook) {
848 		fprintf(stderr, "Cannot change folder from within a hook.\n");
849 		return 1;
850 	}
851 	i = setfile(name, 0);
852 	if (i < 0)
853 		return 1;
854 	callhook(mailname, 0);
855 	if (i > 0 && value("emptystart") == NULL)
856 		return 1;
857 	announce(value("bsdcompat") != NULL || value("bsdannounce") != NULL);
858 	return 0;
859 }
860 
861 static int
shellecho(const char * cp)862 shellecho(const char *cp)
863 {
864 	int	cflag = 0, n;
865 	char	c;
866 
867 	while (*cp) {
868 		if (*cp == '\\') {
869 			switch (*++cp) {
870 			case '\0':
871 				return cflag;
872 			case 'a':
873 				putchar('\a');
874 				break;
875 			case 'b':
876 				putchar('\b');
877 				break;
878 			case 'c':
879 				cflag = 1;
880 				break;
881 			case 'f':
882 				putchar('\f');
883 				break;
884 			case 'n':
885 				putchar('\n');
886 				break;
887 			case 'r':
888 				putchar('\r');
889 				break;
890 			case 't':
891 				putchar('\t');
892 				break;
893 			case 'v':
894 				putchar('\v');
895 				break;
896 			default:
897 				putchar(*cp&0377);
898 				break;
899 			case '0':
900 				c = 0;
901 				n = 3;
902 				while (n-- && octalchar(cp[1]&0377)) {
903 					c <<= 3;
904 					c |= cp[1] - '0';
905 					cp++;
906 				}
907 				putchar(c);
908 			}
909 		} else
910 			putchar(*cp & 0377);
911 		cp++;
912 	}
913 	return cflag;
914 }
915 
916 /*
917  * Expand file names like echo
918  */
919 int
echo(void * v)920 echo(void *v)
921 {
922 	char **argv = v;
923 	char **ap;
924 	char *cp;
925 	int cflag = 0;
926 
927 	for (ap = argv; *ap != NULL; ap++) {
928 		cp = *ap;
929 		if ((cp = expand(cp)) != NULL) {
930 			if (ap != argv)
931 				putchar(' ');
932 			cflag |= shellecho(cp);
933 		}
934 	}
935 	if (!cflag)
936 		putchar('\n');
937 	return 0;
938 }
939 
940 int
Respond(void * v)941 Respond(void *v)
942 {
943 	return (respond_or_Respond('R'))((int *)v, 0);
944 }
945 
946 int
Followup(void * v)947 Followup(void *v)
948 {
949 	return (respond_or_Respond('R'))((int *)v, 1);
950 }
951 
952 /*
953  * Reply to a series of messages by simply mailing to the senders
954  * and not messing around with the To: and Cc: lists as in normal
955  * reply.
956  */
957 static int
Respond_internal(int * msgvec,int recipient_record)958 Respond_internal(int *msgvec, int recipient_record)
959 {
960 	int Eflag;
961 	struct header head;
962 	struct message *mp;
963 	enum gfield	gf = value("fullnames") ? GFULL : GSKIN;
964 	int *ap;
965 	char *cp;
966 
967 	memset(&head, 0, sizeof head);
968 	for (ap = msgvec; *ap != 0; ap++) {
969 		mp = &message[*ap - 1];
970 		touch(mp);
971 		setdot(mp);
972 		if ((cp = hfield("reply-to", mp)) == NULL)
973 			if ((cp = hfield("from", mp)) == NULL)
974 				cp = nameof(mp, 2);
975 		head.h_to = cat(head.h_to, sextract(cp, GTO|gf));
976 	}
977 	if (head.h_to == NULL)
978 		return 0;
979 	mp = &message[msgvec[0] - 1];
980 	if ((head.h_subject = hfield("subject", mp)) == NULL)
981 		head.h_subject = hfield("subj", mp);
982 	head.h_subject = reedit(head.h_subject);
983 	make_ref_and_cs(mp, &head);
984 	Eflag = value("skipemptybody") != NULL;
985 	if (mail1(&head, 1, mp, NULL, recipient_record, 0, 0, Eflag) == OKAY &&
986 			value("markanswered") && (mp->m_flag & MANSWERED) == 0)
987 		mp->m_flag |= MANSWER|MANSWERED;
988 	return 0;
989 }
990 
991 /*
992  * Conditional commands.  These allow one to parameterize one's
993  * .mailrc and do some things if sending, others if receiving.
994  */
995 int
ifcmd(void * v)996 ifcmd(void *v)
997 {
998 	char **argv = v;
999 	char *cp;
1000 
1001 	if (cond != CANY) {
1002 		printf(catgets(catd, CATSET, 42, "Illegal nested \"if\"\n"));
1003 		return(1);
1004 	}
1005 	cond = CANY;
1006 	cp = argv[0];
1007 	switch (*cp) {
1008 	case 'r': case 'R':
1009 		cond = CRCV;
1010 		break;
1011 
1012 	case 's': case 'S':
1013 		cond = CSEND;
1014 		break;
1015 
1016 	case 't': case 'T':
1017 		cond = CTERM;
1018 		break;
1019 
1020 	default:
1021 		printf(catgets(catd, CATSET, 43,
1022 				"Unrecognized if-keyword: \"%s\"\n"), cp);
1023 		return(1);
1024 	}
1025 	return(0);
1026 }
1027 
1028 /*
1029  * Implement 'else'.  This is pretty simple -- we just
1030  * flip over the conditional flag.
1031  */
1032 /*ARGSUSED*/
1033 int
elsecmd(void * v)1034 elsecmd(void *v)
1035 {
1036 
1037 	switch (cond) {
1038 	case CANY:
1039 		printf(catgets(catd, CATSET, 44,
1040 				"\"Else\" without matching \"if\"\n"));
1041 		return(1);
1042 
1043 	case CSEND:
1044 		cond = CRCV;
1045 		break;
1046 
1047 	case CRCV:
1048 		cond = CSEND;
1049 		break;
1050 
1051 	case CTERM:
1052 		cond = CNONTERM;
1053 		break;
1054 
1055 	default:
1056 		printf(catgets(catd, CATSET, 45,
1057 				"Mail's idea of conditions is screwed up\n"));
1058 		cond = CANY;
1059 		break;
1060 	}
1061 	return(0);
1062 }
1063 
1064 /*
1065  * End of if statement.  Just set cond back to anything.
1066  */
1067 /*ARGSUSED*/
1068 int
endifcmd(void * v)1069 endifcmd(void *v)
1070 {
1071 
1072 	if (cond == CANY) {
1073 		printf(catgets(catd, CATSET, 46,
1074 				"\"Endif\" without matching \"if\"\n"));
1075 		return(1);
1076 	}
1077 	cond = CANY;
1078 	return(0);
1079 }
1080 
1081 /*
1082  * Set the list of alternate names.
1083  */
1084 int
alternates(void * v)1085 alternates(void *v)
1086 {
1087 	char **namelist = v;
1088 	int c;
1089 	char **ap, **ap2, *cp;
1090 
1091 	c = argcount(namelist) + 1;
1092 	if (c == 1) {
1093 		if (altnames == 0)
1094 			return(0);
1095 		for (ap = altnames; *ap; ap++)
1096 			printf("%s ", *ap);
1097 		printf("\n");
1098 		return(0);
1099 	}
1100 	if (altnames != 0)
1101 		free(altnames);
1102 	altnames = scalloc(c, sizeof (char *));
1103 	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
1104 		cp = scalloc(strlen(*ap) + 1, sizeof (char));
1105 		strcpy(cp, *ap);
1106 		*ap2 = cp;
1107 	}
1108 	*ap2 = 0;
1109 	return(0);
1110 }
1111 
1112 /*
1113  * Do the real work of resending.
1114  */
1115 static int
resend1(void * v,int add_resent)1116 resend1(void *v, int add_resent)
1117 {
1118 	char *name, *str;
1119 	struct name *to;
1120 	struct name *sn;
1121 	int f, *ip, *msgvec;
1122 
1123 	str = (char *)v;
1124 	/*LINTED*/
1125 	msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
1126 	name = laststring(str, &f, 1);
1127 	if (name == NULL) {
1128 		puts(catgets(catd, CATSET, 47, "No recipient specified."));
1129 		return 1;
1130 	}
1131 	if (!f) {
1132 		*msgvec = first(0, MMNORM);
1133 		if (*msgvec == 0) {
1134 			if (inhook)
1135 				return 0;
1136 			puts(catgets(catd, CATSET, 48,
1137 					"No applicable messages."));
1138 			return 1;
1139 		}
1140 		msgvec[1] = 0;
1141 	} else if (getmsglist(str, msgvec, 0) < 0)
1142 		return 1;
1143 	if (*msgvec == 0) {
1144 		if (inhook)
1145 			return 0;
1146 		printf("No applicable messages.\n");
1147 		return 1;
1148 	}
1149 	sn = nalloc(name, GTO);
1150 	to = usermap(sn);
1151 	for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
1152 		if (resend_msg(&message[*ip - 1], to, add_resent) != OKAY)
1153 			return 1;
1154 	}
1155 	return 0;
1156 }
1157 
1158 /*
1159  * Resend a message list to a third person.
1160  */
1161 int
resendcmd(void * v)1162 resendcmd(void *v)
1163 {
1164 	return resend1(v, 1);
1165 }
1166 
1167 /*
1168  * Resend a message list to a third person without adding headers.
1169  */
1170 int
Resendcmd(void * v)1171 Resendcmd(void *v)
1172 {
1173 	return resend1(v, 0);
1174 }
1175 
1176 /*
1177  * 'newmail' or 'inc' command: Check for new mail without writing old
1178  * mail back.
1179  */
1180 /*ARGSUSED*/
1181 int
newmail(void * v)1182 newmail(void *v)
1183 {
1184 	int val = 1, mdot;
1185 
1186 	if ((mb.mb_type != MB_IMAP || imap_newmail(1)) &&
1187 			(val = setfile(mailname, 1)) == 0) {
1188 		mdot = getmdot(1);
1189 		setdot(&message[mdot - 1]);
1190 	}
1191 	return val;
1192 }
1193 
1194 static void
list_shortcuts(void)1195 list_shortcuts(void)
1196 {
1197 	struct shortcut *s;
1198 
1199 	for (s = shortcuts; s; s = s->sh_next)
1200 		printf("%s=%s\n", s->sh_short, s->sh_long);
1201 }
1202 
1203 int
shortcut(void * v)1204 shortcut(void *v)
1205 {
1206 	char **args = (char **)v;
1207 	struct shortcut *s;
1208 
1209 	if (args[0] == NULL) {
1210 		list_shortcuts();
1211 		return 0;
1212 	}
1213 	if (args[1] == NULL) {
1214 		fprintf(stderr, catgets(catd, CATSET, 220,
1215 				"expansion name for shortcut missing\n"));
1216 		return 1;
1217 	}
1218 	if (args[2] != NULL) {
1219 		fprintf(stderr, catgets(catd, CATSET, 221,
1220 				"too many arguments\n"));
1221 		return 1;
1222 	}
1223 	if ((s = get_shortcut(args[0])) != NULL) {
1224 		free(s->sh_long);
1225 		s->sh_long = sstrdup(args[1]);
1226 	} else {
1227 		s = scalloc(1, sizeof *s);
1228 		s->sh_short = sstrdup(args[0]);
1229 		s->sh_long = sstrdup(args[1]);
1230 		s->sh_next = shortcuts;
1231 		shortcuts = s;
1232 	}
1233 	return 0;
1234 }
1235 
1236 struct shortcut *
get_shortcut(const char * str)1237 get_shortcut(const char *str)
1238 {
1239 	struct shortcut *s;
1240 
1241 	for (s = shortcuts; s; s = s->sh_next)
1242 		if (strcmp(str, s->sh_short) == 0)
1243 			break;
1244 	return s;
1245 }
1246 
1247 static enum okay
delete_shortcut(const char * str)1248 delete_shortcut(const char *str)
1249 {
1250 	struct shortcut *sp, *sq;
1251 
1252 	for (sp = shortcuts, sq = NULL; sp; sq = sp, sp = sp->sh_next) {
1253 		if (strcmp(sp->sh_short, str) == 0) {
1254 			free(sp->sh_short);
1255 			free(sp->sh_long);
1256 			if (sq)
1257 				sq->sh_next = sp->sh_next;
1258 			if (sp == shortcuts)
1259 				shortcuts = sp->sh_next;
1260 			free(sp);
1261 			return OKAY;
1262 		}
1263 	}
1264 	return STOP;
1265 }
1266 
1267 int
unshortcut(void * v)1268 unshortcut(void *v)
1269 {
1270 	char **args = (char **)v;
1271 	int errs = 0;
1272 
1273 	if (args[0] == NULL) {
1274 		fprintf(stderr, catgets(catd, CATSET, 222,
1275 				"need shortcut names to remove\n"));
1276 		return 1;
1277 	}
1278 	while (*args != NULL) {
1279 		if (delete_shortcut(*args) != OKAY) {
1280 			errs = 1;
1281 			fprintf(stderr, catgets(catd, CATSET, 223,
1282 				"%s: no such shortcut\n"), *args);
1283 		}
1284 		args++;
1285 	}
1286 	return errs;
1287 }
1288 
1289 struct oldaccount {
1290 	struct oldaccount	*ac_next;	/* next account in list */
1291 	char	*ac_name;			/* name of account */
1292 	char	**ac_vars;			/* variables to set */
1293 };
1294 
1295 static struct oldaccount	*oldaccounts;
1296 
1297 struct oldaccount *
get_oldaccount(const char * name)1298 get_oldaccount(const char *name)
1299 {
1300 	struct oldaccount	*a;
1301 
1302 	for (a = oldaccounts; a; a = a->ac_next)
1303 		if (a->ac_name && strcmp(name, a->ac_name) == 0)
1304 			break;
1305 	return a;
1306 }
1307 
1308 int
account(void * v)1309 account(void *v)
1310 {
1311 	char	**args = (char **)v;
1312 	struct oldaccount	*a;
1313 	char	*cp;
1314 	int	i, mc;
1315 	FILE	*fp = stdout;
1316 
1317 	if (args[0] == NULL) {
1318 		if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
1319 			perror("tmpfile");
1320 			return 1;
1321 		}
1322 		rm(cp);
1323 		Ftfree(&cp);
1324 		mc = listaccounts(fp);
1325 		for (a = oldaccounts; a; a = a->ac_next)
1326 			if (a->ac_name) {
1327 				if (mc++)
1328 					fputc('\n', fp);
1329 				fprintf(fp, "%s:\n", a->ac_name);
1330 				for (i = 0; a->ac_vars[i]; i++)
1331 					fprintf(fp, "\t%s\n", a->ac_vars[i]);
1332 			}
1333 		if (mc)
1334 			try_pager(fp);
1335 		Fclose(fp);
1336 		return 0;
1337 	}
1338 	if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1339 		if (args[2] != NULL) {
1340 			fprintf(stderr, "Syntax is: account <name> {\n");
1341 			return 1;
1342 		}
1343 		if ((a = get_oldaccount(args[0])) != NULL)
1344 			a->ac_name = NULL;
1345 		return define1(args[0], 1);
1346 	}
1347 	strncpy(mboxname, expand("&"), sizeof mboxname)[sizeof mboxname-1]='\0';
1348 	if ((a = get_oldaccount(args[0])) == NULL) {
1349 		if (args[1]) {
1350 			a = scalloc(1, sizeof *a);
1351 			a->ac_next = oldaccounts;
1352 			oldaccounts = a;
1353 		} else {
1354 			if ((i = callaccount(args[0])) != CBAD)
1355 				goto setf;
1356 			printf("Account %s does not exist.\n", args[0]);
1357 			return 1;
1358 		}
1359 	}
1360 	if (args[1]) {
1361 		delaccount(args[0]);
1362 		a->ac_name = sstrdup(args[0]);
1363 		for (i = 1; args[i]; i++);
1364 		a->ac_vars = scalloc(i, sizeof *a->ac_vars);
1365 		for (i = 0; args[i+1]; i++)
1366 			a->ac_vars[i] = sstrdup(args[i+1]);
1367 	} else {
1368 		unset_allow_undefined = 1;
1369 		set(a->ac_vars);
1370 		unset_allow_undefined = 0;
1371 	setf:	if (!starting)
1372 			return file1("%");
1373 	}
1374 	return 0;
1375 }
1376 
1377 int
cflag(void * v)1378 cflag(void *v)
1379 {
1380 	struct message	*m;
1381 	int	*msgvec = v;
1382 	int	*ip;
1383 
1384 	for (ip = msgvec; *ip != 0; ip++) {
1385 		m = &message[*ip-1];
1386 		setdot(m);
1387 		if ((m->m_flag & (MFLAG|MFLAGGED)) == 0)
1388 			m->m_flag |= MFLAG|MFLAGGED;
1389 	}
1390 	return 0;
1391 }
1392 
1393 int
cunflag(void * v)1394 cunflag(void *v)
1395 {
1396 	struct message	*m;
1397 	int	*msgvec = v;
1398 	int	*ip;
1399 
1400 	for (ip = msgvec; *ip != 0; ip++) {
1401 		m = &message[*ip-1];
1402 		setdot(m);
1403 		if (m->m_flag & (MFLAG|MFLAGGED)) {
1404 			m->m_flag &= ~(MFLAG|MFLAGGED);
1405 			m->m_flag |= MUNFLAG;
1406 		}
1407 	}
1408 	return 0;
1409 }
1410 
1411 int
canswered(void * v)1412 canswered(void *v)
1413 {
1414 	struct message	*m;
1415 	int	*msgvec = v;
1416 	int	*ip;
1417 
1418 	for (ip = msgvec; *ip != 0; ip++) {
1419 		m = &message[*ip-1];
1420 		setdot(m);
1421 		if ((m->m_flag & (MANSWER|MANSWERED)) == 0)
1422 			m->m_flag |= MANSWER|MANSWERED;
1423 	}
1424 	return 0;
1425 }
1426 
1427 int
cunanswered(void * v)1428 cunanswered(void *v)
1429 {
1430 	struct message	*m;
1431 	int	*msgvec = v;
1432 	int	*ip;
1433 
1434 	for (ip = msgvec; *ip != 0; ip++) {
1435 		m = &message[*ip-1];
1436 		setdot(m);
1437 		if (m->m_flag & (MANSWER|MANSWERED)) {
1438 			m->m_flag &= ~(MANSWER|MANSWERED);
1439 			m->m_flag |= MUNANSWER;
1440 		}
1441 	}
1442 	return 0;
1443 }
1444 
1445 int
cdraft(void * v)1446 cdraft(void *v)
1447 {
1448 	struct message	*m;
1449 	int	*msgvec = v;
1450 	int	*ip;
1451 
1452 	for (ip = msgvec; *ip != 0; ip++) {
1453 		m = &message[*ip-1];
1454 		setdot(m);
1455 		if ((m->m_flag & (MDRAFT|MDRAFTED)) == 0)
1456 			m->m_flag |= MDRAFT|MDRAFTED;
1457 	}
1458 	return 0;
1459 }
1460 
1461 int
cundraft(void * v)1462 cundraft(void *v)
1463 {
1464 	struct message	*m;
1465 	int	*msgvec = v;
1466 	int	*ip;
1467 
1468 	for (ip = msgvec; *ip != 0; ip++) {
1469 		m = &message[*ip-1];
1470 		setdot(m);
1471 		if (m->m_flag & (MDRAFT|MDRAFTED)) {
1472 			m->m_flag &= ~(MDRAFT|MDRAFTED);
1473 			m->m_flag |= MUNDRAFT;
1474 		}
1475 	}
1476 	return 0;
1477 }
1478 
1479 static float
huge(void)1480 huge(void)
1481 {
1482 #if defined (_CRAY)
1483 	/*
1484 	 * This is not perfect, but correct for machines with a 32-bit
1485 	 * IEEE float and a 32-bit unsigned long, and does at least not
1486 	 * produce SIGFPE on the Cray Y-MP.
1487 	 */
1488 	union {
1489 		float	f;
1490 		unsigned long	l;
1491 	} u;
1492 
1493 	u.l = 0xff800000; /* -inf */
1494 	return u.f;
1495 #elif defined (INFINITY)
1496 	return -INFINITY;
1497 #elif defined (HUGE_VALF)
1498 	return -HUGE_VALF;
1499 #elif defined (FLT_MAX)
1500 	return -FLT_MAX;
1501 #else
1502 	return -1e10;
1503 #endif
1504 }
1505 
1506 int
ckill(void * v)1507 ckill(void *v)
1508 {
1509 	struct message	*m;
1510 	int	*msgvec = v;
1511 	int	*ip;
1512 
1513 	for (ip = msgvec; *ip != 0; ip++) {
1514 		m = &message[*ip-1];
1515 		m->m_flag |= MKILL;
1516 		m->m_score = huge();
1517 	}
1518 	return 0;
1519 }
1520 
1521 int
cunkill(void * v)1522 cunkill(void *v)
1523 {
1524 	struct message	*m;
1525 	int	*msgvec = v;
1526 	int	*ip;
1527 
1528 	for (ip = msgvec; *ip != 0; ip++) {
1529 		m = &message[*ip-1];
1530 		m->m_flag &= ~MKILL;
1531 		m->m_score = 0;
1532 	}
1533 	return 0;
1534 }
1535 
1536 int
cscore(void * v)1537 cscore(void *v)
1538 {
1539 	char	*str = v;
1540 	char	*sscore, *xp;
1541 	int	f, *msgvec, *ip;
1542 	double	nscore;
1543 	struct message	*m;
1544 
1545 	msgvec = salloc((msgCount+2) * sizeof *msgvec);
1546 	if ((sscore = laststring(str, &f, 0)) == NULL) {
1547 		fprintf(stderr, "No score given.\n");
1548 		return 1;
1549 	}
1550 	nscore = strtod(sscore, &xp);
1551 	if (*xp) {
1552 		fprintf(stderr, "Invalid score: \"%s\"\n", sscore);
1553 		return 1;
1554 	}
1555 	if (nscore > FLT_MAX)
1556 		nscore = FLT_MAX;
1557 	else if (nscore < -FLT_MAX)
1558 		nscore = -FLT_MAX;
1559 	if (!f) {
1560 		*msgvec = first(0, MMNORM);
1561 		if (*msgvec == 0) {
1562 			if (inhook)
1563 				return 0;
1564 			fprintf(stderr, "No messages to score.\n");
1565 			return 1;
1566 		}
1567 		msgvec[1] = 0;
1568 	} else if (getmsglist(str, msgvec, 0) < 0)
1569 		return 1;
1570 	if (*msgvec == 0) {
1571 		if (inhook)
1572 			return 0;
1573 		fprintf(stderr, "No applicable messages.\n");
1574 		return 1;
1575 	}
1576 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1577 		m = &message[*ip-1];
1578 		if (m->m_score != huge()) {
1579 			m->m_score += nscore;
1580 			if (m->m_score < 0)
1581 				m->m_flag |= MKILL;
1582 			else if (m->m_score > 0)
1583 				m->m_flag &= ~MKILL;
1584 			if (m->m_score >= 0)
1585 				setdot(m);
1586 		}
1587 	}
1588 	return 0;
1589 }
1590 
1591 /*ARGSUSED*/
1592 int
cnoop(void * v)1593 cnoop(void *v)
1594 {
1595 	switch (mb.mb_type) {
1596 	case MB_IMAP:
1597 		imap_noop();
1598 		break;
1599 	case MB_POP3:
1600 		pop3_noop();
1601 		break;
1602 	default:
1603 		break;
1604 	}
1605 	return 0;
1606 }
1607 
1608 int
cremove(void * v)1609 cremove(void *v)
1610 {
1611 	char	vb[LINESIZE];
1612 	char	**args = v;
1613 	char	*name;
1614 	int	ec = 0;
1615 
1616 	if (*args == NULL) {
1617 		fprintf(stderr, "Syntax is: remove mailbox ...\n");
1618 		return 1;
1619 	}
1620 	do {
1621 		if ((name = expand(*args)) == NULL)
1622 			continue;
1623 		if (strcmp(name, mailname) == 0) {
1624 			fprintf(stderr,
1625 				"Cannot remove current mailbox \"%s\".\n",
1626 				name);
1627 			ec |= 1;
1628 			continue;
1629 		}
1630 		snprintf(vb, sizeof vb, "Remove \"%s\" (y/n) ? ", name);
1631 		if (yorn(vb) == 0)
1632 			continue;
1633 		switch (which_protocol(name)) {
1634 		case PROTO_FILE:
1635 			if (unlink(name) < 0) {	/* do not handle .gz .bz2 */
1636 				perror(name);
1637 				ec |= 1;
1638 			}
1639 			break;
1640 		case PROTO_POP3:
1641 			fprintf(stderr, "Cannot remove POP3 mailbox \"%s\".\n",
1642 					name);
1643 			ec |= 1;
1644 			break;
1645 		case PROTO_IMAP:
1646 			if (imap_remove(name) != OKAY)
1647 				ec |= 1;
1648 			break;
1649 		case PROTO_MAILDIR:
1650 			if (maildir_remove(name) != OKAY)
1651 				ec |= 1;
1652 			break;
1653 		case PROTO_UNKNOWN:
1654 			fprintf(stderr,
1655 				"Unknown protocol in \"%s\". Not removed.\n",
1656 				name);
1657 			ec |= 1;
1658 		}
1659 	} while (*++args);
1660 	return ec;
1661 }
1662 
1663 int
crename(void * v)1664 crename(void *v)
1665 {
1666 	char	**args = v, *old, *new;
1667 	enum protocol	oldp, newp;
1668 	int	ec = 0;
1669 
1670 	if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1671 		fprintf(stderr, "Syntax: rename old new\n");
1672 		return 1;
1673 	}
1674 	old = expand(args[0]);
1675 	oldp = which_protocol(old);
1676 	new = expand(args[1]);
1677 	newp = which_protocol(new);
1678 	if (strcmp(old, mailname) == 0 || strcmp(new, mailname) == 0) {
1679 		fprintf(stderr, "Cannot rename current mailbox \"%s\".\n", old);
1680 		return 1;
1681 	}
1682 	if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) {
1683 		fprintf(stderr, "Can only rename folders of same type.\n");
1684 		return 1;
1685 	}
1686 	if (newp == PROTO_POP3)
1687 		goto nopop3;
1688 	switch (oldp) {
1689 	case PROTO_FILE:
1690 		if (link(old, new) < 0) {
1691 			switch (errno) {
1692 			case EACCES:
1693 			case EEXIST:
1694 			case ENAMETOOLONG:
1695 			case ENOENT:
1696 			case ENOSPC:
1697 			case EXDEV:
1698 				perror(new);
1699 				break;
1700 			default:
1701 				perror(old);
1702 			}
1703 			ec |= 1;
1704 		} else if (unlink(old) < 0) {
1705 			perror(old);
1706 			ec |= 1;
1707 		}
1708 		break;
1709 	case PROTO_MAILDIR:
1710 		if (rename(old, new) < 0) {
1711 			perror(old);
1712 			ec |= 1;
1713 		}
1714 		break;
1715 	case PROTO_POP3:
1716 	nopop3:	fprintf(stderr, "Cannot rename POP3 mailboxes.\n");
1717 		ec |= 1;
1718 		break;
1719 	case PROTO_IMAP:
1720 		if (imap_rename(old, new) != OKAY)
1721 			ec |= 1;
1722 		break;
1723 	case PROTO_UNKNOWN:
1724 		fprintf(stderr, "Unknown protocol in \"%s\" and \"%s\". "
1725 				"Not renamed.\n", old, new);
1726 		ec |= 1;
1727 	}
1728 	return ec;
1729 }
1730