GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mail/cmd3.c Lines: 15 316 4.7 %
Date: 2017-11-07 Branches: 8 185 4.3 %

Line Branch Exec Source
1
/*	$OpenBSD: cmd3.c,v 1.27 2015/10/16 17:56:07 mmcc Exp $	*/
2
/*	$NetBSD: cmd3.c,v 1.8 1997/07/09 05:29:49 mikel Exp $	*/
3
4
/*
5
 * Copyright (c) 1980, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include "rcv.h"
34
#include "extern.h"
35
36
/*
37
 * Mail -- a mail program
38
 *
39
 * Still more user commands.
40
 */
41
static int diction(const void *, const void *);
42
43
/*
44
 * Process a shell escape by saving signals, ignoring signals,
45
 * and forking a sh -c
46
 */
47
int
48
shell(void *v)
49
{
50
	char *str = v;
51
	char *shell;
52
	char cmd[BUFSIZ];
53
	struct sigaction oact;
54
	sigset_t oset;
55
56
	(void)ignoresig(SIGINT, &oact, &oset);
57
	(void)strlcpy(cmd, str, sizeof(cmd));
58
	if (bangexp(cmd, sizeof(cmd)) < 0)
59
		return(1);
60
	shell = value("SHELL");
61
	(void)run_command(shell, 0, 0, -1, "-c", cmd, NULL);
62
	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
63
	(void)sigaction(SIGINT, &oact, NULL);
64
	puts("!");
65
	return(0);
66
}
67
68
/*
69
 * Fork an interactive shell.
70
 */
71
/*ARGSUSED*/
72
int
73
dosh(void *v)
74
{
75
	char *shell;
76
	struct sigaction oact;
77
	sigset_t oset;
78
79
	shell = value("SHELL");
80
	(void)ignoresig(SIGINT, &oact, &oset);
81
	(void)run_command(shell, 0, 0, -1, NULL, NULL, NULL);
82
	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
83
	(void)sigaction(SIGINT, &oact, NULL);
84
	putchar('\n');
85
	return(0);
86
}
87
88
/*
89
 * Expand the shell escape by expanding unescaped !'s into the
90
 * last issued command where possible.
91
 */
92
int
93
bangexp(char *str, size_t strsize)
94
{
95
	char bangbuf[BUFSIZ];
96
	static char lastbang[BUFSIZ];
97
	char *cp, *cp2;
98
	int n, changed = 0;
99
100
	cp = str;
101
	cp2 = bangbuf;
102
	n = BUFSIZ;
103
	while (*cp) {
104
		if (*cp == '!') {
105
			if (n < strlen(lastbang)) {
106
overf:
107
				puts("Command buffer overflow");
108
				return(-1);
109
			}
110
			changed++;
111
			strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf));
112
			cp2 += strlen(lastbang);
113
			n -= strlen(lastbang);
114
			cp++;
115
			continue;
116
		}
117
		if (*cp == '\\' && cp[1] == '!') {
118
			if (--n <= 1)
119
				goto overf;
120
			*cp2++ = '!';
121
			cp += 2;
122
			changed++;
123
		}
124
		if (--n <= 1)
125
			goto overf;
126
		*cp2++ = *cp++;
127
	}
128
	*cp2 = 0;
129
	if (changed) {
130
		(void)printf("!%s\n", bangbuf);
131
		(void)fflush(stdout);
132
	}
133
	(void)strlcpy(str, bangbuf, strsize);
134
	(void)strlcpy(lastbang, bangbuf, sizeof(lastbang));
135
	return(0);
136
}
137
138
/*
139
 * Print out a nice help message from some file or another.
140
 */
141
int
142
help(void *v)
143
{
144
145
	(void)run_command(value("PAGER"), 0, -1, -1, _PATH_HELP, NULL);
146
	return(0);
147
}
148
149
/*
150
 * Change user's working directory.
151
 */
