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