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