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