fio.c revision 1590
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1980, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 341590Srgrimes#ifndef lint 351590Srgrimesstatic char sccsid[] = "@(#)fio.c 8.1 (Berkeley) 6/6/93"; 361590Srgrimes#endif /* not lint */ 371590Srgrimes 381590Srgrimes#include "rcv.h" 391590Srgrimes#include <sys/file.h> 401590Srgrimes#include <sys/wait.h> 411590Srgrimes 421590Srgrimes#include <unistd.h> 431590Srgrimes#include <paths.h> 441590Srgrimes#include <errno.h> 451590Srgrimes#include "extern.h" 461590Srgrimes 471590Srgrimes/* 481590Srgrimes * Mail -- a mail program 491590Srgrimes * 501590Srgrimes * File I/O. 511590Srgrimes */ 521590Srgrimes 531590Srgrimes/* 541590Srgrimes * Set up the input pointers while copying the mail file into /tmp. 551590Srgrimes */ 561590Srgrimesvoid 571590Srgrimessetptr(ibuf) 581590Srgrimes register FILE *ibuf; 591590Srgrimes{ 601590Srgrimes extern char *tmpdir; 611590Srgrimes register int c, count; 621590Srgrimes register char *cp, *cp2; 631590Srgrimes struct message this; 641590Srgrimes FILE *mestmp; 651590Srgrimes off_t offset; 661590Srgrimes int maybe, inhead; 671590Srgrimes char linebuf[LINESIZE]; 681590Srgrimes 691590Srgrimes /* Get temporary file. */ 701590Srgrimes (void)sprintf(linebuf, "%s/mail.XXXXXX", tmpdir); 711590Srgrimes if ((c = mkstemp(linebuf)) == -1 || 721590Srgrimes (mestmp = Fdopen(c, "r+")) == NULL) { 731590Srgrimes (void)fprintf(stderr, "mail: can't open %s\n", linebuf); 741590Srgrimes exit(1); 751590Srgrimes } 761590Srgrimes (void)unlink(linebuf); 771590Srgrimes 781590Srgrimes msgCount = 0; 791590Srgrimes maybe = 1; 801590Srgrimes inhead = 0; 811590Srgrimes offset = 0; 821590Srgrimes this.m_flag = MUSED|MNEW; 831590Srgrimes this.m_size = 0; 841590Srgrimes this.m_lines = 0; 851590Srgrimes this.m_block = 0; 861590Srgrimes this.m_offset = 0; 871590Srgrimes for (;;) { 881590Srgrimes if (fgets(linebuf, LINESIZE, ibuf) == NULL) { 891590Srgrimes if (append(&this, mestmp)) { 901590Srgrimes perror("temporary file"); 911590Srgrimes exit(1); 921590Srgrimes } 931590Srgrimes makemessage(mestmp); 941590Srgrimes return; 951590Srgrimes } 961590Srgrimes count = strlen(linebuf); 971590Srgrimes (void) fwrite(linebuf, sizeof *linebuf, count, otf); 981590Srgrimes if (ferror(otf)) { 991590Srgrimes perror("/tmp"); 1001590Srgrimes exit(1); 1011590Srgrimes } 1021590Srgrimes linebuf[count - 1] = 0; 1031590Srgrimes if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 1041590Srgrimes msgCount++; 1051590Srgrimes if (append(&this, mestmp)) { 1061590Srgrimes perror("temporary file"); 1071590Srgrimes exit(1); 1081590Srgrimes } 1091590Srgrimes this.m_flag = MUSED|MNEW; 1101590Srgrimes this.m_size = 0; 1111590Srgrimes this.m_lines = 0; 1121590Srgrimes this.m_block = blockof(offset); 1131590Srgrimes this.m_offset = offsetof(offset); 1141590Srgrimes inhead = 1; 1151590Srgrimes } else if (linebuf[0] == 0) { 1161590Srgrimes inhead = 0; 1171590Srgrimes } else if (inhead) { 1181590Srgrimes for (cp = linebuf, cp2 = "status";; cp++) { 1191590Srgrimes if ((c = *cp2++) == 0) { 1201590Srgrimes while (isspace(*cp++)) 1211590Srgrimes ; 1221590Srgrimes if (cp[-1] != ':') 1231590Srgrimes break; 1241590Srgrimes while (c = *cp++) 1251590Srgrimes if (c == 'R') 1261590Srgrimes this.m_flag |= MREAD; 1271590Srgrimes else if (c == 'O') 1281590Srgrimes this.m_flag &= ~MNEW; 1291590Srgrimes inhead = 0; 1301590Srgrimes break; 1311590Srgrimes } 1321590Srgrimes if (*cp != c && *cp != toupper(c)) 1331590Srgrimes break; 1341590Srgrimes } 1351590Srgrimes } 1361590Srgrimes offset += count; 1371590Srgrimes this.m_size += count; 1381590Srgrimes this.m_lines++; 1391590Srgrimes maybe = linebuf[0] == 0; 1401590Srgrimes } 1411590Srgrimes} 1421590Srgrimes 1431590Srgrimes/* 1441590Srgrimes * Drop the passed line onto the passed output buffer. 1451590Srgrimes * If a write error occurs, return -1, else the count of 1461590Srgrimes * characters written, including the newline. 1471590Srgrimes */ 1481590Srgrimesint 1491590Srgrimesputline(obuf, linebuf) 1501590Srgrimes FILE *obuf; 1511590Srgrimes char *linebuf; 1521590Srgrimes{ 1531590Srgrimes register int c; 1541590Srgrimes 1551590Srgrimes c = strlen(linebuf); 1561590Srgrimes (void) fwrite(linebuf, sizeof *linebuf, c, obuf); 1571590Srgrimes (void) putc('\n', obuf); 1581590Srgrimes if (ferror(obuf)) 1591590Srgrimes return (-1); 1601590Srgrimes return (c + 1); 1611590Srgrimes} 1621590Srgrimes 1631590Srgrimes/* 1641590Srgrimes * Read up a line from the specified input into the line 1651590Srgrimes * buffer. Return the number of characters read. Do not 1661590Srgrimes * include the newline at the end. 1671590Srgrimes */ 1681590Srgrimesint 1691590Srgrimesreadline(ibuf, linebuf, linesize) 1701590Srgrimes FILE *ibuf; 1711590Srgrimes char *linebuf; 1721590Srgrimes int linesize; 1731590Srgrimes{ 1741590Srgrimes register int n; 1751590Srgrimes 1761590Srgrimes clearerr(ibuf); 1771590Srgrimes if (fgets(linebuf, linesize, ibuf) == NULL) 1781590Srgrimes return -1; 1791590Srgrimes n = strlen(linebuf); 1801590Srgrimes if (n > 0 && linebuf[n - 1] == '\n') 1811590Srgrimes linebuf[--n] = '\0'; 1821590Srgrimes return n; 1831590Srgrimes} 1841590Srgrimes 1851590Srgrimes/* 1861590Srgrimes * Return a file buffer all ready to read up the 1871590Srgrimes * passed message pointer. 1881590Srgrimes */ 1891590SrgrimesFILE * 1901590Srgrimessetinput(mp) 1911590Srgrimes register struct message *mp; 1921590Srgrimes{ 1931590Srgrimes 1941590Srgrimes fflush(otf); 1951590Srgrimes if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) { 1961590Srgrimes perror("fseek"); 1971590Srgrimes panic("temporary file seek"); 1981590Srgrimes } 1991590Srgrimes return (itf); 2001590Srgrimes} 2011590Srgrimes 2021590Srgrimes/* 2031590Srgrimes * Take the data out of the passed ghost file and toss it into 2041590Srgrimes * a dynamically allocated message structure. 2051590Srgrimes */ 2061590Srgrimesvoid 2071590Srgrimesmakemessage(f) 2081590Srgrimes FILE *f; 2091590Srgrimes{ 2101590Srgrimes register size = (msgCount + 1) * sizeof (struct message); 2111590Srgrimes 2121590Srgrimes if (message != 0) 2131590Srgrimes free((char *) message); 2141590Srgrimes if ((message = (struct message *) malloc((unsigned) size)) == 0) 2151590Srgrimes panic("Insufficient memory for %d messages", msgCount); 2161590Srgrimes dot = message; 2171590Srgrimes size -= sizeof (struct message); 2181590Srgrimes fflush(f); 2191590Srgrimes (void) lseek(fileno(f), (off_t)sizeof *message, 0); 2201590Srgrimes if (read(fileno(f), (char *) message, size) != size) 2211590Srgrimes panic("Message temporary file corrupted"); 2221590Srgrimes message[msgCount].m_size = 0; 2231590Srgrimes message[msgCount].m_lines = 0; 2241590Srgrimes Fclose(f); 2251590Srgrimes} 2261590Srgrimes 2271590Srgrimes/* 2281590Srgrimes * Append the passed message descriptor onto the temp file. 2291590Srgrimes * If the write fails, return 1, else 0 2301590Srgrimes */ 2311590Srgrimesint 2321590Srgrimesappend(mp, f) 2331590Srgrimes struct message *mp; 2341590Srgrimes FILE *f; 2351590Srgrimes{ 2361590Srgrimes return fwrite((char *) mp, sizeof *mp, 1, f) != 1; 2371590Srgrimes} 2381590Srgrimes 2391590Srgrimes/* 2401590Srgrimes * Delete a file, but only if the file is a plain file. 2411590Srgrimes */ 2421590Srgrimesint 2431590Srgrimesrm(name) 2441590Srgrimes char *name; 2451590Srgrimes{ 2461590Srgrimes struct stat sb; 2471590Srgrimes 2481590Srgrimes if (stat(name, &sb) < 0) 2491590Srgrimes return(-1); 2501590Srgrimes if (!S_ISREG(sb.st_mode)) { 2511590Srgrimes errno = EISDIR; 2521590Srgrimes return(-1); 2531590Srgrimes } 2541590Srgrimes return(unlink(name)); 2551590Srgrimes} 2561590Srgrimes 2571590Srgrimesstatic int sigdepth; /* depth of holdsigs() */ 2581590Srgrimesstatic int omask; 2591590Srgrimes/* 2601590Srgrimes * Hold signals SIGHUP, SIGINT, and SIGQUIT. 2611590Srgrimes */ 2621590Srgrimesvoid 2631590Srgrimesholdsigs() 2641590Srgrimes{ 2651590Srgrimes 2661590Srgrimes if (sigdepth++ == 0) 2671590Srgrimes omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)); 2681590Srgrimes} 2691590Srgrimes 2701590Srgrimes/* 2711590Srgrimes * Release signals SIGHUP, SIGINT, and SIGQUIT. 2721590Srgrimes */ 2731590Srgrimesvoid 2741590Srgrimesrelsesigs() 2751590Srgrimes{ 2761590Srgrimes 2771590Srgrimes if (--sigdepth == 0) 2781590Srgrimes sigsetmask(omask); 2791590Srgrimes} 2801590Srgrimes 2811590Srgrimes/* 2821590Srgrimes * Determine the size of the file possessed by 2831590Srgrimes * the passed buffer. 2841590Srgrimes */ 2851590Srgrimesoff_t 2861590Srgrimesfsize(iob) 2871590Srgrimes FILE *iob; 2881590Srgrimes{ 2891590Srgrimes struct stat sbuf; 2901590Srgrimes 2911590Srgrimes if (fstat(fileno(iob), &sbuf) < 0) 2921590Srgrimes return 0; 2931590Srgrimes return sbuf.st_size; 2941590Srgrimes} 2951590Srgrimes 2961590Srgrimes/* 2971590Srgrimes * Evaluate the string given as a new mailbox name. 2981590Srgrimes * Supported meta characters: 2991590Srgrimes * % for my system mail box 3001590Srgrimes * %user for user's system mail box 3011590Srgrimes * # for previous file 3021590Srgrimes * & invoker's mbox file 3031590Srgrimes * +file file in folder directory 3041590Srgrimes * any shell meta character 3051590Srgrimes * Return the file name as a dynamic string. 3061590Srgrimes */ 3071590Srgrimeschar * 3081590Srgrimesexpand(name) 3091590Srgrimes register char *name; 3101590Srgrimes{ 3111590Srgrimes char xname[PATHSIZE]; 3121590Srgrimes char cmdbuf[PATHSIZE]; /* also used for file names */ 3131590Srgrimes register int pid, l; 3141590Srgrimes register char *cp, *shell; 3151590Srgrimes int pivec[2]; 3161590Srgrimes struct stat sbuf; 3171590Srgrimes extern union wait wait_status; 3181590Srgrimes 3191590Srgrimes /* 3201590Srgrimes * The order of evaluation is "%" and "#" expand into constants. 3211590Srgrimes * "&" can expand into "+". "+" can expand into shell meta characters. 3221590Srgrimes * Shell meta characters expand into constants. 3231590Srgrimes * This way, we make no recursive expansion. 3241590Srgrimes */ 3251590Srgrimes switch (*name) { 3261590Srgrimes case '%': 3271590Srgrimes findmail(name[1] ? name + 1 : myname, xname); 3281590Srgrimes return savestr(xname); 3291590Srgrimes case '#': 3301590Srgrimes if (name[1] != 0) 3311590Srgrimes break; 3321590Srgrimes if (prevfile[0] == 0) { 3331590Srgrimes printf("No previous file\n"); 3341590Srgrimes return NOSTR; 3351590Srgrimes } 3361590Srgrimes return savestr(prevfile); 3371590Srgrimes case '&': 3381590Srgrimes if (name[1] == 0 && (name = value("MBOX")) == NOSTR) 3391590Srgrimes name = "~/mbox"; 3401590Srgrimes /* fall through */ 3411590Srgrimes } 3421590Srgrimes if (name[0] == '+' && getfold(cmdbuf) >= 0) { 3431590Srgrimes sprintf(xname, "%s/%s", cmdbuf, name + 1); 3441590Srgrimes name = savestr(xname); 3451590Srgrimes } 3461590Srgrimes /* catch the most common shell meta character */ 3471590Srgrimes if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) { 3481590Srgrimes sprintf(xname, "%s%s", homedir, name + 1); 3491590Srgrimes name = savestr(xname); 3501590Srgrimes } 3511590Srgrimes if (!anyof(name, "~{[*?$`'\"\\")) 3521590Srgrimes return name; 3531590Srgrimes if (pipe(pivec) < 0) { 3541590Srgrimes perror("pipe"); 3551590Srgrimes return name; 3561590Srgrimes } 3571590Srgrimes sprintf(cmdbuf, "echo %s", name); 3581590Srgrimes if ((shell = value("SHELL")) == NOSTR) 3591590Srgrimes shell = _PATH_CSHELL; 3601590Srgrimes pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NOSTR); 3611590Srgrimes if (pid < 0) { 3621590Srgrimes close(pivec[0]); 3631590Srgrimes close(pivec[1]); 3641590Srgrimes return NOSTR; 3651590Srgrimes } 3661590Srgrimes close(pivec[1]); 3671590Srgrimes l = read(pivec[0], xname, BUFSIZ); 3681590Srgrimes close(pivec[0]); 3691590Srgrimes if (wait_child(pid) < 0 && wait_status.w_termsig != SIGPIPE) { 3701590Srgrimes fprintf(stderr, "\"%s\": Expansion failed.\n", name); 3711590Srgrimes return NOSTR; 3721590Srgrimes } 3731590Srgrimes if (l < 0) { 3741590Srgrimes perror("read"); 3751590Srgrimes return NOSTR; 3761590Srgrimes } 3771590Srgrimes if (l == 0) { 3781590Srgrimes fprintf(stderr, "\"%s\": No match.\n", name); 3791590Srgrimes return NOSTR; 3801590Srgrimes } 3811590Srgrimes if (l == BUFSIZ) { 3821590Srgrimes fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 3831590Srgrimes return NOSTR; 3841590Srgrimes } 3851590Srgrimes xname[l] = 0; 3861590Srgrimes for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 3871590Srgrimes ; 3881590Srgrimes cp[1] = '\0'; 3891590Srgrimes if (index(xname, ' ') && stat(xname, &sbuf) < 0) { 3901590Srgrimes fprintf(stderr, "\"%s\": Ambiguous.\n", name); 3911590Srgrimes return NOSTR; 3921590Srgrimes } 3931590Srgrimes return savestr(xname); 3941590Srgrimes} 3951590Srgrimes 3961590Srgrimes/* 3971590Srgrimes * Determine the current folder directory name. 3981590Srgrimes */ 3991590Srgrimesint 4001590Srgrimesgetfold(name) 4011590Srgrimes char *name; 4021590Srgrimes{ 4031590Srgrimes char *folder; 4041590Srgrimes 4051590Srgrimes if ((folder = value("folder")) == NOSTR) 4061590Srgrimes return (-1); 4071590Srgrimes if (*folder == '/') 4081590Srgrimes strcpy(name, folder); 4091590Srgrimes else 4101590Srgrimes sprintf(name, "%s/%s", homedir, folder); 4111590Srgrimes return (0); 4121590Srgrimes} 4131590Srgrimes 4141590Srgrimes/* 4151590Srgrimes * Return the name of the dead.letter file. 4161590Srgrimes */ 4171590Srgrimeschar * 4181590Srgrimesgetdeadletter() 4191590Srgrimes{ 4201590Srgrimes register char *cp; 4211590Srgrimes 4221590Srgrimes if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR) 4231590Srgrimes cp = expand("~/dead.letter"); 4241590Srgrimes else if (*cp != '/') { 4251590Srgrimes char buf[PATHSIZE]; 4261590Srgrimes 4271590Srgrimes (void) sprintf(buf, "~/%s", cp); 4281590Srgrimes cp = expand(buf); 4291590Srgrimes } 4301590Srgrimes return cp; 4311590Srgrimes} 432