fio.c revision 81586
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 34#ifndef lint 35#if 0 36static char sccsid[] = "@(#)fio.c 8.1 (Berkeley) 6/6/93"; 37#endif 38static const char rcsid[] = 39 "$FreeBSD: head/usr.bin/mail/fio.c 81586 2001-08-13 14:06:34Z ru $"; 40#endif /* not lint */ 41 42#include "rcv.h" 43#include <sys/file.h> 44#include <sys/wait.h> 45 46#include <unistd.h> 47#include <paths.h> 48#include <errno.h> 49#include "extern.h" 50 51/* 52 * Mail -- a mail program 53 * 54 * File I/O. 55 */ 56 57extern int wait_status; 58 59/* 60 * Set up the input pointers while copying the mail file into /tmp. 61 */ 62void 63setptr(ibuf) 64 FILE *ibuf; 65{ 66 int c, count; 67 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 /* 98 * Transforms lines ending in <CR><LF> to just <LF>. 99 * This allows mail to be able to read Eudora mailboxes. 100 */ 101 if (count >= 2 && linebuf[count - 1] == '\n' && 102 linebuf[count - 2] == '\r') { 103 count--; 104 linebuf[count - 1] = '\n'; 105 } 106 107 (void)fwrite(linebuf, sizeof(*linebuf), count, otf); 108 if (ferror(otf)) 109 errx(1, "/tmp"); 110 if (count) 111 linebuf[count - 1] = '\0'; 112 if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 113 msgCount++; 114 if (append(&this, mestmp)) 115 errx(1, "temporary file"); 116 this.m_flag = MUSED|MNEW; 117 this.m_size = 0; 118 this.m_lines = 0; 119 this.m_block = blockof(offset); 120 this.m_offset = boffsetof(offset); 121 inhead = 1; 122 } else if (linebuf[0] == 0) { 123 inhead = 0; 124 } else if (inhead) { 125 for (cp = linebuf, cp2 = "status";; cp++) { 126 if ((c = *cp2++) == '\0') { 127 while (isspace(*cp++)) 128 ; 129 if (cp[-1] != ':') 130 break; 131 while ((c = *cp++) != '\0') 132 if (c == 'R') 133 this.m_flag |= MREAD; 134 else if (c == 'O') 135 this.m_flag &= ~MNEW; 136 inhead = 0; 137 break; 138 } 139 if (*cp != c && *cp != toupper(c)) 140 break; 141 } 142 } 143 offset += count; 144 this.m_size += count; 145 this.m_lines++; 146 maybe = linebuf[0] == 0; 147 } 148} 149 150/* 151 * Drop the passed line onto the passed output buffer. 152 * If a write error occurs, return -1, else the count of 153 * characters written, including the newline. 154 */ 155int 156putline(obuf, linebuf) 157 FILE *obuf; 158 char *linebuf; 159{ 160 int c; 161 162 c = strlen(linebuf); 163 (void)fwrite(linebuf, sizeof(*linebuf), c, obuf); 164 fprintf(obuf, "\n"); 165 if (ferror(obuf)) 166 return (-1); 167 return (c + 1); 168} 169 170/* 171 * Read up a line from the specified input into the line 172 * buffer. Return the number of characters read. Do not 173 * include the newline (or carriage return) at the end. 174 */ 175int 176readline(ibuf, linebuf, linesize) 177 FILE *ibuf; 178 char *linebuf; 179 int linesize; 180{ 181 int n; 182 183 clearerr(ibuf); 184 if (fgets(linebuf, linesize, ibuf) == NULL) 185 return (-1); 186 n = strlen(linebuf); 187 if (n > 0 && linebuf[n - 1] == '\n') 188 linebuf[--n] = '\0'; 189 if (n > 0 && linebuf[n - 1] == '\r') 190 linebuf[--n] = '\0'; 191 return (n); 192} 193 194/* 195 * Return a file buffer all ready to read up the 196 * passed message pointer. 197 */ 198FILE * 199setinput(mp) 200 struct message *mp; 201{ 202 203 (void)fflush(otf); 204 if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) 205 err(1, "fseek"); 206 return (itf); 207} 208 209/* 210 * Take the data out of the passed ghost file and toss it into 211 * a dynamically allocated message structure. 212 */ 213void 214makemessage(f) 215 FILE *f; 216{ 217 int size = (msgCount + 1) * sizeof(struct message); 218 219 if (message != 0) 220 (void)free(message); 221 if ((message = malloc((unsigned)size)) == NULL) 222 err(1, "Out of memory"); 223 dot = message; 224 size -= sizeof(struct message); 225 (void)fflush(f); 226 (void)lseek(fileno(f), (off_t)sizeof(*message), 0); 227 if (read(fileno(f), (char *)message, size) != size) 228 errx(1, "Message temporary file corrupted"); 229 message[msgCount].m_size = 0; 230 message[msgCount].m_lines = 0; 231 (void)Fclose(f); 232} 233 234/* 235 * Append the passed message descriptor onto the temp file. 236 * If the write fails, return 1, else 0 237 */ 238int 239append(mp, f) 240 struct message *mp; 241 FILE *f; 242{ 243 return (fwrite((char *)mp, sizeof(*mp), 1, f) != 1); 244} 245 246/* 247 * Delete a file, but only if the file is a plain file. 248 */ 249int 250rm(name) 251 char *name; 252{ 253 struct stat sb; 254 255 if (stat(name, &sb) < 0) 256 return (-1); 257 if (!S_ISREG(sb.st_mode)) { 258 errno = EISDIR; 259 return (-1); 260 } 261 return (unlink(name)); 262} 263 264static int sigdepth; /* depth of holdsigs() */ 265static int omask; 266/* 267 * Hold signals SIGHUP, SIGINT, and SIGQUIT. 268 */ 269void 270holdsigs() 271{ 272 273 if (sigdepth++ == 0) 274 omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)); 275} 276 277/* 278 * Release signals SIGHUP, SIGINT, and SIGQUIT. 279 */ 280void 281relsesigs() 282{ 283 284 if (--sigdepth == 0) 285 (void)sigsetmask(omask); 286} 287 288/* 289 * Determine the size of the file possessed by 290 * the passed buffer. 291 */ 292off_t 293fsize(iob) 294 FILE *iob; 295{ 296 struct stat sbuf; 297 298 if (fstat(fileno(iob), &sbuf) < 0) 299 return (0); 300 return (sbuf.st_size); 301} 302 303/* 304 * Evaluate the string given as a new mailbox name. 305 * Supported meta characters: 306 * % for my system mail box 307 * %user for user's system mail box 308 * # for previous file 309 * & invoker's mbox file 310 * +file file in folder directory 311 * any shell meta character 312 * Return the file name as a dynamic string. 313 */ 314char * 315expand(name) 316 char *name; 317{ 318 char xname[PATHSIZE]; 319 char cmdbuf[PATHSIZE]; /* also used for file names */ 320 int pid, l; 321 char *cp, *sh; 322 int pivec[2]; 323 struct stat sbuf; 324 325 /* 326 * The order of evaluation is "%" and "#" expand into constants. 327 * "&" can expand into "+". "+" can expand into shell meta characters. 328 * Shell meta characters expand into constants. 329 * This way, we make no recursive expansion. 330 */ 331 switch (*name) { 332 case '%': 333 findmail(name[1] ? name + 1 : myname, xname, sizeof(xname)); 334 return (savestr(xname)); 335 case '#': 336 if (name[1] != 0) 337 break; 338 if (prevfile[0] == 0) { 339 printf("No previous file\n"); 340 return (NULL); 341 } 342 return (savestr(prevfile)); 343 case '&': 344 if (name[1] == 0 && (name = value("MBOX")) == NULL) 345 name = "~/mbox"; 346 /* fall through */ 347 } 348 if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) { 349 (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1); 350 name = savestr(xname); 351 } 352 /* catch the most common shell meta character */ 353 if (name[0] == '~' && homedir != NULL && 354 (name[1] == '/' || name[1] == '\0')) { 355 (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1); 356 name = savestr(xname); 357 } 358 if (!strpbrk(name, "~{[*?$`'\"\\")) 359 return (name); 360 if (pipe(pivec) < 0) { 361 warn("pipe"); 362 return (name); 363 } 364 (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name); 365 if ((sh = value("SHELL")) == NULL) 366 sh = _PATH_CSHELL; 367 pid = start_command(sh, 0, -1, pivec[1], "-c", cmdbuf, NULL); 368 if (pid < 0) { 369 (void)close(pivec[0]); 370 (void)close(pivec[1]); 371 return (NULL); 372 } 373 (void)close(pivec[1]); 374 l = read(pivec[0], xname, BUFSIZ); 375 (void)close(pivec[0]); 376 if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) && 377 WTERMSIG(wait_status) != SIGPIPE) { 378 fprintf(stderr, "\"%s\": Expansion failed.\n", name); 379 return (NULL); 380 } 381 if (l < 0) { 382 warn("read"); 383 return (NULL); 384 } 385 if (l == 0) { 386 fprintf(stderr, "\"%s\": No match.\n", name); 387 return (NULL); 388 } 389 if (l == BUFSIZ) { 390 fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 391 return (NULL); 392 } 393 xname[l] = '\0'; 394 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 395 ; 396 cp[1] = '\0'; 397 if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { 398 fprintf(stderr, "\"%s\": Ambiguous.\n", name); 399 return (NULL); 400 } 401 return (savestr(xname)); 402} 403 404/* 405 * Determine the current folder directory name. 406 */ 407int 408getfold(name, namelen) 409 char *name; 410 int namelen; 411{ 412 char *folder; 413 int copylen; 414 415 if ((folder = value("folder")) == NULL) 416 return (-1); 417 if (*folder == '/') 418 copylen = strlcpy(name, folder, namelen); 419 else 420 copylen = snprintf(name, namelen, "%s/%s", 421 homedir ? homedir : ".", folder); 422 return (copylen >= namelen ? (-1) : (0)); 423} 424 425/* 426 * Return the name of the dead.letter file. 427 */ 428char * 429getdeadletter() 430{ 431 char *cp; 432 433 if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL) 434 cp = expand("~/dead.letter"); 435 else if (*cp != '/') { 436 char buf[PATHSIZE]; 437 438 (void)snprintf(buf, sizeof(buf), "~/%s", cp); 439 cp = expand(buf); 440 } 441 return (cp); 442} 443