1*7eb34045Sderaadt /* $OpenBSD: fio.c,v 1.2 1996/06/11 12:53:39 deraadt Exp $ */ 2*7eb34045Sderaadt /* $NetBSD: fio.c,v 1.5 1996/06/08 19:48:22 christos Exp $ */ 3*7eb34045Sderaadt 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. 16df930be7Sderaadt * 3. All advertising materials mentioning features or use of this software 17df930be7Sderaadt * must display the following acknowledgement: 18df930be7Sderaadt * This product includes software developed by the University of 19df930be7Sderaadt * California, Berkeley and its contributors. 20df930be7Sderaadt * 4. Neither the name of the University nor the names of its contributors 21df930be7Sderaadt * may be used to endorse or promote products derived from this software 22df930be7Sderaadt * without specific prior written permission. 23df930be7Sderaadt * 24df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34df930be7Sderaadt * SUCH DAMAGE. 35df930be7Sderaadt */ 36df930be7Sderaadt 37df930be7Sderaadt #ifndef lint 38*7eb34045Sderaadt #if 0 39*7eb34045Sderaadt static char sccsid[] = "@(#)fio.c 8.1 (Berkeley) 6/6/93"; 40*7eb34045Sderaadt #else 41*7eb34045Sderaadt static char rcsid[] = "$OpenBSD: fio.c,v 1.2 1996/06/11 12:53:39 deraadt Exp $"; 42*7eb34045Sderaadt #endif 43df930be7Sderaadt #endif /* not lint */ 44df930be7Sderaadt 45df930be7Sderaadt #include "rcv.h" 46df930be7Sderaadt #include <sys/file.h> 47df930be7Sderaadt #include <sys/wait.h> 48df930be7Sderaadt 49df930be7Sderaadt #include <unistd.h> 50df930be7Sderaadt #include <paths.h> 51df930be7Sderaadt #include <errno.h> 52df930be7Sderaadt #include "extern.h" 53df930be7Sderaadt 54df930be7Sderaadt /* 55df930be7Sderaadt * Mail -- a mail program 56df930be7Sderaadt * 57df930be7Sderaadt * File I/O. 58df930be7Sderaadt */ 59df930be7Sderaadt 60df930be7Sderaadt /* 61df930be7Sderaadt * Set up the input pointers while copying the mail file into /tmp. 62df930be7Sderaadt */ 63df930be7Sderaadt void 64df930be7Sderaadt setptr(ibuf) 65df930be7Sderaadt register FILE *ibuf; 66df930be7Sderaadt { 67df930be7Sderaadt extern char *tmpdir; 68df930be7Sderaadt register int c, count; 69df930be7Sderaadt register char *cp, *cp2; 70df930be7Sderaadt struct message this; 71df930be7Sderaadt FILE *mestmp; 72df930be7Sderaadt off_t offset; 73df930be7Sderaadt int maybe, inhead; 74df930be7Sderaadt char linebuf[LINESIZE]; 75df930be7Sderaadt 76df930be7Sderaadt /* Get temporary file. */ 77df930be7Sderaadt (void)sprintf(linebuf, "%s/mail.XXXXXX", tmpdir); 78df930be7Sderaadt if ((c = mkstemp(linebuf)) == -1 || 79df930be7Sderaadt (mestmp = Fdopen(c, "r+")) == NULL) { 80df930be7Sderaadt (void)fprintf(stderr, "mail: can't open %s\n", linebuf); 81df930be7Sderaadt exit(1); 82df930be7Sderaadt } 83df930be7Sderaadt (void)unlink(linebuf); 84df930be7Sderaadt 85df930be7Sderaadt msgCount = 0; 86df930be7Sderaadt maybe = 1; 87df930be7Sderaadt inhead = 0; 88df930be7Sderaadt offset = 0; 89df930be7Sderaadt this.m_flag = MUSED|MNEW; 90df930be7Sderaadt this.m_size = 0; 91df930be7Sderaadt this.m_lines = 0; 92df930be7Sderaadt this.m_block = 0; 93df930be7Sderaadt this.m_offset = 0; 94df930be7Sderaadt for (;;) { 95df930be7Sderaadt if (fgets(linebuf, LINESIZE, ibuf) == NULL) { 96df930be7Sderaadt if (append(&this, mestmp)) { 97df930be7Sderaadt perror("temporary file"); 98df930be7Sderaadt exit(1); 99df930be7Sderaadt } 100df930be7Sderaadt makemessage(mestmp); 101df930be7Sderaadt return; 102df930be7Sderaadt } 103df930be7Sderaadt count = strlen(linebuf); 104df930be7Sderaadt (void) fwrite(linebuf, sizeof *linebuf, count, otf); 105df930be7Sderaadt if (ferror(otf)) { 106df930be7Sderaadt perror("/tmp"); 107df930be7Sderaadt exit(1); 108df930be7Sderaadt } 109df930be7Sderaadt linebuf[count - 1] = 0; 110df930be7Sderaadt if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 111df930be7Sderaadt msgCount++; 112df930be7Sderaadt if (append(&this, mestmp)) { 113df930be7Sderaadt perror("temporary file"); 114df930be7Sderaadt exit(1); 115df930be7Sderaadt } 116df930be7Sderaadt this.m_flag = MUSED|MNEW; 117df930be7Sderaadt this.m_size = 0; 118df930be7Sderaadt this.m_lines = 0; 119df930be7Sderaadt this.m_block = blockof(offset); 120df930be7Sderaadt this.m_offset = offsetof(offset); 121df930be7Sderaadt inhead = 1; 122df930be7Sderaadt } else if (linebuf[0] == 0) { 123df930be7Sderaadt inhead = 0; 124df930be7Sderaadt } else if (inhead) { 125df930be7Sderaadt for (cp = linebuf, cp2 = "status";; cp++) { 126df930be7Sderaadt if ((c = *cp2++) == 0) { 127df930be7Sderaadt while (isspace(*cp++)) 128df930be7Sderaadt ; 129df930be7Sderaadt if (cp[-1] != ':') 130df930be7Sderaadt break; 131*7eb34045Sderaadt while ((c = *cp++) != '\0') 132df930be7Sderaadt if (c == 'R') 133df930be7Sderaadt this.m_flag |= MREAD; 134df930be7Sderaadt else if (c == 'O') 135df930be7Sderaadt this.m_flag &= ~MNEW; 136df930be7Sderaadt inhead = 0; 137df930be7Sderaadt break; 138df930be7Sderaadt } 139df930be7Sderaadt if (*cp != c && *cp != toupper(c)) 140df930be7Sderaadt break; 141df930be7Sderaadt } 142df930be7Sderaadt } 143df930be7Sderaadt offset += count; 144df930be7Sderaadt this.m_size += count; 145df930be7Sderaadt this.m_lines++; 146df930be7Sderaadt maybe = linebuf[0] == 0; 147df930be7Sderaadt } 148df930be7Sderaadt } 149df930be7Sderaadt 150df930be7Sderaadt /* 151df930be7Sderaadt * Drop the passed line onto the passed output buffer. 152df930be7Sderaadt * If a write error occurs, return -1, else the count of 153df930be7Sderaadt * characters written, including the newline. 154df930be7Sderaadt */ 155df930be7Sderaadt int 156df930be7Sderaadt putline(obuf, linebuf) 157df930be7Sderaadt FILE *obuf; 158df930be7Sderaadt char *linebuf; 159df930be7Sderaadt { 160df930be7Sderaadt register int c; 161df930be7Sderaadt 162df930be7Sderaadt c = strlen(linebuf); 163df930be7Sderaadt (void) fwrite(linebuf, sizeof *linebuf, c, obuf); 164df930be7Sderaadt (void) putc('\n', obuf); 165df930be7Sderaadt if (ferror(obuf)) 166df930be7Sderaadt return (-1); 167df930be7Sderaadt return (c + 1); 168df930be7Sderaadt } 169df930be7Sderaadt 170df930be7Sderaadt /* 171df930be7Sderaadt * Read up a line from the specified input into the line 172df930be7Sderaadt * buffer. Return the number of characters read. Do not 173df930be7Sderaadt * include the newline at the end. 174df930be7Sderaadt */ 175df930be7Sderaadt int 176df930be7Sderaadt readline(ibuf, linebuf, linesize) 177df930be7Sderaadt FILE *ibuf; 178df930be7Sderaadt char *linebuf; 179df930be7Sderaadt int linesize; 180df930be7Sderaadt { 181df930be7Sderaadt register int n; 182df930be7Sderaadt 183df930be7Sderaadt clearerr(ibuf); 184df930be7Sderaadt if (fgets(linebuf, linesize, ibuf) == NULL) 185df930be7Sderaadt return -1; 186df930be7Sderaadt n = strlen(linebuf); 187df930be7Sderaadt if (n > 0 && linebuf[n - 1] == '\n') 188df930be7Sderaadt linebuf[--n] = '\0'; 189df930be7Sderaadt return n; 190df930be7Sderaadt } 191df930be7Sderaadt 192df930be7Sderaadt /* 193df930be7Sderaadt * Return a file buffer all ready to read up the 194df930be7Sderaadt * passed message pointer. 195df930be7Sderaadt */ 196df930be7Sderaadt FILE * 197df930be7Sderaadt setinput(mp) 198df930be7Sderaadt register struct message *mp; 199df930be7Sderaadt { 200df930be7Sderaadt 201df930be7Sderaadt fflush(otf); 202df930be7Sderaadt if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) { 203df930be7Sderaadt perror("fseek"); 204df930be7Sderaadt panic("temporary file seek"); 205df930be7Sderaadt } 206df930be7Sderaadt return (itf); 207df930be7Sderaadt } 208df930be7Sderaadt 209df930be7Sderaadt /* 210df930be7Sderaadt * Take the data out of the passed ghost file and toss it into 211df930be7Sderaadt * a dynamically allocated message structure. 212df930be7Sderaadt */ 213df930be7Sderaadt void 214df930be7Sderaadt makemessage(f) 215df930be7Sderaadt FILE *f; 216df930be7Sderaadt { 217df930be7Sderaadt register size = (msgCount + 1) * sizeof (struct message); 218df930be7Sderaadt 219df930be7Sderaadt if (message != 0) 220df930be7Sderaadt free((char *) message); 221df930be7Sderaadt if ((message = (struct message *) malloc((unsigned) size)) == 0) 222df930be7Sderaadt panic("Insufficient memory for %d messages", msgCount); 223df930be7Sderaadt dot = message; 224df930be7Sderaadt size -= sizeof (struct message); 225df930be7Sderaadt fflush(f); 226df930be7Sderaadt (void) lseek(fileno(f), (off_t)sizeof *message, 0); 227df930be7Sderaadt if (read(fileno(f), (char *) message, size) != size) 228df930be7Sderaadt panic("Message temporary file corrupted"); 229df930be7Sderaadt message[msgCount].m_size = 0; 230df930be7Sderaadt message[msgCount].m_lines = 0; 231df930be7Sderaadt Fclose(f); 232df930be7Sderaadt } 233df930be7Sderaadt 234df930be7Sderaadt /* 235df930be7Sderaadt * Append the passed message descriptor onto the temp file. 236df930be7Sderaadt * If the write fails, return 1, else 0 237df930be7Sderaadt */ 238df930be7Sderaadt int 239df930be7Sderaadt append(mp, f) 240df930be7Sderaadt struct message *mp; 241df930be7Sderaadt FILE *f; 242df930be7Sderaadt { 243df930be7Sderaadt return fwrite((char *) mp, sizeof *mp, 1, f) != 1; 244df930be7Sderaadt } 245df930be7Sderaadt 246df930be7Sderaadt /* 247df930be7Sderaadt * Delete a file, but only if the file is a plain file. 248df930be7Sderaadt */ 249df930be7Sderaadt int 250df930be7Sderaadt rm(name) 251df930be7Sderaadt char *name; 252df930be7Sderaadt { 253df930be7Sderaadt struct stat sb; 254df930be7Sderaadt 255df930be7Sderaadt if (stat(name, &sb) < 0) 256df930be7Sderaadt return(-1); 257df930be7Sderaadt if (!S_ISREG(sb.st_mode)) { 258df930be7Sderaadt errno = EISDIR; 259df930be7Sderaadt return(-1); 260df930be7Sderaadt } 261df930be7Sderaadt return(unlink(name)); 262df930be7Sderaadt } 263df930be7Sderaadt 264df930be7Sderaadt static int sigdepth; /* depth of holdsigs() */ 265*7eb34045Sderaadt static sigset_t nset, oset; 266df930be7Sderaadt /* 267df930be7Sderaadt * Hold signals SIGHUP, SIGINT, and SIGQUIT. 268df930be7Sderaadt */ 269df930be7Sderaadt void 270df930be7Sderaadt holdsigs() 271df930be7Sderaadt { 272df930be7Sderaadt 273*7eb34045Sderaadt if (sigdepth++ == 0) { 274*7eb34045Sderaadt sigemptyset(&nset); 275*7eb34045Sderaadt sigaddset(&nset, SIGHUP); 276*7eb34045Sderaadt sigaddset(&nset, SIGINT); 277*7eb34045Sderaadt sigaddset(&nset, SIGQUIT); 278*7eb34045Sderaadt sigprocmask(SIG_BLOCK, &nset, &oset); 279*7eb34045Sderaadt } 280df930be7Sderaadt } 281df930be7Sderaadt 282df930be7Sderaadt /* 283df930be7Sderaadt * Release signals SIGHUP, SIGINT, and SIGQUIT. 284df930be7Sderaadt */ 285df930be7Sderaadt void 286df930be7Sderaadt relsesigs() 287df930be7Sderaadt { 288df930be7Sderaadt 289df930be7Sderaadt if (--sigdepth == 0) 290*7eb34045Sderaadt sigprocmask(SIG_SETMASK, &oset, NULL); 291df930be7Sderaadt } 292df930be7Sderaadt 293df930be7Sderaadt /* 294df930be7Sderaadt * Determine the size of the file possessed by 295df930be7Sderaadt * the passed buffer. 296df930be7Sderaadt */ 297df930be7Sderaadt off_t 298df930be7Sderaadt fsize(iob) 299df930be7Sderaadt FILE *iob; 300df930be7Sderaadt { 301df930be7Sderaadt struct stat sbuf; 302df930be7Sderaadt 303df930be7Sderaadt if (fstat(fileno(iob), &sbuf) < 0) 304df930be7Sderaadt return 0; 305df930be7Sderaadt return sbuf.st_size; 306df930be7Sderaadt } 307df930be7Sderaadt 308df930be7Sderaadt /* 309df930be7Sderaadt * Evaluate the string given as a new mailbox name. 310df930be7Sderaadt * Supported meta characters: 311df930be7Sderaadt * % for my system mail box 312df930be7Sderaadt * %user for user's system mail box 313df930be7Sderaadt * # for previous file 314df930be7Sderaadt * & invoker's mbox file 315df930be7Sderaadt * +file file in folder directory 316df930be7Sderaadt * any shell meta character 317df930be7Sderaadt * Return the file name as a dynamic string. 318df930be7Sderaadt */ 319df930be7Sderaadt char * 320df930be7Sderaadt expand(name) 321df930be7Sderaadt register char *name; 322df930be7Sderaadt { 323df930be7Sderaadt char xname[PATHSIZE]; 324df930be7Sderaadt char cmdbuf[PATHSIZE]; /* also used for file names */ 325df930be7Sderaadt register int pid, l; 326df930be7Sderaadt register char *cp, *shell; 327df930be7Sderaadt int pivec[2]; 328df930be7Sderaadt struct stat sbuf; 329df930be7Sderaadt extern union wait wait_status; 330df930be7Sderaadt 331df930be7Sderaadt /* 332df930be7Sderaadt * The order of evaluation is "%" and "#" expand into constants. 333df930be7Sderaadt * "&" can expand into "+". "+" can expand into shell meta characters. 334df930be7Sderaadt * Shell meta characters expand into constants. 335df930be7Sderaadt * This way, we make no recursive expansion. 336df930be7Sderaadt */ 337df930be7Sderaadt switch (*name) { 338df930be7Sderaadt case '%': 339df930be7Sderaadt findmail(name[1] ? name + 1 : myname, xname); 340df930be7Sderaadt return savestr(xname); 341df930be7Sderaadt case '#': 342df930be7Sderaadt if (name[1] != 0) 343df930be7Sderaadt break; 344df930be7Sderaadt if (prevfile[0] == 0) { 345df930be7Sderaadt printf("No previous file\n"); 346df930be7Sderaadt return NOSTR; 347df930be7Sderaadt } 348df930be7Sderaadt return savestr(prevfile); 349df930be7Sderaadt case '&': 350df930be7Sderaadt if (name[1] == 0 && (name = value("MBOX")) == NOSTR) 351df930be7Sderaadt name = "~/mbox"; 352df930be7Sderaadt /* fall through */ 353df930be7Sderaadt } 354df930be7Sderaadt if (name[0] == '+' && getfold(cmdbuf) >= 0) { 355df930be7Sderaadt sprintf(xname, "%s/%s", cmdbuf, name + 1); 356df930be7Sderaadt name = savestr(xname); 357df930be7Sderaadt } 358df930be7Sderaadt /* catch the most common shell meta character */ 359df930be7Sderaadt if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) { 360df930be7Sderaadt sprintf(xname, "%s%s", homedir, name + 1); 361df930be7Sderaadt name = savestr(xname); 362df930be7Sderaadt } 363df930be7Sderaadt if (!anyof(name, "~{[*?$`'\"\\")) 364df930be7Sderaadt return name; 365df930be7Sderaadt if (pipe(pivec) < 0) { 366df930be7Sderaadt perror("pipe"); 367df930be7Sderaadt return name; 368df930be7Sderaadt } 369df930be7Sderaadt sprintf(cmdbuf, "echo %s", name); 370df930be7Sderaadt if ((shell = value("SHELL")) == NOSTR) 371df930be7Sderaadt shell = _PATH_CSHELL; 372df930be7Sderaadt pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NOSTR); 373df930be7Sderaadt if (pid < 0) { 374df930be7Sderaadt close(pivec[0]); 375df930be7Sderaadt close(pivec[1]); 376df930be7Sderaadt return NOSTR; 377df930be7Sderaadt } 378df930be7Sderaadt close(pivec[1]); 379df930be7Sderaadt l = read(pivec[0], xname, BUFSIZ); 380df930be7Sderaadt close(pivec[0]); 381df930be7Sderaadt if (wait_child(pid) < 0 && wait_status.w_termsig != SIGPIPE) { 382df930be7Sderaadt fprintf(stderr, "\"%s\": Expansion failed.\n", name); 383df930be7Sderaadt return NOSTR; 384df930be7Sderaadt } 385df930be7Sderaadt if (l < 0) { 386df930be7Sderaadt perror("read"); 387df930be7Sderaadt return NOSTR; 388df930be7Sderaadt } 389df930be7Sderaadt if (l == 0) { 390df930be7Sderaadt fprintf(stderr, "\"%s\": No match.\n", name); 391df930be7Sderaadt return NOSTR; 392df930be7Sderaadt } 393df930be7Sderaadt if (l == BUFSIZ) { 394df930be7Sderaadt fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 395df930be7Sderaadt return NOSTR; 396df930be7Sderaadt } 397df930be7Sderaadt xname[l] = 0; 398df930be7Sderaadt for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 399df930be7Sderaadt ; 400df930be7Sderaadt cp[1] = '\0'; 401df930be7Sderaadt if (index(xname, ' ') && stat(xname, &sbuf) < 0) { 402df930be7Sderaadt fprintf(stderr, "\"%s\": Ambiguous.\n", name); 403df930be7Sderaadt return NOSTR; 404df930be7Sderaadt } 405df930be7Sderaadt return savestr(xname); 406df930be7Sderaadt } 407df930be7Sderaadt 408df930be7Sderaadt /* 409df930be7Sderaadt * Determine the current folder directory name. 410df930be7Sderaadt */ 411df930be7Sderaadt int 412df930be7Sderaadt getfold(name) 413df930be7Sderaadt char *name; 414df930be7Sderaadt { 415df930be7Sderaadt char *folder; 416df930be7Sderaadt 417df930be7Sderaadt if ((folder = value("folder")) == NOSTR) 418df930be7Sderaadt return (-1); 419df930be7Sderaadt if (*folder == '/') 420df930be7Sderaadt strcpy(name, folder); 421df930be7Sderaadt else 422df930be7Sderaadt sprintf(name, "%s/%s", homedir, folder); 423df930be7Sderaadt return (0); 424df930be7Sderaadt } 425df930be7Sderaadt 426df930be7Sderaadt /* 427df930be7Sderaadt * Return the name of the dead.letter file. 428df930be7Sderaadt */ 429df930be7Sderaadt char * 430df930be7Sderaadt getdeadletter() 431df930be7Sderaadt { 432df930be7Sderaadt register char *cp; 433df930be7Sderaadt 434df930be7Sderaadt if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR) 435df930be7Sderaadt cp = expand("~/dead.letter"); 436df930be7Sderaadt else if (*cp != '/') { 437df930be7Sderaadt char buf[PATHSIZE]; 438df930be7Sderaadt 439df930be7Sderaadt (void) sprintf(buf, "~/%s", cp); 440df930be7Sderaadt cp = expand(buf); 441df930be7Sderaadt } 442df930be7Sderaadt return cp; 443df930be7Sderaadt } 444