cmd3.c revision 302968
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
3087701Smarkm#ifndef lint
3187701Smarkm#if 0
3287701Smarkmstatic char sccsid[] = "@(#)cmd3.c	8.2 (Berkeley) 4/20/95";
3387701Smarkm#endif
341590Srgrimes#endif /* not lint */
3528370Scharnier#include <sys/cdefs.h>
361590Srgrimes__FBSDID("$FreeBSD: stable/10/usr.bin/mail/cmd3.c 302968 2016-07-17 18:32:33Z pfg $");
371590Srgrimes
3887701Smarkm#include "rcv.h"
391590Srgrimes#include "extern.h"
401590Srgrimes
4187701Smarkm/*
4228370Scharnier * Mail -- a mail program
431590Srgrimes *
441590Srgrimes * Still more user commands.
451590Srgrimes */
4687701Smarkm
4728370Scharnier/*
4828370Scharnier * Process a shell escape by saving signals, ignoring signals,
4928370Scharnier * and forking a sh -c
501590Srgrimes */
511590Srgrimesint
52200419Sdelphijshell(char *str)
5387701Smarkm{
5428370Scharnier	sig_t sigint = signal(SIGINT, SIG_IGN);
5587701Smarkm	char *sh;
561590Srgrimes	char cmd[BUFSIZ];
571590Srgrimes
5892922Simp	if (strlcpy(cmd, str, sizeof(cmd)) >= sizeof(cmd))
5992922Simp		return (1);
6092922Simp	if (bangexp(cmd, sizeof(cmd)) < 0)
611590Srgrimes		return (1);
621590Srgrimes	if ((sh = value("SHELL")) == NULL)
631590Srgrimes		sh = _PATH_CSHELL;
6468617Sdg	(void)run_command(sh, 0, -1, -1, "-c", cmd, NULL);
651590Srgrimes	(void)signal(SIGINT, sigint);
661590Srgrimes	printf("!\n");
6768617Sdg	return (0);
6850638Speter}
692599Sache
701590Srgrimes/*
711590Srgrimes * Fork an interactive shell.
72102944Sdwmalone */
731590Srgrimes/*ARGSUSED*/
741590Srgrimesint
751590Srgrimesdosh(char *str __unused)
761590Srgrimes{
771590Srgrimes	sig_t sigint = signal(SIGINT, SIG_IGN);
7887701Smarkm	char *sh;
7987701Smarkm
801590Srgrimes	if ((sh = value("SHELL")) == NULL)
811590Srgrimes		sh = _PATH_CSHELL;
8228370Scharnier	(void)run_command(sh, 0, -1, -1, NULL, NULL, NULL);
831590Srgrimes	(void)signal(SIGINT, sigint);
841590Srgrimes	printf("\n");
852599Sache	return (0);
863397Sache}
8728370Scharnier
881590Srgrimes/*
891590Srgrimes * Expand the shell escape by expanding unescaped !'s into the
901590Srgrimes * last issued command where possible.
911590Srgrimes */
921590Srgrimesint
931590Srgrimesbangexp(char *str, size_t strsize)
941590Srgrimes{
951590Srgrimes	char bangbuf[BUFSIZ];
961590Srgrimes	static char lastbang[BUFSIZ];
971590Srgrimes	char *cp, *cp2;
981590Srgrimes	int n, changed = 0;
9924360Simp
1001590Srgrimes	cp = str;
1011590Srgrimes	cp2 = bangbuf;
1021590Srgrimes	n = sizeof(bangbuf);
1031590Srgrimes	while (*cp != '\0') {
1041590Srgrimes		if (*cp == '!') {
1051590Srgrimes			if (n < strlen(lastbang)) {
1061590Srgrimesoverf:
1071590Srgrimes				printf("Command buffer overflow\n");
1081590Srgrimes				return (-1);
1091590Srgrimes			}
1101590Srgrimes			changed++;
11168617Sdg			if (strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf))
1121590Srgrimes			    >= sizeof(bangbuf) - (cp2 - bangbuf))
1131590Srgrimes				goto overf;
1141590Srgrimes			cp2 += strlen(lastbang);
1151590Srgrimes			n -= strlen(lastbang);
1161590Srgrimes			cp++;
1171590Srgrimes			continue;
1181590Srgrimes		}
1191590Srgrimes		if (*cp == '\\' && cp[1] == '!') {
1201590Srgrimes			if (--n <= 1)
1211590Srgrimes				goto overf;
1221590Srgrimes			*cp2++ = '!';
1231590Srgrimes			cp += 2;
12468617Sdg			changed++;
1251590Srgrimes		}
1261590Srgrimes		if (--n <= 1)
1271590Srgrimes			goto overf;
1281590Srgrimes		*cp2++ = *cp++;
1291590Srgrimes	}
1301590Srgrimes	*cp2 = 0;
1311590Srgrimes	if (changed) {
1321590Srgrimes		printf("!%s\n", bangbuf);
1331590Srgrimes		(void)fflush(stdout);
1341590Srgrimes	}
1351590Srgrimes	if (strlcpy(str, bangbuf, strsize) >= strsize)
1361590Srgrimes		goto overf;
1371590Srgrimes	if (strlcpy(lastbang, bangbuf, sizeof(lastbang)) >= sizeof(lastbang))
1381590Srgrimes		goto overf;
1391590Srgrimes	return (0);
1401590Srgrimes}
1411590Srgrimes
1421590Srgrimes/*
1431590Srgrimes * Print out a nice help message from some file or another.
1441590Srgrimes */
1451590Srgrimes
1461590Srgrimesint
1471590Srgrimeshelp(void)
1481590Srgrimes{
1491590Srgrimes	int c;
1501590Srgrimes	FILE *f;
1511590Srgrimes
1521590Srgrimes	if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
1531590Srgrimes		warn("%s", _PATH_HELP);
1541590Srgrimes		return (1);
1551590Srgrimes	}
1561590Srgrimes	while ((c = getc(f)) != EOF)
1571590Srgrimes		putchar(c);
1581590Srgrimes	(void)Fclose(f);
1591590Srgrimes	return (0);
1601590Srgrimes}
1611590Srgrimes
16250638Speter/*
16350638Speter * Change user's working directory.
1641590Srgrimes */
1651590Srgrimesint
1661590Srgrimesschdir(char **arglist)
1671590Srgrimes{
1681590Srgrimes	char *cp;
16950638Speter
17050638Speter	if (*arglist == NULL) {
17150638Speter		if (homedir == NULL)
1721590Srgrimes			return (1);
1731590Srgrimes		cp = homedir;
1741590Srgrimes	} else
1751590Srgrimes		if ((cp = expand(*arglist)) == NULL)
1761590Srgrimes			return (1);
1771590Srgrimes	if (chdir(cp) < 0) {
1781590Srgrimes		warn("%s", cp);
1791590Srgrimes		return (1);
1801590Srgrimes	}
1811590Srgrimes	return (0);
1821590Srgrimes}
1831590Srgrimes
1841590Srgrimesint
1851590Srgrimesrespond(int *msgvec)
1861590Srgrimes{
1871590Srgrimes	if (value("Replyall") == NULL && value("flipr") == NULL)
1881590Srgrimes		return (dorespond(msgvec));
1891590Srgrimes	else
1901590Srgrimes		return (doRespond(msgvec));
1911590Srgrimes}
1921590Srgrimes
1931590Srgrimes/*
1941590Srgrimes * Reply to a list of messages.  Extract each name from the
1951590Srgrimes * message header and send them off to mail1()
1961590Srgrimes */
1971590Srgrimesint
1981590Srgrimesdorespond(int *msgvec)
1991590Srgrimes{
2001590Srgrimes	struct message *mp;
2011590Srgrimes	char *cp, *rcv, *replyto;
2021590Srgrimes	char **ap;
2031590Srgrimes	struct name *np;
20450638Speter	struct header head;
20550638Speter
2061590Srgrimes	if (msgvec[1] != 0) {
2071590Srgrimes		printf("Sorry, can't reply to multiple messages at once\n");
2081590Srgrimes		return (1);
2091590Srgrimes	}
2101590Srgrimes	mp = &message[msgvec[0] - 1];
2111590Srgrimes	touch(mp);
2121590Srgrimes	dot = mp;
2131590Srgrimes	if ((rcv = skin(hfield("from", mp))) == NULL)
2141590Srgrimes		rcv = skin(nameof(mp, 1));
21550638Speter	if ((replyto = skin(hfield("reply-to", mp))) != NULL)
21650638Speter		np = extract(replyto, GTO);
21750638Speter	else if ((cp = skin(hfield("to", mp))) != NULL)
21850638Speter		np = extract(cp, GTO);
21950638Speter	else
22050638Speter		np = NULL;
22150638Speter	np = elide(np);
2221590Srgrimes	/*
22350638Speter	 * Delete my name from the reply list,
22450638Speter	 * and with it, all my alternate names.
22550638Speter	 */
22650638Speter	np = delname(np, myname);
22750638Speter	if (altnames)
22850638Speter		for (ap = altnames; *ap != NULL; ap++)
22950638Speter			np = delname(np, *ap);
2301590Srgrimes	if (np != NULL && replyto == NULL)
2311590Srgrimes		np = cat(np, extract(rcv, GTO));
2321590Srgrimes	else if (np == NULL) {
2331590Srgrimes		if (replyto != NULL)
2341590Srgrimes			printf("Empty reply-to field -- replying to author\n");
2351590Srgrimes		np = extract(rcv, GTO);
2361590Srgrimes	}
2371590Srgrimes	head.h_to = np;
2381590Srgrimes	if ((head.h_subject = hfield("subject", mp)) == NULL)
2391590Srgrimes		head.h_subject = hfield("subj", mp);
240102944Sdwmalone	head.h_subject = reedit(head.h_subject);
2411590Srgrimes	if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
2421590Srgrimes		np = elide(extract(cp, GCC));
2431590Srgrimes		np = delname(np, myname);
2441590Srgrimes		if (altnames != 0)
2451590Srgrimes			for (ap = altnames; *ap != NULL; ap++)
2461590Srgrimes				np = delname(np, *ap);
2471590Srgrimes		head.h_cc = np;
2481590Srgrimes	} else
2491590Srgrimes		head.h_cc = NULL;
2501590Srgrimes	head.h_bcc = NULL;
2511590Srgrimes	head.h_smopts = NULL;
2528524Sache	head.h_replyto = value("REPLYTO");
2531590Srgrimes	head.h_inreplyto = skin(hfield("message-id", mp));
2541590Srgrimes	mail1(&head, 1);
2551590Srgrimes	return (0);
2561590Srgrimes}
2571590Srgrimes
2581590Srgrimes/*
2591590Srgrimes * Modify the subject we are replying to to begin with Re: if
2601590Srgrimes * it does not already.
2611590Srgrimes */
2621590Srgrimeschar *
2631590Srgrimesreedit(char *subj)
2641590Srgrimes{
2651590Srgrimes	char *newsubj;
2661590Srgrimes
2671590Srgrimes	if (subj == NULL)
268102944Sdwmalone		return (NULL);
2691590Srgrimes	if ((subj[0] == 'r' || subj[0] == 'R') &&
2701590Srgrimes	    (subj[1] == 'e' || subj[1] == 'E') &&
27128370Scharnier	    subj[2] == ':')
27228370Scharnier		return (subj);
27328370Scharnier	newsubj = salloc(strlen(subj) + 5);
2741590Srgrimes	sprintf(newsubj, "Re: %s", subj);
2751590Srgrimes	return (newsubj);
2761590Srgrimes}
27787701Smarkm
2781590Srgrimes/*
2791590Srgrimes * Preserve the named messages, so that they will be sent
28087701Smarkm * back to the system mailbox.
2811590Srgrimes */
2821590Srgrimesint
28387701Smarkmpreserve(int *msgvec)
2841590Srgrimes{
2851590Srgrimes	int *ip, mesg;
2861590Srgrimes	struct message *mp;
2871590Srgrimes
2881590Srgrimes	if (edit) {
2891590Srgrimes		printf("Cannot \"preserve\" in edit mode\n");
290102944Sdwmalone		return (1);
2911590Srgrimes	}
29228370Scharnier	for (ip = msgvec; *ip != 0; ip++) {
29328370Scharnier		mesg = *ip;
29428370Scharnier		mp = &message[mesg-1];
2951590Srgrimes		mp->m_flag |= MPRESERVE;
2961590Srgrimes		mp->m_flag &= ~MBOX;
29728370Scharnier		dot = mp;
298	}
299	return (0);
300}
301
302/*
303 * Mark all given messages as unread.
304 */
305int
306unread(int msgvec[])
307{
308	int *ip;
309
310	for (ip = msgvec; *ip != 0; ip++) {
311		dot = &message[*ip-1];
312		dot->m_flag &= ~(MREAD|MTOUCH);
313		dot->m_flag |= MSTATUS;
314	}
315	return (0);
316}
317
318/*
319 * Print the size of each message.
320 */
321int
322messize(int *msgvec)
323{
324	struct message *mp;
325	int *ip, mesg;
326
327	for (ip = msgvec; *ip != 0; ip++) {
328		mesg = *ip;
329		mp = &message[mesg-1];
330		printf("%d: %ld/%ld\n", mesg, mp->m_lines, mp->m_size);
331	}
332	return (0);
333}
334
335/*
336 * Quit quickly.  If we are sourcing, just pop the input level
337 * by returning an error.
338 */
339int
340rexit(int e __unused)
341{
342	if (sourcing)
343		return (1);
344	exit(0);
345	/*NOTREACHED*/
346}
347
348/*
349 * Set or display a variable value.  Syntax is similar to that
350 * of csh.
351 */
352int
353set(char **arglist)
354{
355	struct var *vp;
356	char *cp, *cp2;
357	char varbuf[BUFSIZ], **ap, **p;
358	int errs, h, s;
359
360	if (*arglist == NULL) {
361		for (h = 0, s = 1; h < HSHSIZE; h++)
362			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
363				s++;
364		ap = (char **)salloc(s * sizeof(*ap));
365		for (h = 0, p = ap; h < HSHSIZE; h++)
366			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
367				*p++ = vp->v_name;
368		*p = NULL;
369		sort(ap);
370		for (p = ap; *p != NULL; p++)
371			printf("%s\t%s\n", *p, value(*p));
372		return (0);
373	}
374	errs = 0;
375	for (ap = arglist; *ap != NULL; ap++) {
376		cp = *ap;
377		cp2 = varbuf;
378		while (cp2 < varbuf + sizeof(varbuf) - 1 && *cp != '=' && *cp != '\0')
379			*cp2++ = *cp++;
380		*cp2 = '\0';
381		if (*cp == '\0')
382			cp = "";
383		else
384			cp++;
385		if (equal(varbuf, "")) {
386			printf("Non-null variable name required\n");
387			errs++;
388			continue;
389		}
390		assign(varbuf, cp);
391	}
392	return (errs);
393}
394
395/*
396 * Unset a bunch of variable values.
397 */
398int
399unset(char **arglist)
400{
401	struct var *vp, *vp2;
402	int errs, h;
403	char **ap;
404
405	errs = 0;
406	for (ap = arglist; *ap != NULL; ap++) {
407		if ((vp2 = lookup(*ap)) == NULL) {
408			if (getenv(*ap))
409				unsetenv(*ap);
410			else if (!sourcing) {
411				printf("\"%s\": undefined variable\n", *ap);
412				errs++;
413			}
414			continue;
415		}
416		h = hash(*ap);
417		if (vp2 == variables[h]) {
418			variables[h] = variables[h]->v_link;
419			vfree(vp2->v_name);
420			vfree(vp2->v_value);
421			(void)free(vp2);
422			continue;
423		}
424		for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
425			;
426		vp->v_link = vp2->v_link;
427		vfree(vp2->v_name);
428		vfree(vp2->v_value);
429		(void)free(vp2);
430	}
431	return (errs);
432}
433
434/*
435 * Put add users to a group.
436 */
437int
438group(char **argv)
439{
440	struct grouphead *gh;
441	struct group *gp;
442	char **ap, *gname, **p;
443	int h, s;
444
445	if (*argv == NULL) {
446		for (h = 0, s = 1; h < HSHSIZE; h++)
447			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
448				s++;
449		ap = (char **)salloc(s * sizeof(*ap));
450		for (h = 0, p = ap; h < HSHSIZE; h++)
451			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
452				*p++ = gh->g_name;
453		*p = NULL;
454		sort(ap);
455		for (p = ap; *p != NULL; p++)
456			printgroup(*p);
457		return (0);
458	}
459	if (argv[1] == NULL) {
460		printgroup(*argv);
461		return (0);
462	}
463	gname = *argv;
464	h = hash(gname);
465	if ((gh = findgroup(gname)) == NULL) {
466		if ((gh = calloc(1, sizeof(*gh))) == NULL)
467			err(1, "Out of memory");
468		gh->g_name = vcopy(gname);
469		gh->g_list = NULL;
470		gh->g_link = groups[h];
471		groups[h] = gh;
472	}
473
474	/*
475	 * Insert names from the command list into the group.
476	 * Who cares if there are duplicates?  They get tossed
477	 * later anyway.
478	 */
479
480	for (ap = argv+1; *ap != NULL; ap++) {
481		if ((gp = calloc(1, sizeof(*gp))) == NULL)
482			err(1, "Out of memory");
483		gp->ge_name = vcopy(*ap);
484		gp->ge_link = gh->g_list;
485		gh->g_list = gp;
486	}
487	return (0);
488}
489
490/*
491 * Sort the passed string vecotor into ascending dictionary
492 * order.
493 */
494void
495sort(char **list)
496{
497	char **ap;
498
499	for (ap = list; *ap != NULL; ap++)
500		;
501	if (ap-list < 2)
502		return;
503	qsort(list, ap-list, sizeof(*list), diction);
504}
505
506/*
507 * Do a dictionary order comparison of the arguments from
508 * qsort.
509 */
510int
511diction(const void *a, const void *b)
512{
513	return (strcmp(*(const char **)a, *(const char **)b));
514}
515
516/*
517 * The do nothing command for comments.
518 */
519
520/*ARGSUSED*/
521int
522null(int e __unused)
523{
524	return (0);
525}
526
527/*
528 * Change to another file.  With no argument, print information about
529 * the current file.
530 */
531int
532file(char **argv)
533{
534
535	if (argv[0] == NULL) {
536		newfileinfo(0);
537		return (0);
538	}
539	if (setfile(*argv) < 0)
540		return (1);
541	announce();
542	return (0);
543}
544
545/*
546 * Expand file names like echo
547 */
548int
549echo(char **argv)
550{
551	char **ap, *cp;
552
553	for (ap = argv; *ap != NULL; ap++) {
554		cp = *ap;
555		if ((cp = expand(cp)) != NULL) {
556			if (ap != argv)
557				printf(" ");
558			printf("%s", cp);
559		}
560	}
561	printf("\n");
562	return (0);
563}
564
565int
566Respond(int *msgvec)
567{
568	if (value("Replyall") == NULL && value("flipr") == NULL)
569		return (doRespond(msgvec));
570	else
571		return (dorespond(msgvec));
572}
573
574/*
575 * Reply to a series of messages by simply mailing to the senders
576 * and not messing around with the To: and Cc: lists as in normal
577 * reply.
578 */
579int
580doRespond(int msgvec[])
581{
582	struct header head;
583	struct message *mp;
584	int *ap;
585	char *cp, *mid;
586
587	head.h_to = NULL;
588	for (ap = msgvec; *ap != 0; ap++) {
589		mp = &message[*ap - 1];
590		touch(mp);
591		dot = mp;
592		if ((cp = skin(hfield("from", mp))) == NULL)
593			cp = skin(nameof(mp, 2));
594		head.h_to = cat(head.h_to, extract(cp, GTO));
595		mid = skin(hfield("message-id", mp));
596	}
597	if (head.h_to == NULL)
598		return (0);
599	mp = &message[msgvec[0] - 1];
600	if ((head.h_subject = hfield("subject", mp)) == NULL)
601		head.h_subject = hfield("subj", mp);
602	head.h_subject = reedit(head.h_subject);
603	head.h_cc = NULL;
604	head.h_bcc = NULL;
605	head.h_smopts = NULL;
606	head.h_replyto = value("REPLYTO");
607	head.h_inreplyto = mid;
608	mail1(&head, 1);
609	return (0);
610}
611
612/*
613 * Conditional commands.  These allow one to parameterize one's
614 * .mailrc and do some things if sending, others if receiving.
615 */
616int
617ifcmd(char **argv)
618{
619	char *cp;
620
621	if (cond != CANY) {
622		printf("Illegal nested \"if\"\n");
623		return (1);
624	}
625	cond = CANY;
626	cp = argv[0];
627	switch (*cp) {
628	case 'r': case 'R':
629		cond = CRCV;
630		break;
631
632	case 's': case 'S':
633		cond = CSEND;
634		break;
635
636	default:
637		printf("Unrecognized if-keyword: \"%s\"\n", cp);
638		return (1);
639	}
640	return (0);
641}
642
643/*
644 * Implement 'else'.  This is pretty simple -- we just
645 * flip over the conditional flag.
646 */
647int
648elsecmd(void)
649{
650
651	switch (cond) {
652	case CANY:
653		printf("\"Else\" without matching \"if\"\n");
654		return (1);
655
656	case CSEND:
657		cond = CRCV;
658		break;
659
660	case CRCV:
661		cond = CSEND;
662		break;
663
664	default:
665		printf("Mail's idea of conditions is screwed up\n");
666		cond = CANY;
667		break;
668	}
669	return (0);
670}
671
672/*
673 * End of if statement.  Just set cond back to anything.
674 */
675int
676endifcmd(void)
677{
678
679	if (cond == CANY) {
680		printf("\"Endif\" without matching \"if\"\n");
681		return (1);
682	}
683	cond = CANY;
684	return (0);
685}
686
687/*
688 * Set the list of alternate names.
689 */
690int
691alternates(char **namelist)
692{
693	int c;
694	char **ap, **ap2, *cp;
695
696	c = argcount(namelist) + 1;
697	if (c == 1) {
698		if (altnames == 0)
699			return (0);
700		for (ap = altnames; *ap != NULL; ap++)
701			printf("%s ", *ap);
702		printf("\n");
703		return (0);
704	}
705	if (altnames != 0)
706		(void)free(altnames);
707	if ((altnames = calloc((unsigned)c, sizeof(char *))) == NULL)
708		err(1, "Out of memory");
709	for (ap = namelist, ap2 = altnames; *ap != NULL; ap++, ap2++) {
710		cp = calloc((unsigned)strlen(*ap) + 1, sizeof(char));
711		strcpy(cp, *ap);
712		*ap2 = cp;
713	}
714	*ap2 = 0;
715	return (0);
716}
717