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