155714Skris/*- 255714Skris * SPDX-License-Identifier: BSD-3-Clause 355714Skris * 455714Skris * Copyright (c) 1980, 1993 555714Skris * The Regents of the University of California. All rights reserved. 655714Skris * 755714Skris * Redistribution and use in source and binary forms, with or without 855714Skris * modification, are permitted provided that the following conditions 955714Skris * are met: 1055714Skris * 1. Redistributions of source code must retain the above copyright 1155714Skris * notice, this list of conditions and the following disclaimer. 1255714Skris * 2. Redistributions in binary form must reproduce the above copyright 1355714Skris * notice, this list of conditions and the following disclaimer in the 1455714Skris * documentation and/or other materials provided with the distribution. 1555714Skris * 3. Neither the name of the University nor the names of its contributors 1655714Skris * may be used to endorse or promote products derived from this software 1755714Skris * without specific prior written permission. 1855714Skris * 1955714Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2055714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2155714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2255714Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2355714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2455714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2555714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2655714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2755714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2855714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2955714Skris * SUCH DAMAGE. 3055714Skris */ 3155714Skris 3255714Skris#include "rcv.h" 3355714Skris#include <sys/wait.h> 3455714Skris#include "extern.h" 3555714Skris 3655714Skris/* 3755714Skris * Mail -- a mail program 3855714Skris * 3955714Skris * More user commands. 4055714Skris */ 4155714Skris 4255714Skrisextern int wait_status; 4355714Skris 4455714Skris/* 4555714Skris * If any arguments were given, go to the next applicable argument 4655714Skris * following dot, otherwise, go to the next applicable message. 4755714Skris * If given as first command with no arguments, print first message. 4855714Skris */ 4955714Skrisint 5055714Skrisnext(void *v) 5155714Skris{ 5255714Skris struct message *mp; 5355714Skris int *msgvec = v; 5455714Skris int *ip, *ip2, list[2], mdot; 5555714Skris 5655714Skris if (*msgvec != 0) { 5755714Skris 5855714Skris /* 5955714Skris * If some messages were supplied, find the 6055714Skris * first applicable one following dot using 6155714Skris * wrap around. 6255714Skris */ 6355714Skris 6455714Skris mdot = dot - &message[0] + 1; 6555714Skris 6655714Skris /* 6755714Skris * Find the first message in the supplied 6855714Skris * message list which follows dot. 6955714Skris */ 7055714Skris 7155714Skris for (ip = msgvec; *ip != 0; ip++) 7255714Skris if (*ip > mdot) 7355714Skris break; 7455714Skris if (*ip == 0) 7555714Skris ip = msgvec; 7655714Skris ip2 = ip; 7755714Skris do { 7855714Skris mp = &message[*ip2 - 1]; 7955714Skris if ((mp->m_flag & MDELETED) == 0) { 8055714Skris dot = mp; 8155714Skris goto hitit; 8255714Skris } 8355714Skris if (*ip2 != 0) 8455714Skris ip2++; 8555714Skris if (*ip2 == 0) 8655714Skris ip2 = msgvec; 8755714Skris } while (ip2 != ip); 8855714Skris printf("No messages applicable\n"); 8955714Skris return (1); 9055714Skris } 9155714Skris 9255714Skris /* 9355714Skris * If this is the first command, select message 1. 9455714Skris * Note that this must exist for us to get here at all. 9555714Skris */ 9655714Skris 9755714Skris if (!sawcom) 9855714Skris goto hitit; 9955714Skris 10055714Skris /* 10155714Skris * Just find the next good message after dot, no 10255714Skris * wraparound. 10355714Skris */ 10455714Skris 10555714Skris for (mp = dot+1; mp < &message[msgCount]; mp++) 10655714Skris if ((mp->m_flag & (MDELETED|MSAVED)) == 0) 10755714Skris break; 10855714Skris if (mp >= &message[msgCount]) { 10955714Skris printf("At EOF\n"); 11055714Skris return (0); 11155714Skris } 11255714Skris dot = mp; 11355714Skrishitit: 11455714Skris /* 11555714Skris * Print dot. 11655714Skris */ 11755714Skris 11855714Skris list[0] = dot - &message[0] + 1; 11955714Skris list[1] = 0; 12055714Skris return (type(list)); 12155714Skris} 12255714Skris 12355714Skris/* 12455714Skris * Save a message in a file. Mark the message as saved 12555714Skris * so we can discard when the user quits. 12655714Skris */ 12755714Skrisint 12855714Skrissave(void *v) 12955714Skris{ 13055714Skris char *str = v; 13155714Skris 13255714Skris return (save1(str, 1, "save", saveignore)); 13355714Skris} 13455714Skris 13555714Skris/* 13655714Skris * Copy a message to a file without affected its saved-ness 13755714Skris */ 13855714Skrisint 13955714Skriscopycmd(void *v) 14055714Skris{ 14155714Skris char *str = v; 14255714Skris 14355714Skris return (save1(str, 0, "copy", saveignore)); 14455714Skris} 14555714Skris 14655714Skris/* 14755714Skris * Save/copy the indicated messages at the end of the passed file name. 14855714Skris * If mark is true, mark the message "saved." 149109998Smarkm */ 150109998Smarkmint 151109998Smarkmsave1(char str[], int mark, const char *cmd, struct ignoretab *ignore) 152109998Smarkm{ 153246772Sjkim struct message *mp; 154109998Smarkm char *file; 15555714Skris const char *disp; 15655714Skris int f, *msgvec, *ip; 157109998Smarkm FILE *obuf; 15855714Skris 15955714Skris msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec)); 16055714Skris if ((file = snarf(str, &f)) == NULL) 16155714Skris return (1); 16255714Skris if (!f) { 16355714Skris *msgvec = first(0, MMNORM); 16455714Skris if (*msgvec == 0) { 16555714Skris printf("No messages to %s.\n", cmd); 16655714Skris return (1); 16755714Skris } 16855714Skris msgvec[1] = 0; 16955714Skris } 17055714Skris if (f && getmsglist(str, msgvec, 0) < 0) 17155714Skris return (1); 17255714Skris if ((file = expand(file)) == NULL) 17355714Skris return (1); 17455714Skris printf("\"%s\" ", file); 17555714Skris (void)fflush(stdout); 17655714Skris if (access(file, 0) >= 0) 17755714Skris disp = "[Appended]"; 17855714Skris else 17955714Skris disp = "[New file]"; 18055714Skris if ((obuf = Fopen(file, "a")) == NULL) { 18155714Skris warn((char *)NULL); 18255714Skris return (1); 18355714Skris } 18455714Skris for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 18555714Skris mp = &message[*ip - 1]; 18655714Skris touch(mp); 18755714Skris if (sendmessage(mp, obuf, ignore, NULL) < 0) { 18855714Skris warnx("%s", file); 18955714Skris (void)Fclose(obuf); 19055714Skris return (1); 19155714Skris } 19255714Skris if (mark) 19355714Skris mp->m_flag |= MSAVED; 19455714Skris } 19555714Skris (void)fflush(obuf); 19655714Skris if (ferror(obuf)) 19755714Skris warn("%s", file); 19855714Skris (void)Fclose(obuf); 19955714Skris printf("%s\n", disp); 20055714Skris return (0); 20155714Skris} 20255714Skris 20355714Skris/* 20455714Skris * Write the indicated messages at the end of the passed 20555714Skris * file name, minus header and trailing blank line. 20655714Skris */ 20755714Skrisint 20855714Skrisswrite(void *v) 20955714Skris{ 21055714Skris char *str = v; 21155714Skris 21255714Skris return (save1(str, 1, "write", ignoreall)); 21355714Skris} 21455714Skris 21555714Skris/* 21655714Skris * Snarf the file from the end of the command line and 21755714Skris * return a pointer to it. If there is no file attached, 21855714Skris * just return NULL. Put a null in front of the file 21955714Skris * name so that the message list processing won't see it, 22055714Skris * unless the file name is the only thing on the line, in 22155714Skris * which case, return 0 in the reference flag variable. 22255714Skris */ 22355714Skris 22455714Skrischar * 22555714Skrissnarf(char *linebuf, int *flag) 22655714Skris{ 22755714Skris char *cp; 22855714Skris 22955714Skris *flag = 1; 23055714Skris cp = strlen(linebuf) + linebuf - 1; 23155714Skris 23255714Skris /* 23355714Skris * Strip away trailing blanks. 23455714Skris */ 23555714Skris 23655714Skris while (cp > linebuf && isspace((unsigned char)*cp)) 23755714Skris cp--; 23855714Skris *++cp = '\0'; 23955714Skris 24055714Skris /* 24155714Skris * Now search for the beginning of the file name. 24255714Skris */ 24355714Skris 24455714Skris while (cp > linebuf && !isspace((unsigned char)*cp)) 24555714Skris cp--; 24655714Skris if (*cp == '\0') { 24755714Skris printf("No file specified.\n"); 24855714Skris return (NULL); 24955714Skris } 25055714Skris if (isspace((unsigned char)*cp)) 25155714Skris *cp++ = '\0'; 25255714Skris else 25355714Skris *flag = 0; 25455714Skris return (cp); 25555714Skris} 25655714Skris 25755714Skris/* 25855714Skris * Delete messages. 25955714Skris */ 26055714Skrisint 26155714Skrisdeletecmd(void *v) 26255714Skris{ 26355714Skris int *msgvec = v; 26455714Skris 26555714Skris delm(msgvec); 26655714Skris return (0); 26755714Skris} 26855714Skris 26955714Skris/* 27055714Skris * Delete messages, then type the new dot. 27155714Skris */ 27255714Skrisint 27355714Skrisdeltype(void *v) 27455714Skris{ 27555714Skris int *msgvec = v; 27655714Skris int list[2]; 27755714Skris int lastdot; 27855714Skris 27955714Skris lastdot = dot - &message[0] + 1; 28055714Skris if (delm(msgvec) >= 0) { 28155714Skris list[0] = dot - &message[0] + 1; 28255714Skris if (list[0] > lastdot) { 28355714Skris touch(dot); 28455714Skris list[1] = 0; 28555714Skris return (type(list)); 28655714Skris } 28755714Skris printf("At EOF\n"); 28855714Skris } else 28955714Skris printf("No more messages\n"); 29055714Skris return (0); 29155714Skris} 29255714Skris 29355714Skris/* 29455714Skris * Delete the indicated messages. 29555714Skris * Set dot to some nice place afterwards. 29655714Skris * Internal interface. 29755714Skris */ 29855714Skrisint 29955714Skrisdelm(int *msgvec) 30055714Skris{ 30155714Skris struct message *mp; 30255714Skris int *ip, last; 30355714Skris 30455714Skris last = 0; 30555714Skris for (ip = msgvec; *ip != 0; ip++) { 30655714Skris mp = &message[*ip - 1]; 30755714Skris touch(mp); 30855714Skris mp->m_flag |= MDELETED|MTOUCH; 30955714Skris mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); 31055714Skris last = *ip; 31155714Skris } 31255714Skris if (last != 0) { 31355714Skris dot = &message[last-1]; 31455714Skris last = first(0, MDELETED); 31555714Skris if (last != 0) { 31655714Skris dot = &message[last-1]; 31755714Skris return (0); 31855714Skris } 31955714Skris else { 32055714Skris dot = &message[0]; 32155714Skris return (-1); 32255714Skris } 323160814Ssimon } 324160814Ssimon 32555714Skris /* 326109998Smarkm * Following can't happen -- it keeps lint happy 327109998Smarkm */ 328109998Smarkm 329109998Smarkm return (-1); 330109998Smarkm} 331109998Smarkm 332109998Smarkm/* 333109998Smarkm * Undelete the indicated messages. 334109998Smarkm */ 335109998Smarkmint 336109998Smarkmundeletecmd(void *v) 337109998Smarkm{ 338109998Smarkm int *msgvec = v; 339109998Smarkm int *ip; 340109998Smarkm struct message *mp; 341109998Smarkm 342109998Smarkm for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 343160814Ssimon mp = &message[*ip - 1]; 34455714Skris touch(mp); 34555714Skris dot = mp; 34655714Skris mp->m_flag &= ~MDELETED; 34755714Skris } 34855714Skris return (0); 34955714Skris} 350 351/* 352 * Interactively dump core on "core" 353 */ 354int 355core(void *arg __unused) 356{ 357 int pid; 358 359 switch (pid = fork()) { 360 case -1: 361 warn("fork"); 362 return (1); 363 case 0: 364 abort(); 365 _exit(1); 366 } 367 printf("Okie dokie"); 368 (void)fflush(stdout); 369 wait_child(pid); 370 if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status)) 371 printf(" -- Core dumped.\n"); 372 else 373 printf(" -- Can't dump core.\n"); 374 return (0); 375} 376 377/* 378 * Clobber as many bytes of stack as the user requests. 379 */ 380int 381clobber(void *arg) 382{ 383 char **argv = arg; 384 int times; 385 386 if (argv[0] == 0) 387 times = 1; 388 else 389 times = (atoi(argv[0]) + 511) / 512; 390 clob1(times); 391 return (0); 392} 393 394/* 395 * Clobber the stack. 396 */ 397void 398clob1(int n) 399{ 400 char buf[512]; 401 char *cp; 402 403 if (n <= 0) 404 return; 405 for (cp = buf; cp < &buf[512]; *cp++ = 0xFF) 406 ; 407 clob1(n - 1); 408} 409 410/* 411 * Add the given header fields to the retained list. 412 * If no arguments, print the current list of retained fields. 413 */ 414int 415retfield(void *v) 416{ 417 char **list = v; 418 419 return (ignore1(list, ignore + 1, "retained")); 420} 421 422/* 423 * Add the given header fields to the ignored list. 424 * If no arguments, print the current list of ignored fields. 425 */ 426int 427igfield(void *v) 428{ 429 char **list = v; 430 431 return (ignore1(list, ignore, "ignored")); 432} 433 434int 435saveretfield(void *v) 436{ 437 char **list = v; 438 439 return (ignore1(list, saveignore + 1, "retained")); 440} 441 442int 443saveigfield(void *v) 444{ 445 char **list = v; 446 447 return (ignore1(list, saveignore, "ignored")); 448} 449 450int 451ignore1(char **list, struct ignoretab *tab, const char *which) 452{ 453 char field[LINESIZE]; 454 char **ap; 455 struct ignore *igp; 456 int h; 457 458 if (*list == NULL) 459 return (igshow(tab, which)); 460 for (ap = list; *ap != 0; ap++) { 461 istrncpy(field, *ap, sizeof(field)); 462 if (member(field, tab)) 463 continue; 464 h = hash(field); 465 igp = calloc(1, sizeof(struct ignore)); 466 igp->i_field = calloc((unsigned)strlen(field) + 1, 467 sizeof(char)); 468 strcpy(igp->i_field, field); 469 igp->i_link = tab->i_head[h]; 470 tab->i_head[h] = igp; 471 tab->i_count++; 472 } 473 return (0); 474} 475 476/* 477 * Print out all currently retained fields. 478 */ 479int 480igshow(struct ignoretab *tab, const char *which) 481{ 482 int h; 483 struct ignore *igp; 484 char **ap, **ring; 485 486 if (tab->i_count == 0) { 487 printf("No fields currently being %s.\n", which); 488 return (0); 489 } 490 ring = (char **)salloc((tab->i_count + 1) * sizeof(char *)); 491 ap = ring; 492 for (h = 0; h < HSHSIZE; h++) 493 for (igp = tab->i_head[h]; igp != NULL; igp = igp->i_link) 494 *ap++ = igp->i_field; 495 *ap = 0; 496 qsort(ring, tab->i_count, sizeof(char *), igcomp); 497 for (ap = ring; *ap != 0; ap++) 498 printf("%s\n", *ap); 499 return (0); 500} 501 502/* 503 * Compare two names for sorting ignored field list. 504 */ 505int 506igcomp(const void *l, const void *r) 507{ 508 509 return (strcmp(*(const char **)l, *(const char **)r)); 510} 511