1 /* $NetBSD: cmd2.c,v 1.11 2001/02/05 02:07:52 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. 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.11 2001/02/05 02:07:52 christos 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 extern int wait_status; 54 static int igcomp __P((const void *, const void *)); 55 56 /* 57 * If any arguments were given, go to the next applicable argument 58 * following dot, otherwise, go to the next applicable message. 59 * If given as first command with no arguments, print first message. 60 */ 61 int 62 next(v) 63 void *v; 64 { 65 int *msgvec = v; 66 struct message *mp; 67 int *ip, *ip2; 68 int list[2], mdot; 69 70 if (*msgvec != 0) { 71 72 /* 73 * If some messages were supplied, find the 74 * first applicable one following dot using 75 * wrap around. 76 */ 77 78 mdot = dot - &message[0] + 1; 79 80 /* 81 * Find the first message in the supplied 82 * message list which follows dot. 83 */ 84 85 for (ip = msgvec; *ip != 0; ip++) 86 if (*ip > mdot) 87 break; 88 if (*ip == 0) 89 ip = msgvec; 90 ip2 = ip; 91 do { 92 mp = &message[*ip2 - 1]; 93 if ((mp->m_flag & MDELETED) == 0) { 94 dot = mp; 95 goto hitit; 96 } 97 if (*ip2 != 0) 98 ip2++; 99 if (*ip2 == 0) 100 ip2 = msgvec; 101 } while (ip2 != ip); 102 printf("No messages applicable\n"); 103 return(1); 104 } 105 106 /* 107 * If this is the first command, select message 1. 108 * Note that this must exist for us to get here at all. 109 */ 110 111 if (!sawcom) 112 goto hitit; 113 114 /* 115 * Just find the next good message after dot, no 116 * wraparound. 117 */ 118 119 for (mp = dot+1; mp < &message[msgCount]; mp++) 120 if ((mp->m_flag & (MDELETED|MSAVED)) == 0) 121 break; 122 if (mp >= &message[msgCount]) { 123 printf("At EOF\n"); 124 return(0); 125 } 126 dot = mp; 127 hitit: 128 /* 129 * Print dot. 130 */ 131 132 list[0] = dot - &message[0] + 1; 133 list[1] = 0; 134 return(type(list)); 135 } 136 137 /* 138 * Save a message in a file. Mark the message as saved 139 * so we can discard when the user quits. 140 */ 141 int 142 save(v) 143 void *v; 144 { 145 char *str = v; 146 147 return save1(str, 1, "save", saveignore); 148 } 149 150 /* 151 * Copy a message to a file without affected its saved-ness 152 */ 153 int 154 copycmd(v) 155 void *v; 156 { 157 char *str = v; 158 159 return save1(str, 0, "copy", saveignore); 160 } 161 162 /* 163 * Save/copy the indicated messages at the end of the passed file name. 164 * If mark is true, mark the message "saved." 165 */ 166 int 167 save1(str, mark, cmd, ignore) 168 char str[]; 169 int mark; 170 char *cmd; 171 struct ignoretab *ignore; 172 { 173 int *ip; 174 struct message *mp; 175 char *file, *disp; 176 int f, *msgvec; 177 FILE *obuf; 178 179 msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec); 180 if ((file = snarf(str, &f)) == NOSTR) 181 return(1); 182 if (!f) { 183 *msgvec = first(0, MMNORM); 184 if (*msgvec == 0) { 185 printf("No messages to %s.\n", cmd); 186 return(1); 187 } 188 msgvec[1] = 0; 189 } 190 if (f && getmsglist(str, msgvec, 0) < 0) 191 return(1); 192 if ((file = expand(file)) == NOSTR) 193 return(1); 194 printf("\"%s\" ", file); 195 fflush(stdout); 196 if (access(file, 0) >= 0) 197 disp = "[Appended]"; 198 else 199 disp = "[New file]"; 200 if ((obuf = Fopen(file, "a")) == NULL) { 201 perror(NOSTR); 202 return(1); 203 } 204 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 205 mp = &message[*ip - 1]; 206 touch(mp); 207 if (sendmessage(mp, obuf, ignore, NOSTR) < 0) { 208 perror(file); 209 Fclose(obuf); 210 return(1); 211 } 212 if (mark) 213 mp->m_flag |= MSAVED; 214 } 215 fflush(obuf); 216 if (ferror(obuf)) 217 perror(file); 218 Fclose(obuf); 219 printf("%s\n", disp); 220 return(0); 221 } 222 223 /* 224 * Write the indicated messages at the end of the passed 225 * file name, minus header and trailing blank line. 226 */ 227 int 228 swrite(v) 229 void *v; 230 { 231 char *str = v; 232 233 return save1(str, 1, "write", ignoreall); 234 } 235 236 /* 237 * Snarf the file from the end of the command line and 238 * return a pointer to it. If there is no file attached, 239 * just return NOSTR. Put a null in front of the file 240 * name so that the message list processing won't see it, 241 * unless the file name is the only thing on the line, in 242 * which case, return 0 in the reference flag variable. 243 */ 244 245 char * 246 snarf(linebuf, flag) 247 char linebuf[]; 248 int *flag; 249 { 250 char *cp; 251 252 *flag = 1; 253 cp = strlen(linebuf) + linebuf - 1; 254 255 /* 256 * Strip away trailing blanks. 257 */ 258 259 while (cp > linebuf && isspace((unsigned char)*cp)) 260 cp--; 261 *++cp = 0; 262 263 /* 264 * Now search for the beginning of the file name. 265 */ 266 267 while (cp > linebuf && !isspace((unsigned char)*cp)) 268 cp--; 269 if (*cp == '\0') { 270 printf("No file specified.\n"); 271 return(NOSTR); 272 } 273 if (isspace((unsigned char)*cp)) 274 *cp++ = 0; 275 else 276 *flag = 0; 277 return(cp); 278 } 279 280 /* 281 * Delete messages. 282 */ 283 int 284 delete(v) 285 void *v; 286 { 287 int *msgvec = v; 288 delm(msgvec); 289 return 0; 290 } 291 292 /* 293 * Delete messages, then type the new dot. 294 */ 295 int 296 deltype(v) 297 void *v; 298 { 299 int *msgvec = v; 300 int list[2]; 301 int lastdot; 302 303 lastdot = dot - &message[0] + 1; 304 if (delm(msgvec) >= 0) { 305 list[0] = dot - &message[0] + 1; 306 if (list[0] > lastdot) { 307 touch(dot); 308 list[1] = 0; 309 return(type(list)); 310 } 311 printf("At EOF\n"); 312 } else 313 printf("No more messages\n"); 314 return(0); 315 } 316 317 /* 318 * Delete the indicated messages. 319 * Set dot to some nice place afterwards. 320 * Internal interface. 321 */ 322 int 323 delm(msgvec) 324 int *msgvec; 325 { 326 struct message *mp; 327 int *ip; 328 int last; 329 330 last = 0; 331 for (ip = msgvec; *ip != 0; ip++) { 332 mp = &message[*ip - 1]; 333 touch(mp); 334 mp->m_flag |= MDELETED|MTOUCH; 335 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); 336 last = *ip; 337 } 338 if (last != 0) { 339 dot = &message[last-1]; 340 last = first(0, MDELETED); 341 if (last != 0) { 342 dot = &message[last-1]; 343 return(0); 344 } 345 else { 346 dot = &message[0]; 347 return(-1); 348 } 349 } 350 351 /* 352 * Following can't happen -- it keeps lint happy 353 */ 354 355 return(-1); 356 } 357 358 /* 359 * Undelete the indicated messages. 360 */ 361 int 362 undeletecmd(v) 363 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 core(v) 383 void *v; 384 { 385 int pid; 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 (WCOREDUMP(wait_status)) 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