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