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