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