152
int
153
schdir(void *v)
154
{
155
	char **arglist = v;
156
	char *cp;
157
158
	if (*arglist == NULL) {
159
		if (homedir == NULL)
160
			return(1);
161
		cp = homedir;
162
	} else {
163
		if ((cp = expand(*arglist)) == NULL)
164
			return(1);
165
	}
166
	if (chdir(cp) < 0) {
167
		warn("%s", cp);
168
		return(1);
169
	}
170
	return(0);
171
}
172
173
int
174
respond(void *v)
175
{
176
	int *msgvec = v;
177
178
	if (value("Replyall") == NULL)
179
		return(_respond(msgvec));
180
	else
181
		return(_Respond(msgvec));
182
}
183
184
/*
185
 * Reply to a list of messages.  Extract each name from the
186
 * message header and send them off to mail1()
187
 */
188
int
189
_respond(msgvec)
190
	int *msgvec;
191
{
192
	struct message *mp;
193
	char *cp, *rcv, *replyto;
194
	char **ap;
195
	struct name *np;
196
	struct header head;
197
198
	if (msgvec[1] != 0) {
199
		puts("Sorry, can't reply to multiple messages at once");
200
		return(1);
201
	}
202
	mp = &message[msgvec[0] - 1];
203
	touch(mp);
204
	dot = mp;
205
	if ((rcv = skin(hfield("from", mp))) == NULL)
206
		rcv = skin(nameof(mp, 1));
207
	if ((replyto = skin(hfield("reply-to", mp))) != NULL)
208
		np = extract(replyto, GTO);
209
	else if ((cp = skin(hfield("to", mp))) != NULL)
210
		np = extract(cp, GTO);
211
	else
212
		np = NULL;
213
	/*
214
	 * Delete my name from the reply list,
215
	 * and with it, all my alternate names.
216
	 */
217
	np = delname(np, myname);
218
	if (altnames)
219
		for (ap = altnames; *ap; ap++)
220
			np = delname(np, *ap);
221
	if (np != NULL && replyto == NULL)
222
		np = cat(np, extract(rcv, GTO));
223
	else if (np == NULL) {
224
		if (replyto != NULL)
225
			puts("Empty reply-to field -- replying to author");
226
		np = extract(rcv, GTO);
227
	}
228
	np = elide(np);
229
	head.h_to = np;
230
	head.h_from = NULL;
231
	if ((head.h_subject = hfield("subject", mp)) == NULL)
232
		head.h_subject = hfield("subj", mp);
233
	head.h_subject = reedit(head.h_subject);
234
	if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
235
		np = elide(extract(cp, GCC));
236
		np = delname(np, myname);
237
		if (altnames != 0)
238
			for (ap = altnames; *ap; ap++)
239
				np = delname(np, *ap);
240
		head.h_cc = np;
241
	} else
242
		head.h_cc = NULL;
243
	head.h_bcc = NULL;
244
	head.h_smopts = NULL;
245
	mail1(&head, 1);
246
	return(0);
247
}
248
249
/*
250
 * Modify the subject we are replying to to begin with Re: if
251
 * it does not already.
252
 */
253
char *
254
reedit(char *subj)
255
{
256
	char *newsubj;
257
	size_t len;
258
259
	if (subj == NULL)
260
		return(NULL);
261
	if (strncasecmp(subj, "re:", 3) == 0)
262
		return(subj);
263
	len = strlen(subj) + 5;
264
	newsubj = salloc(len);
265
	strlcpy(newsubj, "Re: ", len);
266
	strlcat(newsubj, subj, len);
267
	return(newsubj);
268
}
269
270
/*
271
 * Mark new the named messages, so that they will be left in the system
272
 * mailbox as unread.
273
 */
274
int
275
marknew(void *v)
276
{
277
	int *msgvec = v;
278
	int *ip;
279
280
	for (ip = msgvec; *ip != 0; ip++) {
281
		dot = &message[*ip-1];
282
		dot->m_flag &= ~(MBOX|MREAD|MTOUCH);
283
		dot->m_flag |= MNEW|MSTATUS;
284
	}
285
	return(0);
286
}
287
288
/*
289
 * Preserve the named messages, so that they will be sent
290
 * back to the system mailbox.
291
 */
