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