1 /* $NetBSD: cmd2.c,v 1.17 2003/08/07 11:14:36 agc 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.17 2003/08/07 11:14:36 agc Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include "rcv.h" 42 #include "extern.h" 43 44 /* 45 * Mail -- a mail program 46 * 47 * More user commands. 48 */ 49 extern int wait_status; 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 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 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(v) 151 void *v; 152 { 153 char *str = v; 154 155 return save1(str, 1, "Save", NULL); 156 } 157 158 /* 159 * Copy a message to a file without affected its saved-ness 160 */ 161 int 162 copycmd(void *v) 163 { 164 char *str = v; 165 166 return save1(str, 0, "copy", saveignore); 167 } 168 169 /* 170 * Save/copy the indicated messages at the end of the passed file name. 171 * If markmsg is true, mark the message "saved." 172 */ 173 int 174 save1(char str[], int markmsg, char *cmd, struct ignoretab *ignoretabs) 175 { 176 int *ip; 177 struct message *mp; 178 char *fn, *disp; 179 int f, *msgvec; 180 FILE *obuf; 181 182 msgvec = (int *) 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 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 printf("\"%s\" ", fn); 198 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 if (sendmessage(mp, obuf, ignoretabs, NULL) < 0) { 211 warn("%s", fn); 212 Fclose(obuf); 213 return(1); 214 } 215 if (markmsg) 216 mp->m_flag |= MSAVED; 217 } 218 fflush(obuf); 219 if (ferror(obuf)) 220 warn("%s", fn); 221 Fclose(obuf); 222 printf("%s\n", disp); 223 return(0); 224 } 225 226 /* 227 * Write the indicated messages at the end of the passed 228 * file name, minus header and trailing blank line. 229 */ 230 int 231 swrite(void *v) 232 { 233 char *str = v; 234 235 return save1(str, 1, "write", ignoreall); 236 } 237 238 /* 239 * Snarf the file from the end of the command line and 240 * return a pointer to it. If there is no file attached, 241 * just return NULL. Put a null in front of the file 242 * name so that the message list processing won't see it, 243 * unless the file name is the only thing on the line, in 244 * which case, return 0 in the reference flag variable. 245 */ 246 247 char * 248 snarf(char linebuf[], 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(NULL); 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(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(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] = 0; 307 return(type(list)); 308 } 309 printf("At EOF\n"); 310 } else 311 printf("No more messages\n"); 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(int *msgvec) 322 { 323 struct message *mp; 324 int *ip; 325 int last; 326 327 last = 0; 328 for (ip = msgvec; *ip != 0; 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 != 0) { 336 dot = &message[last-1]; 337 last = first(0, MDELETED); 338 if (last != 0) { 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(void *v) 360 { 361 int *msgvec = v; 362 struct message *mp; 363 int *ip; 364 365 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 366 mp = &message[*ip - 1]; 367 touch(mp); 368 dot = mp; 369 mp->m_flag &= ~MDELETED; 370 } 371 return 0; 372 } 373 374 /* 375 * Interactively dump core on "core" 376 */ 377 int 378 core(void *v) 379 { 380 int pid; 381 382 switch (pid = vfork()) { 383 case -1: 384 warn("fork"); 385 return(1); 386 case 0: 387 abort(); 388 _exit(1); 389 } 390 printf("Okie dokie"); 391 fflush(stdout); 392 wait_child(pid); 393 if (WCOREDUMP(wait_status)) 394 printf(" -- Core dumped.\n"); 395 else 396 printf(" -- Can't dump core.\n"); 397 return 0; 398 } 399 400 /* 401 * Clobber as many bytes of stack as the user requests. 402 */ 403 int 404 clobber(void *v) 405 { 406 char **argv = v; 407 int times; 408 409 if (argv[0] == 0) 410 times = 1; 411 else 412 times = (atoi(argv[0]) + 511) / 512; 413 clob1(times); 414 return 0; 415 } 416 417 /* 418 * Clobber the stack. 419 */ 420 void 421 clob1(int n) 422 { 423 char buf[512]; 424 char *cp; 425 426 if (n <= 0) 427 return; 428 for (cp = buf; cp < &buf[512]; *cp++ = 0xFF) 429 ; 430 clob1(n - 1); 431 } 432 433 /* 434 * Add the given header fields to the retained list. 435 * If no arguments, print the current list of retained fields. 436 */ 437 int 438 retfield(void *v) 439 { 440 char **list = v; 441 442 return ignore1(list, ignore + 1, "retained"); 443 } 444 445 /* 446 * Add the given header fields to the ignored list. 447 * If no arguments, print the current list of ignored fields. 448 */ 449 int 450 igfield(void *v) 451 { 452 char **list = v; 453 454 return ignore1(list, ignore, "ignored"); 455 } 456 457 int 458 saveretfield(void *v) 459 { 460 char **list = v; 461 462 return ignore1(list, saveignore + 1, "retained"); 463 } 464 465 int 466 saveigfield(void *v) 467 { 468 char **list = v; 469 470 return ignore1(list, saveignore, "ignored"); 471 } 472 473 int 474 ignore1(char *list[], struct ignoretab *tab, char *which) 475 { 476 char field[LINESIZE]; 477 int h; 478 struct ignore *igp; 479 char **ap; 480 481 if (*list == NULL) 482 return igshow(tab, which); 483 for (ap = list; *ap != 0; ap++) { 484 istrcpy(field, *ap); 485 if (member(field, tab)) 486 continue; 487 h = hash(field); 488 igp = (struct ignore *) calloc(1, sizeof (struct ignore)); 489 igp->i_field = calloc((unsigned) strlen(field) + 1, 490 sizeof (char)); 491 strcpy(igp->i_field, field); 492 igp->i_link = tab->i_head[h]; 493 tab->i_head[h] = igp; 494 tab->i_count++; 495 } 496 return 0; 497 } 498 499 /* 500 * Print out all currently retained fields. 501 */ 502 int 503 igshow(struct ignoretab *tab, char *which) 504 { 505 int h; 506 struct ignore *igp; 507 char **ap, **ring; 508 509 if (tab->i_count == 0) { 510 printf("No fields currently being %s.\n", which); 511 return 0; 512 } 513 ring = (char **) salloc((tab->i_count + 1) * sizeof (char *)); 514 ap = ring; 515 for (h = 0; h < HSHSIZE; h++) 516 for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link) 517 *ap++ = igp->i_field; 518 *ap = 0; 519 qsort(ring, tab->i_count, sizeof (char *), igcomp); 520 for (ap = ring; *ap != 0; ap++) 521 printf("%s\n", *ap); 522 return 0; 523 } 524 525 /* 526 * Compare two names for sorting ignored field list. 527 */ 528 static int 529 igcomp(const void *l, const void *r) 530 { 531 return (strcmp(*(char **)l, *(char **)r)); 532 } 533