292
int
293
preserve(void *v)
294
{
295
	int *msgvec = v;
296
	int *ip, mesg;
297
	struct message *mp;
298
299
	if (edit) {
300
		puts("Cannot \"preserve\" in edit mode");
301
		return(1);
302
	}
303
	for (ip = msgvec; *ip != 0; ip++) {
304
		mesg = *ip;
305
		mp = &message[mesg-1];
306
		mp->m_flag |= MPRESERVE;
307
		mp->m_flag &= ~MBOX;
308
		dot = mp;
309
	}
310
	return(0);
311
}
312
313
/*
314
 * Mark all given messages as unread.
315
 */
316
int
317
unread(void *v)
318
{
319
	int *msgvec = v;
320
	int *ip;
321
322
	for (ip = msgvec; *ip != 0; ip++) {
323
		dot = &message[*ip-1];
324
		dot->m_flag &= ~(MREAD|MTOUCH);
325
		dot->m_flag |= MSTATUS;
326
	}
327
	return(0);
328
}
329
330
/*
331
 * Print the size of each message.
332
 */
333
int
334
messize(void *v)
335
{
336
	int *msgvec = v;
337
	struct message *mp;
338
	int *ip, mesg;
339
340
	for (ip = msgvec; *ip != 0; ip++) {
341
		mesg = *ip;
342
		mp = &message[mesg-1];
343
		printf("%d: %d/%d\n", mesg, mp->m_lines, mp->m_size);
344
	}
345
	return(0);
346
}
347
348
/*
349
 * Quit quickly.  If we are sourcing, just pop the input level
350
 * by returning an error.
351
 */
352
int
353
rexit(void *v)
354
{
355
356
	if (sourcing)
357
		return(1);
358
	exit(0);
359
	/*NOTREACHED*/
360
}
361
362
/*
363
 * Set or display a variable value.  Syntax is similar to that
364
 * of csh.
365
 */
366
int
367
set(void *v)
368
{
369
4
	char **arglist = v;
370
	struct var *vp;
371
	char *cp, *cp2;
372
2
	char varbuf[BUFSIZ], **ap, **p;
373
	int errs, h, s;
374
375
2
	if (*arglist == NULL) {
376
		for (h = 0, s = 1; h < HSHSIZE; h++)
377
			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
378
				s++;
379
		ap = (char **)salloc(s * sizeof(*ap));
380
		for (h = 0, p = ap; h < HSHSIZE; h++)
381
			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
382
				*p++ = vp->v_name;
383
		*p = NULL;
384
		sort(ap);
385
		for (p = ap; *p != NULL; p++)
386
			printf("%s\t%s\n", *p, value(*p));
387
		return(0);
388
	}
389
	errs = 0;
390
20
	for (ap = arglist; *ap != NULL; ap++) {
391
		cp = *ap;
392
8
		cp2 = varbuf;
393

138
		while (*cp != '=' && *cp != '\0')
394
38
			*cp2++ = *cp++;
395
8
		*cp2 = '\0';
396
8
		if (*cp == '\0')
397
8
			cp = "";
398
		else
399
			cp++;
400
8
		if (equal(varbuf, "")) {
401
			puts("Non-null variable name required");
402
			errs++;
403
			continue;
404
		}
405
8
		assign(varbuf, cp);
406
8
	}
407
2
	return(errs);
408
2
}
409
410
/*
411
 * Unset a bunch of variable values.
412
 */
413
int
414
unset(void *v)
415
{
416
	char **arglist = v;
417
	struct var *vp, *vp2;
418
	int errs, h;
419
	char **ap;
420
421
	errs = 0;
422
	for (ap = arglist; *ap != NULL; ap++) {
423
		if ((vp2 = lookup(*ap)) == NULL) {
424
			if (!sourcing) {
425
				printf("\"%s\": undefined variable\n", *ap);
426
				errs++;
427
			}
428
			continue;
429
		}
430
		h = hash(*ap);
431
		if (vp2 == variables[h]) {
432
			variables[h] = variables[h]->v_link;
433
			vfree(vp2->v_name);
434
			vfree(vp2->v_value);
435
			(void)free(vp2);
436
			continue;
437
		}
438
		for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
439
			;
440
		vp->v_link = vp2->v_link;
441
		vfree(vp2->v_name);
442
		vfree(vp2->v_value);
443
		(void)free(vp2);
444
	}
445
	return(errs);
446
}
447
448
/*
449
 * Put add users to a group.
450
 */
