1 /* $NetBSD: cmd2.c,v 1.16 2002/03/29 15:10:02 ross 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.16 2002/03/29 15:10:02 ross 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(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(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(void *v) 142 { 143 char *str = v; 144 145 return save1(str, 1, "save", saveignore); 146 } 147 148 /* 149 * Save a message in a file. Mark the message as saved 150 * so we can discard when the user quits. Save all fields 151 * overriding saveignore and saveretain. 152 */ 153 int 154 Save(v) 155 void *v; 156 { 157 char *str = v; 158 159 return save1(str, 1, "Save", NULL); 160 } 161 162 /* 163 * Copy a message to a file without affected its saved-ness 164 */ 165 int 166 copycmd(void *v) 167 { 168 char *str = v; 169 170 return save1(str, 0, "copy", saveignore); 171 } 172 173 /* 174 * Save/copy the indicated messages at the end of the passed file name. 175 * If markmsg is true, mark the message "saved." 176 */ 177 int 178 save1(char str[], int markmsg, char *cmd, struct ignoretab *ignoretabs) 179 { 180 int *ip; 181 struct message *mp; 182 char *fn, *disp; 183 int f, *msgvec; 184 FILE *obuf; 185 186 msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec); 187 if ((fn = snarf(str, &f)) == NULL) 188 return(1); 189 if (!f) { 190 *msgvec = first(0, MMNORM); 191 if (*msgvec == 0) { 192 printf("No messages to %s.\n", cmd); 193 return(1); 194 } 195 msgvec[1] = 0; 196 } 197 if (f && getmsglist(str, msgvec, 0) < 0) 198 return(1); 199 if ((fn = expand(fn)) == NULL) 200 return(1); 201 printf("\"%s\" ", fn); 202 fflush(stdout); 203 if (access(fn, 0) >= 0) 204 disp = "[Appended]"; 205 else 206 disp = "[New file]"; 207 if ((obuf = Fopen(fn, "a")) == NULL) { 208 warn(NULL); 209 return(1); 210 } 211 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 212 mp = &message[*ip - 1]; 213 touch(mp); 214 if (sendmessage(mp, obuf, ignoretabs, NULL) < 0) { 215 warn("%s", fn); 216 Fclose(obuf); 217 return(1); 218 } 219 if (markmsg) 220 mp->m_flag |= MSAVED; 221 } 222 fflush(obuf); 223 if (ferror(obuf)) 224 warn("%s", fn); 225 Fclose(obuf); 226 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 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 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 printf("At EOF\n"); 314 } else 315 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 core(void *v) 383 { 384 int pid; 385 386 switch (pid = vfork()) { 387 case -1: 388 warn("fork"); 389 return(1); 390 case 0: 391 abort(); 392 _exit(1); 393 } 394 printf("Okie dokie"); 395 fflush(stdout); 396 wait_child(pid); 397 if (WCOREDUMP(wait_status)) 398 printf(" -- Core dumped.\n"); 399 else 400 printf(" -- Can't dump core.\n"); 401 return 0; 402 } 403 404 /* 405 * Clobber as many bytes of stack as the user requests. 406 */ 407 int 408 clobber(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(int n) 426 { 427 char buf[512]; 428 char *cp; 429 430 if (n <= 0) 431 return; 432 for (cp = buf; cp < &buf[512]; *cp++ = 0xFF) 433 ; 434 clob1(n - 1); 435 } 436 437 /* 438 * Add the given header fields to the retained list. 439 * If no arguments, print the current list of retained fields. 440 */ 441 int 442 retfield(void *v) 443 { 444 char **list = v; 445 446 return ignore1(list, ignore + 1, "retained"); 447 } 448 449 /* 450 * Add the given header fields to the ignored list. 451 * If no arguments, print the current list of ignored fields. 452 */ 453 int 454 igfield(void *v) 455 { 456 char **list = v; 457 458 return ignore1(list, ignore, "ignored"); 459 } 460 461 int 462 saveretfield(void *v) 463 { 464 char **list = v; 465 466 return ignore1(list, saveignore + 1, "retained"); 467 } 468 469 int 470 saveigfield(void *v) 471 { 472 char **list = v; 473 474 return ignore1(list, saveignore, "ignored"); 475 } 476 477 int 478 ignore1(char *list[], struct ignoretab *tab, char *which) 479 { 480 char field[LINESIZE]; 481 int h; 482 struct ignore *igp; 483 char **ap; 484 485 if (*list == NULL) 486 return igshow(tab, which); 487 for (ap = list; *ap != 0; ap++) { 488 istrcpy(field, *ap); 489 if (member(field, tab)) 490 continue; 491 h = hash(field); 492 igp = (struct ignore *) calloc(1, sizeof (struct ignore)); 493 igp->i_field = calloc((unsigned) strlen(field) + 1, 494 sizeof (char)); 495 strcpy(igp->i_field, field); 496 igp->i_link = tab->i_head[h]; 497 tab->i_head[h] = igp; 498 tab->i_count++; 499 } 500 return 0; 501 } 502 503 /* 504 * Print out all currently retained fields. 505 */ 506 int 507 igshow(struct ignoretab *tab, char *which) 508 { 509 int h; 510 struct ignore *igp; 511 char **ap, **ring; 512 513 if (tab->i_count == 0) { 514 printf("No fields currently being %s.\n", which); 515 return 0; 516 } 517 ring = (char **) salloc((tab->i_count + 1) * sizeof (char *)); 518 ap = ring; 519 for (h = 0; h < HSHSIZE; h++) 520 for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link) 521 *ap++ = igp->i_field; 522 *ap = 0; 523 qsort(ring, tab->i_count, sizeof (char *), igcomp); 524 for (ap = ring; *ap != 0; ap++) 525 printf("%s\n", *ap); 526 return 0; 527 } 528 529 /* 530 * Compare two names for sorting ignored field list. 531 */ 532 static int 533 igcomp(const void *l, const void *r) 534 { 535 return (strcmp(*(char **)l, *(char **)r)); 536 } 537