cmd3.c revision 1.41 1 1.41 christos /* $NetBSD: cmd3.c,v 1.41 2009/04/11 14:22:32 christos Exp $ */
2 1.5 christos
3 1.1 cgd /*
4 1.4 deraadt * Copyright (c) 1980, 1993
5 1.4 deraadt * The Regents of the University of California. All rights reserved.
6 1.1 cgd *
7 1.1 cgd * Redistribution and use in source and binary forms, with or without
8 1.1 cgd * modification, are permitted provided that the following conditions
9 1.1 cgd * are met:
10 1.1 cgd * 1. Redistributions of source code must retain the above copyright
11 1.1 cgd * notice, this list of conditions and the following disclaimer.
12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 cgd * notice, this list of conditions and the following disclaimer in the
14 1.1 cgd * documentation and/or other materials provided with the distribution.
15 1.21 agc * 3. Neither the name of the University nor the names of its contributors
16 1.1 cgd * may be used to endorse or promote products derived from this software
17 1.1 cgd * without specific prior written permission.
18 1.1 cgd *
19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 1.1 cgd * SUCH DAMAGE.
30 1.1 cgd */
31 1.1 cgd
32 1.9 lukem #include <sys/cdefs.h>
33 1.1 cgd #ifndef lint
34 1.5 christos #if 0
35 1.6 tls static char sccsid[] = "@(#)cmd3.c 8.2 (Berkeley) 4/20/95";
36 1.5 christos #else
37 1.41 christos __RCSID("$NetBSD: cmd3.c,v 1.41 2009/04/11 14:22:32 christos Exp $");
38 1.5 christos #endif
39 1.1 cgd #endif /* not lint */
40 1.1 cgd
41 1.1 cgd #include "rcv.h"
42 1.38 christos #include <assert.h>
43 1.31 christos #include <util.h>
44 1.4 deraadt #include "extern.h"
45 1.31 christos #include "mime.h"
46 1.40 christos #include "sig.h"
47 1.33 christos #include "thread.h"
48 1.1 cgd
49 1.1 cgd /*
50 1.1 cgd * Mail -- a mail program
51 1.1 cgd *
52 1.1 cgd * Still more user commands.
53 1.1 cgd */
54 1.33 christos
55 1.1 cgd
56 1.1 cgd /*
57 1.33 christos * Do a dictionary order comparison of the arguments from
58 1.33 christos * qsort.
59 1.1 cgd */
60 1.33 christos static int
61 1.33 christos diction(const void *a, const void *b)
62 1.1 cgd {
63 1.40 christos
64 1.33 christos return strcmp(*(const char *const *)a, *(const char *const *)b);
65 1.1 cgd }
66 1.1 cgd
67 1.1 cgd /*
68 1.33 christos * Sort the passed string vector into ascending dictionary
69 1.33 christos * order.
70 1.1 cgd */
71 1.33 christos PUBLIC void
72 1.33 christos sort(const char **list)
73 1.1 cgd {
74 1.33 christos const char **ap;
75 1.1 cgd
76 1.33 christos for (ap = list; *ap != NULL; ap++)
77 1.33 christos continue;
78 1.40 christos if (ap - list < 2)
79 1.33 christos return;
80 1.40 christos qsort(list, (size_t)(ap - list), sizeof(*list), diction);
81 1.1 cgd }
82 1.1 cgd
83 1.1 cgd /*
84 1.1 cgd * Expand the shell escape by expanding unescaped !'s into the
85 1.1 cgd * last issued command where possible.
86 1.1 cgd */
87 1.33 christos static int
88 1.12 wiz bangexp(char *str)
89 1.1 cgd {
90 1.33 christos static char lastbang[128];
91 1.33 christos char bangbuf[LINESIZE];
92 1.9 lukem char *cp, *cp2;
93 1.40 christos ssize_t n;
94 1.40 christos int changed;
95 1.1 cgd
96 1.40 christos changed = 0;
97 1.1 cgd cp = str;
98 1.1 cgd cp2 = bangbuf;
99 1.33 christos n = sizeof(bangbuf); /* bytes left in bangbuf */
100 1.1 cgd while (*cp) {
101 1.1 cgd if (*cp == '!') {
102 1.33 christos if (n < (int)strlen(lastbang)) {
103 1.40 christos overf:
104 1.27 christos (void)printf("Command buffer overflow\n");
105 1.33 christos return -1;
106 1.1 cgd }
107 1.1 cgd changed++;
108 1.27 christos (void)strcpy(cp2, lastbang);
109 1.1 cgd cp2 += strlen(lastbang);
110 1.1 cgd n -= strlen(lastbang);
111 1.1 cgd cp++;
112 1.1 cgd continue;
113 1.1 cgd }
114 1.1 cgd if (*cp == '\\' && cp[1] == '!') {
115 1.1 cgd if (--n <= 1)
116 1.1 cgd goto overf;
117 1.1 cgd *cp2++ = '!';
118 1.1 cgd cp += 2;
119 1.1 cgd changed++;
120 1.1 cgd }
121 1.1 cgd if (--n <= 1)
122 1.1 cgd goto overf;
123 1.1 cgd *cp2++ = *cp++;
124 1.1 cgd }
125 1.1 cgd *cp2 = 0;
126 1.1 cgd if (changed) {
127 1.27 christos (void)printf("!%s\n", bangbuf);
128 1.27 christos (void)fflush(stdout);
129 1.1 cgd }
130 1.27 christos (void)strcpy(str, bangbuf);
131 1.33 christos (void)strlcpy(lastbang, bangbuf, sizeof(lastbang));
132 1.33 christos return 0;
133 1.33 christos }
134 1.33 christos
135 1.33 christos /*
136 1.33 christos * Process a shell escape by saving signals, ignoring signals,
137 1.33 christos * and forking a sh -c
138 1.33 christos */
139 1.33 christos PUBLIC int
140 1.33 christos shell(void *v)
141 1.33 christos {
142 1.40 christos struct sigaction osa;
143 1.40 christos sigset_t oset;
144 1.40 christos char *str;
145 1.33 christos const char *shellcmd;
146 1.33 christos char cmd[LINESIZE];
147 1.33 christos
148 1.40 christos str = v;
149 1.40 christos sig_check();
150 1.40 christos (void)sig_ignore(SIGINT, &osa, &oset);
151 1.33 christos (void)strcpy(cmd, str);
152 1.33 christos if (bangexp(cmd) < 0)
153 1.33 christos return 1;
154 1.33 christos if ((shellcmd = value(ENAME_SHELL)) == NULL)
155 1.33 christos shellcmd = _PATH_CSHELL;
156 1.40 christos (void)run_command(shellcmd, NULL, 0, 1, "-c", cmd, NULL);
157 1.40 christos (void)sig_restore(SIGINT, &osa, &oset);
158 1.33 christos (void)printf("!\n");
159 1.40 christos sig_check();
160 1.33 christos return 0;
161 1.33 christos }
162 1.33 christos
163 1.33 christos /*
164 1.33 christos * Fork an interactive shell.
165 1.33 christos */
166 1.33 christos /*ARGSUSED*/
167 1.33 christos PUBLIC int
168 1.33 christos dosh(void *v __unused)
169 1.33 christos {
170 1.40 christos struct sigaction osa;
171 1.40 christos sigset_t oset;
172 1.33 christos const char *shellcmd;
173 1.33 christos
174 1.40 christos sig_check();
175 1.40 christos (void)sig_ignore(SIGINT, &osa, &oset);
176 1.33 christos if ((shellcmd = value(ENAME_SHELL)) == NULL)
177 1.33 christos shellcmd = _PATH_CSHELL;
178 1.40 christos (void)run_command(shellcmd, NULL, 0, 1, NULL);
179 1.40 christos (void)sig_restore(SIGINT, &osa, &oset);
180 1.33 christos (void)putchar('\n');
181 1.40 christos sig_check();
182 1.33 christos return 0;
183 1.1 cgd }
184 1.1 cgd
185 1.1 cgd /*
186 1.1 cgd * Print out a nice help message from some file or another.
187 1.1 cgd */
188 1.1 cgd
189 1.27 christos /*ARGSUSED*/
190 1.33 christos PUBLIC int
191 1.31 christos help(void *v __unused)
192 1.1 cgd {
193 1.40 christos
194 1.34 christos cathelp(_PATH_HELP);
195 1.33 christos return 0;
196 1.1 cgd }
197 1.1 cgd
198 1.1 cgd /*
199 1.1 cgd * Change user's working directory.
200 1.1 cgd */
201 1.33 christos PUBLIC int
202 1.12 wiz schdir(void *v)
203 1.1 cgd {
204 1.40 christos char **arglist;
205 1.26 christos const char *cp;
206 1.1 cgd
207 1.40 christos arglist = v;
208 1.14 wiz if (*arglist == NULL)
209 1.1 cgd cp = homedir;
210 1.1 cgd else
211 1.14 wiz if ((cp = expand(*arglist)) == NULL)
212 1.33 christos return 1;
213 1.1 cgd if (chdir(cp) < 0) {
214 1.17 wiz warn("%s", cp);
215 1.33 christos return 1;
216 1.1 cgd }
217 1.1 cgd return 0;
218 1.1 cgd }
219 1.1 cgd
220 1.33 christos /*
221 1.33 christos * Return the smopts field if "ReplyAsRecipient" is defined.
222 1.33 christos */
223 1.29 christos static struct name *
224 1.29 christos set_smopts(struct message *mp)
225 1.29 christos {
226 1.29 christos char *cp;
227 1.40 christos struct name *np;
228 1.40 christos char *reply_as_recipient;
229 1.29 christos
230 1.40 christos np = NULL;
231 1.40 christos reply_as_recipient = value(ENAME_REPLYASRECIPIENT);
232 1.30 christos if (reply_as_recipient &&
233 1.29 christos (cp = skin(hfield("to", mp))) != NULL &&
234 1.30 christos extract(cp, GTO)->n_flink == NULL) { /* check for one recipient */
235 1.29 christos char *p, *q;
236 1.31 christos size_t len = strlen(cp);
237 1.31 christos /*
238 1.31 christos * XXX - perhaps we always want to ignore
239 1.31 christos * "undisclosed-recipients:;" ?
240 1.31 christos */
241 1.31 christos for (p = q = reply_as_recipient; *p; p = q) {
242 1.35 christos while (*q != '\0' && *q != ',' && !is_WSP(*q))
243 1.29 christos q++;
244 1.31 christos if (p + len == q && strncasecmp(cp, p, len) == 0)
245 1.29 christos return np;
246 1.35 christos while (*q == ',' || is_WSP(*q))
247 1.29 christos q++;
248 1.29 christos }
249 1.29 christos np = extract(__UNCONST("-f"), GSMOPTS);
250 1.29 christos np = cat(np, extract(cp, GSMOPTS));
251 1.29 christos }
252 1.29 christos
253 1.29 christos return np;
254 1.29 christos }
255 1.29 christos
256 1.1 cgd /*
257 1.33 christos * Modify the subject we are replying to to begin with Re: if
258 1.33 christos * it does not already.
259 1.33 christos */
260 1.33 christos static char *
261 1.38 christos reedit(char *subj, const char *pref)
262 1.33 christos {
263 1.33 christos char *newsubj;
264 1.38 christos size_t preflen;
265 1.33 christos
266 1.38 christos assert(pref != NULL);
267 1.33 christos if (subj == NULL)
268 1.38 christos return __UNCONST(pref);
269 1.38 christos preflen = strlen(pref);
270 1.38 christos if (strncasecmp(subj, pref, preflen) == 0)
271 1.33 christos return subj;
272 1.38 christos newsubj = salloc(strlen(subj) + preflen + 1 + 1);
273 1.38 christos (void)sprintf(newsubj, "%s %s", pref, subj);
274 1.33 christos return newsubj;
275 1.33 christos }
276 1.33 christos
277 1.33 christos /*
278 1.33 christos * Set the "In-Reply-To" and "References" header fields appropriately.
279 1.33 christos * Used in replies.
280 1.33 christos */
281 1.33 christos static void
282 1.33 christos set_ident_fields(struct header *hp, struct message *mp)
283 1.33 christos {
284 1.33 christos char *in_reply_to;
285 1.33 christos char *references;
286 1.33 christos
287 1.33 christos in_reply_to = hfield("message-id", mp);
288 1.33 christos hp->h_in_reply_to = in_reply_to;
289 1.33 christos
290 1.33 christos references = hfield("references", mp);
291 1.33 christos hp->h_references = extract(references, GMISC);
292 1.33 christos hp->h_references = cat(hp->h_references, extract(in_reply_to, GMISC));
293 1.33 christos }
294 1.33 christos
295 1.33 christos /*
296 1.1 cgd * Reply to a list of messages. Extract each name from the
297 1.1 cgd * message header and send them off to mail1()
298 1.1 cgd */
299 1.33 christos static int
300 1.36 christos respond_core(int *msgvec)
301 1.1 cgd {
302 1.1 cgd struct message *mp;
303 1.1 cgd char *cp, *rcv, *replyto;
304 1.1 cgd char **ap;
305 1.1 cgd struct name *np;
306 1.1 cgd struct header head;
307 1.1 cgd
308 1.33 christos /* ensure that all header fields are initially NULL */
309 1.33 christos (void)memset(&head, 0, sizeof(head));
310 1.33 christos
311 1.1 cgd if (msgvec[1] != 0) {
312 1.27 christos (void)printf("Sorry, can't reply to multiple messages at once\n");
313 1.33 christos return 1;
314 1.1 cgd }
315 1.33 christos mp = get_message(msgvec[0]);
316 1.1 cgd touch(mp);
317 1.1 cgd dot = mp;
318 1.14 wiz if ((rcv = skin(hfield("from", mp))) == NULL)
319 1.1 cgd rcv = skin(nameof(mp, 1));
320 1.14 wiz if ((replyto = skin(hfield("reply-to", mp))) != NULL)
321 1.1 cgd np = extract(replyto, GTO);
322 1.14 wiz else if ((cp = skin(hfield("to", mp))) != NULL)
323 1.1 cgd np = extract(cp, GTO);
324 1.1 cgd else
325 1.15 wiz np = NULL;
326 1.1 cgd np = elide(np);
327 1.1 cgd /*
328 1.1 cgd * Delete my name from the reply list,
329 1.1 cgd * and with it, all my alternate names.
330 1.1 cgd */
331 1.1 cgd np = delname(np, myname);
332 1.1 cgd if (altnames)
333 1.1 cgd for (ap = altnames; *ap; ap++)
334 1.1 cgd np = delname(np, *ap);
335 1.15 wiz if (np != NULL && replyto == NULL)
336 1.1 cgd np = cat(np, extract(rcv, GTO));
337 1.15 wiz else if (np == NULL) {
338 1.14 wiz if (replyto != NULL)
339 1.27 christos (void)printf("Empty reply-to field -- replying to author\n");
340 1.1 cgd np = extract(rcv, GTO);
341 1.1 cgd }
342 1.1 cgd head.h_to = np;
343 1.14 wiz if ((head.h_subject = hfield("subject", mp)) == NULL)
344 1.1 cgd head.h_subject = hfield("subj", mp);
345 1.38 christos head.h_subject = reedit(head.h_subject, "Re:");
346 1.14 wiz if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
347 1.1 cgd np = elide(extract(cp, GCC));
348 1.1 cgd np = delname(np, myname);
349 1.1 cgd if (altnames != 0)
350 1.1 cgd for (ap = altnames; *ap; ap++)
351 1.1 cgd np = delname(np, *ap);
352 1.1 cgd head.h_cc = np;
353 1.1 cgd } else
354 1.15 wiz head.h_cc = NULL;
355 1.15 wiz head.h_bcc = NULL;
356 1.29 christos head.h_smopts = set_smopts(mp);
357 1.31 christos #ifdef MIME_SUPPORT
358 1.31 christos head.h_attach = NULL;
359 1.31 christos #endif
360 1.33 christos set_ident_fields(&head, mp);
361 1.1 cgd mail1(&head, 1);
362 1.33 christos return 0;
363 1.1 cgd }
364 1.1 cgd
365 1.1 cgd /*
366 1.33 christos * Reply to a series of messages by simply mailing to the senders
367 1.33 christos * and not messing around with the To: and Cc: lists as in normal
368 1.33 christos * reply.
369 1.1 cgd */
370 1.33 christos static int
371 1.36 christos Respond_core(int msgvec[])
372 1.33 christos {
373 1.33 christos struct header head;
374 1.33 christos struct message *mp;
375 1.33 christos int *ap;
376 1.33 christos char *cp;
377 1.33 christos
378 1.33 christos /* ensure that all header fields are initially NULL */
379 1.33 christos (void)memset(&head, 0, sizeof(head));
380 1.33 christos
381 1.33 christos head.h_to = NULL;
382 1.33 christos for (ap = msgvec; *ap != 0; ap++) {
383 1.33 christos mp = get_message(*ap);
384 1.33 christos touch(mp);
385 1.33 christos dot = mp;
386 1.33 christos if ((cp = skin(hfield("from", mp))) == NULL)
387 1.33 christos cp = skin(nameof(mp, 2));
388 1.33 christos head.h_to = cat(head.h_to, extract(cp, GTO));
389 1.33 christos }
390 1.33 christos if (head.h_to == NULL)
391 1.33 christos return 0;
392 1.33 christos mp = get_message(msgvec[0]);
393 1.33 christos if ((head.h_subject = hfield("subject", mp)) == NULL)
394 1.33 christos head.h_subject = hfield("subj", mp);
395 1.38 christos head.h_subject = reedit(head.h_subject, "Re:");
396 1.33 christos head.h_cc = NULL;
397 1.33 christos head.h_bcc = NULL;
398 1.33 christos head.h_smopts = set_smopts(mp);
399 1.33 christos #ifdef MIME_SUPPORT
400 1.33 christos head.h_attach = NULL;
401 1.33 christos #endif
402 1.33 christos set_ident_fields(&head, mp);
403 1.33 christos mail1(&head, 1);
404 1.33 christos return 0;
405 1.33 christos }
406 1.33 christos
407 1.33 christos PUBLIC int
408 1.33 christos respond(void *v)
409 1.1 cgd {
410 1.33 christos int *msgvec = v;
411 1.33 christos if (value(ENAME_REPLYALL) == NULL)
412 1.36 christos return respond_core(msgvec);
413 1.33 christos else
414 1.36 christos return Respond_core(msgvec);
415 1.33 christos }
416 1.1 cgd
417 1.33 christos PUBLIC int
418 1.33 christos Respond(void *v)
419 1.33 christos {
420 1.33 christos int *msgvec = v;
421 1.33 christos if (value(ENAME_REPLYALL) == NULL)
422 1.36 christos return Respond_core(msgvec);
423 1.33 christos else
424 1.36 christos return respond_core(msgvec);
425 1.36 christos }
426 1.36 christos
427 1.39 christos #ifdef MIME_SUPPORT
428 1.38 christos static int
429 1.38 christos forward_one(int msgno, struct name *h_to)
430 1.38 christos {
431 1.38 christos struct attachment attach;
432 1.38 christos struct message *mp;
433 1.38 christos struct header hdr;
434 1.38 christos
435 1.38 christos mp = get_message(msgno);
436 1.38 christos if (mp == NULL) {
437 1.38 christos (void)printf("no such message %d\n", msgno);
438 1.38 christos return 1;
439 1.38 christos }
440 1.38 christos (void)printf("message %d\n", msgno);
441 1.38 christos
442 1.38 christos (void)memset(&attach, 0, sizeof(attach));
443 1.38 christos attach.a_type = ATTACH_MSG;
444 1.38 christos attach.a_msg = mp;
445 1.38 christos attach.a_Content = get_mime_content(&attach, 0);
446 1.38 christos
447 1.38 christos (void)memset(&hdr, 0, sizeof(hdr));
448 1.38 christos hdr.h_to = h_to;
449 1.38 christos if ((hdr.h_subject = hfield("subject", mp)) == NULL)
450 1.38 christos hdr.h_subject = hfield("subj", mp);
451 1.38 christos hdr.h_subject = reedit(hdr.h_subject, "Fwd:");
452 1.38 christos hdr.h_attach = &attach;
453 1.38 christos hdr.h_smopts = set_smopts(mp);
454 1.38 christos
455 1.38 christos set_ident_fields(&hdr, mp);
456 1.38 christos mail1(&hdr, 1);
457 1.38 christos return 0;
458 1.38 christos }
459 1.38 christos
460 1.36 christos PUBLIC int
461 1.36 christos forward(void *v)
462 1.36 christos {
463 1.36 christos int *msgvec = v;
464 1.36 christos int *ip;
465 1.38 christos struct header hdr;
466 1.38 christos int rval;
467 1.38 christos
468 1.38 christos if (forwardtab[0].i_count == 0) {
469 1.38 christos /* setup the forward tab */
470 1.38 christos add_ignore("Status", forwardtab);
471 1.38 christos }
472 1.38 christos (void)memset(&hdr, 0, sizeof(hdr));
473 1.38 christos if ((rval = grabh(&hdr, GTO)) != 0)
474 1.38 christos return rval;
475 1.38 christos
476 1.38 christos if (hdr.h_to == NULL) {
477 1.38 christos (void)printf("address missing!\n");
478 1.38 christos return 1;
479 1.38 christos }
480 1.41 christos for (ip = msgvec; *ip; ip++) {
481 1.38 christos int e;
482 1.38 christos if ((e = forward_one(*ip, hdr.h_to)) != 0)
483 1.38 christos return e;
484 1.38 christos }
485 1.36 christos return 0;
486 1.36 christos }
487 1.39 christos #endif /* MIME_SUPPORT */
488 1.36 christos
489 1.36 christos static int
490 1.36 christos bounce_one(int msgno, const char **smargs, struct name *h_to)
491 1.36 christos {
492 1.36 christos char mailtempname[PATHSIZE];
493 1.36 christos struct message *mp;
494 1.36 christos int fd;
495 1.36 christos FILE *obuf;
496 1.36 christos int rval;
497 1.36 christos
498 1.36 christos rval = 0;
499 1.36 christos
500 1.36 christos obuf = NULL;
501 1.36 christos (void)snprintf(mailtempname, sizeof(mailtempname),
502 1.36 christos "%s/mail.RsXXXXXXXXXX", tmpdir);
503 1.36 christos if ((fd = mkstemp(mailtempname)) == -1 ||
504 1.36 christos (obuf = Fdopen(fd, "w+")) == NULL) {
505 1.36 christos if (fd != -1)
506 1.36 christos (void)close(fd);
507 1.36 christos warn("%s", mailtempname);
508 1.36 christos rval = 1;
509 1.36 christos goto done;
510 1.36 christos }
511 1.36 christos (void)rm(mailtempname);
512 1.36 christos
513 1.36 christos mp = get_message(msgno);
514 1.36 christos
515 1.36 christos if (mp == NULL) {
516 1.36 christos (void)printf("no such message %d\n", msgno);
517 1.36 christos rval = 1;
518 1.36 christos goto done;
519 1.36 christos }
520 1.36 christos else {
521 1.36 christos char *cp;
522 1.36 christos char **ap;
523 1.36 christos struct name *np;
524 1.36 christos struct header hdr;
525 1.36 christos
526 1.36 christos /*
527 1.36 christos * Construct and output a new "To:" field:
528 1.36 christos * Remove our address from anything in the old "To:" field
529 1.36 christos * and append that list to the bounce address(es).
530 1.36 christos */
531 1.36 christos np = NULL;
532 1.36 christos if ((cp = skin(hfield("to", mp))) != NULL)
533 1.36 christos np = extract(cp, GTO);
534 1.36 christos np = delname(np, myname);
535 1.36 christos if (altnames)
536 1.36 christos for (ap = altnames; *ap; ap++)
537 1.36 christos np = delname(np, *ap);
538 1.36 christos np = cat(h_to, np);
539 1.36 christos (void)memset(&hdr, 0, sizeof(hdr));
540 1.36 christos hdr.h_to = elide(np);
541 1.36 christos (void)puthead(&hdr, obuf, GTO | GCOMMA);
542 1.36 christos }
543 1.36 christos if (sendmessage(mp, obuf, bouncetab, NULL, NULL)) {
544 1.36 christos (void)printf("bounce failed for message %d\n", msgno);
545 1.36 christos rval = 1;
546 1.36 christos goto done;
547 1.36 christos }
548 1.36 christos rewind(obuf); /* XXX - here or inside mail2() */
549 1.36 christos mail2(obuf, smargs);
550 1.36 christos done:
551 1.36 christos if (obuf)
552 1.36 christos (void)Fclose(obuf);
553 1.36 christos return rval;
554 1.36 christos }
555 1.36 christos
556 1.36 christos PUBLIC int
557 1.36 christos bounce(void *v)
558 1.36 christos {
559 1.40 christos int *msgvec;
560 1.36 christos int *ip;
561 1.36 christos const char **smargs;
562 1.36 christos struct header hdr;
563 1.36 christos int rval;
564 1.36 christos
565 1.40 christos msgvec = v;
566 1.36 christos if (bouncetab[0].i_count == 0) {
567 1.36 christos /* setup the bounce tab */
568 1.36 christos add_ignore("Status", bouncetab);
569 1.36 christos add_ignore("Delivered-To", bouncetab);
570 1.36 christos add_ignore("To", bouncetab);
571 1.36 christos add_ignore("X-Original-To", bouncetab);
572 1.36 christos }
573 1.36 christos (void)memset(&hdr, 0, sizeof(hdr));
574 1.36 christos if ((rval = grabh(&hdr, GTO)) != 0)
575 1.36 christos return rval;
576 1.36 christos
577 1.36 christos if (hdr.h_to == NULL)
578 1.36 christos return 1;
579 1.36 christos
580 1.36 christos smargs = unpack(hdr.h_to);
581 1.41 christos for (ip = msgvec; *ip; ip++) {
582 1.36 christos int e;
583 1.36 christos if ((e = bounce_one(*ip, smargs, hdr.h_to)) != 0)
584 1.36 christos return e;
585 1.36 christos }
586 1.36 christos return 0;
587 1.1 cgd }
588 1.1 cgd
589 1.1 cgd /*
590 1.1 cgd * Preserve the named messages, so that they will be sent
591 1.1 cgd * back to the system mailbox.
592 1.1 cgd */
593 1.33 christos PUBLIC int
594 1.12 wiz preserve(void *v)
595 1.1 cgd {
596 1.40 christos int *msgvec;
597 1.33 christos int *ip;
598 1.1 cgd
599 1.40 christos msgvec = v;
600 1.1 cgd if (edit) {
601 1.27 christos (void)printf("Cannot \"preserve\" in edit mode\n");
602 1.33 christos return 1;
603 1.1 cgd }
604 1.33 christos for (ip = msgvec; *ip != 0; ip++)
605 1.33 christos dot = set_m_flag(*ip, ~(MBOX | MPRESERVE), MPRESERVE);
606 1.33 christos
607 1.33 christos return 0;
608 1.1 cgd }
609 1.1 cgd
610 1.1 cgd /*
611 1.33 christos * Mark all given messages as unread, preserving the new status.
612 1.1 cgd */
613 1.33 christos PUBLIC int
614 1.12 wiz unread(void *v)
615 1.1 cgd {
616 1.40 christos int *msgvec;
617 1.9 lukem int *ip;
618 1.1 cgd
619 1.40 christos msgvec = v;
620 1.33 christos for (ip = msgvec; *ip != 0; ip++)
621 1.33 christos dot = set_m_flag(*ip, ~(MREAD | MTOUCH | MSTATUS), MSTATUS);
622 1.33 christos
623 1.33 christos return 0;
624 1.1 cgd }
625 1.1 cgd
626 1.1 cgd /*
627 1.32 christos * Mark all given messages as read.
628 1.32 christos */
629 1.33 christos PUBLIC int
630 1.32 christos markread(void *v)
631 1.32 christos {
632 1.40 christos int *msgvec;
633 1.32 christos int *ip;
634 1.32 christos
635 1.40 christos msgvec = v;
636 1.33 christos for (ip = msgvec; *ip != 0; ip++)
637 1.33 christos dot = set_m_flag(*ip,
638 1.33 christos ~(MNEW | MTOUCH | MREAD | MSTATUS), MREAD | MSTATUS);
639 1.33 christos
640 1.33 christos return 0;
641 1.32 christos }
642 1.32 christos
643 1.32 christos /*
644 1.1 cgd * Print the size of each message.
645 1.1 cgd */
646 1.33 christos PUBLIC int
647 1.12 wiz messize(void *v)
648 1.1 cgd {
649 1.40 christos int *msgvec;
650 1.9 lukem struct message *mp;
651 1.9 lukem int *ip, mesg;
652 1.1 cgd
653 1.40 christos msgvec = v;
654 1.7 pk for (ip = msgvec; *ip != 0; ip++) {
655 1.1 cgd mesg = *ip;
656 1.33 christos mp = get_message(mesg);
657 1.27 christos (void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
658 1.27 christos (unsigned long long)mp->m_size);
659 1.1 cgd }
660 1.33 christos return 0;
661 1.1 cgd }
662 1.1 cgd
663 1.1 cgd /*
664 1.1 cgd * Quit quickly. If we are sourcing, just pop the input level
665 1.1 cgd * by returning an error.
666 1.1 cgd */
667 1.27 christos /*ARGSUSED*/
668 1.33 christos PUBLIC int
669 1.31 christos rexit(void *v __unused)
670 1.1 cgd {
671 1.1 cgd if (sourcing)
672 1.33 christos return 1;
673 1.5 christos exit(0);
674 1.1 cgd /*NOTREACHED*/
675 1.1 cgd }
676 1.1 cgd
677 1.1 cgd /*
678 1.1 cgd * Set or display a variable value. Syntax is similar to that
679 1.1 cgd * of csh.
680 1.1 cgd */
681 1.33 christos PUBLIC int
682 1.12 wiz set(void *v)
683 1.1 cgd {
684 1.33 christos const char **arglist = v;
685 1.9 lukem struct var *vp;
686 1.26 christos const char *cp;
687 1.33 christos char varbuf[LINESIZE];
688 1.33 christos const char **ap, **p;
689 1.1 cgd int errs, h, s;
690 1.23 ross size_t l;
691 1.1 cgd
692 1.14 wiz if (*arglist == NULL) {
693 1.1 cgd for (h = 0, s = 1; h < HSHSIZE; h++)
694 1.15 wiz for (vp = variables[h]; vp != NULL; vp = vp->v_link)
695 1.1 cgd s++;
696 1.37 christos ap = salloc(s * sizeof(*ap));
697 1.1 cgd for (h = 0, p = ap; h < HSHSIZE; h++)
698 1.15 wiz for (vp = variables[h]; vp != NULL; vp = vp->v_link)
699 1.1 cgd *p++ = vp->v_name;
700 1.14 wiz *p = NULL;
701 1.1 cgd sort(ap);
702 1.14 wiz for (p = ap; *p != NULL; p++)
703 1.27 christos (void)printf("%s\t%s\n", *p, value(*p));
704 1.33 christos return 0;
705 1.1 cgd }
706 1.1 cgd errs = 0;
707 1.14 wiz for (ap = arglist; *ap != NULL; ap++) {
708 1.1 cgd cp = *ap;
709 1.1 cgd while (*cp != '=' && *cp != '\0')
710 1.23 ross ++cp;
711 1.23 ross l = cp - *ap;
712 1.37 christos if (l >= sizeof(varbuf))
713 1.33 christos l = sizeof(varbuf) - 1;
714 1.27 christos (void)strncpy(varbuf, *ap, l);
715 1.24 ross varbuf[l] = '\0';
716 1.1 cgd if (*cp == '\0')
717 1.1 cgd cp = "";
718 1.1 cgd else
719 1.1 cgd cp++;
720 1.1 cgd if (equal(varbuf, "")) {
721 1.27 christos (void)printf("Non-null variable name required\n");
722 1.1 cgd errs++;
723 1.1 cgd continue;
724 1.1 cgd }
725 1.1 cgd assign(varbuf, cp);
726 1.1 cgd }
727 1.33 christos return errs;
728 1.1 cgd }
729 1.1 cgd
730 1.1 cgd /*
731 1.1 cgd * Unset a bunch of variable values.
732 1.1 cgd */
733 1.33 christos PUBLIC int
734 1.12 wiz unset(void *v)
735 1.1 cgd {
736 1.5 christos char **arglist = v;
737 1.9 lukem struct var *vp, *vp2;
738 1.1 cgd int errs, h;
739 1.1 cgd char **ap;
740 1.1 cgd
741 1.1 cgd errs = 0;
742 1.14 wiz for (ap = arglist; *ap != NULL; ap++) {
743 1.15 wiz if ((vp2 = lookup(*ap)) == NULL) {
744 1.11 dean if (getenv(*ap)) {
745 1.27 christos (void)unsetenv(*ap);
746 1.11 dean } else if (!sourcing) {
747 1.27 christos (void)printf("\"%s\": undefined variable\n", *ap);
748 1.1 cgd errs++;
749 1.1 cgd }
750 1.1 cgd continue;
751 1.1 cgd }
752 1.1 cgd h = hash(*ap);
753 1.1 cgd if (vp2 == variables[h]) {
754 1.1 cgd variables[h] = variables[h]->v_link;
755 1.10 wsanchez v_free(vp2->v_name);
756 1.10 wsanchez v_free(vp2->v_value);
757 1.27 christos free(vp2);
758 1.1 cgd continue;
759 1.1 cgd }
760 1.1 cgd for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
761 1.33 christos continue;
762 1.1 cgd vp->v_link = vp2->v_link;
763 1.10 wsanchez v_free(vp2->v_name);
764 1.10 wsanchez v_free(vp2->v_value);
765 1.27 christos free(vp2);
766 1.1 cgd }
767 1.33 christos return errs;
768 1.1 cgd }
769 1.1 cgd
770 1.1 cgd /*
771 1.29 christos * Show a variable value.
772 1.29 christos */
773 1.33 christos PUBLIC int
774 1.29 christos show(void *v)
775 1.29 christos {
776 1.33 christos const char **arglist = v;
777 1.29 christos struct var *vp;
778 1.33 christos const char **ap, **p;
779 1.29 christos int h, s;
780 1.29 christos
781 1.29 christos if (*arglist == NULL) {
782 1.29 christos for (h = 0, s = 1; h < HSHSIZE; h++)
783 1.29 christos for (vp = variables[h]; vp != NULL; vp = vp->v_link)
784 1.29 christos s++;
785 1.37 christos ap = salloc(s * sizeof(*ap));
786 1.29 christos for (h = 0, p = ap; h < HSHSIZE; h++)
787 1.29 christos for (vp = variables[h]; vp != NULL; vp = vp->v_link)
788 1.29 christos *p++ = vp->v_name;
789 1.29 christos *p = NULL;
790 1.29 christos sort(ap);
791 1.29 christos for (p = ap; *p != NULL; p++)
792 1.29 christos (void)printf("%s=%s\n", *p, value(*p));
793 1.33 christos return 0;
794 1.29 christos }
795 1.29 christos
796 1.29 christos for (ap = arglist; *ap != NULL; ap++) {
797 1.29 christos char *val = value(*ap);
798 1.29 christos (void)printf("%s=%s\n", *ap, val ? val : "<null>");
799 1.29 christos }
800 1.29 christos return 0;
801 1.29 christos }
802 1.29 christos
803 1.32 christos
804 1.29 christos /*
805 1.1 cgd * Put add users to a group.
806 1.1 cgd */
807 1.33 christos PUBLIC int
808 1.12 wiz group(void *v)
809 1.1 cgd {
810 1.33 christos const char **argv = v;
811 1.9 lukem struct grouphead *gh;
812 1.9 lukem struct group *gp;
813 1.9 lukem int h;
814 1.1 cgd int s;
815 1.33 christos const char *gname;
816 1.33 christos const char **ap, **p;
817 1.1 cgd
818 1.14 wiz if (*argv == NULL) {
819 1.1 cgd for (h = 0, s = 1; h < HSHSIZE; h++)
820 1.15 wiz for (gh = groups[h]; gh != NULL; gh = gh->g_link)
821 1.1 cgd s++;
822 1.37 christos ap = salloc(s * sizeof(*ap));
823 1.1 cgd for (h = 0, p = ap; h < HSHSIZE; h++)
824 1.15 wiz for (gh = groups[h]; gh != NULL; gh = gh->g_link)
825 1.1 cgd *p++ = gh->g_name;
826 1.14 wiz *p = NULL;
827 1.1 cgd sort(ap);
828 1.14 wiz for (p = ap; *p != NULL; p++)
829 1.1 cgd printgroup(*p);
830 1.33 christos return 0;
831 1.1 cgd }
832 1.14 wiz if (argv[1] == NULL) {
833 1.1 cgd printgroup(*argv);
834 1.33 christos return 0;
835 1.1 cgd }
836 1.1 cgd gname = *argv;
837 1.1 cgd h = hash(gname);
838 1.15 wiz if ((gh = findgroup(gname)) == NULL) {
839 1.37 christos gh = ecalloc(1, sizeof(*gh));
840 1.1 cgd gh->g_name = vcopy(gname);
841 1.15 wiz gh->g_list = NULL;
842 1.1 cgd gh->g_link = groups[h];
843 1.1 cgd groups[h] = gh;
844 1.1 cgd }
845 1.1 cgd
846 1.1 cgd /*
847 1.1 cgd * Insert names from the command list into the group.
848 1.1 cgd * Who cares if there are duplicates? They get tossed
849 1.1 cgd * later anyway.
850 1.1 cgd */
851 1.1 cgd
852 1.31 christos for (ap = argv + 1; *ap != NULL; ap++) {
853 1.37 christos gp = ecalloc(1, sizeof(*gp));
854 1.1 cgd gp->ge_name = vcopy(*ap);
855 1.1 cgd gp->ge_link = gh->g_list;
856 1.1 cgd gh->g_list = gp;
857 1.1 cgd }
858 1.28 christos return 0;
859 1.28 christos }
860 1.28 christos
861 1.28 christos /*
862 1.28 christos * Delete the named group alias. Return zero if the group was
863 1.28 christos * successfully deleted, or -1 if there was no such group.
864 1.28 christos */
865 1.28 christos static int
866 1.28 christos delgroup(const char *name)
867 1.28 christos {
868 1.28 christos struct grouphead *gh, *p;
869 1.28 christos struct group *g;
870 1.28 christos int h;
871 1.28 christos
872 1.28 christos h = hash(name);
873 1.28 christos for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
874 1.28 christos if (strcmp(gh->g_name, name) == 0) {
875 1.28 christos if (p == NULL)
876 1.28 christos groups[h] = gh->g_link;
877 1.28 christos else
878 1.28 christos p->g_link = gh->g_link;
879 1.28 christos while (gh->g_list != NULL) {
880 1.28 christos g = gh->g_list;
881 1.28 christos gh->g_list = g->ge_link;
882 1.28 christos free(g->ge_name);
883 1.28 christos free(g);
884 1.28 christos }
885 1.28 christos free(gh->g_name);
886 1.28 christos free(gh);
887 1.28 christos return 0;
888 1.28 christos }
889 1.28 christos return -1;
890 1.28 christos }
891 1.28 christos
892 1.28 christos /*
893 1.33 christos * The unalias command takes a list of alises
894 1.33 christos * and discards the remembered groups of users.
895 1.1 cgd */
896 1.33 christos PUBLIC int
897 1.33 christos unalias(void *v)
898 1.1 cgd {
899 1.9 lukem char **ap;
900 1.1 cgd
901 1.33 christos for (ap = v; *ap != NULL; ap++)
902 1.33 christos (void)delgroup(*ap);
903 1.33 christos return 0;
904 1.1 cgd }
905 1.1 cgd
906 1.1 cgd /*
907 1.1 cgd * The do nothing command for comments.
908 1.1 cgd */
909 1.1 cgd /*ARGSUSED*/
910 1.33 christos PUBLIC int
911 1.31 christos null(void *v __unused)
912 1.1 cgd {
913 1.1 cgd return 0;
914 1.1 cgd }
915 1.1 cgd
916 1.1 cgd /*
917 1.1 cgd * Change to another file. With no argument, print information about
918 1.1 cgd * the current file.
919 1.1 cgd */
920 1.33 christos PUBLIC int
921 1.12 wiz file(void *v)
922 1.1 cgd {
923 1.5 christos char **argv = v;
924 1.1 cgd
925 1.14 wiz if (argv[0] == NULL) {
926 1.27 christos (void)newfileinfo(0);
927 1.1 cgd return 0;
928 1.1 cgd }
929 1.1 cgd if (setfile(*argv) < 0)
930 1.1 cgd return 1;
931 1.1 cgd announce();
932 1.33 christos
933 1.1 cgd return 0;
934 1.1 cgd }
935 1.1 cgd
936 1.1 cgd /*
937 1.1 cgd * Expand file names like echo
938 1.1 cgd */
939 1.33 christos PUBLIC int
940 1.12 wiz echo(void *v)
941 1.1 cgd {
942 1.5 christos char **argv = v;
943 1.9 lukem char **ap;
944 1.26 christos const char *cp;
945 1.1 cgd
946 1.14 wiz for (ap = argv; *ap != NULL; ap++) {
947 1.1 cgd cp = *ap;
948 1.14 wiz if ((cp = expand(cp)) != NULL) {
949 1.1 cgd if (ap != argv)
950 1.27 christos (void)putchar(' ');
951 1.27 christos (void)printf("%s", cp);
952 1.1 cgd }
953 1.1 cgd }
954 1.27 christos (void)putchar('\n');
955 1.1 cgd return 0;
956 1.1 cgd }
957 1.1 cgd
958 1.33 christos /*
959 1.33 christos * Routines to push and pop the condition code to support nested
960 1.33 christos * if/else/endif statements.
961 1.33 christos */
962 1.33 christos static void
963 1.33 christos push_cond(int c_cond)
964 1.1 cgd {
965 1.33 christos struct cond_stack_s *csp;
966 1.33 christos csp = emalloc(sizeof(*csp));
967 1.33 christos csp->c_cond = c_cond;
968 1.33 christos csp->c_next = cond_stack;
969 1.33 christos cond_stack = csp;
970 1.1 cgd }
971 1.1 cgd
972 1.33 christos static int
973 1.33 christos pop_cond(void)
974 1.1 cgd {
975 1.33 christos int c_cond;
976 1.33 christos struct cond_stack_s *csp;
977 1.33 christos
978 1.33 christos if ((csp = cond_stack) == NULL)
979 1.33 christos return -1;
980 1.1 cgd
981 1.33 christos c_cond = csp->c_cond;
982 1.33 christos cond_stack = csp->c_next;
983 1.33 christos free(csp);
984 1.35 christos return c_cond;
985 1.1 cgd }
986 1.1 cgd
987 1.1 cgd /*
988 1.1 cgd * Conditional commands. These allow one to parameterize one's
989 1.1 cgd * .mailrc and do some things if sending, others if receiving.
990 1.1 cgd */
991 1.33 christos static int
992 1.33 christos if_push(void)
993 1.33 christos {
994 1.33 christos push_cond(cond);
995 1.33 christos cond &= ~CELSE;
996 1.33 christos if ((cond & (CIF | CSKIP)) == (CIF | CSKIP)) {
997 1.33 christos cond |= CIGN;
998 1.33 christos return 1;
999 1.33 christos }
1000 1.33 christos return 0;
1001 1.33 christos }
1002 1.33 christos
1003 1.33 christos PUBLIC int
1004 1.12 wiz ifcmd(void *v)
1005 1.1 cgd {
1006 1.5 christos char **argv = v;
1007 1.33 christos char *keyword = argv[0];
1008 1.33 christos static const struct modetbl_s {
1009 1.33 christos const char *m_name;
1010 1.33 christos enum mailmode_e m_mode;
1011 1.33 christos } modetbl[] = {
1012 1.33 christos { "receiving", mm_receiving },
1013 1.33 christos { "sending", mm_sending },
1014 1.33 christos { "headersonly", mm_hdrsonly },
1015 1.33 christos { NULL, 0 },
1016 1.33 christos };
1017 1.33 christos const struct modetbl_s *mtp;
1018 1.33 christos
1019 1.33 christos if (if_push())
1020 1.33 christos return 0;
1021 1.1 cgd
1022 1.33 christos cond = CIF;
1023 1.33 christos for (mtp = modetbl; mtp->m_name; mtp++)
1024 1.33 christos if (strcasecmp(keyword, mtp->m_name) == 0)
1025 1.33 christos break;
1026 1.33 christos
1027 1.33 christos if (mtp->m_name == NULL) {
1028 1.33 christos cond = CNONE;
1029 1.33 christos (void)printf("Unrecognized if-keyword: \"%s\"\n", keyword);
1030 1.33 christos return 1;
1031 1.1 cgd }
1032 1.33 christos if (mtp->m_mode != mailmode)
1033 1.33 christos cond |= CSKIP;
1034 1.33 christos
1035 1.33 christos return 0;
1036 1.33 christos }
1037 1.33 christos
1038 1.33 christos PUBLIC int
1039 1.33 christos ifdefcmd(void *v)
1040 1.33 christos {
1041 1.33 christos char **argv = v;
1042 1.33 christos
1043 1.33 christos if (if_push())
1044 1.33 christos return 0;
1045 1.33 christos
1046 1.33 christos cond = CIF;
1047 1.33 christos if (value(argv[0]) == NULL)
1048 1.33 christos cond |= CSKIP;
1049 1.33 christos
1050 1.33 christos return 0;
1051 1.33 christos }
1052 1.33 christos
1053 1.33 christos PUBLIC int
1054 1.33 christos ifndefcmd(void *v)
1055 1.33 christos {
1056 1.33 christos int rval;
1057 1.33 christos rval = ifdefcmd(v);
1058 1.33 christos cond ^= CSKIP;
1059 1.33 christos return rval;
1060 1.1 cgd }
1061 1.1 cgd
1062 1.1 cgd /*
1063 1.1 cgd * Implement 'else'. This is pretty simple -- we just
1064 1.1 cgd * flip over the conditional flag.
1065 1.1 cgd */
1066 1.27 christos /*ARGSUSED*/
1067 1.33 christos PUBLIC int
1068 1.31 christos elsecmd(void *v __unused)
1069 1.1 cgd {
1070 1.33 christos if (cond_stack == NULL || (cond & (CIF | CELSE)) != CIF) {
1071 1.33 christos (void)printf("\"else\" without matching \"if\"\n");
1072 1.33 christos cond = CNONE;
1073 1.33 christos return 1;
1074 1.33 christos }
1075 1.33 christos if ((cond & CIGN) == 0) {
1076 1.33 christos cond ^= CSKIP;
1077 1.33 christos cond |= CELSE;
1078 1.1 cgd }
1079 1.33 christos return 0;
1080 1.1 cgd }
1081 1.1 cgd
1082 1.1 cgd /*
1083 1.1 cgd * End of if statement. Just set cond back to anything.
1084 1.1 cgd */
1085 1.27 christos /*ARGSUSED*/
1086 1.33 christos PUBLIC int
1087 1.31 christos endifcmd(void *v __unused)
1088 1.1 cgd {
1089 1.33 christos if (cond_stack == NULL || (cond & CIF) != CIF) {
1090 1.33 christos (void)printf("\"endif\" without matching \"if\"\n");
1091 1.33 christos cond = CNONE;
1092 1.33 christos return 1;
1093 1.1 cgd }
1094 1.33 christos cond = pop_cond();
1095 1.33 christos return 0;
1096 1.1 cgd }
1097 1.1 cgd
1098 1.1 cgd /*
1099 1.1 cgd * Set the list of alternate names.
1100 1.1 cgd */
1101 1.33 christos PUBLIC int
1102 1.12 wiz alternates(void *v)
1103 1.1 cgd {
1104 1.5 christos char **namelist = v;
1105 1.36 christos size_t c;
1106 1.9 lukem char **ap, **ap2, *cp;
1107 1.1 cgd
1108 1.1 cgd c = argcount(namelist) + 1;
1109 1.1 cgd if (c == 1) {
1110 1.1 cgd if (altnames == 0)
1111 1.33 christos return 0;
1112 1.1 cgd for (ap = altnames; *ap; ap++)
1113 1.27 christos (void)printf("%s ", *ap);
1114 1.27 christos (void)printf("\n");
1115 1.33 christos return 0;
1116 1.1 cgd }
1117 1.1 cgd if (altnames != 0)
1118 1.27 christos free(altnames);
1119 1.36 christos altnames = ecalloc(c, sizeof(char *));
1120 1.1 cgd for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
1121 1.36 christos cp = ecalloc(strlen(*ap) + 1, sizeof(char));
1122 1.27 christos (void)strcpy(cp, *ap);
1123 1.1 cgd *ap2 = cp;
1124 1.1 cgd }
1125 1.1 cgd *ap2 = 0;
1126 1.33 christos return 0;
1127 1.1 cgd }
1128