1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#)cmd2.c 8.1 (Berkeley) 6/6/93 30 * $FreeBSD: src/usr.bin/mail/cmd2.c,v 1.5.6.3 2003/01/06 05:46:03 mikeh Exp $ 31 */ 32 33 #include "rcv.h" 34 #include <sys/wait.h> 35 #include "extern.h" 36 37 /* 38 * Mail -- a mail program 39 * 40 * More user commands. 41 */ 42 43 extern int wait_status; 44 45 /* 46 * If any arguments were given, go to the next applicable argument 47 * following dot, otherwise, go to the next applicable message. 48 * If given as first command with no arguments, print first message. 49 */ 50 int 51 next(int *msgvec) 52 { 53 struct message *mp; 54 int *ip, *ip2, list[2], mdot; 55 56 if (*msgvec != 0) { 57 58 /* 59 * If some messages were supplied, find the 60 * first applicable one following dot using 61 * wrap around. 62 */ 63 64 mdot = dot - &message[0] + 1; 65 66 /* 67 * Find the first message in the supplied 68 * message list which follows dot. 69 */ 70 71 for (ip = msgvec; *ip != 0; ip++) 72 if (*ip > mdot) 73 break; 74 if (*ip == 0) 75 ip = msgvec; 76 ip2 = ip; 77 do { 78 mp = &message[*ip2 - 1]; 79 if ((mp->m_flag & MDELETED) == 0) { 80 dot = mp; 81 goto hitit; 82 } 83 if (*ip2 != 0) 84 ip2++; 85 if (*ip2 == 0) 86 ip2 = msgvec; 87 } while (ip2 != ip); 88 printf("No messages applicable\n"); 89 return (1); 90 } 91 92 /* 93 * If this is the first command, select message 1. 94 * Note that this must exist for us to get here at all. 95 */ 96 97 if (!sawcom) 98 goto hitit; 99 100 /* 101 * Just find the next good message after dot, no 102 * wraparound. 103 */ 104 105 for (mp = dot+1; mp < &message[msgCount]; mp++) 106 if ((mp->m_flag & (MDELETED|MSAVED)) == 0) 107 break; 108 if (mp >= &message[msgCount]) { 109 printf("At EOF\n"); 110 return (0); 111 } 112 dot = mp; 113 hitit: 114 /* 115 * Print dot. 116 */ 117 118 list[0] = dot - &message[0] + 1; 119 list[1] = 0; 120 return (type(list)); 121 } 122 123 /* 124 * Save a message in a file. Mark the message as saved 125 * so we can discard when the user quits. 126 */ 127 int 128 save(char *str) 129 { 130 return (save1(str, 1, "save", saveignore)); 131 } 132 133 /* 134 * Copy a message to a file without affected its saved-ness 135 */ 136 int 137 copycmd(char *str) 138 { 139 return (save1(str, 0, "copy", saveignore)); 140 } 141 142 /* 143 * Save/copy the indicated messages at the end of the passed file name. 144 * If mark is true, mark the message "saved." 145 */ 146 int 147 save1(char *str, int mark, const char *cmd, struct ignoretab *ignore) 148 { 149 struct message *mp; 150 char *file; 151 const char *disp; 152 int f, *msgvec, *ip; 153 FILE *obuf; 154 155 msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec)); 156 if ((file = snarf(str, &f)) == NULL) 157 return (1); 158 if (!f) { 159 *msgvec = first(0, MMNORM); 160 if (*msgvec == 0) { 161 printf("No messages to %s.\n", cmd); 162 return (1); 163 } 164 msgvec[1] = 0; 165 } 166 if (f && getmsglist(str, msgvec, 0) < 0) 167 return (1); 168 if ((file = expand(file)) == NULL) 169 return (1); 170 printf("\"%s\" ", file); 171 fflush(stdout); 172 if (access(file, 0) >= 0) 173 disp = "[Appended]"; 174 else 175 disp = "[New file]"; 176 if ((obuf = Fopen(file, "a")) == NULL) { 177 warn(NULL); 178 return (1); 179 } 180 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 181 mp = &message[*ip - 1]; 182 touch(mp); 183 if (sendmessage(mp, obuf, ignore, NULL) < 0) { 184 warnx("%s", file); 185 Fclose(obuf); 186 return (1); 187 } 188 if (mark) 189 mp->m_flag |= MSAVED; 190 } 191 fflush(obuf); 192 if (ferror(obuf)) 193 warn("%s", file); 194 Fclose(obuf); 195 printf("%s\n", disp); 196 return (0); 197 } 198 199 /* 200 * Write the indicated messages at the end of the passed 201 * file name, minus header and trailing blank line. 202 */ 203 int 204 swrite(char *str) 205 { 206 return (save1(str, 1, "write", ignoreall)); 207 } 208 209 /* 210 * Snarf the file from the end of the command line and 211 * return a pointer to it. If there is no file attached, 212 * just return NULL. Put a null in front of the file 213 * name so that the message list processing won't see it, 214 * unless the file name is the only thing on the line, in 215 * which case, return 0 in the reference flag variable. 216 */ 217 218 char * 219 snarf(char *linebuf, int *flag) 220 { 221 char *cp; 222 223 *flag = 1; 224 cp = strlen(linebuf) + linebuf - 1; 225 226 /* 227 * Strip away trailing blanks. 228 */ 229 230 while (cp > linebuf && isspace((unsigned char)*cp)) 231 cp--; 232 *++cp = '\0'; 233 234 /* 235 * Now search for the beginning of the file name. 236 */ 237 238 while (cp > linebuf && !isspace((unsigned char)*cp)) 239 cp--; 240 if (*cp == '\0') { 241 printf("No file specified.\n"); 242 return (NULL); 243 } 244 if (isspace((unsigned char)*cp)) 245 *cp++ = '\0'; 246 else 247 *flag = 0; 248 return (cp); 249 } 250 251 /* 252 * Delete messages. 253 */ 254 int 255 delete(int *msgvec) 256 { 257 delm(msgvec); 258 return (0); 259 } 260 261 /* 262 * Delete messages, then type the new dot. 263 */ 264 int 265 deltype(int *msgvec) 266 { 267 int list[2]; 268 int lastdot; 269 270 lastdot = dot - &message[0] + 1; 271 if (delm(msgvec) >= 0) { 272 list[0] = dot - &message[0] + 1; 273 if (list[0] > lastdot) { 274 touch(dot); 275 list[1] = 0; 276 return (type(list)); 277 } 278 printf("At EOF\n"); 279 } else 280 printf("No more messages\n"); 281 return (0); 282 } 283 284 /* 285 * Delete the indicated messages. 286 * Set dot to some nice place afterwards. 287 * Internal interface. 288 */ 289 int 290 delm(int *msgvec) 291 { 292 struct message *mp; 293 int *ip, last; 294 295 last = 0; 296 for (ip = msgvec; *ip != 0; ip++) { 297 mp = &message[*ip - 1]; 298 touch(mp); 299 mp->m_flag |= MDELETED|MTOUCH; 300 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); 301 last = *ip; 302 } 303 if (last != 0) { 304 dot = &message[last-1]; 305 last = first(0, MDELETED); 306 if (last != 0) { 307 dot = &message[last-1]; 308 return (0); 309 } 310 else { 311 dot = &message[0]; 312 return (-1); 313 } 314 } 315 316 /* 317 * Following can't happen -- it keeps lint happy 318 */ 319 320 return (-1); 321 } 322 323 /* 324 * Undelete the indicated messages. 325 */ 326 int 327 undelete_messages(int *msgvec) 328 { 329 struct message *mp; 330 int *ip; 331 332 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 333 mp = &message[*ip - 1]; 334 touch(mp); 335 dot = mp; 336 mp->m_flag &= ~MDELETED; 337 } 338 return (0); 339 } 340 341 /* 342 * Interactively dump core on "core" 343 */ 344 int 345 core(void) 346 { 347 int pid; 348 349 switch (pid = fork()) { 350 case -1: 351 warn("fork"); 352 return (1); 353 case 0: 354 abort(); 355 _exit(1); 356 } 357 printf("Okie dokie"); 358 fflush(stdout); 359 wait_child(pid); 360 if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status)) 361 printf(" -- Core dumped.\n"); 362 else 363 printf(" -- Can't dump core.\n"); 364 return (0); 365 } 366 367 /* 368 * Clobber as many bytes of stack as the user requests. 369 */ 370 int 371 clobber(char **argv) 372 { 373 int times; 374 375 if (argv[0] == NULL) 376 times = 1; 377 else 378 times = (atoi(argv[0]) + 511) / 512; 379 clob1(times); 380 return (0); 381 } 382 383 /* 384 * Clobber the stack. 385 */ 386 void 387 clob1(int n) 388 { 389 char buf[512]; 390 char *cp; 391 392 if (n <= 0) 393 return; 394 for (cp = buf; cp < &buf[512]; *cp++ = 0xFF) 395 ; 396 clob1(n - 1); 397 } 398 399 /* 400 * Add the given header fields to the retained list. 401 * If no arguments, print the current list of retained fields. 402 */ 403 int 404 retfield(char **list) 405 { 406 return (ignore1(list, ignore + 1, "retained")); 407 } 408 409 /* 410 * Add the given header fields to the ignored list. 411 * If no arguments, print the current list of ignored fields. 412 */ 413 int 414 igfield(char **list) 415 { 416 return (ignore1(list, ignore, "ignored")); 417 } 418 419 int 420 saveretfield(char **list) 421 { 422 return (ignore1(list, saveignore + 1, "retained")); 423 } 424 425 int 426 saveigfield(char **list) 427 { 428 return (ignore1(list, saveignore, "ignored")); 429 } 430 431 int 432 ignore1(char **list, struct ignoretab *tab, const char *which) 433 { 434 char field[LINESIZE]; 435 int h; 436 struct ignore *igp; 437 char **ap; 438 439 if (*list == NULL) 440 return (igshow(tab, which)); 441 for (ap = list; *ap != NULL; ap++) { 442 istrncpy(field, *ap, sizeof(field)); 443 if (member(field, tab)) 444 continue; 445 h = hash(field); 446 igp = calloc(1, sizeof(struct ignore)); 447 igp->i_field = calloc((unsigned)strlen(field) + 1, 448 sizeof(char)); 449 strcpy(igp->i_field, field); 450 igp->i_link = tab->i_head[h]; 451 tab->i_head[h] = igp; 452 tab->i_count++; 453 } 454 return (0); 455 } 456 457 /* 458 * Print out all currently retained fields. 459 */ 460 int 461 igshow(struct ignoretab *tab, const char *which) 462 { 463 int h; 464 struct ignore *igp; 465 char **ap, **ring; 466 467 if (tab->i_count == 0) { 468 printf("No fields currently being %s.\n", which); 469 return (0); 470 } 471 ring = (char **)salloc((tab->i_count + 1) * sizeof(char *)); 472 ap = ring; 473 for (h = 0; h < HSHSIZE; h++) 474 for (igp = tab->i_head[h]; igp != NULL; igp = igp->i_link) 475 *ap++ = igp->i_field; 476 *ap = NULL; 477 qsort(ring, tab->i_count, sizeof(char *), igcomp); 478 for (ap = ring; *ap != NULL; ap++) 479 printf("%s\n", *ap); 480 return (0); 481 } 482 483 /* 484 * Compare two names for sorting ignored field list. 485 */ 486 int 487 igcomp(const void *l, const void *r) 488 { 489 return (strcmp(*(const char **)l, *(const char **)r)); 490 } 491