1 /* $OpenBSD: cmd2.c,v 1.8 1997/11/14 00:23:43 millert Exp $ */ 2 /* $NetBSD: cmd2.c,v 1.7 1997/05/17 19:55:10 pk Exp $ */ 3 4 /* 5 * Copyright (c) 1980, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)cmd2.c 8.1 (Berkeley) 6/6/93"; 40 #else 41 static char rcsid[] = "$OpenBSD: cmd2.c,v 1.8 1997/11/14 00:23:43 millert Exp $"; 42 #endif 43 #endif /* not lint */ 44 45 #include "rcv.h" 46 #include <sys/wait.h> 47 #include "extern.h" 48 49 /* 50 * Mail -- a mail program 51 * 52 * More user commands. 53 */ 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 struct message *mp; 66 int *msgvec = v; 67 int *ip, *ip2, list[2], mdot; 68 69 if (*msgvec != NULL) { 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 != NULL; ip++) 85 if (*ip > mdot) 86 break; 87 if (*ip == NULL) 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 != NULL) 97 ip2++; 98 if (*ip2 == NULL) 99 ip2 = msgvec; 100 } while (ip2 != ip); 101 puts("No messages applicable"); 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 puts("At EOF"); 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] = NULL; 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 struct message *mp; 173 char *file, *disp; 174 int f, *msgvec, *ip; 175 FILE *obuf; 176 177 msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec)); 178 if ((file = snarf(str, &f)) == NULL) 179 return(1); 180 if (!f) { 181 *msgvec = first(0, MMNORM); 182 if (*msgvec == NULL) { 183 printf("No messages to %s.\n", cmd); 184 return(1); 185 } 186 msgvec[1] = NULL; 187 } 188 if (f && getmsglist(str, msgvec, 0) < 0) 189 return(1); 190 if ((file = expand(file)) == NULL) 191 return(1); 192 printf("\"%s\" ", file); 193 fflush(stdout); 194 if (access(file, 0) >= 0) 195 disp = "[Appended]"; 196 else 197 disp = "[New file]"; 198 if ((obuf = Fopen(file, "a")) == NULL) { 199 warn(NULL); 200 return(1); 201 } 202 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 203 mp = &message[*ip - 1]; 204 touch(mp); 205 if (send(mp, obuf, ignore, NULL) < 0) { 206 warn("%s", file); 207 (void)Fclose(obuf); 208 return(1); 209 } 210 if (mark) 211 mp->m_flag |= MSAVED; 212 } 213 fflush(obuf); 214 if (ferror(obuf)) 215 warn("%s", file); 216 (void)Fclose(obuf); 217 printf("%s\n", disp); 218 return(0); 219 } 220 221 /* 222 * Write the indicated messages at the end of the passed 223 * file name, minus header and trailing blank line. 224 */ 225 int 226 swrite(v) 227 void *v; 228 { 229 char *str = v; 230 231 return(save1(str, 1, "write", ignoreall)); 232 } 233 234 /* 235 * Snarf the file from the end of the command line and 236 * return a pointer to it. If there is no file attached, 237 * just return NULL. Put a null in front of the file 238 * name so that the message list processing won't see it, 239 * unless the file name is the only thing on the line, in 240 * which case, return 0 in the reference flag variable. 241 */ 242 243 char * 244 snarf(linebuf, flag) 245 char linebuf[]; 246 int *flag; 247 { 248 char *cp; 249 250 *flag = 1; 251 cp = strlen(linebuf) + linebuf - 1; 252 253 /* 254 * Strip away trailing blanks. 255 */ 256 257 while (cp > linebuf && isspace(*cp)) 258 cp--; 259 *++cp = 0; 260 261 /* 262 * Now search for the beginning of the file name. 263 */ 264 265 while (cp > linebuf && !isspace(*cp)) 266 cp--; 267 if (*cp == '\0') { 268 puts("No file specified."); 269 return(NULL); 270 } 271 if (isspace(*cp)) 272 *cp++ = 0; 273 else 274 *flag = 0; 275 return(cp); 276 } 277 278 /* 279 * Delete messages. 280 */ 281 int 282 delete(v) 283 void *v; 284 { 285 int *msgvec = v; 286 delm(msgvec); 287 return(0); 288 } 289 290 /* 291 * Delete messages, then type the new dot. 292 */ 293 int 294 deltype(v) 295 void *v; 296 { 297 int *msgvec = v; 298 int list[2]; 299 int lastdot; 300 301 lastdot = dot - &message[0] + 1; 302 if (delm(msgvec) >= 0) { 303 list[0] = dot - &message[0] + 1; 304 if (list[0] > lastdot) { 305 touch(dot); 306 list[1] = NULL; 307 return(type(list)); 308 } 309 puts("At EOF"); 310 } else 311 puts("No more messages"); 312 return(0); 313 } 314 315 /* 316 * Delete the indicated messages. 317 * Set dot to some nice place afterwards. 318 * Internal interface. 319 */ 320 int 321 delm(msgvec) 322 int *msgvec; 323 { 324 struct message *mp; 325 int *ip, last; 326 327 last = NULL; 328 for (ip = msgvec; *ip != NULL; ip++) { 329 mp = &message[*ip - 1]; 330 touch(mp); 331 mp->m_flag |= MDELETED|MTOUCH; 332 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); 333 last = *ip; 334 } 335 if (last != NULL) { 336 dot = &message[last-1]; 337 last = first(0, MDELETED); 338 if (last != NULL) { 339 dot = &message[last-1]; 340 return(0); 341 } 342 else { 343 dot = &message[0]; 344 return(-1); 345 } 346 } 347 348 /* 349 * Following can't happen -- it keeps lint happy 350 */ 351 352 return(-1); 353 } 354 355 /* 356 * Undelete the indicated messages. 357 */ 358 int 359 undeletecmd(v) 360 void *v; 361 { 362 int *msgvec = v; 363 int *ip; 364 struct message *mp; 365 366 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 367 mp = &message[*ip - 1]; 368 touch(mp); 369 dot = mp; 370 mp->m_flag &= ~MDELETED; 371 } 372 return(0); 373 } 374 375 /* 376 * Interactively dump core on "core" 377 */ 378 int 379 core(v) 380 void *v; 381 { 382 int pid; 383 extern int wait_status; 384 385 switch (pid = vfork()) { 386 case -1: 387 warn("vfork"); 388 return(1); 389 case 0: 390 abort(); 391 _exit(1); 392 } 393 fputs("Okie dokie", stdout); 394 fflush(stdout); 395 wait_child(pid); 396 if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status)) 397 puts(" -- Core dumped."); 398 else 399 puts(" -- Can't dump core."); 400 return(0); 401 } 402 403 /* 404 * Clobber as many bytes of stack as the user requests. 405 */ 406 int 407 clobber(v) 408 void *v; 409 { 410 char **argv = v; 411 int times; 412 413 if (argv[0] == 0) 414 times = 1; 415 else 416 times = (atoi(argv[0]) + 511) / 512; 417 clob1(times); 418 return(0); 419 } 420 421 /* 422 * Clobber the stack. 423 */ 424 void 425 clob1(n) 426 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++ = 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(v) 444 void *v; 445 { 446 char **list = v; 447 448 return(ignore1(list, ignore + 1, "retained")); 449 } 450 451 /* 452 * Add the given header fields to the ignored list. 453 * If no arguments, print the current list of ignored fields. 454 */ 455 int 456 igfield(v) 457 void *v; 458 { 459 char **list = v; 460 461 return(ignore1(list, ignore, "ignored")); 462 } 463 464 int 465 saveretfield(v) 466 void *v; 467 { 468 char **list = v; 469 470 return(ignore1(list, saveignore + 1, "retained")); 471 } 472 473 int 474 saveigfield(v) 475 void *v; 476 { 477 char **list = v; 478 479 return(ignore1(list, saveignore, "ignored")); 480 } 481 482 int 483 ignore1(list, tab, which) 484 char *list[]; 485 struct ignoretab *tab; 486 char *which; 487 { 488 char field[LINESIZE]; 489 char **ap; 490 struct ignore *igp; 491 int h; 492 493 if (*list == NULL) 494 return(igshow(tab, which)); 495 for (ap = list; *ap != 0; ap++) { 496 istrncpy(field, *ap, sizeof(field)); 497 if (member(field, tab)) 498 continue; 499 h = hash(field); 500 igp = (struct ignore *)calloc(1, sizeof(struct ignore)); 501 igp->i_field = (char *)calloc(strlen(field) + 1, sizeof(char)); 502 strcpy(igp->i_field, field); 503 igp->i_link = tab->i_head[h]; 504 tab->i_head[h] = igp; 505 tab->i_count++; 506 } 507 return(0); 508 } 509 510 /* 511 * Print out all currently retained fields. 512 */ 513 int 514 igshow(tab, which) 515 struct ignoretab *tab; 516 char *which; 517 { 518 int h; 519 struct ignore *igp; 520 char **ap, **ring; 521 522 if (tab->i_count == 0) { 523 printf("No fields currently being %s.\n", which); 524 return(0); 525 } 526 ring = (char **)salloc((tab->i_count + 1) * sizeof(char *)); 527 ap = ring; 528 for (h = 0; h < HSHSIZE; h++) 529 for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link) 530 *ap++ = igp->i_field; 531 *ap = 0; 532 qsort(ring, tab->i_count, sizeof(char *), igcomp); 533 for (ap = ring; *ap != 0; ap++) 534 puts(*ap); 535 return(0); 536 } 537 538 /* 539 * Compare two names for sorting ignored field list. 540 */ 541 static int 542 igcomp(l, r) 543 const void *l, *r; 544 { 545 return(strcmp(*(char **)l, *(char **)r)); 546 } 547