451
int
452
group(void *v)
453
{
454
	char **argv = v;
455
	struct grouphead *gh;
456
	struct group *gp;
457
	char **ap, *gname, **p;
458
	int h, s;
459
460
	if (*argv == NULL) {
461
		for (h = 0, s = 1; h < HSHSIZE; h++)
462
			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
463
				s++;
464
		ap = (char **)salloc(s * sizeof(*ap));
465
		for (h = 0, p = ap; h < HSHSIZE; h++)
466
			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
467
				*p++ = gh->g_name;
468
		*p = NULL;
469
		sort(ap);
470
		for (p = ap; *p != NULL; p++)
471
			printgroup(*p);
472
		return(0);
473
	}
474
	if (argv[1] == NULL) {
475
		printgroup(*argv);
476
		return(0);
477
	}
478
	gname = *argv;
479
	h = hash(gname);
480
	if ((gh = findgroup(gname)) == NULL) {
481
		if ((gh = calloc(1, sizeof(*gh))) == NULL)
482
			err(1, "calloc");
483
		gh->g_name = vcopy(gname);
484
		gh->g_list = NULL;
485
		gh->g_link = groups[h];
486
		groups[h] = gh;
487
	}
488
489
	/*
490
	 * Insert names from the command list into the group.
491
	 * Who cares if there are duplicates?  They get tossed
492
	 * later anyway.
493
	 */
494
495
	for (ap = argv+1; *ap != NULL; ap++) {
496
		if ((gp = calloc(1, sizeof(*gp))) == NULL)
497
			err(1, "calloc");
498
		gp->ge_name = vcopy(*ap);
499
		gp->ge_link = gh->g_list;
500
		gh->g_list = gp;
501
	}
502
	return(0);
503
}
504
505
/*
506
 * Sort the passed string vector into ascending dictionary
507
 * order.
508
 */
509
void
510
sort(char **list)
511
{
512
	char **ap;
513
514
	for (ap = list; *ap != NULL; ap++)
515
		;
516
	if (ap-list < 2)
517
		return;
518
	qsort(list, ap-list, sizeof(*list), diction);
519
}
520
521
/*
522
 * Do a dictionary order comparison of the arguments from
523
 * qsort.
524
 */
525
static int
526
diction(const void *a, const void *b)
527
{
528
529
	return(strcmp(*(char **)a, *(char **)b));
530
}
531
532
/*
533
 * The do nothing command for comments.
534
 */
535
/*ARGSUSED*/
536
int
537
null(void *v)
538
{
539
540
	return(0);
541
}
542
543
/*
544
 * Change to another file.  With no argument, print information about
545
 * the current file.
546
 */
547
int
548
file(void *v)
549
{
550
	char **argv = v;
551
552
	if (argv[0] == NULL) {
553
		newfileinfo(0);
554
		clearnew();
555
		return(0);
556
	}
557
	if (setfile(*argv) < 0)
558
		return(1);
559
	announce();
560
	return(0);
561
}
562
563
/*
564
 * Expand file names like echo
565
 */
566
int
567
echo(void *v)
568
{
569
	char **argv = v;
570
	char **ap, *cp;
571
572
	for (ap = argv; *ap != NULL; ap++) {
573
		cp = *ap;
574
		if ((cp = expand(cp)) != NULL) {
575
			if (ap != argv)
576
				putchar(' ');
577
			fputs(cp, stdout);
578
		}
579
	}
580
	putchar('\n');
581
	return(0);
582
}
583
584
int
585
Respond(void *v)
586
{
587
	int *msgvec = v;
588
589
	if (value("Replyall") == NULL)
590
		return(_Respond(msgvec));
591
	else
592
		return(_respond(msgvec));
593
}
594
595
/*
596
 * Reply to a series of messages by simply mailing to the senders
597
 * and not messing around with the To: and Cc: lists as in normal
598
 * reply.
599
 */
