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