1*65389327Skrw /* $OpenBSD: fio.c,v 1.28 2007/05/25 21:27:16 krw Exp $ */ 2db59c1a6Smillert /* $NetBSD: fio.c,v 1.8 1997/07/07 22:57:55 phil Exp $ */ 37eb34045Sderaadt 4df930be7Sderaadt /* 5df930be7Sderaadt * Copyright (c) 1980, 1993 6df930be7Sderaadt * The Regents of the University of California. All rights reserved. 7df930be7Sderaadt * 8df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 9df930be7Sderaadt * modification, are permitted provided that the following conditions 10df930be7Sderaadt * are met: 11df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 12df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 13df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 14df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 15df930be7Sderaadt * documentation and/or other materials provided with the distribution. 16f75387cbSmillert * 3. Neither the name of the University nor the names of its contributors 17df930be7Sderaadt * may be used to endorse or promote products derived from this software 18df930be7Sderaadt * without specific prior written permission. 19df930be7Sderaadt * 20df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30df930be7Sderaadt * SUCH DAMAGE. 31df930be7Sderaadt */ 32df930be7Sderaadt 33df930be7Sderaadt #ifndef lint 347eb34045Sderaadt #if 0 354a9caef2Smillert static const char sccsid[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95"; 367eb34045Sderaadt #else 37*65389327Skrw static const char rcsid[] = "$OpenBSD: fio.c,v 1.28 2007/05/25 21:27:16 krw Exp $"; 387eb34045Sderaadt #endif 39df930be7Sderaadt #endif /* not lint */ 40df930be7Sderaadt 41df930be7Sderaadt #include "rcv.h" 42df930be7Sderaadt #include <sys/file.h> 43df930be7Sderaadt #include <sys/wait.h> 44df930be7Sderaadt 45df930be7Sderaadt #include <unistd.h> 46df930be7Sderaadt #include <paths.h> 47df930be7Sderaadt #include <errno.h> 48df930be7Sderaadt #include "extern.h" 49df930be7Sderaadt 50df930be7Sderaadt /* 51df930be7Sderaadt * Mail -- a mail program 52df930be7Sderaadt * 53df930be7Sderaadt * File I/O. 54df930be7Sderaadt */ 55df930be7Sderaadt 567f56ba93Smillert static volatile sig_atomic_t fiosignal; 577f56ba93Smillert 58df930be7Sderaadt /* 59ca8b07b0Smillert * Wrapper for read() to catch EINTR. 60ca8b07b0Smillert */ 61d33b10b9Sderaadt static ssize_t 624a9caef2Smillert myread(int fd, char *buf, int len) 63ca8b07b0Smillert { 64ca8b07b0Smillert ssize_t nread; 65ca8b07b0Smillert 66ca8b07b0Smillert while ((nread = read(fd, buf, len)) == -1 && errno == EINTR) 67ca8b07b0Smillert ; 68ca8b07b0Smillert return(nread); 69ca8b07b0Smillert } 70ca8b07b0Smillert 71ca8b07b0Smillert /* 72df930be7Sderaadt * Set up the input pointers while copying the mail file into /tmp. 73df930be7Sderaadt */ 74df930be7Sderaadt void 754a9caef2Smillert setptr(FILE *ibuf, off_t offset) 76df930be7Sderaadt { 7736999bedSmillert int c, count; 7836999bedSmillert char *cp, *cp2; 79df930be7Sderaadt struct message this; 80df930be7Sderaadt FILE *mestmp; 8136999bedSmillert int maybe, inhead, omsgCount; 82c32a3250Sderaadt char linebuf[LINESIZE], pathbuf[PATHSIZE]; 83df930be7Sderaadt 84df930be7Sderaadt /* Get temporary file. */ 85db59c1a6Smillert (void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir); 86db59c1a6Smillert if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL) 87db59c1a6Smillert err(1, "can't open %s", pathbuf); 880ecc72fcSmillert (void)rm(pathbuf); 89df930be7Sderaadt 90db59c1a6Smillert if (offset == 0) { 91df930be7Sderaadt msgCount = 0; 92db59c1a6Smillert } else { 93db59c1a6Smillert /* Seek into the file to get to the new messages */ 942a187660Sderaadt (void)fseeko(ibuf, offset, 0); 95db59c1a6Smillert /* 96db59c1a6Smillert * We need to make "offset" a pointer to the end of 97db59c1a6Smillert * the temp file that has the copy of the mail file. 98db59c1a6Smillert * If any messages have been edited, this will be 99db59c1a6Smillert * different from the offset into the mail file. 100db59c1a6Smillert */ 1012a187660Sderaadt (void)fseeko(otf, (off_t)0, SEEK_END); 102db59c1a6Smillert offset = ftell(otf); 103db59c1a6Smillert } 104db59c1a6Smillert omsgCount = msgCount; 105df930be7Sderaadt maybe = 1; 106df930be7Sderaadt inhead = 0; 107df930be7Sderaadt this.m_flag = MUSED|MNEW; 108df930be7Sderaadt this.m_size = 0; 109df930be7Sderaadt this.m_lines = 0; 110df930be7Sderaadt this.m_block = 0; 111df930be7Sderaadt this.m_offset = 0; 112df930be7Sderaadt for (;;) { 113db59c1a6Smillert if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) { 114db59c1a6Smillert if (append(&this, mestmp)) 115db59c1a6Smillert err(1, "temporary file"); 116db59c1a6Smillert makemessage(mestmp, omsgCount); 117df930be7Sderaadt return; 118df930be7Sderaadt } 119df930be7Sderaadt count = strlen(linebuf); 1207f7d0dfbSmillert /* 1217f7d0dfbSmillert * Transforms lines ending in <CR><LF> to just <LF>. 1227f7d0dfbSmillert * This allows mail to be able to read Eudora mailboxes 1237f7d0dfbSmillert * that reside on a DOS partition. 1247f7d0dfbSmillert */ 1257f7d0dfbSmillert if (count >= 2 && linebuf[count-1] == '\n' && 126ad26b615Sray linebuf[count - 2] == '\r') { 127ad26b615Sray linebuf[count - 2] = '\n'; 128ad26b615Sray linebuf[count - 1] = '\0'; 129ad26b615Sray count--; 130ad26b615Sray } 1317f7d0dfbSmillert 132db59c1a6Smillert (void)fwrite(linebuf, sizeof(*linebuf), count, otf); 133db59c1a6Smillert if (ferror(otf)) 134db59c1a6Smillert err(1, "/tmp"); 1355da7328bSderaadt if (count) 136db59c1a6Smillert linebuf[count - 1] = '\0'; 137df930be7Sderaadt if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 138df930be7Sderaadt msgCount++; 139db59c1a6Smillert if (append(&this, mestmp)) 140db59c1a6Smillert err(1, "temporary file"); 141df930be7Sderaadt this.m_flag = MUSED|MNEW; 142df930be7Sderaadt this.m_size = 0; 143df930be7Sderaadt this.m_lines = 0; 144df930be7Sderaadt this.m_block = blockof(offset); 145df930be7Sderaadt this.m_offset = offsetof(offset); 146df930be7Sderaadt inhead = 1; 147df930be7Sderaadt } else if (linebuf[0] == 0) { 148df930be7Sderaadt inhead = 0; 149df930be7Sderaadt } else if (inhead) { 150df930be7Sderaadt for (cp = linebuf, cp2 = "status";; cp++) { 151df930be7Sderaadt if ((c = *cp2++) == 0) { 152df930be7Sderaadt while (isspace(*cp++)) 153df930be7Sderaadt ; 154df930be7Sderaadt if (cp[-1] != ':') 155df930be7Sderaadt break; 1567eb34045Sderaadt while ((c = *cp++) != '\0') 157df930be7Sderaadt if (c == 'R') 158df930be7Sderaadt this.m_flag |= MREAD; 159df930be7Sderaadt else if (c == 'O') 160df930be7Sderaadt this.m_flag &= ~MNEW; 161df930be7Sderaadt inhead = 0; 162df930be7Sderaadt break; 163df930be7Sderaadt } 164df930be7Sderaadt if (*cp != c && *cp != toupper(c)) 165df930be7Sderaadt break; 166df930be7Sderaadt } 167df930be7Sderaadt } 168df930be7Sderaadt offset += count; 169df930be7Sderaadt this.m_size += count; 170df930be7Sderaadt this.m_lines++; 171df930be7Sderaadt maybe = linebuf[0] == 0; 172df930be7Sderaadt } 173df930be7Sderaadt } 174df930be7Sderaadt 175df930be7Sderaadt /* 176df930be7Sderaadt * Drop the passed line onto the passed output buffer. 177df930be7Sderaadt * If a write error occurs, return -1, else the count of 178db59c1a6Smillert * characters written, including the newline if requested. 179df930be7Sderaadt */ 180df930be7Sderaadt int 1814a9caef2Smillert putline(FILE *obuf, char *linebuf, int outlf) 182df930be7Sderaadt { 18336999bedSmillert int c; 184df930be7Sderaadt 185df930be7Sderaadt c = strlen(linebuf); 186db59c1a6Smillert (void)fwrite(linebuf, sizeof(*linebuf), c, obuf); 187db59c1a6Smillert if (outlf) { 188df930be7Sderaadt (void)putc('\n', obuf); 189db59c1a6Smillert c++; 190db59c1a6Smillert } 191df930be7Sderaadt if (ferror(obuf)) 192df930be7Sderaadt return(-1); 193db59c1a6Smillert return(c); 194df930be7Sderaadt } 195df930be7Sderaadt 196df930be7Sderaadt /* 197df930be7Sderaadt * Read up a line from the specified input into the line 198df930be7Sderaadt * buffer. Return the number of characters read. Do not 1997f7d0dfbSmillert * include the newline (or carriage return) at the end. 200df930be7Sderaadt */ 201df930be7Sderaadt int 2024a9caef2Smillert readline(FILE *ibuf, char *linebuf, int linesize, int *signo) 203df930be7Sderaadt { 2047f56ba93Smillert struct sigaction act; 2057f56ba93Smillert struct sigaction savetstp; 2067f56ba93Smillert struct sigaction savettou; 2077f56ba93Smillert struct sigaction savettin; 2087f56ba93Smillert struct sigaction saveint; 2097f56ba93Smillert struct sigaction savehup; 2107f56ba93Smillert sigset_t oset; 21136999bedSmillert int n; 212df930be7Sderaadt 2137f56ba93Smillert /* 2147f56ba93Smillert * Setup signal handlers if the caller asked us to catch signals. 2157f56ba93Smillert * Note that we do not restart system calls since we need the 216*65389327Skrw * read to be interruptible. 2177f56ba93Smillert */ 2187f56ba93Smillert if (signo) { 2197f56ba93Smillert fiosignal = 0; 2207f56ba93Smillert sigemptyset(&act.sa_mask); 2217f56ba93Smillert act.sa_flags = 0; 2227f56ba93Smillert act.sa_handler = fioint; 2237f56ba93Smillert if (sigaction(SIGINT, NULL, &saveint) == 0 && 2247f56ba93Smillert saveint.sa_handler != SIG_IGN) { 2257f56ba93Smillert (void)sigaction(SIGINT, &act, &saveint); 2267f56ba93Smillert (void)sigprocmask(SIG_UNBLOCK, &intset, &oset); 2277f56ba93Smillert } 2287f56ba93Smillert if (sigaction(SIGHUP, NULL, &savehup) == 0 && 2297f56ba93Smillert savehup.sa_handler != SIG_IGN) 2307f56ba93Smillert (void)sigaction(SIGHUP, &act, &savehup); 2317f56ba93Smillert (void)sigaction(SIGTSTP, &act, &savetstp); 2327f56ba93Smillert (void)sigaction(SIGTTOU, &act, &savettou); 2337f56ba93Smillert (void)sigaction(SIGTTIN, &act, &savettin); 2347f56ba93Smillert } 235db59c1a6Smillert 2367f56ba93Smillert clearerr(ibuf); 2377f56ba93Smillert if (fgets(linebuf, linesize, ibuf) == NULL) { 2387f56ba93Smillert if (ferror(ibuf)) 2397f56ba93Smillert clearerr(ibuf); 2407f56ba93Smillert n = -1; 2417f56ba93Smillert } else { 242df930be7Sderaadt n = strlen(linebuf); 243df930be7Sderaadt if (n > 0 && linebuf[n - 1] == '\n') 244df930be7Sderaadt linebuf[--n] = '\0'; 2457f7d0dfbSmillert if (n > 0 && linebuf[n - 1] == '\r') 2467f7d0dfbSmillert linebuf[--n] = '\0'; 2477f56ba93Smillert } 2487f56ba93Smillert 2497f56ba93Smillert if (signo) { 2507f56ba93Smillert (void)sigprocmask(SIG_SETMASK, &oset, NULL); 2517f56ba93Smillert (void)sigaction(SIGINT, &saveint, NULL); 2527f56ba93Smillert (void)sigaction(SIGHUP, &savehup, NULL); 2537f56ba93Smillert (void)sigaction(SIGTSTP, &savetstp, NULL); 2547f56ba93Smillert (void)sigaction(SIGTTOU, &savettou, NULL); 2557f56ba93Smillert (void)sigaction(SIGTTIN, &savettin, NULL); 2567f56ba93Smillert *signo = fiosignal; 2577f56ba93Smillert } 2587f56ba93Smillert 259db59c1a6Smillert return(n); 260df930be7Sderaadt } 261df930be7Sderaadt 262df930be7Sderaadt /* 263df930be7Sderaadt * Return a file buffer all ready to read up the 264df930be7Sderaadt * passed message pointer. 265df930be7Sderaadt */ 266df930be7Sderaadt FILE * 2674a9caef2Smillert setinput(struct message *mp) 268df930be7Sderaadt { 269df930be7Sderaadt 270df930be7Sderaadt fflush(otf); 27136999bedSmillert if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) 27236999bedSmillert err(1, "fseek"); 273df930be7Sderaadt return(itf); 274df930be7Sderaadt } 275df930be7Sderaadt 276df930be7Sderaadt /* 277df930be7Sderaadt * Take the data out of the passed ghost file and toss it into 278df930be7Sderaadt * a dynamically allocated message structure. 279df930be7Sderaadt */ 280df930be7Sderaadt void 2814a9caef2Smillert makemessage(FILE *f, int omsgCount) 282df930be7Sderaadt { 2833462a97cSmillert size_t size; 2843462a97cSmillert struct message *nmessage; 285df930be7Sderaadt 2863462a97cSmillert size = (msgCount + 1) * sizeof(struct message); 2873462a97cSmillert nmessage = (struct message *)realloc(message, size); 2883462a97cSmillert if (nmessage == 0) 28946347781Smpech errx(1, "Insufficient memory for %d messages", 29036999bedSmillert msgCount); 2913462a97cSmillert if (omsgCount == 0 || message == NULL) 2923462a97cSmillert dot = nmessage; 2933462a97cSmillert else 2943462a97cSmillert dot = nmessage + (dot - message); 2953462a97cSmillert message = nmessage; 296db59c1a6Smillert size -= (omsgCount + 1) * sizeof(struct message); 297df930be7Sderaadt fflush(f); 29885c482d6Sderaadt (void)lseek(fileno(f), (off_t)sizeof(*message), SEEK_SET); 299ca8b07b0Smillert if (myread(fileno(f), (void *) &message[omsgCount], size) != size) 30036999bedSmillert errx(1, "Message temporary file corrupted"); 301df930be7Sderaadt message[msgCount].m_size = 0; 302df930be7Sderaadt message[msgCount].m_lines = 0; 303db59c1a6Smillert (void)Fclose(f); 304df930be7Sderaadt } 305df930be7Sderaadt 306df930be7Sderaadt /* 307df930be7Sderaadt * Append the passed message descriptor onto the temp file. 308df930be7Sderaadt * If the write fails, return 1, else 0 309df930be7Sderaadt */ 310df930be7Sderaadt int 3114a9caef2Smillert append(struct message *mp, FILE *f) 312df930be7Sderaadt { 3134a9caef2Smillert 314db59c1a6Smillert return(fwrite((char *) mp, sizeof(*mp), 1, f) != 1); 315df930be7Sderaadt } 316df930be7Sderaadt 317df930be7Sderaadt /* 3180b426e12Smillert * Delete or truncate a file, but only if the file is a plain file. 319df930be7Sderaadt */ 320df930be7Sderaadt int 3214a9caef2Smillert rm(char *name) 322df930be7Sderaadt { 323df930be7Sderaadt struct stat sb; 324df930be7Sderaadt 325df930be7Sderaadt if (stat(name, &sb) < 0) 326df930be7Sderaadt return(-1); 327df930be7Sderaadt if (!S_ISREG(sb.st_mode)) { 328df930be7Sderaadt errno = EISDIR; 329df930be7Sderaadt return(-1); 330df930be7Sderaadt } 3310b426e12Smillert if (unlink(name) == -1) { 3320b426e12Smillert if (errno == EPERM) 3332a187660Sderaadt return(truncate(name, (off_t)0)); 3340b426e12Smillert else 3350b426e12Smillert return(-1); 3360b426e12Smillert } 3370b426e12Smillert return(0); 338df930be7Sderaadt } 339df930be7Sderaadt 340df930be7Sderaadt static int sigdepth; /* depth of holdsigs() */ 3417eb34045Sderaadt static sigset_t nset, oset; 342df930be7Sderaadt /* 343df930be7Sderaadt * Hold signals SIGHUP, SIGINT, and SIGQUIT. 344df930be7Sderaadt */ 345df930be7Sderaadt void 3464a9caef2Smillert holdsigs(void) 347df930be7Sderaadt { 348df930be7Sderaadt 3497eb34045Sderaadt if (sigdepth++ == 0) { 3507eb34045Sderaadt sigemptyset(&nset); 3517eb34045Sderaadt sigaddset(&nset, SIGHUP); 3527eb34045Sderaadt sigaddset(&nset, SIGINT); 3537eb34045Sderaadt sigaddset(&nset, SIGQUIT); 3547eb34045Sderaadt sigprocmask(SIG_BLOCK, &nset, &oset); 3557eb34045Sderaadt } 356df930be7Sderaadt } 357df930be7Sderaadt 358df930be7Sderaadt /* 359df930be7Sderaadt * Release signals SIGHUP, SIGINT, and SIGQUIT. 360df930be7Sderaadt */ 361df930be7Sderaadt void 3624a9caef2Smillert relsesigs(void) 363df930be7Sderaadt { 364df930be7Sderaadt 365df930be7Sderaadt if (--sigdepth == 0) 3667eb34045Sderaadt sigprocmask(SIG_SETMASK, &oset, NULL); 367df930be7Sderaadt } 368df930be7Sderaadt 369df930be7Sderaadt /* 3707f56ba93Smillert * Unblock and ignore a signal 3717f56ba93Smillert */ 3727f56ba93Smillert int 3734a9caef2Smillert ignoresig(int sig, struct sigaction *oact, sigset_t *oset) 3747f56ba93Smillert { 3757f56ba93Smillert struct sigaction act; 3767f56ba93Smillert sigset_t nset; 3777f56ba93Smillert int error; 3787f56ba93Smillert 3797f56ba93Smillert sigemptyset(&act.sa_mask); 3807f56ba93Smillert act.sa_flags = SA_RESTART; 3817f56ba93Smillert act.sa_handler = SIG_IGN; 3827f56ba93Smillert error = sigaction(sig, &act, oact); 3837f56ba93Smillert 3847f56ba93Smillert if (error == 0) { 3857f56ba93Smillert sigemptyset(&nset); 3867f56ba93Smillert sigaddset(&nset, sig); 3877f56ba93Smillert (void)sigprocmask(SIG_UNBLOCK, &nset, oset); 3887f56ba93Smillert } else if (oset != NULL) 3897f56ba93Smillert (void)sigprocmask(SIG_BLOCK, NULL, oset); 3907f56ba93Smillert 3917f56ba93Smillert return(error); 3927f56ba93Smillert } 3937f56ba93Smillert 3947f56ba93Smillert /* 395df930be7Sderaadt * Determine the size of the file possessed by 396df930be7Sderaadt * the passed buffer. 397df930be7Sderaadt */ 398df930be7Sderaadt off_t 3994a9caef2Smillert fsize(FILE *iob) 400df930be7Sderaadt { 401df930be7Sderaadt struct stat sbuf; 402df930be7Sderaadt 403df930be7Sderaadt if (fstat(fileno(iob), &sbuf) < 0) 404db59c1a6Smillert return(0); 405db59c1a6Smillert return(sbuf.st_size); 406df930be7Sderaadt } 407df930be7Sderaadt 408df930be7Sderaadt /* 409df930be7Sderaadt * Evaluate the string given as a new mailbox name. 410df930be7Sderaadt * Supported meta characters: 411df930be7Sderaadt * % for my system mail box 412df930be7Sderaadt * %user for user's system mail box 413df930be7Sderaadt * # for previous file 414df930be7Sderaadt * & invoker's mbox file 415df930be7Sderaadt * +file file in folder directory 416df930be7Sderaadt * any shell meta character 417df930be7Sderaadt * Return the file name as a dynamic string. 418df930be7Sderaadt */ 419df930be7Sderaadt char * 4204a9caef2Smillert expand(char *name) 421df930be7Sderaadt { 422df930be7Sderaadt char xname[PATHSIZE]; 423df930be7Sderaadt char cmdbuf[PATHSIZE]; /* also used for file names */ 4244a9caef2Smillert pid_t pid; 4254a9caef2Smillert int l; 42636999bedSmillert char *cp, *shell; 427df930be7Sderaadt int pivec[2]; 428df930be7Sderaadt struct stat sbuf; 42959eb7d25Smillert extern int wait_status; 430df930be7Sderaadt 431df930be7Sderaadt /* 432df930be7Sderaadt * The order of evaluation is "%" and "#" expand into constants. 433df930be7Sderaadt * "&" can expand into "+". "+" can expand into shell meta characters. 434df930be7Sderaadt * Shell meta characters expand into constants. 435df930be7Sderaadt * This way, we make no recursive expansion. 436df930be7Sderaadt */ 437df930be7Sderaadt switch (*name) { 438df930be7Sderaadt case '%': 439db59c1a6Smillert findmail(name[1] ? name + 1 : myname, xname, sizeof(xname)); 440db59c1a6Smillert return(savestr(xname)); 441df930be7Sderaadt case '#': 442df930be7Sderaadt if (name[1] != 0) 443df930be7Sderaadt break; 444df930be7Sderaadt if (prevfile[0] == 0) { 445db59c1a6Smillert puts("No previous file"); 446c318c72bSmillert return(NULL); 447df930be7Sderaadt } 448db59c1a6Smillert return(savestr(prevfile)); 449df930be7Sderaadt case '&': 450c318c72bSmillert if (name[1] == 0 && (name = value("MBOX")) == NULL) 451df930be7Sderaadt name = "~/mbox"; 452df930be7Sderaadt /* fall through */ 453df930be7Sderaadt } 454db59c1a6Smillert if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) { 4550ecc72fcSmillert (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1); 456df930be7Sderaadt name = savestr(xname); 457df930be7Sderaadt } 458df930be7Sderaadt /* catch the most common shell meta character */ 4595c85bdadSmillert if (name[0] == '~' && homedir && (name[1] == '/' || name[1] == '\0')) { 4600ecc72fcSmillert (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1); 461df930be7Sderaadt name = savestr(xname); 462df930be7Sderaadt } 4634a9caef2Smillert if (strpbrk(name, "~{[*?$`'\"\\") == NULL) 464db59c1a6Smillert return(name); 4654a9caef2Smillert /* XXX - just use glob(3) and env expansion instead? */ 466df930be7Sderaadt if (pipe(pivec) < 0) { 467db59c1a6Smillert warn("pipe"); 468db59c1a6Smillert return(name); 469df930be7Sderaadt } 4700ecc72fcSmillert (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name); 471ca8b07b0Smillert shell = value("SHELL"); 472c318c72bSmillert pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NULL); 473df930be7Sderaadt if (pid < 0) { 474db59c1a6Smillert (void)close(pivec[0]); 475db59c1a6Smillert (void)close(pivec[1]); 476c318c72bSmillert return(NULL); 477df930be7Sderaadt } 478db59c1a6Smillert (void)close(pivec[1]); 479ca8b07b0Smillert l = myread(pivec[0], xname, PATHSIZE); 480ca8b07b0Smillert if (l < 0) 481ca8b07b0Smillert warn("read"); /* report error before errno changes */ 482db59c1a6Smillert (void)close(pivec[0]); 48359eb7d25Smillert if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) && 48459eb7d25Smillert WTERMSIG(wait_status) != SIGPIPE) { 485df930be7Sderaadt fprintf(stderr, "\"%s\": Expansion failed.\n", name); 486c318c72bSmillert return(NULL); 487df930be7Sderaadt } 488ca8b07b0Smillert if (l < 0) 489c318c72bSmillert return(NULL); 490df930be7Sderaadt if (l == 0) { 491df930be7Sderaadt fprintf(stderr, "\"%s\": No match.\n", name); 492c318c72bSmillert return(NULL); 493df930be7Sderaadt } 494db59c1a6Smillert if (l == PATHSIZE) { 495df930be7Sderaadt fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 496c318c72bSmillert return(NULL); 497df930be7Sderaadt } 498db59c1a6Smillert xname[l] = '\0'; 499df930be7Sderaadt for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 500df930be7Sderaadt ; 501df930be7Sderaadt cp[1] = '\0'; 502180acc8fSmillert if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { 503df930be7Sderaadt fprintf(stderr, "\"%s\": Ambiguous.\n", name); 504c318c72bSmillert return(NULL); 505df930be7Sderaadt } 506db59c1a6Smillert return(savestr(xname)); 507df930be7Sderaadt } 508df930be7Sderaadt 509df930be7Sderaadt /* 510df930be7Sderaadt * Determine the current folder directory name. 511df930be7Sderaadt */ 512df930be7Sderaadt int 5134a9caef2Smillert getfold(char *name, int namelen) 514df930be7Sderaadt { 515df930be7Sderaadt char *folder; 516df930be7Sderaadt 517c318c72bSmillert if ((folder = value("folder")) == NULL) 518df930be7Sderaadt return(-1); 5194a9caef2Smillert if (*folder == '/') 5204a9caef2Smillert strlcpy(name, folder, namelen); 5214a9caef2Smillert else 5225c85bdadSmillert (void)snprintf(name, namelen, "%s/%s", homedir ? homedir : ".", 5235c85bdadSmillert folder); 524df930be7Sderaadt return(0); 525df930be7Sderaadt } 526df930be7Sderaadt 527df930be7Sderaadt /* 528df930be7Sderaadt * Return the name of the dead.letter file. 529df930be7Sderaadt */ 530df930be7Sderaadt char * 5314a9caef2Smillert getdeadletter(void) 532df930be7Sderaadt { 53336999bedSmillert char *cp; 534df930be7Sderaadt 535c318c72bSmillert if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL) 536df930be7Sderaadt cp = expand("~/dead.letter"); 537df930be7Sderaadt else if (*cp != '/') { 538df930be7Sderaadt char buf[PATHSIZE]; 539df930be7Sderaadt 540db59c1a6Smillert (void)snprintf(buf, sizeof(buf), "~/%s", cp); 541df930be7Sderaadt cp = expand(buf); 542df930be7Sderaadt } 543db59c1a6Smillert return(cp); 544df930be7Sderaadt } 5457f56ba93Smillert 5467f56ba93Smillert /* 5477f56ba93Smillert * Signal handler used by readline() to catch SIGINT, SIGHUP, SIGTSTP, 5487f56ba93Smillert * SIGTTOU, SIGTTIN. 5497f56ba93Smillert */ 5507f56ba93Smillert void 5514a9caef2Smillert fioint(int s) 5527f56ba93Smillert { 5537f56ba93Smillert 5547f56ba93Smillert fiosignal = s; 5557f56ba93Smillert } 556