1 /* $NetBSD: fio.c,v 1.12 1998/12/19 16:32:34 christos 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.12 1998/12/19 16:32:34 christos 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 if(count) 127 linebuf[count - 1] = 0; 128 if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 129 msgCount++; 130 if (append(&this, mestmp)) { 131 perror("temporary file"); 132 exit(1); 133 } 134 this.m_flag = MUSED|MNEW; 135 this.m_size = 0; 136 this.m_lines = 0; 137 this.m_block = blockof(offset); 138 this.m_offset = offsetof(offset); 139 inhead = 1; 140 } else if (linebuf[0] == 0) { 141 inhead = 0; 142 } else if (inhead) { 143 for (cp = linebuf, cp2 = "status";; cp++) { 144 if ((c = *cp2++) == 0) { 145 while (isspace((unsigned char)*cp++)) 146 ; 147 if (cp[-1] != ':') 148 break; 149 while ((c = *cp++) != '\0') 150 if (c == 'R') 151 this.m_flag |= MREAD; 152 else if (c == 'O') 153 this.m_flag &= ~MNEW; 154 inhead = 0; 155 break; 156 } 157 if (*cp != c && *cp != toupper(c)) 158 break; 159 } 160 } 161 offset += count; 162 this.m_size += count; 163 this.m_lines++; 164 maybe = linebuf[0] == 0; 165 } 166 } 167 168 /* 169 * Drop the passed line onto the passed output buffer. 170 * If a write error occurs, return -1, else the count of 171 * characters written, including the newline if requested. 172 */ 173 int 174 putline(obuf, linebuf, outlf) 175 FILE *obuf; 176 char *linebuf; 177 int outlf; 178 { 179 int c; 180 181 c = strlen(linebuf); 182 (void) fwrite(linebuf, sizeof *linebuf, c, obuf); 183 if (outlf) { 184 (void) putc('\n', obuf); 185 c++; 186 } 187 if (ferror(obuf)) 188 return (-1); 189 return (c); 190 } 191 192 /* 193 * Read up a line from the specified input into the line 194 * buffer. Return the number of characters read. Do not 195 * include the newline at the end. 196 */ 197 int 198 readline(ibuf, linebuf, linesize) 199 FILE *ibuf; 200 char *linebuf; 201 int linesize; 202 { 203 int n; 204 205 clearerr(ibuf); 206 if (fgets(linebuf, linesize, ibuf) == NULL) 207 return -1; 208 n = strlen(linebuf); 209 if (n > 0 && linebuf[n - 1] == '\n') 210 linebuf[--n] = '\0'; 211 return n; 212 } 213 214 /* 215 * Return a file buffer all ready to read up the 216 * passed message pointer. 217 */ 218 FILE * 219 setinput(mp) 220 struct message *mp; 221 { 222 223 fflush(otf); 224 if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) 225 err(1, "fseek"); 226 return (itf); 227 } 228 229 /* 230 * Take the data out of the passed ghost file and toss it into 231 * a dynamically allocated message structure. 232 */ 233 void 234 makemessage(f, omsgCount) 235 FILE *f; 236 int omsgCount; 237 { 238 int size = (msgCount + 1) * sizeof (struct message); 239 240 if (omsgCount) { 241 message = (struct message *)realloc(message, (unsigned) size); 242 if (message == 0) 243 errx(1, "Insufficient memory for %d messages\n", 244 msgCount); 245 } else { 246 if (message != 0) 247 free((char *) message); 248 if ((message = (struct message *) malloc((unsigned) size)) == 0) 249 errx(1, "Insufficient memory for %d messages", 250 msgCount); 251 dot = message; 252 } 253 size -= (omsgCount + 1) * sizeof (struct message); 254 fflush(f); 255 (void) lseek(fileno(f), (off_t)sizeof *message, 0); 256 if (read(fileno(f), (char *) &message[omsgCount], size) != size) 257 errx(1, "Message temporary file corrupted"); 258 message[msgCount].m_size = 0; 259 message[msgCount].m_lines = 0; 260 Fclose(f); 261 } 262 263 /* 264 * Append the passed message descriptor onto the temp file. 265 * If the write fails, return 1, else 0 266 */ 267 int 268 append(mp, f) 269 struct message *mp; 270 FILE *f; 271 { 272 return fwrite((char *) mp, sizeof *mp, 1, f) != 1; 273 } 274 275 /* 276 * Delete a file, but only if the file is a plain file. 277 */ 278 int 279 rm(name) 280 char *name; 281 { 282 struct stat sb; 283 284 if (stat(name, &sb) < 0) 285 return(-1); 286 if (!S_ISREG(sb.st_mode)) { 287 errno = EISDIR; 288 return(-1); 289 } 290 return(unlink(name)); 291 } 292 293 static int sigdepth; /* depth of holdsigs() */ 294 static sigset_t nset, oset; 295 /* 296 * Hold signals SIGHUP, SIGINT, and SIGQUIT. 297 */ 298 void 299 holdsigs() 300 { 301 302 if (sigdepth++ == 0) { 303 sigemptyset(&nset); 304 sigaddset(&nset, SIGHUP); 305 sigaddset(&nset, SIGINT); 306 sigaddset(&nset, SIGQUIT); 307 sigprocmask(SIG_BLOCK, &nset, &oset); 308 } 309 } 310 311 /* 312 * Release signals SIGHUP, SIGINT, and SIGQUIT. 313 */ 314 void 315 relsesigs() 316 { 317 318 if (--sigdepth == 0) 319 sigprocmask(SIG_SETMASK, &oset, NULL); 320 } 321 322 /* 323 * Determine the size of the file possessed by 324 * the passed buffer. 325 */ 326 off_t 327 fsize(iob) 328 FILE *iob; 329 { 330 struct stat sbuf; 331 332 if (fstat(fileno(iob), &sbuf) < 0) 333 return 0; 334 return sbuf.st_size; 335 } 336 337 /* 338 * Evaluate the string given as a new mailbox name. 339 * Supported meta characters: 340 * % for my system mail box 341 * %user for user's system mail box 342 * # for previous file 343 * & invoker's mbox file 344 * +file file in folder directory 345 * any shell meta character 346 * Return the file name as a dynamic string. 347 */ 348 char * 349 expand(name) 350 char *name; 351 { 352 char xname[PATHSIZE]; 353 char cmdbuf[PATHSIZE]; /* also used for file names */ 354 int pid, l; 355 char *cp, *shell; 356 int pivec[2]; 357 struct stat sbuf; 358 extern int wait_status; 359 360 /* 361 * The order of evaluation is "%" and "#" expand into constants. 362 * "&" can expand into "+". "+" can expand into shell meta characters. 363 * Shell meta characters expand into constants. 364 * This way, we make no recursive expansion. 365 */ 366 switch (*name) { 367 case '%': 368 findmail(name[1] ? name + 1 : myname, xname); 369 return savestr(xname); 370 case '#': 371 if (name[1] != 0) 372 break; 373 if (prevfile[0] == 0) { 374 printf("No previous file\n"); 375 return NOSTR; 376 } 377 return savestr(prevfile); 378 case '&': 379 if (name[1] == 0 && (name = value("MBOX")) == NOSTR) 380 name = "~/mbox"; 381 /* fall through */ 382 } 383 if (name[0] == '+' && getfold(cmdbuf) >= 0) { 384 snprintf(xname, PATHSIZE, "%s/%s", cmdbuf, name + 1); 385 name = savestr(xname); 386 } 387 /* catch the most common shell meta character */ 388 if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) { 389 snprintf(xname, PATHSIZE, "%s%s", homedir, name + 1); 390 name = savestr(xname); 391 } 392 if (!anyof(name, "~{[*?$`'\"\\")) 393 return name; 394 if (pipe(pivec) < 0) { 395 perror("pipe"); 396 return name; 397 } 398 snprintf(cmdbuf, PATHSIZE, "echo %s", name); 399 if ((shell = value("SHELL")) == NOSTR) 400 shell = _PATH_CSHELL; 401 pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NOSTR); 402 if (pid < 0) { 403 close(pivec[0]); 404 close(pivec[1]); 405 return NOSTR; 406 } 407 close(pivec[1]); 408 l = read(pivec[0], xname, PATHSIZE); 409 close(pivec[0]); 410 if (wait_child(pid) < 0 && WTERMSIG(wait_status) != SIGPIPE) { 411 fprintf(stderr, "\"%s\": Expansion failed.\n", name); 412 return NOSTR; 413 } 414 if (l < 0) { 415 perror("read"); 416 return NOSTR; 417 } 418 if (l == 0) { 419 fprintf(stderr, "\"%s\": No match.\n", name); 420 return NOSTR; 421 } 422 if (l == PATHSIZE) { 423 fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 424 return NOSTR; 425 } 426 xname[l] = '\0'; 427 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 428 ; 429 cp[1] = '\0'; 430 if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { 431 fprintf(stderr, "\"%s\": Ambiguous.\n", name); 432 return NOSTR; 433 } 434 return savestr(xname); 435 } 436 437 /* 438 * Determine the current folder directory name. 439 */ 440 int 441 getfold(name) 442 char *name; 443 { 444 char *folder; 445 446 if ((folder = value("folder")) == NOSTR) 447 return (-1); 448 if (*folder == '/') { 449 strncpy(name, folder, PATHSIZE - 1); 450 name[PATHSIZE - 1] = '\0' ; 451 } 452 else 453 snprintf(name, PATHSIZE, "%s/%s", homedir, folder); 454 return (0); 455 } 456 457 /* 458 * Return the name of the dead.letter file. 459 */ 460 char * 461 getdeadletter() 462 { 463 char *cp; 464 465 if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR) 466 cp = expand("~/dead.letter"); 467 else if (*cp != '/') { 468 char buf[PATHSIZE]; 469 470 (void) snprintf(buf, PATHSIZE, "~/%s", cp); 471 cp = expand(buf); 472 } 473 return cp; 474 } 475