xref: /netbsd-src/usr.bin/mail/cmd3.c (revision b7ae68fde0d8ef1c03714e8bbb1ee7c6118ea93b)
1 /*	$NetBSD: cmd3.c,v 1.30 2006/09/19 20:31:49 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)cmd3.c	8.2 (Berkeley) 4/20/95";
36 #else
37 __RCSID("$NetBSD: cmd3.c,v 1.30 2006/09/19 20:31:49 christos Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include "rcv.h"
42 #include "extern.h"
43 
44 /*
45  * Mail -- a mail program
46  *
47  * Still more user commands.
48  */
49 static int delgroup(const char *);
50 static int diction(const void *, const void *);
51 
52 /*
53  * Process a shell escape by saving signals, ignoring signals,
54  * and forking a sh -c
55  */
56 int
57 shell(void *v)
58 {
59 	char *str = v;
60 	sig_t sigint = signal(SIGINT, SIG_IGN);
61 	const char *shellcmd;
62 	char cmd[BUFSIZ];
63 
64 	(void)strcpy(cmd, str);
65 	if (bangexp(cmd) < 0)
66 		return 1;
67 	if ((shellcmd = value("SHELL")) == NULL)
68 		shellcmd = _PATH_CSHELL;
69 	(void)run_command(shellcmd, 0, 0, 1, "-c", cmd, NULL);
70 	(void)signal(SIGINT, sigint);
71 	(void)printf("!\n");
72 	return 0;
73 }
74 
75 /*
76  * Fork an interactive shell.
77  */
78 /*ARGSUSED*/
79 int
80 dosh(void *v)
81 {
82 	sig_t sigint = signal(SIGINT, SIG_IGN);
83 	const char *shellcmd;
84 
85 	if ((shellcmd = value("SHELL")) == NULL)
86 		shellcmd = _PATH_CSHELL;
87 	(void)run_command(shellcmd, 0, 0, 1, NULL);
88 	(void)signal(SIGINT, sigint);
89 	(void)putchar('\n');
90 	return 0;
91 }
92 
93 /*
94  * Expand the shell escape by expanding unescaped !'s into the
95  * last issued command where possible.
96  */
97 
98 char	lastbang[128];
99 
100 int
101 bangexp(char *str)
102 {
103 	char bangbuf[BUFSIZ];
104 	char *cp, *cp2;
105 	int n;
106 	int changed = 0;
107 
108 	cp = str;
109 	cp2 = bangbuf;
110 	n = BUFSIZ;
111 	while (*cp) {
112 		if (*cp == '!') {
113 			if (n < strlen(lastbang)) {
114 overf:
115 				(void)printf("Command buffer overflow\n");
116 				return(-1);
117 			}
118 			changed++;
119 			(void)strcpy(cp2, lastbang);
120 			cp2 += strlen(lastbang);
121 			n -= strlen(lastbang);
122 			cp++;
123 			continue;
124 		}
125 		if (*cp == '\\' && cp[1] == '!') {
126 			if (--n <= 1)
127 				goto overf;
128 			*cp2++ = '!';
129 			cp += 2;
130 			changed++;
131 		}
132 		if (--n <= 1)
133 			goto overf;
134 		*cp2++ = *cp++;
135 	}
136 	*cp2 = 0;
137 	if (changed) {
138 		(void)printf("!%s\n", bangbuf);
139 		(void)fflush(stdout);
140 	}
141 	(void)strcpy(str, bangbuf);
142 	(void)strncpy(lastbang, bangbuf, 128);
143 	lastbang[127] = 0;
144 	return(0);
145 }
146 
147 /*
148  * Print out a nice help message from some file or another.
149  */
150 
151 int
152 /*ARGSUSED*/
153 help(void *v)
154 {
155 	int c;
156 	FILE *f;
157 
158 	if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
159 		warn(_PATH_HELP);
160 		return(1);
161 	}
162 	while ((c = getc(f)) != EOF)
163 		(void)putchar(c);
164 	(void)Fclose(f);
165 	return(0);
166 }
167 
168 /*
169  * Change user's working directory.
170  */
171 int
172 schdir(void *v)
173 {
174 	char **arglist = v;
175 	const char *cp;
176 
177 	if (*arglist == NULL)
178 		cp = homedir;
179 	else
180 		if ((cp = expand(*arglist)) == NULL)
181 			return(1);
182 	if (chdir(cp) < 0) {
183 		warn("%s", cp);
184 		return(1);
185 	}
186 	return 0;
187 }
188 
189 int
190 respond(void *v)
191 {
192 	int *msgvec = v;
193 	if (value("Replyall") == NULL)
194 		return (_respond(msgvec));
195 	else
196 		return (_Respond(msgvec));
197 }
198 
199 static struct name *
200 set_smopts(struct message *mp)
201 {
202 	char *cp;
203 	struct name *np = NULL;
204 	char *reply_as_recipient = value("ReplyAsRecipient");
205 
206 	if (reply_as_recipient &&
207 	    (cp = skin(hfield("to", mp))) != NULL &&
208 	    extract(cp, GTO)->n_flink == NULL) {  /* check for one recipient */
209 		char *p, *q;
210 		int len = strlen(cp);
211 		for (p = q = reply_as_recipient ; *p ; p = q) {
212 			while (*q != '\0' && *q != ',' && *q != ' ' && *q != '\t')
213 				q++;
214 			if (p + len == q && strncmp(cp, p, len) == 0)
215 				return np;
216 			while (*q == ',' || *q == ' ' || *q == '\t')
217 				q++;
218 		}
219 		np = extract(__UNCONST("-f"), GSMOPTS);
220 		np = cat(np, extract(cp, GSMOPTS));
221 	}
222 
223 	return np;
224 }
225 
226 /*
227  * Reply to a list of messages.  Extract each name from the
228  * message header and send them off to mail1()
229  */
230 int
231 _respond(int *msgvec)
232 {
233 	struct message *mp;
234 	char *cp, *rcv, *replyto;
235 	char **ap;
236 	struct name *np;
237 	struct header head;
238 
239 	if (msgvec[1] != 0) {
240 		(void)printf("Sorry, can't reply to multiple messages at once\n");
241 		return(1);
242 	}
243 	mp = &message[msgvec[0] - 1];
244 	touch(mp);
245 	dot = mp;
246 	if ((rcv = skin(hfield("from", mp))) == NULL)
247 		rcv = skin(nameof(mp, 1));
248 	if ((replyto = skin(hfield("reply-to", mp))) != NULL)
249 		np = extract(replyto, GTO);
250 	else if ((cp = skin(hfield("to", mp))) != NULL)
251 		np = extract(cp, GTO);
252 	else
253 		np = NULL;
254 	np = elide(np);
255 	/*
256 	 * Delete my name from the reply list,
257 	 * and with it, all my alternate names.
258 	 */
259 	np = delname(np, myname);
260 	if (altnames)
261 		for (ap = altnames; *ap; ap++)
262 			np = delname(np, *ap);
263 	if (np != NULL && replyto == NULL)
264 		np = cat(np, extract(rcv, GTO));
265 	else if (np == NULL) {
266 		if (replyto != NULL)
267 			(void)printf("Empty reply-to field -- replying to author\n");
268 		np = extract(rcv, GTO);
269 	}
270 	head.h_to = np;
271 	if ((head.h_subject = hfield("subject", mp)) == NULL)
272 		head.h_subject = hfield("subj", mp);
273 	head.h_subject = reedit(head.h_subject);
274 	if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
275 		np = elide(extract(cp, GCC));
276 		np = delname(np, myname);
277 		if (altnames != 0)
278 			for (ap = altnames; *ap; ap++)
279 				np = delname(np, *ap);
280 		head.h_cc = np;
281 	} else
282 		head.h_cc = NULL;
283 	head.h_bcc = NULL;
284 	head.h_smopts = set_smopts(mp);
285 	mail1(&head, 1);
286 	return(0);
287 }
288 
289 /*
290  * Modify the subject we are replying to to begin with Re: if
291  * it does not already.
292  */
293 char *
294 reedit(char *subj)
295 {
296 	char *newsubj;
297 
298 	if (subj == NULL)
299 		return NULL;
300 	if ((subj[0] == 'r' || subj[0] == 'R') &&
301 	    (subj[1] == 'e' || subj[1] == 'E') &&
302 	    subj[2] == ':')
303 		return subj;
304 	newsubj = salloc(strlen(subj) + 5);
305 	(void)strcpy(newsubj, "Re: ");
306 	(void)strcpy(newsubj + 4, subj);
307 	return newsubj;
308 }
309 
310 /*
311  * Preserve the named messages, so that they will be sent
312  * back to the system mailbox.
313  */
314 int
315 preserve(void *v)
316 {
317 	int *msgvec = v;
318 	struct message *mp;
319 	int *ip, mesg;
320 
321 	if (edit) {
322 		(void)printf("Cannot \"preserve\" in edit mode\n");
323 		return(1);
324 	}
325 	for (ip = msgvec; *ip != 0; ip++) {
326 		mesg = *ip;
327 		mp = &message[mesg-1];
328 		mp->m_flag |= MPRESERVE;
329 		mp->m_flag &= ~MBOX;
330 		dot = mp;
331 	}
332 	return(0);
333 }
334 
335 /*
336  * Mark all given messages as unread.
337  */
338 int
339 unread(void *v)
340 {
341 	int *msgvec = v;
342 	int *ip;
343 
344 	for (ip = msgvec; *ip != 0; ip++) {
345 		dot = &message[*ip-1];
346 		dot->m_flag &= ~(MREAD|MTOUCH);
347 		dot->m_flag |= MSTATUS;
348 	}
349 	return(0);
350 }
351 
352 /*
353  * Print the size of each message.
354  */
355 int
356 messize(void *v)
357 {
358 	int *msgvec = v;
359 	struct message *mp;
360 	int *ip, mesg;
361 
362 	for (ip = msgvec; *ip != 0; ip++) {
363 		mesg = *ip;
364 		mp = &message[mesg-1];
365 		(void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
366 		    /*LINTED*/
367 		    (unsigned long long)mp->m_size);
368 	}
369 	return(0);
370 }
371 
372 /*
373  * Quit quickly.  If we are sourcing, just pop the input level
374  * by returning an error.
375  */
376 int
377 /*ARGSUSED*/
378 rexit(void *v)
379 {
380 	if (sourcing)
381 		return(1);
382 	exit(0);
383 	/*NOTREACHED*/
384 }
385 
386 /*
387  * Set or display a variable value.  Syntax is similar to that
388  * of csh.
389  */
390 int
391 set(void *v)
392 {
393 	char **arglist = v;
394 	struct var *vp;
395 	const char *cp;
396 	char varbuf[BUFSIZ], **ap, **p;
397 	int errs, h, s;
398 	size_t l;
399 
400 	if (*arglist == NULL) {
401 		for (h = 0, s = 1; h < HSHSIZE; h++)
402 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
403 				s++;
404 		ap = salloc(s * sizeof *ap);
405 		for (h = 0, p = ap; h < HSHSIZE; h++)
406 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
407 				*p++ = vp->v_name;
408 		*p = NULL;
409 		sort(ap);
410 		for (p = ap; *p != NULL; p++)
411 			(void)printf("%s\t%s\n", *p, value(*p));
412 		return(0);
413 	}
414 	errs = 0;
415 	for (ap = arglist; *ap != NULL; ap++) {
416 		cp = *ap;
417 		while (*cp != '=' && *cp != '\0')
418 			++cp;
419 		l = cp - *ap;
420 		if (l >= sizeof varbuf)
421 			l = sizeof varbuf - 1;
422 		(void)strncpy(varbuf, *ap, l);
423 		varbuf[l] = '\0';
424 		if (*cp == '\0')
425 			cp = "";
426 		else
427 			cp++;
428 		if (equal(varbuf, "")) {
429 			(void)printf("Non-null variable name required\n");
430 			errs++;
431 			continue;
432 		}
433 		assign(varbuf, cp);
434 	}
435 	return(errs);
436 }
437 
438 /*
439  * Unset a bunch of variable values.
440  */
441 int
442 unset(void *v)
443 {
444 	char **arglist = v;
445 	struct var *vp, *vp2;
446 	int errs, h;
447 	char **ap;
448 
449 	errs = 0;
450 	for (ap = arglist; *ap != NULL; ap++) {
451 		if ((vp2 = lookup(*ap)) == NULL) {
452 			if (getenv(*ap)) {
453 				(void)unsetenv(*ap);
454 			} else if (!sourcing) {
455 				(void)printf("\"%s\": undefined variable\n", *ap);
456 				errs++;
457 			}
458 			continue;
459 		}
460 		h = hash(*ap);
461 		if (vp2 == variables[h]) {
462 			variables[h] = variables[h]->v_link;
463 			v_free(vp2->v_name);
464                         v_free(vp2->v_value);
465 			free(vp2);
466 			continue;
467 		}
468 		for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
469 			;
470 		vp->v_link = vp2->v_link;
471                 v_free(vp2->v_name);
472                 v_free(vp2->v_value);
473 		free(vp2);
474 	}
475 	return(errs);
476 }
477 
478 /*
479  * Show a variable value.
480  */
481 int
482 show(void *v)
483 {
484 	char **arglist = v;
485 	struct var *vp;
486 	char **ap, **p;
487 	int h, s;
488 
489 	if (*arglist == NULL) {
490 		for (h = 0, s = 1; h < HSHSIZE; h++)
491 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
492 				s++;
493 		ap = salloc(s * sizeof *ap);
494 		for (h = 0, p = ap; h < HSHSIZE; h++)
495 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
496 				*p++ = vp->v_name;
497 		*p = NULL;
498 		sort(ap);
499 		for (p = ap; *p != NULL; p++)
500 			(void)printf("%s=%s\n", *p, value(*p));
501 		return(0);
502 	}
503 
504 	for (ap = arglist; *ap != NULL; ap++) {
505 		char *val = value(*ap);
506 		(void)printf("%s=%s\n", *ap, val ? val : "<null>");
507 	}
508 	return 0;
509 }
510 
511 /*
512  * Put add users to a group.
513  */
514 int
515 group(void *v)
516 {
517 	char **argv = v;
518 	struct grouphead *gh;
519 	struct group *gp;
520 	int h;
521 	int s;
522 	char **ap, *gname, **p;
523 
524 	if (*argv == NULL) {
525 		for (h = 0, s = 1; h < HSHSIZE; h++)
526 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
527 				s++;
528 		ap = salloc(s * sizeof *ap);
529 		for (h = 0, p = ap; h < HSHSIZE; h++)
530 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
531 				*p++ = gh->g_name;
532 		*p = NULL;
533 		sort(ap);
534 		for (p = ap; *p != NULL; p++)
535 			printgroup(*p);
536 		return(0);
537 	}
538 	if (argv[1] == NULL) {
539 		printgroup(*argv);
540 		return(0);
541 	}
542 	gname = *argv;
543 	h = hash(gname);
544 	if ((gh = findgroup(gname)) == NULL) {
545 		gh = (struct grouphead *) calloc(1, sizeof *gh);
546 		gh->g_name = vcopy(gname);
547 		gh->g_list = NULL;
548 		gh->g_link = groups[h];
549 		groups[h] = gh;
550 	}
551 
552 	/*
553 	 * Insert names from the command list into the group.
554 	 * Who cares if there are duplicates?  They get tossed
555 	 * later anyway.
556 	 */
557 
558 	for (ap = argv+1; *ap != NULL; ap++) {
559 		gp = (struct group *) calloc(1, sizeof *gp);
560 		gp->ge_name = vcopy(*ap);
561 		gp->ge_link = gh->g_list;
562 		gh->g_list = gp;
563 	}
564 	return(0);
565 }
566 
567 /*
568  * The unalias command takes a list of alises
569  * and discards the remembered groups of users.
570  */
571 int
572 unalias(void *v)
573 {
574 	char **ap;
575 
576 	for (ap = v; *ap != NULL; ap++)
577 		(void)delgroup(*ap);
578 	return 0;
579 }
580 
581 /*
582  * Delete the named group alias. Return zero if the group was
583  * successfully deleted, or -1 if there was no such group.
584  */
585 static int
586 delgroup(const char *name)
587 {
588 	struct grouphead *gh, *p;
589 	struct group *g;
590 	int h;
591 
592 	h = hash(name);
593 	for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
594 		if (strcmp(gh->g_name, name) == 0) {
595 			if (p == NULL)
596 				groups[h] = gh->g_link;
597 			else
598 				p->g_link = gh->g_link;
599 			while (gh->g_list != NULL) {
600 				g = gh->g_list;
601 				gh->g_list = g->ge_link;
602 				free(g->ge_name);
603 				free(g);
604 			}
605 			free(gh->g_name);
606 			free(gh);
607 			return 0;
608 		}
609 	return -1;
610 }
611 
612 /*
613  * Sort the passed string vecotor into ascending dictionary
614  * order.
615  */
616 void
617 sort(char **list)
618 {
619 	char **ap;
620 
621 	for (ap = list; *ap != NULL; ap++)
622 		;
623 	if (ap-list < 2)
624 		return;
625 	qsort(list, (size_t)(ap-list), sizeof(*list), diction);
626 }
627 
628 /*
629  * Do a dictionary order comparison of the arguments from
630  * qsort.
631  */
632 static int
633 diction(const void *a, const void *b)
634 {
635 	return(strcmp(*(const char *const *)a, *(const char *const *)b));
636 }
637 
638 /*
639  * The do nothing command for comments.
640  */
641 
642 /*ARGSUSED*/
643 int
644 null(void *v)
645 {
646 	return 0;
647 }
648 
649 /*
650  * Change to another file.  With no argument, print information about
651  * the current file.
652  */
653 int
654 file(void *v)
655 {
656 	char **argv = v;
657 
658 	if (argv[0] == NULL) {
659 		(void)newfileinfo(0);
660 		return 0;
661 	}
662 	if (setfile(*argv) < 0)
663 		return 1;
664 	announce();
665 	return 0;
666 }
667 
668 /*
669  * Expand file names like echo
670  */
671 int
672 echo(void *v)
673 {
674 	char **argv = v;
675 	char **ap;
676 	const char *cp;
677 
678 	for (ap = argv; *ap != NULL; ap++) {
679 		cp = *ap;
680 		if ((cp = expand(cp)) != NULL) {
681 			if (ap != argv)
682 				(void)putchar(' ');
683 			(void)printf("%s", cp);
684 		}
685 	}
686 	(void)putchar('\n');
687 	return 0;
688 }
689 
690 int
691 Respond(void *v)
692 {
693 	int *msgvec = v;
694 	if (value("Replyall") == NULL)
695 		return (_Respond(msgvec));
696 	else
697 		return (_respond(msgvec));
698 }
699 
700 /*
701  * Reply to a series of messages by simply mailing to the senders
702  * and not messing around with the To: and Cc: lists as in normal
703  * reply.
704  */
705 int
706 _Respond(int msgvec[])
707 {
708 	struct header head;
709 	struct message *mp;
710 	int *ap;
711 	char *cp;
712 
713 	head.h_to = NULL;
714 	for (ap = msgvec; *ap != 0; ap++) {
715 		mp = &message[*ap - 1];
716 		touch(mp);
717 		dot = mp;
718 		if ((cp = skin(hfield("from", mp))) == NULL)
719 			cp = skin(nameof(mp, 2));
720 		head.h_to = cat(head.h_to, extract(cp, GTO));
721 	}
722 	if (head.h_to == NULL)
723 		return 0;
724 	mp = &message[msgvec[0] - 1];
725 	if ((head.h_subject = hfield("subject", mp)) == NULL)
726 		head.h_subject = hfield("subj", mp);
727 	head.h_subject = reedit(head.h_subject);
728 	head.h_cc = NULL;
729 	head.h_bcc = NULL;
730 	head.h_smopts = set_smopts(mp);
731 	mail1(&head, 1);
732 	return 0;
733 }
734 
735 /*
736  * Conditional commands.  These allow one to parameterize one's
737  * .mailrc and do some things if sending, others if receiving.
738  */
739 int
740 ifcmd(void *v)
741 {
742 	char **argv = v;
743 	char *cp;
744 
745 	if (cond != CANY) {
746 		(void)printf("Illegal nested \"if\"\n");
747 		return(1);
748 	}
749 	cond = CANY;
750 	cp = argv[0];
751 	switch (*cp) {
752 	case 'r': case 'R':
753 		cond = CRCV;
754 		break;
755 
756 	case 's': case 'S':
757 		cond = CSEND;
758 		break;
759 
760 	default:
761 		(void)printf("Unrecognized if-keyword: \"%s\"\n", cp);
762 		return(1);
763 	}
764 	return(0);
765 }
766 
767 /*
768  * Implement 'else'.  This is pretty simple -- we just
769  * flip over the conditional flag.
770  */
771 int
772 /*ARGSUSED*/
773 elsecmd(void *v)
774 {
775 
776 	switch (cond) {
777 	case CANY:
778 		(void)printf("\"Else\" without matching \"if\"\n");
779 		return(1);
780 
781 	case CSEND:
782 		cond = CRCV;
783 		break;
784 
785 	case CRCV:
786 		cond = CSEND;
787 		break;
788 
789 	default:
790 		(void)printf("Mail's idea of conditions is screwed up\n");
791 		cond = CANY;
792 		break;
793 	}
794 	return(0);
795 }
796 
797 /*
798  * End of if statement.  Just set cond back to anything.
799  */
800 int
801 /*ARGSUSED*/
802 endifcmd(void *v)
803 {
804 
805 	if (cond == CANY) {
806 		(void)printf("\"Endif\" without matching \"if\"\n");
807 		return(1);
808 	}
809 	cond = CANY;
810 	return(0);
811 }
812 
813 /*
814  * Set the list of alternate names.
815  */
816 int
817 alternates(void *v)
818 {
819 	char **namelist = v;
820 	int c;
821 	char **ap, **ap2, *cp;
822 
823 	c = argcount(namelist) + 1;
824 	if (c == 1) {
825 		if (altnames == 0)
826 			return(0);
827 		for (ap = altnames; *ap; ap++)
828 			(void)printf("%s ", *ap);
829 		(void)printf("\n");
830 		return(0);
831 	}
832 	if (altnames != 0)
833 		free(altnames);
834 	altnames = (char **) calloc((unsigned) c, sizeof (char *));
835 	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
836 		cp = calloc((unsigned) strlen(*ap) + 1, sizeof (char));
837 		(void)strcpy(cp, *ap);
838 		*ap2 = cp;
839 	}
840 	*ap2 = 0;
841 	return(0);
842 }
843