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