1 /* $NetBSD: cmd2.c,v 1.21 2006/10/31 20:07:32 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)cmd2.c 8.1 (Berkeley) 6/6/93"; 36 #else 37 __RCSID("$NetBSD: cmd2.c,v 1.21 2006/10/31 20:07:32 christos Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include "rcv.h" 42 #include <util.h> 43 #include "extern.h" 44 45 /* 46 * Mail -- a mail program 47 * 48 * More user commands. 49 */ 50 static int igcomp(const void *, const void *); 51 52 /* 53 * If any arguments were given, go to the next applicable argument 54 * following dot, otherwise, go to the next applicable message. 55 * If given as first command with no arguments, print first message. 56 */ 57 int 58 next(void *v) 59 { 60 int *msgvec = v; 61 struct message *mp; 62 int *ip, *ip2; 63 int list[2], mdot; 64 65 if (*msgvec != 0) { 66 67 /* 68 * If some messages were supplied, find the 69 * first applicable one following dot using 70 * wrap around. 71 */ 72 73 mdot = dot - &message[0] + 1; 74 75 /* 76 * Find the first message in the supplied 77 * message list which follows dot. 78 */ 79 80 for (ip = msgvec; *ip != 0; ip++) 81 if (*ip > mdot) 82 break; 83 if (*ip == 0) 84 ip = msgvec; 85 ip2 = ip; 86 do { 87 mp = &message[*ip2 - 1]; 88 if ((mp->m_flag & MDELETED) == 0) { 89 dot = mp; 90 goto hitit; 91 } 92 if (*ip2 != 0) 93 ip2++; 94 if (*ip2 == 0) 95 ip2 = msgvec; 96 } while (ip2 != ip); 97 (void)printf("No messages applicable\n"); 98 return(1); 99 } 100 101 /* 102 * If this is the first command, select message 1. 103 * Note that this must exist for us to get here at all. 104 */ 105 106 if (!sawcom) 107 goto hitit; 108 109 /* 110 * Just find the next good message after dot, no 111 * wraparound. 112 */ 113 114 for (mp = dot + 1; mp < &message[msgCount]; mp++) 115 if ((mp->m_flag & (MDELETED|MSAVED)) == 0) 116 break; 117 if (mp >= &message[msgCount]) { 118 (void)printf("At EOF\n"); 119 return(0); 120 } 121 dot = mp; 122 hitit: 123 /* 124 * Print dot. 125 */ 126 127 list[0] = dot - &message[0] + 1; 128 list[1] = 0; 129 return(type(list)); 130 } 131 132 /* 133 * Save a message in a file. Mark the message as saved 134 * so we can discard when the user quits. 135 */ 136 int 137 save(void *v) 138 { 139 char *str = v; 140 141 return save1(str, 1, "save", saveignore); 142 } 143 144 /* 145 * Save a message in a file. Mark the message as saved 146 * so we can discard when the user quits. Save all fields 147 * overriding saveignore and saveretain. 148 */ 149 int 150 Save(void *v) 151 { 152 char *str = v; 153 154 return save1(str, 1, "Save", NULL); 155 } 156 157 /* 158 * Copy a message to a file without affected its saved-ness 159 */ 160 int 161 copycmd(void *v) 162 { 163 char *str = v; 164 165 return save1(str, 0, "copy", saveignore); 166 } 167 168 /* 169 * Save/copy the indicated messages at the end of the passed file name. 170 * If markmsg is true, mark the message "saved." 171 */ 172 int 173 save1(char str[], int markmsg, const char *cmd, struct ignoretab *ignoretabs) 174 { 175 int *ip; 176 struct message *mp; 177 const char *fn; 178 const char *disp; 179 int f, *msgvec; 180 FILE *obuf; 181 182 msgvec = salloc((msgCount + 2) * sizeof *msgvec); 183 if ((fn = snarf(str, &f)) == NULL) 184 return(1); 185 if (!f) { 186 *msgvec = first(0, MMNORM); 187 if (*msgvec == 0) { 188 (void)printf("No messages to %s.\n", cmd); 189 return(1); 190 } 191 msgvec[1] = 0; 192 } 193 if (f && getmsglist(str, msgvec, 0) < 0) 194 return(1); 195 if ((fn = expand(fn)) == NULL) 196 return(1); 197 (void)printf("\"%s\" ", fn); 198 (void)fflush(stdout); 199 if (access(fn, 0) >= 0) 200 disp = "[Appended]"; 201 else 202 disp = "[New file]"; 203 if ((obuf = Fopen(fn, "a")) == NULL) { 204 warn(NULL); 205 return(1); 206 } 207 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 208 mp = &message[*ip - 1]; 209 touch(mp); 210 #ifdef MIME_SUPPORT 211 if (sendmessage(mp, obuf, ignoretabs, NULL, NULL) < 0) { 212 #else 213 if (sendmessage(mp, obuf, ignoretabs, NULL) < 0) { 214 #endif 215 warn("%s", fn); 216 (void)Fclose(obuf); 217 return(1); 218 } 219 if (markmsg) 220 mp->m_flag |= MSAVED; 221 } 222 (void)fflush(obuf); 223 if (ferror(obuf)) 224 warn("%s", fn); 225 (void)Fclose(obuf); 226 (void)printf("%s\n", disp); 227 return(0); 228 } 229 230 /* 231 * Write the indicated messages at the end of the passed 232 * file name, minus header and trailing blank line. 233 */ 234 int 235 swrite(void *v) 236 { 237 char *str = v; 238 239 return save1(str, 1, "write", ignoreall); 240 } 241 242 /* 243 * Snarf the file from the end of the command line and 244 * return a pointer to it. If there is no file attached, 245 * just return NULL. Put a null in front of the file 246 * name so that the message list processing won't see it, 247 * unless the file name is the only thing on the line, in 248 * which case, return 0 in the reference flag variable. 249 */ 250 251 char * 252 snarf(char linebuf[], int *flag) 253 { 254 char *cp; 255 256 *flag = 1; 257 cp = strlen(linebuf) + linebuf - 1; 258 259 /* 260 * Strip away trailing blanks. 261 */ 262 263 while (cp > linebuf && isspace((unsigned char)*cp)) 264 cp--; 265 *++cp = 0; 266 267 /* 268 * Now search for the beginning of the file name. 269 */ 270 271 while (cp > linebuf && !isspace((unsigned char)*cp)) 272 cp--; 273 if (*cp == '\0') { 274 (void)printf("No file specified.\n"); 275 return(NULL); 276 } 277 if (isspace((unsigned char)*cp)) 278 *cp++ = 0; 279 else 280 *flag = 0; 281 return(cp); 282 } 283 284 /* 285 * Delete messages. 286 */ 287 int 288 delete(void *v) 289 { 290 int *msgvec = v; 291 (void)delm(msgvec); 292 return 0; 293 } 294 295 /* 296 * Delete messages, then type the new dot. 297 */ 298 int 299 deltype(void *v) 300 { 301 int *msgvec = v; 302 int list[2]; 303 int lastdot; 304 305 lastdot = dot - &message[0] + 1; 306 if (delm(msgvec) >= 0) { 307 list[0] = dot - &message[0] + 1; 308 if (list[0] > lastdot) { 309 touch(dot); 310 list[1] = 0; 311 return(type(list)); 312 } 313 (void)printf("At EOF\n"); 314 } else 315 (void)printf("No more messages\n"); 316 return(0); 317 } 318 319 /* 320 * Delete the indicated messages. 321 * Set dot to some nice place afterwards. 322 * Internal interface. 323 */ 324 int 325 delm(int *msgvec) 326 { 327 struct message *mp; 328 int *ip; 329 int last; 330 331 last = 0; 332 for (ip = msgvec; *ip != 0; ip++) { 333 mp = &message[*ip - 1]; 334 touch(mp); 335 mp->m_flag |= MDELETED|MTOUCH; 336 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); 337 last = *ip; 338 } 339 if (last != 0) { 340 dot = &message[last - 1]; 341 last = first(0, MDELETED); 342 if (last != 0) { 343 dot = &message[last - 1]; 344 return(0); 345 } 346 else { 347 dot = &message[0]; 348 return(-1); 349 } 350 } 351 352 /* 353 * Following can't happen -- it keeps lint happy 354 */ 355 356 return(-1); 357 } 358 359 /* 360 * Undelete the indicated messages. 361 */ 362 int 363 undeletecmd(void *v) 364 { 365 int *msgvec = v; 366 struct message *mp; 367 int *ip; 368 369 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 370 mp = &message[*ip - 1]; 371 touch(mp); 372 dot = mp; 373 mp->m_flag &= ~MDELETED; 374 } 375 return 0; 376 } 377 378 /* 379 * Interactively dump core on "core" 380 */ 381 int 382 /*ARGSUSED*/ 383 core(void *v __unused) 384 { 385 int pid; 386 387 switch (pid = vfork()) { 388 case -1: 389 warn("fork"); 390 return(1); 391 case 0: 392 abort(); 393 _exit(1); 394 } 395 (void)printf("Okie dokie"); 396 (void)fflush(stdout); 397 (void)wait_child(pid); 398 if (WCOREDUMP(wait_status)) 399 (void)printf(" -- Core dumped.\n"); 400 else 401 (void)printf(" -- Can't dump core.\n"); 402 return 0; 403 } 404 405 /* 406 * Clobber as many bytes of stack as the user requests. 407 */ 408 int 409 clobber(void *v) 410 { 411 char **argv = v; 412 int times; 413 414 if (argv[0] == 0) 415 times = 1; 416 else 417 times = (atoi(argv[0]) + 511) / 512; 418 clob1(times); 419 return 0; 420 } 421 422 /* 423 * Clobber the stack. 424 */ 425 void 426 clob1(int n) 427 { 428 char buf[512]; 429 char *cp; 430 431 if (n <= 0) 432 return; 433 for (cp = buf; cp < &buf[512]; *cp++ = (char)0xFF) 434 ; 435 clob1(n - 1); 436 } 437 438 /* 439 * Add the given header fields to the retained list. 440 * If no arguments, print the current list of retained fields. 441 */ 442 int 443 retfield(void *v) 444 { 445 char **list = v; 446 447 return ignore1(list, ignore + 1, "retained"); 448 } 449 450 /* 451 * Add the given header fields to the ignored list. 452 * If no arguments, print the current list of ignored fields. 453 */ 454 int 455 igfield(void *v) 456 { 457 char **list = v; 458 459 return ignore1(list, ignore, "ignored"); 460 } 461 462 int 463 saveretfield(void *v) 464 { 465 char **list = v; 466 467 return ignore1(list, saveignore + 1, "retained"); 468 } 469 470 int 471 saveigfield(void *v) 472 { 473 char **list = v; 474 475 return ignore1(list, saveignore, "ignored"); 476 } 477 478 int 479 ignore1(char *list[], struct ignoretab *tab, const char *which) 480 { 481 char field[LINESIZE]; 482 int h; 483 struct ignore *igp; 484 char **ap; 485 486 if (*list == NULL) 487 return igshow(tab, which); 488 for (ap = list; *ap != 0; ap++) { 489 istrcpy(field, *ap); 490 if (member(field, tab)) 491 continue; 492 h = hash(field); 493 igp = (struct ignore *) ecalloc(1, sizeof (struct ignore)); 494 igp->i_field = ecalloc((unsigned) strlen(field) + 1, 495 sizeof (char)); 496 (void)strcpy(igp->i_field, field); 497 igp->i_link = tab->i_head[h]; 498 tab->i_head[h] = igp; 499 tab->i_count++; 500 } 501 return 0; 502 } 503 504 /* 505 * Print out all currently retained fields. 506 */ 507 int 508 igshow(struct ignoretab *tab, const char *which) 509 { 510 int h; 511 struct ignore *igp; 512 char **ap, **ring; 513 514 if (tab->i_count == 0) { 515 (void)printf("No fields currently being %s.\n", which); 516 return 0; 517 } 518 ring = salloc((tab->i_count + 1) * sizeof (char *)); 519 ap = ring; 520 for (h = 0; h < HSHSIZE; h++) 521 for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link) 522 *ap++ = igp->i_field; 523 *ap = 0; 524 qsort(ring, tab->i_count, sizeof (char *), igcomp); 525 for (ap = ring; *ap != 0; ap++) 526 (void)printf("%s\n", *ap); 527 return 0; 528 } 529 530 /* 531 * Compare two names for sorting ignored field list. 532 */ 533 static int 534 igcomp(const void *l, const void *r) 535 { 536 return (strcmp(*(const char *const *)l, *(const char *const *)r)); 537 } 538