1 /* $OpenBSD: cmd2.c,v 1.11 2001/11/21 20:41:55 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 const char sccsid[] = "@(#)cmd2.c 8.1 (Berkeley) 6/6/93"; 40 #else 41 static const char rcsid[] = "$OpenBSD: cmd2.c,v 1.11 2001/11/21 20:41:55 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(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 struct message *mp; 65 int *msgvec = v; 66 int *ip, *ip2, list[2], mdot; 67 68 if (*msgvec != NULL) { 69 /* 70 * If some messages were supplied, find the 71 * first applicable one following dot using 72 * wrap around. 73 */ 74 mdot = dot - &message[0] + 1; 75 76 /* 77 * Find the first message in the supplied 78 * message list which follows dot. 79 */ 80 for (ip = msgvec; *ip != NULL; ip++) 81 if (*ip > mdot) 82 break; 83 if (*ip == NULL) 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 != NULL) 93 ip2++; 94 if (*ip2 == NULL) 95 ip2 = msgvec; 96 } while (ip2 != ip); 97 puts("No messages applicable"); 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 if (!sawcom) 106 goto hitit; 107 108 /* 109 * Just find the next good message after dot, no 110 * wraparound. 111 */ 112 for (mp = dot+1; mp < &message[msgCount]; mp++) 113 if ((mp->m_flag & (MDELETED|MSAVED)) == 0) 114 break; 115 if (mp >= &message[msgCount]) { 116 puts("At EOF"); 117 return(0); 118 } 119 dot = mp; 120 hitit: 121 /* 122 * Print dot. 123 */ 124 list[0] = dot - &message[0] + 1; 125 list[1] = NULL; 126 return(type(list)); 127 } 128 129 /* 130 * Save a message in a file. Mark the message as saved 131 * so we can discard when the user quits. 132 */ 133 int 134 save(void *v) 135 { 136 char *str = v; 137 138 return(save1(str, 1, "save", saveignore)); 139 } 140 141 /* 142 * Copy a message to a file without affected its saved-ness 143 */ 144 int 145 copycmd(void *v) 146 { 147 char *str = v; 148 149 return(save1(str, 0, "copy", saveignore)); 150 } 151 152 /* 153 * Save/copy the indicated messages at the end of the passed file name. 154 * If mark is true, mark the message "saved." 155 */ 156 int 157 save1(char *str, int mark, char *cmd, struct ignoretab *ignore) 158 { 159 struct message *mp; 160 char *file, *disp; 161 int f, *msgvec, *ip; 162 FILE *obuf; 163 164 msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec)); 165 if ((file = snarf(str, &f)) == NULL) 166 return(1); 167 if (!f) { 168 *msgvec = first(0, MMNORM); 169 if (*msgvec == NULL) { 170 printf("No messages to %s.\n", cmd); 171 return(1); 172 } 173 msgvec[1] = NULL; 174 } 175 if (f && getmsglist(str, msgvec, 0) < 0) 176 return(1); 177 if ((file = expand(file)) == NULL) 178 return(1); 179 printf("\"%s\" ", file); 180 fflush(stdout); 181 if (access(file, 0) >= 0) 182 disp = "[Appended]"; 183 else 184 disp = "[New file]"; 185 if ((obuf = Fopen(file, "a")) == NULL) { 186 warn(NULL); 187 return(1); 188 } 189 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 190 mp = &message[*ip - 1]; 191 touch(mp); 192 if (sendmessage(mp, obuf, ignore, NULL) < 0) { 193 warn("%s", file); 194 (void)Fclose(obuf); 195 return(1); 196 } 197 if (mark) 198 mp->m_flag |= MSAVED; 199 } 200 fflush(obuf); 201 if (ferror(obuf)) 202 warn("%s", file); 203 (void)Fclose(obuf); 204 printf("%s\n", disp); 205 return(0); 206 } 207 208 /* 209 * Write the indicated messages at the end of the passed 210 * file name, minus header and trailing blank line. 211 */ 212 int 213 swrite(void *v) 214 { 215 char *str = v; 216 217 return(save1(str, 1, "write", ignoreall)); 218 } 219 220 /* 221 * Snarf the file from the end of the command line and 222 * return a pointer to it. If there is no file attached, 223 * just return NULL. Put a null in front of the file 224 * name so that the message list processing won't see it, 225 * unless the file name is the only thing on the line, in 226 * which case, return 0 in the reference flag variable. 227 */ 228 char * 229 snarf(char *linebuf, int *flag) 230 { 231 char *cp; 232 233 *flag = 1; 234 cp = strlen(linebuf) + linebuf - 1; 235 236 /* 237 * Strip away trailing blanks. 238 */ 239 while (cp > linebuf && isspace(*cp)) 240 cp--; 241 *++cp = 0; 242 243 /* 244 * Now search for the beginning of the file name. 245 */ 246 while (cp > linebuf && !isspace(*cp)) 247 cp--; 248 if (*cp == '\0') { 249 puts("No file specified."); 250 return(NULL); 251 } 252 if (isspace(*cp)) 253 *cp++ = 0; 254 else 255 *flag = 0; 256 return(cp); 257 } 258 259 /* 260 * Delete messages. 261 */ 262 int 263 delete(void *v) 264 { 265 int *msgvec = v; 266 267 delm(msgvec); 268 return(0); 269 } 270 271 /* 272 * Delete messages, then type the new dot. 273 */ 274 int 275 deltype(void *v) 276 { 277 int *msgvec = v; 278 int list[2]; 279 int lastdot; 280 281 lastdot = dot - &message[0] + 1; 282 if (delm(msgvec) >= 0) { 283 list[0] = dot - &message[0] + 1; 284 if (list[0] > lastdot) { 285 touch(dot); 286 list[1] = NULL; 287 return(type(list)); 288 } 289 puts("At EOF"); 290 } else 291 puts("No more messages"); 292 return(0); 293 } 294 295 /* 296 * Delete the indicated messages. 297 * Set dot to some nice place afterwards. 298 * Internal interface. 299 */ 300 int 301 delm(int *msgvec) 302 { 303 struct message *mp; 304 int *ip, last; 305 306 last = NULL; 307 for (ip = msgvec; *ip != NULL; ip++) { 308 mp = &message[*ip - 1]; 309 touch(mp); 310 mp->m_flag |= MDELETED|MTOUCH; 311 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); 312 last = *ip; 313 } 314 if (last != NULL) { 315 dot = &message[last-1]; 316 last = first(0, MDELETED); 317 if (last != NULL) { 318 dot = &message[last-1]; 319 return(0); 320 } 321 else { 322 dot = &message[0]; 323 return(-1); 324 } 325 } 326 327 /* 328 * Following can't happen -- it keeps lint happy 329 */ 330 return(-1); 331 } 332 333 /* 334 * Undelete the indicated messages. 335 */ 336 int 337 undeletecmd(void *v) 338 { 339 int *msgvec = v; 340 int *ip; 341 struct message *mp; 342 343 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 344 mp = &message[*ip - 1]; 345 touch(mp); 346 dot = mp; 347 mp->m_flag &= ~MDELETED; 348 } 349 return(0); 350 } 351 352 /* 353 * Interactively dump core on "core" 354 */ 355 int 356 core(void *v) 357 { 358 pid_t pid; 359 extern int wait_status; 360 361 switch (pid = vfork()) { 362 case -1: 363 warn("vfork"); 364 return(1); 365 case 0: 366 abort(); 367 _exit(1); 368 } 369 fputs("Okie dokie", stdout); 370 fflush(stdout); 371 wait_child(pid); 372 if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status)) 373 puts(" -- Core dumped."); 374 else 375 puts(" -- Can't dump core."); 376 return(0); 377 } 378 379 /* 380 * Clobber as many bytes of stack as the user requests. 381 */ 382 int 383 clobber(void *v) 384 { 385 char **argv = v; 386 int times; 387 388 if (argv[0] == 0) 389 times = 1; 390 else 391 times = (atoi(argv[0]) + 511) / 512; 392 clob1(times); 393 return(0); 394 } 395 396 /* 397 * Clobber the stack. 398 */ 399 void 400 clob1(n) 401 int n; 402 { 403 char buf[512]; 404 char *cp; 405 406 if (n <= 0) 407 return; 408 for (cp = buf; cp < &buf[512]; *cp++ = 0xFF) 409 ; 410 clob1(n - 1); 411 } 412 413 /* 414 * Add the given header fields to the retained list. 415 * If no arguments, print the current list of retained fields. 416 */ 417 int 418 retfield(void *v) 419 { 420 char **list = v; 421 422 return(ignore1(list, ignore + 1, "retained")); 423 } 424 425 /* 426 * Add the given header fields to the ignored list. 427 * If no arguments, print the current list of ignored fields. 428 */ 429 int 430 igfield(void *v) 431 { 432 char **list = v; 433 434 return(ignore1(list, ignore, "ignored")); 435 } 436 437 int 438 saveretfield(void *v) 439 { 440 char **list = v; 441 442 return(ignore1(list, saveignore + 1, "retained")); 443 } 444 445 int 446 saveigfield(void *v) 447 { 448 char **list = v; 449 450 return(ignore1(list, saveignore, "ignored")); 451 } 452 453 int 454 ignore1(char **list, struct ignoretab *tab, char *which) 455 { 456 char field[LINESIZE]; 457 char **ap; 458 struct ignore *igp; 459 int h; 460 461 if (*list == NULL) 462 return(igshow(tab, which)); 463 for (ap = list; *ap != 0; ap++) { 464 istrlcpy(field, *ap, sizeof(field)); 465 if (member(field, tab)) 466 continue; 467 h = hash(field); 468 igp = (struct ignore *)calloc(1, sizeof(struct ignore)); 469 if (igp == NULL) 470 errx(1, "Out of memory"); 471 igp->i_field = strdup(field); 472 if (igp->i_field == NULL) 473 errx(1, "Out of memory"); 474 igp->i_link = tab->i_head[h]; 475 tab->i_head[h] = igp; 476 tab->i_count++; 477 } 478 return(0); 479 } 480 481 /* 482 * Print out all currently retained fields. 483 */ 484 int 485 igshow(struct ignoretab *tab, char *which) 486 { 487 int h; 488 struct ignore *igp; 489 char **ap, **ring; 490 491 if (tab->i_count == 0) { 492 printf("No fields currently being %s.\n", which); 493 return(0); 494 } 495 ring = (char **)salloc((tab->i_count + 1) * sizeof(char *)); 496 ap = ring; 497 for (h = 0; h < HSHSIZE; h++) 498 for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link) 499 *ap++ = igp->i_field; 500 *ap = 0; 501 qsort(ring, tab->i_count, sizeof(char *), igcomp); 502 for (ap = ring; *ap != 0; ap++) 503 puts(*ap); 504 return(0); 505 } 506 507 /* 508 * Compare two names for sorting ignored field list. 509 */ 510 static int 511 igcomp(const void *l, const void *r) 512 { 513 514 return(strcmp(*(char **)l, *(char **)r)); 515 } 516