600
int
601
_Respond(int *msgvec)
602
{
603
	struct header head;
604
	struct message *mp;
605
	int *ap;
606
	char *cp;
607
608
	head.h_to = NULL;
609
	for (ap = msgvec; *ap != 0; ap++) {
610
		mp = &message[*ap - 1];
611
		touch(mp);
612
		dot = mp;
613
		if ((cp = skin(hfield("from", mp))) == NULL)
614
			cp = skin(nameof(mp, 2));
615
		head.h_to = cat(head.h_to, extract(cp, GTO));
616
	}
617
	if (head.h_to == NULL)
618
		return(0);
619
	mp = &message[msgvec[0] - 1];
620
	if ((head.h_subject = hfield("subject", mp)) == NULL)
621
		head.h_subject = hfield("subj", mp);
622
	head.h_subject = reedit(head.h_subject);
623
	head.h_from = NULL;
624
	head.h_cc = NULL;
625
	head.h_bcc = NULL;
626
	head.h_smopts = NULL;
627
	mail1(&head, 1);
628
	return(0);
629
}
630
631
/*
632
 * Conditional commands.  These allow one to parameterize one's
633
 * .mailrc and do some things if sending, others if receiving.
634
 */
635
int
636
ifcmd(void *v)
637
{
638
	char **argv = v;
639
	char *cp;
640
641
	if (cond != CANY) {
642
		puts("Illegal nested \"if\"");
643
		return(1);
644
	}
645
	cond = CANY;
646
	cp = argv[0];
647
	switch (*cp) {
648
	case 'r': case 'R':
649
		cond = CRCV;
650
		break;
651
652
	case 's': case 'S':
653
		cond = CSEND;
654
		break;
655
656
	default:
657
		printf("Unrecognized if-keyword: \"%s\"\n", cp);
658
		return(1);
659
	}
660
	return(0);
661
}
662
663
/*
664
 * Implement 'else'.  This is pretty simple -- we just
665
 * flip over the conditional flag.
666
 */
667
int
668
elsecmd(void *v)
669
{
670
671
	switch (cond) {
672
	case CANY:
673
		puts("\"Else\" without matching \"if\"");
674
		return(1);
675
676
	case CSEND:
677
		cond = CRCV;
678
		break;
679
680
	case CRCV:
681
		cond = CSEND;
682
		break;
683
684
	default:
685
		puts("mail's idea of conditions is screwed up");
686
		cond = CANY;
687
		break;
688
	}
689
	return(0);
690
}
691
692
/*
693
 * End of if statement.  Just set cond back to anything.
694
 */
695
int
696
endifcmd(void *v)
697
{
698
699
	if (cond == CANY) {
700
		puts("\"Endif\" without matching \"if\"");
701
		return(1);
702
	}
703
	cond = CANY;
704
	return(0);
705
}
706
707
/*
708
 * Set the list of alternate names.
709
 */
710
int
711
alternates(void *v)
712
{
713
	char **namelist = v;
714
	char **ap, **ap2;
715
	int c;
716
717
	c = argcount(namelist) + 1;
718
	if (c == 1) {
719
		if (altnames == 0)
720
			return(0);
721
		for (ap = altnames; *ap; ap++)
722
			printf("%s ", *ap);
723
		putchar('\n');
724
		return(0);
725
	}
726
	if (altnames != 0)
727
		(void)free(altnames);
728
	if ((altnames = calloc(c, sizeof(char *))) == NULL)
729
		err(1, "calloc");
730
	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
731
		if ((*ap2 = strdup(*ap)) == NULL)
732
			err(1, "strdup");
733
	}
734
	*ap2 = 0;
735
	return(0);
736
}