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