10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 70Sstevel@tonic-gate * with the License. 80Sstevel@tonic-gate * 90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 110Sstevel@tonic-gate * See the License for the specific language governing permissions 120Sstevel@tonic-gate * and limitations under the License. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 190Sstevel@tonic-gate * 200Sstevel@tonic-gate * CDDL HEADER END 210Sstevel@tonic-gate */ 220Sstevel@tonic-gate 230Sstevel@tonic-gate /* 24*18Srobbin * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 250Sstevel@tonic-gate * Use is subject to license terms. 260Sstevel@tonic-gate */ 270Sstevel@tonic-gate 28*18Srobbin /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 29*18Srobbin /* All Rights Reserved */ 300Sstevel@tonic-gate 310Sstevel@tonic-gate /* 320Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 330Sstevel@tonic-gate * The Regents of the University of California 340Sstevel@tonic-gate * All Rights Reserved 350Sstevel@tonic-gate * 360Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 370Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 380Sstevel@tonic-gate * contributors. 390Sstevel@tonic-gate */ 400Sstevel@tonic-gate 410Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 420Sstevel@tonic-gate 430Sstevel@tonic-gate #include "rcv.h" 440Sstevel@tonic-gate #include <locale.h> 450Sstevel@tonic-gate 460Sstevel@tonic-gate /* 470Sstevel@tonic-gate * mailx -- a modified version of a University of California at Berkeley 480Sstevel@tonic-gate * mail program 490Sstevel@tonic-gate * 500Sstevel@tonic-gate * More user commands. 510Sstevel@tonic-gate */ 520Sstevel@tonic-gate 530Sstevel@tonic-gate static int igshow(void); 540Sstevel@tonic-gate static int igcomp(const void *l, const void *r); 550Sstevel@tonic-gate static int save1(char str[], int mark); 560Sstevel@tonic-gate static int Save1(int *msgvec, int mark); 570Sstevel@tonic-gate static void savemsglist(char *file, int *msgvec, int flag); 580Sstevel@tonic-gate static int put1(char str[], int doign); 590Sstevel@tonic-gate static int svputs(const char *line, FILE *obuf); 600Sstevel@tonic-gate static int wrputs(const char *line, FILE *obuf); 610Sstevel@tonic-gate static int retshow(void); 620Sstevel@tonic-gate 630Sstevel@tonic-gate /* flags for savemsglist() */ 640Sstevel@tonic-gate #define S_MARK 1 /* mark the message as saved */ 650Sstevel@tonic-gate #define S_NOHEADER 2 /* don't write out the header */ 660Sstevel@tonic-gate #define S_SAVING 4 /* doing save/copy */ 670Sstevel@tonic-gate #define S_NOIGNORE 8 /* don't do ignore processing */ 680Sstevel@tonic-gate 690Sstevel@tonic-gate /* 700Sstevel@tonic-gate * If any arguments were given, go to the next applicable argument 710Sstevel@tonic-gate * following dot, otherwise, go to the next applicable message. 720Sstevel@tonic-gate * If given as first command with no arguments, print first message. 730Sstevel@tonic-gate */ 740Sstevel@tonic-gate 750Sstevel@tonic-gate int 760Sstevel@tonic-gate next(int *msgvec) 770Sstevel@tonic-gate { 780Sstevel@tonic-gate register struct message *mp; 790Sstevel@tonic-gate register int *ip, *ip2; 800Sstevel@tonic-gate int list[2], mdot; 810Sstevel@tonic-gate 820Sstevel@tonic-gate if (*msgvec != NULL) { 830Sstevel@tonic-gate 840Sstevel@tonic-gate /* 850Sstevel@tonic-gate * If some messages were supplied, find the 860Sstevel@tonic-gate * first applicable one following dot using 870Sstevel@tonic-gate * wrap around. 880Sstevel@tonic-gate */ 890Sstevel@tonic-gate 900Sstevel@tonic-gate mdot = dot - &message[0] + 1; 910Sstevel@tonic-gate 920Sstevel@tonic-gate /* 930Sstevel@tonic-gate * Find the first message in the supplied 940Sstevel@tonic-gate * message list which follows dot. 950Sstevel@tonic-gate */ 960Sstevel@tonic-gate 970Sstevel@tonic-gate for (ip = msgvec; *ip != NULL; ip++) 980Sstevel@tonic-gate if (*ip > mdot) 990Sstevel@tonic-gate break; 1000Sstevel@tonic-gate if (*ip == NULL) 1010Sstevel@tonic-gate ip = msgvec; 1020Sstevel@tonic-gate ip2 = ip; 1030Sstevel@tonic-gate do { 1040Sstevel@tonic-gate mp = &message[*ip2 - 1]; 1050Sstevel@tonic-gate if ((mp->m_flag & MDELETED) == 0) { 1060Sstevel@tonic-gate dot = mp; 1070Sstevel@tonic-gate goto hitit; 1080Sstevel@tonic-gate } 1090Sstevel@tonic-gate if (*ip2 != NULL) 1100Sstevel@tonic-gate ip2++; 1110Sstevel@tonic-gate if (*ip2 == NULL) 1120Sstevel@tonic-gate ip2 = msgvec; 1130Sstevel@tonic-gate } while (ip2 != ip); 1140Sstevel@tonic-gate printf(gettext("No messages applicable\n")); 1150Sstevel@tonic-gate return(1); 1160Sstevel@tonic-gate } 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate /* 1190Sstevel@tonic-gate * If this is the first command, select message 1. 1200Sstevel@tonic-gate * Note that this must exist for us to get here at all. 1210Sstevel@tonic-gate */ 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate if (!sawcom) 1240Sstevel@tonic-gate goto hitit; 1250Sstevel@tonic-gate 1260Sstevel@tonic-gate /* 1270Sstevel@tonic-gate * Just find the next good message after dot, no 1280Sstevel@tonic-gate * wraparound. 1290Sstevel@tonic-gate */ 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate for (mp = dot+1; mp < &message[msgCount]; mp++) 1320Sstevel@tonic-gate if ((mp->m_flag & (MDELETED|MSAVED)) == 0) 1330Sstevel@tonic-gate break; 1340Sstevel@tonic-gate if (mp >= &message[msgCount]) { 1350Sstevel@tonic-gate printf(gettext("At EOF\n")); 1360Sstevel@tonic-gate return(0); 1370Sstevel@tonic-gate } 1380Sstevel@tonic-gate dot = mp; 1390Sstevel@tonic-gate hitit: 1400Sstevel@tonic-gate /* 1410Sstevel@tonic-gate * Print dot. 1420Sstevel@tonic-gate */ 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate list[0] = dot - &message[0] + 1; 1450Sstevel@tonic-gate list[1] = NULL; 1460Sstevel@tonic-gate return(type(list)); 1470Sstevel@tonic-gate } 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate /* 1500Sstevel@tonic-gate * Save a message in a file. Mark the message as saved 1510Sstevel@tonic-gate * so we can discard when the user quits. 1520Sstevel@tonic-gate */ 1530Sstevel@tonic-gate int 1540Sstevel@tonic-gate save(char str[]) 1550Sstevel@tonic-gate { 1560Sstevel@tonic-gate return(save1(str, S_MARK)); 1570Sstevel@tonic-gate } 1580Sstevel@tonic-gate 1590Sstevel@tonic-gate /* 1600Sstevel@tonic-gate * Copy a message to a file without affected its saved-ness 1610Sstevel@tonic-gate */ 1620Sstevel@tonic-gate int 1630Sstevel@tonic-gate copycmd(char str[]) 1640Sstevel@tonic-gate { 1650Sstevel@tonic-gate return(save1(str, 0)); 1660Sstevel@tonic-gate } 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate /* 1690Sstevel@tonic-gate * Save/copy the indicated messages at the end of the passed file name. 1700Sstevel@tonic-gate * If mark is true, mark the message "saved." 1710Sstevel@tonic-gate */ 1720Sstevel@tonic-gate static int 1730Sstevel@tonic-gate save1(char str[], int mark) 1740Sstevel@tonic-gate { 1750Sstevel@tonic-gate char *file, *cmd; 1760Sstevel@tonic-gate int f, *msgvec; 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate cmd = mark ? "save" : "copy"; 1790Sstevel@tonic-gate msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec); 1800Sstevel@tonic-gate if ((file = snarf(str, &f, 0)) == NOSTR) 1810Sstevel@tonic-gate file = Getf("MBOX"); 1820Sstevel@tonic-gate if (f==-1) 1830Sstevel@tonic-gate return(1); 1840Sstevel@tonic-gate if (!f) { 1850Sstevel@tonic-gate *msgvec = first(0, MMNORM); 1860Sstevel@tonic-gate if (*msgvec == NULL) { 1870Sstevel@tonic-gate printf(gettext("No messages to %s.\n"), cmd); 1880Sstevel@tonic-gate return(1); 1890Sstevel@tonic-gate } 1900Sstevel@tonic-gate msgvec[1] = NULL; 1910Sstevel@tonic-gate } 1920Sstevel@tonic-gate if (f && getmsglist(str, msgvec, 0) < 0) 1930Sstevel@tonic-gate return(1); 1940Sstevel@tonic-gate if ((file = expand(file)) == NOSTR) 1950Sstevel@tonic-gate return(1); 1960Sstevel@tonic-gate savemsglist(file, msgvec, mark | S_SAVING); 1970Sstevel@tonic-gate return(0); 1980Sstevel@tonic-gate } 1990Sstevel@tonic-gate 2000Sstevel@tonic-gate int 2010Sstevel@tonic-gate Save(int *msgvec) 2020Sstevel@tonic-gate { 2030Sstevel@tonic-gate return(Save1(msgvec, S_MARK)); 2040Sstevel@tonic-gate } 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate int 2070Sstevel@tonic-gate Copy(int *msgvec) 2080Sstevel@tonic-gate { 2090Sstevel@tonic-gate return(Save1(msgvec, 0)); 2100Sstevel@tonic-gate } 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate /* 2130Sstevel@tonic-gate * save/copy the indicated messages at the end of a file named 2140Sstevel@tonic-gate * by the sender of the first message in the msglist. 2150Sstevel@tonic-gate */ 2160Sstevel@tonic-gate static int 2170Sstevel@tonic-gate Save1(int *msgvec, int mark) 2180Sstevel@tonic-gate { 2190Sstevel@tonic-gate register char *from; 2200Sstevel@tonic-gate char recfile[BUFSIZ]; 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate #ifdef notdef 2230Sstevel@tonic-gate from = striphosts(nameof(&message[*msgvec-1], 0)); 2240Sstevel@tonic-gate #else 2250Sstevel@tonic-gate from = nameof(&message[*msgvec-1]); 2260Sstevel@tonic-gate #endif 2270Sstevel@tonic-gate getrecf(from, recfile, 1, sizeof (recfile)); 2280Sstevel@tonic-gate if (*recfile != '\0') 2290Sstevel@tonic-gate savemsglist(safeexpand(recfile), msgvec, mark | S_SAVING); 2300Sstevel@tonic-gate return(0); 2310Sstevel@tonic-gate } 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate int 2340Sstevel@tonic-gate sput(char str[]) 2350Sstevel@tonic-gate { 2360Sstevel@tonic-gate return(put1(str, 0)); 2370Sstevel@tonic-gate } 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate int 2400Sstevel@tonic-gate Sput(char str[]) 2410Sstevel@tonic-gate { 2420Sstevel@tonic-gate return(put1(str, S_NOIGNORE)); 2430Sstevel@tonic-gate } 2440Sstevel@tonic-gate 2450Sstevel@tonic-gate /* 2460Sstevel@tonic-gate * Put the indicated messages at the end of the passed file name. 2470Sstevel@tonic-gate */ 2480Sstevel@tonic-gate static int 2490Sstevel@tonic-gate put1(char str[], int doign) 2500Sstevel@tonic-gate { 2510Sstevel@tonic-gate char *file; 2520Sstevel@tonic-gate int f, *msgvec; 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec); 2550Sstevel@tonic-gate if ((file = snarf(str, &f, 0)) == NOSTR) 2560Sstevel@tonic-gate file = Getf("MBOX"); 2570Sstevel@tonic-gate if (f==-1) 2580Sstevel@tonic-gate return(1); 2590Sstevel@tonic-gate if (!f) { 2600Sstevel@tonic-gate *msgvec = first(0, MMNORM); 2610Sstevel@tonic-gate if (*msgvec == NULL) { 2620Sstevel@tonic-gate printf(gettext("No messages to put.\n")); 2630Sstevel@tonic-gate return(1); 2640Sstevel@tonic-gate } 2650Sstevel@tonic-gate msgvec[1] = NULL; 2660Sstevel@tonic-gate } 2670Sstevel@tonic-gate if (f && getmsglist(str, msgvec, 0) < 0) 2680Sstevel@tonic-gate return(1); 2690Sstevel@tonic-gate if ((file = expand(file)) == NOSTR) 2700Sstevel@tonic-gate return(1); 2710Sstevel@tonic-gate savemsglist(file, msgvec, doign); 2720Sstevel@tonic-gate return(0); 2730Sstevel@tonic-gate } 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate /* 2760Sstevel@tonic-gate * save a message list in a file. 2770Sstevel@tonic-gate * if wr set, doing "write" instead 2780Sstevel@tonic-gate * of "save" or "copy" so don't put 2790Sstevel@tonic-gate * out header. 2800Sstevel@tonic-gate */ 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate static int wr_linecount; /* count of lines written */ 2830Sstevel@tonic-gate static int wr_charcount; /* char count of lines written */ 2840Sstevel@tonic-gate static int wr_inlines; /* count of lines read */ 2850Sstevel@tonic-gate static long wr_maxlines; /* total lines in message */ 2860Sstevel@tonic-gate static int wr_inhead; /* in header of message */ 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate static void 2890Sstevel@tonic-gate savemsglist(char *file, int *msgvec, int flag) 2900Sstevel@tonic-gate { 2910Sstevel@tonic-gate register int *ip, mesg; 2920Sstevel@tonic-gate register struct message *mp; 2930Sstevel@tonic-gate char *disp; 2940Sstevel@tonic-gate FILE *obuf; 2950Sstevel@tonic-gate struct stat statb; 2960Sstevel@tonic-gate long lc, cc, t; 2970Sstevel@tonic-gate int bnry, mflag; 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate printf("\"%s\" ", file); 3000Sstevel@tonic-gate flush(); 3010Sstevel@tonic-gate if (stat(file, &statb) >= 0) 3020Sstevel@tonic-gate disp = "[Appended]"; 3030Sstevel@tonic-gate else 3040Sstevel@tonic-gate disp = "[New file]"; 3050Sstevel@tonic-gate if ((obuf = fopen(file, "a")) == NULL) { 3060Sstevel@tonic-gate perror(""); 3070Sstevel@tonic-gate return; 3080Sstevel@tonic-gate } 3090Sstevel@tonic-gate lc = cc = 0; 3100Sstevel@tonic-gate bnry = 0; 3110Sstevel@tonic-gate if (flag & S_SAVING) 3120Sstevel@tonic-gate mflag = (int)value("alwaysignore")?(M_IGNORE|M_SAVING):M_SAVING; 3130Sstevel@tonic-gate else if (flag & S_NOIGNORE) 3140Sstevel@tonic-gate mflag = 0; 3150Sstevel@tonic-gate else 3160Sstevel@tonic-gate mflag = M_IGNORE; 3170Sstevel@tonic-gate for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 3180Sstevel@tonic-gate mesg = *ip; 3190Sstevel@tonic-gate mp = &message[mesg-1]; 3200Sstevel@tonic-gate if (!mp->m_text) { 3210Sstevel@tonic-gate bnry = 1; 3220Sstevel@tonic-gate } 3230Sstevel@tonic-gate wr_linecount = 0; 3240Sstevel@tonic-gate wr_charcount = 0; 3250Sstevel@tonic-gate if (flag & S_NOHEADER) { 3260Sstevel@tonic-gate wr_inhead = 1; 3270Sstevel@tonic-gate wr_maxlines = mp->m_lines; 3280Sstevel@tonic-gate wr_inlines = 0; 3290Sstevel@tonic-gate t = msend(mp, obuf, 0, wrputs); 3300Sstevel@tonic-gate } else { 3310Sstevel@tonic-gate t = msend(mp, obuf, mflag, svputs); 3320Sstevel@tonic-gate } 3330Sstevel@tonic-gate if (t < 0) { 3340Sstevel@tonic-gate perror(file); 3350Sstevel@tonic-gate fclose(obuf); 3360Sstevel@tonic-gate return; 3370Sstevel@tonic-gate } 3380Sstevel@tonic-gate touch(mesg); 3390Sstevel@tonic-gate dot = mp; 3400Sstevel@tonic-gate lc += wr_linecount; 3410Sstevel@tonic-gate cc += wr_charcount; 3420Sstevel@tonic-gate if (flag & S_MARK) 3430Sstevel@tonic-gate mp->m_flag |= MSAVED; 3440Sstevel@tonic-gate } 3450Sstevel@tonic-gate fflush(obuf); 3460Sstevel@tonic-gate if (fferror(obuf)) 3470Sstevel@tonic-gate perror(file); 3480Sstevel@tonic-gate fclose(obuf); 3490Sstevel@tonic-gate if (!bnry) { 3500Sstevel@tonic-gate printf("%s %ld/%ld\n", disp, lc, cc); 3510Sstevel@tonic-gate } else { 3520Sstevel@tonic-gate printf("%s binary/%ld\n", disp, cc); 3530Sstevel@tonic-gate } 3540Sstevel@tonic-gate } 3550Sstevel@tonic-gate 3560Sstevel@tonic-gate static int 3570Sstevel@tonic-gate svputs(const char *line, FILE *obuf) 3580Sstevel@tonic-gate { 3590Sstevel@tonic-gate wr_linecount++; 3600Sstevel@tonic-gate wr_charcount += strlen(line); 3610Sstevel@tonic-gate return(fputs(line, obuf)); 3620Sstevel@tonic-gate } 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate static int 3650Sstevel@tonic-gate wrputs(const char *line, FILE *obuf) 3660Sstevel@tonic-gate { 3670Sstevel@tonic-gate /* 3680Sstevel@tonic-gate * If this is a header line or 3690Sstevel@tonic-gate * the last line, don't write it out. Since we may add a 3700Sstevel@tonic-gate * "Status" line the line count may be off by one so insist 3710Sstevel@tonic-gate * that the last line is blank before we skip it. 3720Sstevel@tonic-gate */ 3730Sstevel@tonic-gate wr_inlines++; 3740Sstevel@tonic-gate if (wr_inhead) { 3750Sstevel@tonic-gate if (strcmp(line, "\n") == 0) 3760Sstevel@tonic-gate wr_inhead = 0; 3770Sstevel@tonic-gate return(0); 3780Sstevel@tonic-gate } 3790Sstevel@tonic-gate if (wr_inlines >= wr_maxlines && strcmp(line, "\n") == 0) 3800Sstevel@tonic-gate return(0); 3810Sstevel@tonic-gate wr_linecount++; 3820Sstevel@tonic-gate wr_charcount += strlen(line); 3830Sstevel@tonic-gate return(fputs(line, obuf)); 3840Sstevel@tonic-gate } 3850Sstevel@tonic-gate 3860Sstevel@tonic-gate /* 3870Sstevel@tonic-gate * Write the indicated messages at the end of the passed 3880Sstevel@tonic-gate * file name, minus header and trailing blank line. 3890Sstevel@tonic-gate */ 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate int 3920Sstevel@tonic-gate swrite(char str[]) 3930Sstevel@tonic-gate { 3940Sstevel@tonic-gate register char *file; 3950Sstevel@tonic-gate int f, *msgvec; 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec); 3980Sstevel@tonic-gate if ((file = snarf(str, &f, 1)) == NOSTR) 3990Sstevel@tonic-gate return(1); 4000Sstevel@tonic-gate if (f==-1) 4010Sstevel@tonic-gate return(1); 4020Sstevel@tonic-gate if ((file = expand(file)) == NOSTR) 4030Sstevel@tonic-gate return(1); 4040Sstevel@tonic-gate if (!f) { 4050Sstevel@tonic-gate *msgvec = first(0, MMNORM); 4060Sstevel@tonic-gate if (*msgvec == NULL) { 4070Sstevel@tonic-gate printf(gettext("No messages to write.\n")); 4080Sstevel@tonic-gate return(1); 4090Sstevel@tonic-gate } 4100Sstevel@tonic-gate msgvec[1] = NULL; 4110Sstevel@tonic-gate } 4120Sstevel@tonic-gate if (f && getmsglist(str, msgvec, 0) < 0) 4130Sstevel@tonic-gate return(1); 4140Sstevel@tonic-gate savemsglist(file, msgvec, S_MARK|S_NOHEADER); 4150Sstevel@tonic-gate return(0); 4160Sstevel@tonic-gate } 4170Sstevel@tonic-gate 4180Sstevel@tonic-gate /* 4190Sstevel@tonic-gate * Snarf the file from the end of the command line and 4200Sstevel@tonic-gate * return a pointer to it. If there is no file attached, 4210Sstevel@tonic-gate * just return NOSTR. Put a null in front of the file 4220Sstevel@tonic-gate * name so that the message list processing won't see it, 4230Sstevel@tonic-gate * unless the file name is the only thing on the line, in 4240Sstevel@tonic-gate * which case, return 0 in the reference flag variable. 4250Sstevel@tonic-gate */ 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate /* 4280Sstevel@tonic-gate * The following definitions are used to characterize the syntactic 4290Sstevel@tonic-gate * category of the preceding character in the following parse procedure. 4300Sstevel@tonic-gate * The variable pc_type assumes these values. 4310Sstevel@tonic-gate */ 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate #define SN_DELIM 1 /* Delimiter (<blank> or line beginning) */ 4340Sstevel@tonic-gate #define SN_TOKEN 2 /* A part of a token */ 4350Sstevel@tonic-gate #define SN_QUOTE 4 /* An entire quoted string (ie, "...") */ 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate char * 4380Sstevel@tonic-gate snarf(char linebuf[], int *flag, int erf) 4390Sstevel@tonic-gate { 4400Sstevel@tonic-gate register char *p; /* utility pointer */ 4410Sstevel@tonic-gate register char qc; /* quotation character to match */ 4420Sstevel@tonic-gate register unsigned int pc_type; /* preceding character type */ 4430Sstevel@tonic-gate register char *tok_beg; /* beginning of last token */ 4440Sstevel@tonic-gate register char *tok_end; /* end of last token */ 4450Sstevel@tonic-gate char *line_beg; /* beginning of line, after */ 4460Sstevel@tonic-gate /* leading whitespace */ 4470Sstevel@tonic-gate 4480Sstevel@tonic-gate /* 4490Sstevel@tonic-gate * Skip leading whitespace. 4500Sstevel@tonic-gate */ 4510Sstevel@tonic-gate for (line_beg = linebuf; 4520Sstevel@tonic-gate *line_beg && any(*line_beg, " \t"); 4530Sstevel@tonic-gate line_beg++) { 4540Sstevel@tonic-gate /* empty body */ 4550Sstevel@tonic-gate } 4560Sstevel@tonic-gate if (!*line_beg) { 4570Sstevel@tonic-gate if (erf) { 4580Sstevel@tonic-gate printf(gettext("No file specified\n.")); 4590Sstevel@tonic-gate } 4600Sstevel@tonic-gate *flag = 0; 4610Sstevel@tonic-gate return(NOSTR); 4620Sstevel@tonic-gate } 4630Sstevel@tonic-gate /* 4640Sstevel@tonic-gate * Process line from left-to-right, 1 char at a time. 4650Sstevel@tonic-gate */ 4660Sstevel@tonic-gate for (pc_type = SN_DELIM, tok_beg = tok_end = NOSTR, p = line_beg; 4670Sstevel@tonic-gate *p != '\0'; ) { 4680Sstevel@tonic-gate if (any(*p, " \t")) { 4690Sstevel@tonic-gate /* This character is a DELIMITER */ 4700Sstevel@tonic-gate if (pc_type & (SN_TOKEN|SN_QUOTE)) { 4710Sstevel@tonic-gate tok_end = p - 1; 4720Sstevel@tonic-gate } 4730Sstevel@tonic-gate pc_type = SN_DELIM; 4740Sstevel@tonic-gate p++; 4750Sstevel@tonic-gate } else if ((qc = *p) == '"' || qc == '\'') { 4760Sstevel@tonic-gate /* This character is a QUOTE character */ 4770Sstevel@tonic-gate if (pc_type == SN_TOKEN) { 4780Sstevel@tonic-gate /* embedded quotation symbols are simply */ 4790Sstevel@tonic-gate /* token characters. */ 4800Sstevel@tonic-gate p++; 4810Sstevel@tonic-gate continue; 4820Sstevel@tonic-gate } 4830Sstevel@tonic-gate /* Search for the matching QUOTE character */ 4840Sstevel@tonic-gate for (tok_beg = p, tok_end = NOSTR, p++; 4850Sstevel@tonic-gate *p != '\0' && *p != qc; 4860Sstevel@tonic-gate p++) { 4870Sstevel@tonic-gate if (*p == '\\' && *(p+1) == qc) { 4880Sstevel@tonic-gate p++; 4890Sstevel@tonic-gate } 4900Sstevel@tonic-gate } 4910Sstevel@tonic-gate if (*p == '\0') { 4920Sstevel@tonic-gate printf(gettext("Syntax error: missing " 4930Sstevel@tonic-gate "%c.\n"), qc); 4940Sstevel@tonic-gate *flag = -1; 4950Sstevel@tonic-gate return(NOSTR); 4960Sstevel@tonic-gate } 4970Sstevel@tonic-gate tok_end = p; 4980Sstevel@tonic-gate pc_type = SN_QUOTE; 4990Sstevel@tonic-gate p++; 5000Sstevel@tonic-gate } else { 5010Sstevel@tonic-gate /* This character should be a TOKEN character */ 5020Sstevel@tonic-gate if (pc_type & (SN_DELIM|SN_TOKEN)) { 5030Sstevel@tonic-gate if (pc_type & SN_DELIM) { 5040Sstevel@tonic-gate tok_beg = p; 5050Sstevel@tonic-gate tok_end = NOSTR; 5060Sstevel@tonic-gate } 5070Sstevel@tonic-gate } else { 5080Sstevel@tonic-gate printf(gettext("improper quotes" 5090Sstevel@tonic-gate " at \"%s\".\n"), p); 5100Sstevel@tonic-gate *flag = -1; 5110Sstevel@tonic-gate return(NOSTR); 5120Sstevel@tonic-gate } 5130Sstevel@tonic-gate if (*p == '\\' && *++p == '\0') { 5140Sstevel@tonic-gate printf(gettext("\'\\\' at " 5150Sstevel@tonic-gate "end of line.\n")); 5160Sstevel@tonic-gate *flag = -1; 5170Sstevel@tonic-gate return(NOSTR); 5180Sstevel@tonic-gate } 5190Sstevel@tonic-gate pc_type = SN_TOKEN; 5200Sstevel@tonic-gate p++; 5210Sstevel@tonic-gate } 5220Sstevel@tonic-gate } 5230Sstevel@tonic-gate if (pc_type == SN_TOKEN) { 5240Sstevel@tonic-gate tok_end = p - 1; 5250Sstevel@tonic-gate } 5260Sstevel@tonic-gate if (tok_beg != NOSTR && tok_end != NOSTR) { 5270Sstevel@tonic-gate if (tok_beg == line_beg) { 5280Sstevel@tonic-gate *flag = 0; 5290Sstevel@tonic-gate } else { 5300Sstevel@tonic-gate tok_beg[-1] = '\0'; 5310Sstevel@tonic-gate *flag = 1; 5320Sstevel@tonic-gate } 5330Sstevel@tonic-gate tok_end[1] = '\0'; 5340Sstevel@tonic-gate return(tok_beg); 5350Sstevel@tonic-gate } else { 5360Sstevel@tonic-gate if (erf) { 5370Sstevel@tonic-gate printf(gettext("No file specified\n.")); 5380Sstevel@tonic-gate } 5390Sstevel@tonic-gate *flag = 0; 5400Sstevel@tonic-gate return(NOSTR); 5410Sstevel@tonic-gate } 5420Sstevel@tonic-gate } 5430Sstevel@tonic-gate 5440Sstevel@tonic-gate /* 5450Sstevel@tonic-gate * Delete messages, then type the new dot. 5460Sstevel@tonic-gate */ 5470Sstevel@tonic-gate 5480Sstevel@tonic-gate int 5490Sstevel@tonic-gate deltype(int msgvec[]) 5500Sstevel@tonic-gate { 5510Sstevel@tonic-gate int list[2]; 5520Sstevel@tonic-gate int lastdot; 5530Sstevel@tonic-gate 5540Sstevel@tonic-gate lastdot = dot - &message[0] + 1; 5550Sstevel@tonic-gate if (delm(msgvec) >= 0) { 5560Sstevel@tonic-gate list[0] = dot - &message[0]; 5570Sstevel@tonic-gate list[0]++; 5580Sstevel@tonic-gate if (list[0] > lastdot) { 5590Sstevel@tonic-gate touch(list[0]); 5600Sstevel@tonic-gate list[1] = NULL; 5610Sstevel@tonic-gate return(type(list)); 5620Sstevel@tonic-gate } 5630Sstevel@tonic-gate printf(gettext("At EOF\n")); 5640Sstevel@tonic-gate return(0); 5650Sstevel@tonic-gate } 5660Sstevel@tonic-gate else { 5670Sstevel@tonic-gate printf(gettext("No more messages\n")); 5680Sstevel@tonic-gate return(0); 5690Sstevel@tonic-gate } 5700Sstevel@tonic-gate } 5710Sstevel@tonic-gate 5720Sstevel@tonic-gate /* 5730Sstevel@tonic-gate * Delete the indicated messages. 5740Sstevel@tonic-gate * Set dot to some nice place afterwards. 5750Sstevel@tonic-gate */ 5760Sstevel@tonic-gate int 5770Sstevel@tonic-gate delm(int *msgvec) 5780Sstevel@tonic-gate { 5790Sstevel@tonic-gate register struct message *mp; 580*18Srobbin int *ip, mesg; 5810Sstevel@tonic-gate int last; 5820Sstevel@tonic-gate 5830Sstevel@tonic-gate last = NULL; 5840Sstevel@tonic-gate for (ip = msgvec; *ip != NULL; ip++) { 5850Sstevel@tonic-gate mesg = *ip; 5860Sstevel@tonic-gate touch(mesg); 5870Sstevel@tonic-gate mp = &message[mesg-1]; 5880Sstevel@tonic-gate mp->m_flag |= MDELETED|MTOUCH; 5890Sstevel@tonic-gate mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); 5900Sstevel@tonic-gate last = mesg; 5910Sstevel@tonic-gate } 5920Sstevel@tonic-gate if (last != NULL) { 5930Sstevel@tonic-gate dot = &message[last-1]; 5940Sstevel@tonic-gate last = first(0, MDELETED); 5950Sstevel@tonic-gate if (last != NULL) { 5960Sstevel@tonic-gate dot = &message[last-1]; 5970Sstevel@tonic-gate return(0); 5980Sstevel@tonic-gate } 5990Sstevel@tonic-gate else { 6000Sstevel@tonic-gate dot = &message[0]; 6010Sstevel@tonic-gate return(-1); 6020Sstevel@tonic-gate } 6030Sstevel@tonic-gate } 6040Sstevel@tonic-gate 6050Sstevel@tonic-gate /* 6060Sstevel@tonic-gate * Following can't happen -- it keeps lint happy 6070Sstevel@tonic-gate */ 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate return(-1); 6100Sstevel@tonic-gate } 6110Sstevel@tonic-gate 6120Sstevel@tonic-gate /* 6130Sstevel@tonic-gate * Undelete the indicated messages. 6140Sstevel@tonic-gate */ 6150Sstevel@tonic-gate int 6160Sstevel@tonic-gate undelete(int *msgvec) 6170Sstevel@tonic-gate { 6180Sstevel@tonic-gate register struct message *mp; 619*18Srobbin int *ip, mesg; 6200Sstevel@tonic-gate 6210Sstevel@tonic-gate for (ip = msgvec; ip-msgvec < msgCount; ip++) { 6220Sstevel@tonic-gate mesg = *ip; 6230Sstevel@tonic-gate if (mesg == 0) 6240Sstevel@tonic-gate return(0); 6250Sstevel@tonic-gate touch(mesg); 6260Sstevel@tonic-gate mp = &message[mesg-1]; 6270Sstevel@tonic-gate dot = mp; 6280Sstevel@tonic-gate mp->m_flag &= ~MDELETED; 6290Sstevel@tonic-gate } 6300Sstevel@tonic-gate return(0); 6310Sstevel@tonic-gate } 6320Sstevel@tonic-gate 6330Sstevel@tonic-gate /* 6340Sstevel@tonic-gate * Add the given header fields to the retained list. 6350Sstevel@tonic-gate * If no arguments, print the current list of retained fields. 6360Sstevel@tonic-gate */ 6370Sstevel@tonic-gate int 6380Sstevel@tonic-gate retfield(char *list[]) 6390Sstevel@tonic-gate { 6400Sstevel@tonic-gate char field[BUFSIZ]; 6410Sstevel@tonic-gate register int h; 6420Sstevel@tonic-gate register struct ignore *igp; 6430Sstevel@tonic-gate char **ap; 6440Sstevel@tonic-gate 6450Sstevel@tonic-gate if (argcount(list) == 0) 6460Sstevel@tonic-gate return(retshow()); 6470Sstevel@tonic-gate for (ap = list; *ap != 0; ap++) { 6480Sstevel@tonic-gate istrcpy(field, sizeof (field), *ap); 6490Sstevel@tonic-gate 6500Sstevel@tonic-gate if (member(field, retain)) 6510Sstevel@tonic-gate continue; 6520Sstevel@tonic-gate 6530Sstevel@tonic-gate h = hash(field); 6540Sstevel@tonic-gate if ((igp = (struct ignore *) 6550Sstevel@tonic-gate calloc(1, sizeof (struct ignore))) == NULL) { 6560Sstevel@tonic-gate panic("Couldn't allocate memory"); 6570Sstevel@tonic-gate } 6580Sstevel@tonic-gate if ((igp->i_field = (char *) 6590Sstevel@tonic-gate calloc(strlen(field) + 1, sizeof (char))) == NULL) { 6600Sstevel@tonic-gate panic("Couldn't allocate memory"); 6610Sstevel@tonic-gate } 6620Sstevel@tonic-gate strcpy(igp->i_field, field); 6630Sstevel@tonic-gate igp->i_link = retain[h]; 6640Sstevel@tonic-gate retain[h] = igp; 6650Sstevel@tonic-gate nretained++; 6660Sstevel@tonic-gate } 6670Sstevel@tonic-gate return(0); 6680Sstevel@tonic-gate } 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate /* 6710Sstevel@tonic-gate * Print out all currently retained fields. 6720Sstevel@tonic-gate */ 6730Sstevel@tonic-gate static int 6740Sstevel@tonic-gate retshow(void) 6750Sstevel@tonic-gate { 6760Sstevel@tonic-gate register int h, count; 6770Sstevel@tonic-gate struct ignore *igp; 6780Sstevel@tonic-gate char **ap, **ring; 6790Sstevel@tonic-gate 6800Sstevel@tonic-gate count = 0; 6810Sstevel@tonic-gate for (h = 0; h < HSHSIZE; h++) 6820Sstevel@tonic-gate for (igp = retain[h]; igp != 0; igp = igp->i_link) 6830Sstevel@tonic-gate count++; 6840Sstevel@tonic-gate if (count == 0) { 6850Sstevel@tonic-gate printf(gettext("No fields currently being retained.\n")); 6860Sstevel@tonic-gate return(0); 6870Sstevel@tonic-gate } 6880Sstevel@tonic-gate ring = (char **) salloc((count + 1) * sizeof (char *)); 6890Sstevel@tonic-gate ap = ring; 6900Sstevel@tonic-gate for (h = 0; h < HSHSIZE; h++) 6910Sstevel@tonic-gate for (igp = retain[h]; igp != 0; igp = igp->i_link) 6920Sstevel@tonic-gate *ap++ = igp->i_field; 6930Sstevel@tonic-gate *ap = 0; 6940Sstevel@tonic-gate qsort(ring, count, sizeof (char *), igcomp); 6950Sstevel@tonic-gate for (ap = ring; *ap != 0; ap++) 6960Sstevel@tonic-gate printf("%s\n", *ap); 6970Sstevel@tonic-gate return(0); 6980Sstevel@tonic-gate } 6990Sstevel@tonic-gate 7000Sstevel@tonic-gate /* 7010Sstevel@tonic-gate * Remove a list of fields from the retain list. 7020Sstevel@tonic-gate */ 7030Sstevel@tonic-gate int 7040Sstevel@tonic-gate unretfield(char *list[]) 7050Sstevel@tonic-gate { 7060Sstevel@tonic-gate char **ap, field[BUFSIZ]; 7070Sstevel@tonic-gate register int h, count = 0; 7080Sstevel@tonic-gate register struct ignore *ig1, *ig2; 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate if (argcount(list) == 0) { 7110Sstevel@tonic-gate for (h = 0; h < HSHSIZE; h++) { 7120Sstevel@tonic-gate ig1 = retain[h]; 7130Sstevel@tonic-gate while (ig1) { 7140Sstevel@tonic-gate free(ig1->i_field); 7150Sstevel@tonic-gate ig2 = ig1->i_link; 7160Sstevel@tonic-gate free((char *) ig1); 7170Sstevel@tonic-gate ig1 = ig2; 7180Sstevel@tonic-gate count++; 7190Sstevel@tonic-gate } 7200Sstevel@tonic-gate retain[h] = NULL; 7210Sstevel@tonic-gate } 7220Sstevel@tonic-gate if (count == 0) 7230Sstevel@tonic-gate printf(gettext( 7240Sstevel@tonic-gate "No fields currently being retained.\n")); 7250Sstevel@tonic-gate nretained = 0; 7260Sstevel@tonic-gate return 0; 7270Sstevel@tonic-gate } 7280Sstevel@tonic-gate for (ap = list; *ap; ap++) { 7290Sstevel@tonic-gate istrcpy(field, sizeof (field), *ap); 7300Sstevel@tonic-gate h = hash(field); 7310Sstevel@tonic-gate for (ig1 = retain[h]; ig1; ig2 = ig1, ig1 = ig1->i_link) 7320Sstevel@tonic-gate if (strcmp(ig1->i_field, field) == 0) { 7330Sstevel@tonic-gate if (ig1 == retain[h]) 7340Sstevel@tonic-gate retain[h] = ig1->i_link; 7350Sstevel@tonic-gate else 7360Sstevel@tonic-gate ig2->i_link = ig1->i_link; 7370Sstevel@tonic-gate free(ig1->i_field); 7380Sstevel@tonic-gate free((char *) ig1); 7390Sstevel@tonic-gate nretained--; 7400Sstevel@tonic-gate break; 7410Sstevel@tonic-gate } 7420Sstevel@tonic-gate } 7430Sstevel@tonic-gate return 0; 7440Sstevel@tonic-gate } 7450Sstevel@tonic-gate 7460Sstevel@tonic-gate /* 7470Sstevel@tonic-gate * Add the given header fields to the ignored list. 7480Sstevel@tonic-gate * If no arguments, print the current list of ignored fields. 7490Sstevel@tonic-gate */ 7500Sstevel@tonic-gate int 7510Sstevel@tonic-gate igfield(char *list[]) 7520Sstevel@tonic-gate { 7530Sstevel@tonic-gate char field[BUFSIZ]; 7540Sstevel@tonic-gate register int h; 7550Sstevel@tonic-gate register struct ignore *igp; 7560Sstevel@tonic-gate char **ap; 7570Sstevel@tonic-gate 7580Sstevel@tonic-gate if (argcount(list) == 0) 7590Sstevel@tonic-gate return(igshow()); 7600Sstevel@tonic-gate for (ap = list; *ap != 0; ap++) { 7610Sstevel@tonic-gate if (isign(*ap, 0)) 7620Sstevel@tonic-gate continue; 7630Sstevel@tonic-gate istrcpy(field, sizeof (field), *ap); 7640Sstevel@tonic-gate h = hash(field); 7650Sstevel@tonic-gate if ((igp = (struct ignore *) 7660Sstevel@tonic-gate calloc(1, sizeof (struct ignore))) == NULL) { 7670Sstevel@tonic-gate panic("Couldn't allocate memory"); 7680Sstevel@tonic-gate } 7690Sstevel@tonic-gate if ((igp->i_field = (char *) 7700Sstevel@tonic-gate calloc((unsigned)strlen(field) + 1, 7710Sstevel@tonic-gate sizeof (char))) == NULL) { 7720Sstevel@tonic-gate panic("Couldn't allocate memory"); 7730Sstevel@tonic-gate } 7740Sstevel@tonic-gate strcpy(igp->i_field, field); 7750Sstevel@tonic-gate igp->i_link = ignore[h]; 7760Sstevel@tonic-gate ignore[h] = igp; 7770Sstevel@tonic-gate } 7780Sstevel@tonic-gate return(0); 7790Sstevel@tonic-gate } 7800Sstevel@tonic-gate 7810Sstevel@tonic-gate /* 7820Sstevel@tonic-gate * Print out all currently ignored fields. 7830Sstevel@tonic-gate */ 7840Sstevel@tonic-gate static int 7850Sstevel@tonic-gate igshow(void) 7860Sstevel@tonic-gate { 7870Sstevel@tonic-gate register int h, count; 7880Sstevel@tonic-gate struct ignore *igp; 7890Sstevel@tonic-gate char **ap, **ring; 7900Sstevel@tonic-gate 7910Sstevel@tonic-gate count = 0; 7920Sstevel@tonic-gate for (h = 0; h < HSHSIZE; h++) 7930Sstevel@tonic-gate for (igp = ignore[h]; igp != 0; igp = igp->i_link) 7940Sstevel@tonic-gate count++; 7950Sstevel@tonic-gate if (count == 0) { 7960Sstevel@tonic-gate printf(gettext("No fields currently being ignored.\n")); 7970Sstevel@tonic-gate return(0); 7980Sstevel@tonic-gate } 7990Sstevel@tonic-gate ring = (char **) salloc((count + 1) * sizeof (char *)); 8000Sstevel@tonic-gate ap = ring; 8010Sstevel@tonic-gate for (h = 0; h < HSHSIZE; h++) 8020Sstevel@tonic-gate for (igp = ignore[h]; igp != 0; igp = igp->i_link) 8030Sstevel@tonic-gate *ap++ = igp->i_field; 8040Sstevel@tonic-gate *ap = 0; 8050Sstevel@tonic-gate qsort((char *) ring, (unsigned) count, sizeof (char *), igcomp); 8060Sstevel@tonic-gate for (ap = ring; *ap != 0; ap++) 8070Sstevel@tonic-gate printf("%s\n", *ap); 8080Sstevel@tonic-gate return(0); 8090Sstevel@tonic-gate } 8100Sstevel@tonic-gate 8110Sstevel@tonic-gate /* 8120Sstevel@tonic-gate * Compare two names for sorting ignored field list. 8130Sstevel@tonic-gate */ 8140Sstevel@tonic-gate static int 8150Sstevel@tonic-gate igcomp(const void *l, const void *r) 8160Sstevel@tonic-gate { 8170Sstevel@tonic-gate return(strcmp(*(char **)l, *(char **)r)); 8180Sstevel@tonic-gate } 8190Sstevel@tonic-gate 8200Sstevel@tonic-gate /* 8210Sstevel@tonic-gate * Remove a list of fields from the ignore list. 8220Sstevel@tonic-gate */ 8230Sstevel@tonic-gate int 8240Sstevel@tonic-gate unigfield(char *list[]) 8250Sstevel@tonic-gate { 8260Sstevel@tonic-gate char **ap, field[BUFSIZ]; 8270Sstevel@tonic-gate register int h, count = 0; 8280Sstevel@tonic-gate register struct ignore *ig1, *ig2; 8290Sstevel@tonic-gate 8300Sstevel@tonic-gate if (argcount(list) == 0) { 8310Sstevel@tonic-gate for (h = 0; h < HSHSIZE; h++) { 8320Sstevel@tonic-gate ig1 = ignore[h]; 8330Sstevel@tonic-gate while (ig1) { 8340Sstevel@tonic-gate free(ig1->i_field); 8350Sstevel@tonic-gate ig2 = ig1->i_link; 8360Sstevel@tonic-gate free((char *) ig1); 8370Sstevel@tonic-gate ig1 = ig2; 8380Sstevel@tonic-gate count++; 8390Sstevel@tonic-gate } 8400Sstevel@tonic-gate ignore[h] = NULL; 8410Sstevel@tonic-gate } 8420Sstevel@tonic-gate if (count == 0) 8430Sstevel@tonic-gate printf(gettext("No fields currently being ignored.\n")); 8440Sstevel@tonic-gate return 0; 8450Sstevel@tonic-gate } 8460Sstevel@tonic-gate for (ap = list; *ap; ap++) { 8470Sstevel@tonic-gate istrcpy(field, sizeof (field), *ap); 8480Sstevel@tonic-gate h = hash(field); 8490Sstevel@tonic-gate for (ig1 = ignore[h]; ig1; ig2 = ig1, ig1 = ig1->i_link) 8500Sstevel@tonic-gate if (strcmp(ig1->i_field, field) == 0) { 8510Sstevel@tonic-gate if (ig1 == ignore[h]) 8520Sstevel@tonic-gate ignore[h] = ig1->i_link; 8530Sstevel@tonic-gate else 8540Sstevel@tonic-gate ig2->i_link = ig1->i_link; 8550Sstevel@tonic-gate free(ig1->i_field); 8560Sstevel@tonic-gate free((char *) ig1); 8570Sstevel@tonic-gate break; 8580Sstevel@tonic-gate } 8590Sstevel@tonic-gate } 8600Sstevel@tonic-gate return 0; 8610Sstevel@tonic-gate } 862