1 1.44 christos /* $NetBSD: cmd3.c,v 1.44 2017/11/09 20:27:50 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.44 christos __RCSID("$NetBSD: cmd3.c,v 1.44 2017/11/09 20:27:50 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.44 christos (obuf = Fdopen(fd, "wef+")) == 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.43 christos smargs = unpack(NULL, 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