fio.c revision 82793
18876Srgrimes/* 24Srgrimes * Copyright (c) 1980, 1993 34Srgrimes * The Regents of the University of California. All rights reserved. 44Srgrimes * 58876Srgrimes * Redistribution and use in source and binary forms, with or without 64Srgrimes * modification, are permitted provided that the following conditions 74Srgrimes * are met: 84Srgrimes * 1. Redistributions of source code must retain the above copyright 94Srgrimes * notice, this list of conditions and the following disclaimer. 104Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 118876Srgrimes * notice, this list of conditions and the following disclaimer in the 128876Srgrimes * documentation and/or other materials provided with the distribution. 134Srgrimes * 3. All advertising materials mentioning features or use of this software 144Srgrimes * must display the following acknowledgement: 158876Srgrimes * This product includes software developed by the University of 164Srgrimes * California, Berkeley and its contributors. 178876Srgrimes * 4. Neither the name of the University nor the names of its contributors 184Srgrimes * may be used to endorse or promote products derived from this software 194Srgrimes * without specific prior written permission. 204Srgrimes * 214Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 228876Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 234Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 244Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25118Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 264Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 274Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28115683Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29115683Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30115683Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 314Srgrimes * SUCH DAMAGE. 324Srgrimes */ 334Srgrimes 342056Swollman#ifndef lint 3524494Sbde#if 0 362056Swollmanstatic char sccsid[] = "@(#)fio.c 8.1 (Berkeley) 6/6/93"; 374Srgrimes#endif 384Srgrimesstatic const char rcsid[] = 394Srgrimes "$FreeBSD: head/usr.bin/mail/fio.c 82793 2001-09-02 14:40:51Z ache $"; 404Srgrimes#endif /* not lint */ 414Srgrimes 424Srgrimes#include "rcv.h" 434Srgrimes#include <sys/file.h> 444Srgrimes#include <sys/wait.h> 454Srgrimes 464Srgrimes#include <unistd.h> 474Srgrimes#include <paths.h> 484Srgrimes#include <errno.h> 494Srgrimes#include "extern.h" 504Srgrimes 514Srgrimes/* 524Srgrimes * Mail -- a mail program 534Srgrimes * 544Srgrimes * File I/O. 554Srgrimes */ 564Srgrimes 574Srgrimesextern int wait_status; 584Srgrimes 594Srgrimes/* 604Srgrimes * Set up the input pointers while copying the mail file into /tmp. 614Srgrimes */ 624Srgrimesvoid 634Srgrimessetptr(ibuf) 644Srgrimes FILE *ibuf; 654Srgrimes{ 664Srgrimes int c, count; 674Srgrimes char *cp, *cp2; 684Srgrimes struct message this; 694Srgrimes FILE *mestmp; 704Srgrimes off_t offset; 714Srgrimes int maybe, inhead; 724Srgrimes char linebuf[LINESIZE], pathbuf[PATHSIZE]; 734Srgrimes 744Srgrimes /* Get temporary file. */ 754Srgrimes (void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir); 764Srgrimes if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL) 774Srgrimes err(1, "can't open %s", pathbuf); 784Srgrimes (void)rm(pathbuf); 794Srgrimes 804Srgrimes msgCount = 0; 814Srgrimes maybe = 1; 824Srgrimes inhead = 0; 834Srgrimes offset = 0; 844Srgrimes this.m_flag = MUSED|MNEW; 854Srgrimes this.m_size = 0; 864Srgrimes this.m_lines = 0; 874Srgrimes this.m_block = 0; 884Srgrimes this.m_offset = 0; 8921277Sbde for (;;) { 9021277Sbde if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) { 9121277Sbde if (append(&this, mestmp)) 924Srgrimes errx(1, "temporary file"); 9311940Sbde makemessage(mestmp); 9414887Swollman return; 954Srgrimes } 964Srgrimes count = strlen(linebuf); 974Srgrimes /* 9817109Sbde * Transforms lines ending in <CR><LF> to just <LF>. 994Srgrimes * This allows mail to be able to read Eudora mailboxes. 1004Srgrimes */ 1014Srgrimes if (count >= 2 && linebuf[count - 1] == '\n' && 1024Srgrimes linebuf[count - 2] == '\r') { 1034Srgrimes count--; 1044Srgrimes linebuf[count - 1] = '\n'; 10511940Sbde } 10614887Swollman 1074Srgrimes (void)fwrite(linebuf, sizeof(*linebuf), count, otf); 1084Srgrimes if (ferror(otf)) 10917109Sbde errx(1, "/tmp"); 1104Srgrimes if (count) 1114Srgrimes linebuf[count - 1] = '\0'; 1124Srgrimes if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 11314887Swollman msgCount++; 1144Srgrimes if (append(&this, mestmp)) 1154Srgrimes errx(1, "temporary file"); 1164Srgrimes this.m_flag = MUSED|MNEW; 1174Srgrimes this.m_size = 0; 1184Srgrimes this.m_lines = 0; 1194Srgrimes this.m_block = blockof(offset); 1204Srgrimes this.m_offset = boffsetof(offset); 1214Srgrimes inhead = 1; 1224Srgrimes } else if (linebuf[0] == 0) { 1234Srgrimes inhead = 0; 12414887Swollman } else if (inhead) { 1254Srgrimes for (cp = linebuf, cp2 = "status";; cp++) { 1264Srgrimes if ((c = *cp2++) == '\0') { 1274Srgrimes while (isspace(*cp++)) 1284Srgrimes ; 1294Srgrimes if (cp[-1] != ':') 1304Srgrimes break; 1314Srgrimes while ((c = *cp++) != '\0') 1324Srgrimes if (c == 'R') 1334Srgrimes this.m_flag |= MREAD; 1344Srgrimes else if (c == 'O') 13514887Swollman this.m_flag &= ~MNEW; 1364Srgrimes inhead = 0; 1374Srgrimes break; 1384Srgrimes } 1394Srgrimes if (*cp != c && *cp != toupper(c)) 1404Srgrimes break; 1414Srgrimes } 1424Srgrimes } 1434Srgrimes offset += count; 1444Srgrimes this.m_size += count; 1454Srgrimes this.m_lines++; 14621277Sbde maybe = linebuf[0] == 0; 14721277Sbde } 14821277Sbde} 14921277Sbde 15021277Sbde/* 15121277Sbde * Drop the passed line onto the passed output buffer. 15221277Sbde * If a write error occurs, return -1, else the count of 15321277Sbde * characters written, including the newline. 15421277Sbde */ 15521277Sbdeint 15621277Sbdeputline(obuf, linebuf) 15714887Swollman FILE *obuf; 15817109Sbde char *linebuf; 15917109Sbde{ 1604Srgrimes int c; 1614Srgrimes 1624Srgrimes c = strlen(linebuf); 1634Srgrimes (void)fwrite(linebuf, sizeof(*linebuf), c, obuf); 1644Srgrimes fprintf(obuf, "\n"); 1654Srgrimes if (ferror(obuf)) 1664Srgrimes return (-1); 1674Srgrimes return (c + 1); 1684Srgrimes} 1694Srgrimes 1704Srgrimes/* 1714Srgrimes * Read up a line from the specified input into the line 1724Srgrimes * buffer. Return the number of characters read. Do not 1734Srgrimes * include the newline (or carriage return) at the end. 1744Srgrimes */ 1754Srgrimesint 1764Srgrimesreadline(ibuf, linebuf, linesize) 17717109Sbde FILE *ibuf; 17821277Sbde char *linebuf; 17921277Sbde int linesize; 18021277Sbde{ 18121277Sbde int n; 18221277Sbde 1834Srgrimes clearerr(ibuf); 18421277Sbde if (fgets(linebuf, linesize, ibuf) == NULL) 1854Srgrimes return (-1); 1864Srgrimes n = strlen(linebuf); 1874Srgrimes if (n > 0 && linebuf[n - 1] == '\n') 1884Srgrimes linebuf[--n] = '\0'; 1894Srgrimes if (n > 0 && linebuf[n - 1] == '\r') 1904Srgrimes linebuf[--n] = '\0'; 1914Srgrimes return (n); 1924Srgrimes} 1934Srgrimes 1944Srgrimes/* 1954Srgrimes * Return a file buffer all ready to read up the 1964Srgrimes * passed message pointer. 19714887Swollman */ 19814887SwollmanFILE * 19914887Swollmansetinput(mp) 20014887Swollman struct message *mp; 20114887Swollman{ 20214887Swollman 20314887Swollman (void)fflush(otf); 20414887Swollman if (fseeko(itf, 20514887Swollman positionof(mp->m_block, mp->m_offset), SEEK_SET) < 0) 20614887Swollman err(1, "fseek"); 20714887Swollman return (itf); 20814887Swollman} 20914887Swollman 21014887Swollman/* 21114887Swollman * Take the data out of the passed ghost file and toss it into 21214887Swollman * a dynamically allocated message structure. 21314887Swollman */ 21414887Swollmanvoid 21514887Swollmanmakemessage(f) 21614887Swollman FILE *f; 21717109Sbde{ 2184Srgrimes int size = (msgCount + 1) * sizeof(struct message); 2194Srgrimes 2204Srgrimes if (message != 0) 2214Srgrimes (void)free(message); 2224Srgrimes if ((message = malloc((unsigned)size)) == NULL) 2234Srgrimes err(1, "Out of memory"); 2244Srgrimes dot = message; 2254Srgrimes size -= sizeof(struct message); 2264Srgrimes (void)fflush(f); 2274Srgrimes (void)lseek(fileno(f), (off_t)sizeof(*message), 0); 2284Srgrimes if (read(fileno(f), (char *)message, size) != size) 2294Srgrimes errx(1, "Message temporary file corrupted"); 2304Srgrimes message[msgCount].m_size = 0; 2314Srgrimes message[msgCount].m_lines = 0; 2324Srgrimes (void)Fclose(f); 2334Srgrimes} 2344Srgrimes 2354Srgrimes/* 2364Srgrimes * Append the passed message descriptor onto the temp file. 23717109Sbde * If the write fails, return 1, else 0 2384Srgrimes */ 2394Srgrimesint 2404Srgrimesappend(mp, f) 2414Srgrimes struct message *mp; 2424Srgrimes FILE *f; 2434Srgrimes{ 2444Srgrimes return (fwrite((char *)mp, sizeof(*mp), 1, f) != 1); 2454Srgrimes} 2464Srgrimes 2474Srgrimes/* 2484Srgrimes * Delete a file, but only if the file is a plain file. 2494Srgrimes */ 2504Srgrimesint 2514Srgrimesrm(name) 2524Srgrimes char *name; 2534Srgrimes{ 2544Srgrimes struct stat sb; 2554Srgrimes 2564Srgrimes if (stat(name, &sb) < 0) 25717109Sbde return (-1); 2584Srgrimes if (!S_ISREG(sb.st_mode)) { 2594Srgrimes errno = EISDIR; 26021277Sbde return (-1); 26121277Sbde } 26217109Sbde return (unlink(name)); 26317109Sbde} 2644Srgrimes 2654Srgrimesstatic int sigdepth; /* depth of holdsigs() */ 2664Srgrimesstatic int omask; 2674Srgrimes/* 2684Srgrimes * Hold signals SIGHUP, SIGINT, and SIGQUIT. 26921277Sbde */ 27021277Sbdevoid 27117109Sbdeholdsigs() 27217109Sbde{ 2734Srgrimes 2744Srgrimes if (sigdepth++ == 0) 2754Srgrimes omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)); 2764Srgrimes} 27717109Sbde 27821277Sbde/* 27921277Sbde * Release signals SIGHUP, SIGINT, and SIGQUIT. 2804Srgrimes */ 28121277Sbdevoid 2824Srgrimesrelsesigs() 2834Srgrimes{ 28421277Sbde 28521277Sbde if (--sigdepth == 0) 2864Srgrimes (void)sigsetmask(omask); 2874Srgrimes} 2884Srgrimes 28917109Sbde/* 2904Srgrimes * Determine the size of the file possessed by 2914Srgrimes * the passed buffer. 2924Srgrimes */ 29321277Sbdeoff_t 29421277Sbdefsize(iob) 2954Srgrimes FILE *iob; 2964Srgrimes{ 29717109Sbde struct stat sbuf; 2984Srgrimes 2994Srgrimes if (fstat(fileno(iob), &sbuf) < 0) 3004Srgrimes return (0); 3014Srgrimes return (sbuf.st_size); 3024Srgrimes} 3034Srgrimes 3044Srgrimes/* 30521277Sbde * Evaluate the string given as a new mailbox name. 30621277Sbde * Supported meta characters: 30721277Sbde * % for my system mail box 30821277Sbde * %user for user's system mail box 30921277Sbde * # for previous file 31021277Sbde * & invoker's mbox file 31121277Sbde * +file file in folder directory 31221277Sbde * any shell meta character 31321277Sbde * Return the file name as a dynamic string. 3144Srgrimes */ 3154Srgrimeschar * 31614887Swollmanexpand(name) 3174Srgrimes char *name; 3184Srgrimes{ 3194Srgrimes char xname[PATHSIZE]; 32014887Swollman char cmdbuf[PATHSIZE]; /* also used for file names */ 3214Srgrimes int pid, l; 3224Srgrimes char *cp, *sh; 3234Srgrimes int pivec[2]; 3244Srgrimes struct stat sbuf; 3254Srgrimes 3264Srgrimes /* 3274Srgrimes * The order of evaluation is "%" and "#" expand into constants. 3284Srgrimes * "&" can expand into "+". "+" can expand into shell meta characters. 3294Srgrimes * Shell meta characters expand into constants. 3304Srgrimes * This way, we make no recursive expansion. 33121277Sbde */ 3324Srgrimes switch (*name) { 3334Srgrimes case '%': 3344Srgrimes findmail(name[1] ? name + 1 : myname, xname, sizeof(xname)); 33514887Swollman return (savestr(xname)); 3364Srgrimes case '#': 3374Srgrimes if (name[1] != 0) 33814887Swollman break; 3394Srgrimes if (prevfile[0] == 0) { 3404Srgrimes printf("No previous file\n"); 34117109Sbde return (NULL); 3424Srgrimes } 3434Srgrimes return (savestr(prevfile)); 34417109Sbde case '&': 3454Srgrimes if (name[1] == 0 && (name = value("MBOX")) == NULL) 3464Srgrimes name = "~/mbox"; 3474Srgrimes /* fall through */ 34814887Swollman } 3494Srgrimes if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) { 3504Srgrimes (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1); 3514Srgrimes name = savestr(xname); 35221277Sbde } 3534Srgrimes /* catch the most common shell meta character */ 3544Srgrimes if (name[0] == '~' && homedir != NULL && 3554Srgrimes (name[1] == '/' || name[1] == '\0')) { 35617109Sbde (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1); 35721277Sbde name = savestr(xname); 3584Srgrimes } 3594Srgrimes if (!strpbrk(name, "~{[*?$`'\"\\")) 36014887Swollman return (name); 3614Srgrimes if (pipe(pivec) < 0) { 3624Srgrimes warn("pipe"); 3634Srgrimes return (name); 36417109Sbde } 3654Srgrimes (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name); 3664Srgrimes if ((sh = value("SHELL")) == NULL) 3674Srgrimes sh = _PATH_CSHELL; 36814887Swollman pid = start_command(sh, 0, -1, pivec[1], "-c", cmdbuf, NULL); 3694Srgrimes if (pid < 0) { 3704Srgrimes (void)close(pivec[0]); 3714Srgrimes (void)close(pivec[1]); 3724Srgrimes return (NULL); 3734Srgrimes } 3744Srgrimes (void)close(pivec[1]); 3754Srgrimes l = read(pivec[0], xname, BUFSIZ); 3764Srgrimes (void)close(pivec[0]); 3774Srgrimes if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) && 3784Srgrimes WTERMSIG(wait_status) != SIGPIPE) { 37914887Swollman fprintf(stderr, "\"%s\": Expansion failed.\n", name); 3804Srgrimes return (NULL); 3814Srgrimes } 38217109Sbde if (l < 0) { 38321277Sbde warn("read"); 38417109Sbde return (NULL); 38517109Sbde } 38617109Sbde if (l == 0) { 38717109Sbde fprintf(stderr, "\"%s\": No match.\n", name); 3884Srgrimes return (NULL); 3894Srgrimes } 39014887Swollman if (l == BUFSIZ) { 39121277Sbde fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 39221277Sbde return (NULL); 39321277Sbde } 39421277Sbde xname[l] = '\0'; 39521277Sbde for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 39621277Sbde ; 39721277Sbde cp[1] = '\0'; 39821277Sbde if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { 3994Srgrimes fprintf(stderr, "\"%s\": Ambiguous.\n", name); 4004Srgrimes return (NULL); 40114887Swollman } 40221277Sbde return (savestr(xname)); 4034Srgrimes} 40421277Sbde 40521277Sbde/* 40617109Sbde * Determine the current folder directory name. 4074Srgrimes */ 4084Srgrimesint 4094Srgrimesgetfold(name, namelen) 4104Srgrimes char *name; 4114Srgrimes int namelen; 41214887Swollman{ 4134Srgrimes char *folder; 4144Srgrimes int copylen; 41521277Sbde 41621277Sbde if ((folder = value("folder")) == NULL) 4174Srgrimes return (-1); 4184Srgrimes if (*folder == '/') 4194Srgrimes copylen = strlcpy(name, folder, namelen); 4204Srgrimes else 4214Srgrimes copylen = snprintf(name, namelen, "%s/%s", 4224Srgrimes homedir ? homedir : ".", folder); 42314887Swollman return (copylen < 0 || copylen >= namelen ? (-1) : (0)); 4244Srgrimes} 4254Srgrimes 4264Srgrimes/* 4274Srgrimes * Return the name of the dead.letter file. 4284Srgrimes */ 4294Srgrimeschar * 4304Srgrimesgetdeadletter() 4314Srgrimes{ 4324Srgrimes char *cp; 4334Srgrimes 43414887Swollman if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL) 43521277Sbde cp = expand("~/dead.letter"); 43621277Sbde else if (*cp != '/') { 43721277Sbde char buf[PATHSIZE]; 43821277Sbde 43921277Sbde (void)snprintf(buf, sizeof(buf), "~/%s", cp); 44021277Sbde cp = expand(buf); 44121277Sbde } 44221277Sbde return (cp); 4434Srgrimes} 4444Srgrimes