122452Sdist /*
262083Sbostic * Copyright (c) 1980, 1993
362083Sbostic * The Regents of the University of California. All rights reserved.
433499Sbostic *
542741Sbostic * %sccs.include.redist.c%
622452Sdist */
722452Sdist
834905Sbostic #ifndef lint
9*68818Sdab static char sccsid[] = "@(#)fio.c 8.2 (Berkeley) 04/20/95";
1034905Sbostic #endif /* not lint */
111230Skas
121230Skas #include "rcv.h"
1331142Sedward #include <sys/file.h>
1431142Sedward #include <sys/wait.h>
1554505Sbostic
1654199Sbostic #include <unistd.h>
1746242Sbostic #include <paths.h>
181230Skas #include <errno.h>
1954505Sbostic #include "extern.h"
201230Skas
211230Skas /*
221230Skas * Mail -- a mail program
231230Skas *
241230Skas * File I/O.
251230Skas */
261230Skas
271230Skas /*
2846242Sbostic * Set up the input pointers while copying the mail file into /tmp.
291230Skas */
3054505Sbostic void
setptr(ibuf,offset)31*68818Sdab setptr(ibuf, offset)
3231142Sedward register FILE *ibuf;
33*68818Sdab off_t offset;
341230Skas {
3552909Sbostic extern char *tmpdir;
3646242Sbostic register int c, count;
372059Skas register char *cp, *cp2;
3846242Sbostic struct message this;
3931142Sedward FILE *mestmp;
4046242Sbostic int maybe, inhead;
4146242Sbostic char linebuf[LINESIZE];
42*68818Sdab int omsgCount;
431230Skas
4446242Sbostic /* Get temporary file. */
4552909Sbostic (void)sprintf(linebuf, "%s/mail.XXXXXX", tmpdir);
4646242Sbostic if ((c = mkstemp(linebuf)) == -1 ||
4746242Sbostic (mestmp = Fdopen(c, "r+")) == NULL) {
4846242Sbostic (void)fprintf(stderr, "mail: can't open %s\n", linebuf);
491230Skas exit(1);
5046242Sbostic }
5146242Sbostic (void)unlink(linebuf);
5246242Sbostic
53*68818Sdab if (offset == 0) {
54*68818Sdab msgCount = 0;
55*68818Sdab } else {
56*68818Sdab /* Seek into the file to get to the new messages */
57*68818Sdab (void) fseek(ibuf, offset, 0);
58*68818Sdab /*
59*68818Sdab * We need to make "offset" a pointer to the end of
60*68818Sdab * the temp file that has the copy of the mail file.
61*68818Sdab * If any messages have been edited, this will be
62*68818Sdab * different from the offset into the mail file.
63*68818Sdab */
64*68818Sdab (void) fseek(otf, 0L, 2);
65*68818Sdab offset = ftell(otf);
66*68818Sdab }
67*68818Sdab omsgCount = msgCount;
6831142Sedward maybe = 1;
6931142Sedward inhead = 0;
7031142Sedward this.m_flag = MUSED|MNEW;
7131142Sedward this.m_size = 0;
7231142Sedward this.m_lines = 0;
7331142Sedward this.m_block = 0;
7431142Sedward this.m_offset = 0;
751230Skas for (;;) {
7624536Sdonn if (fgets(linebuf, LINESIZE, ibuf) == NULL) {
771230Skas if (append(&this, mestmp)) {
7846242Sbostic perror("temporary file");
791230Skas exit(1);
801230Skas }
81*68818Sdab makemessage(mestmp, omsgCount);
821230Skas return;
831230Skas }
8424536Sdonn count = strlen(linebuf);
8536554Sedward (void) fwrite(linebuf, sizeof *linebuf, count, otf);
862059Skas if (ferror(otf)) {
871230Skas perror("/tmp");
881230Skas exit(1);
891230Skas }
9031142Sedward linebuf[count - 1] = 0;
911474Skas if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
921230Skas msgCount++;
931230Skas if (append(&this, mestmp)) {
9446242Sbostic perror("temporary file");
951230Skas exit(1);
961230Skas }
9731142Sedward this.m_flag = MUSED|MNEW;
9831142Sedward this.m_size = 0;
9931142Sedward this.m_lines = 0;
10031142Sedward this.m_block = blockof(offset);
10131142Sedward this.m_offset = offsetof(offset);
10231142Sedward inhead = 1;
10331142Sedward } else if (linebuf[0] == 0) {
1041474Skas inhead = 0;
10531142Sedward } else if (inhead) {
10631142Sedward for (cp = linebuf, cp2 = "status";; cp++) {
10731142Sedward if ((c = *cp2++) == 0) {
10831142Sedward while (isspace(*cp++))
10931142Sedward ;
11031142Sedward if (cp[-1] != ':')
11131142Sedward break;
11231142Sedward while (c = *cp++)
11331142Sedward if (c == 'R')
11431142Sedward this.m_flag |= MREAD;
11531142Sedward else if (c == 'O')
11631142Sedward this.m_flag &= ~MNEW;
11731142Sedward inhead = 0;
11831142Sedward break;
11931142Sedward }
12031142Sedward if (*cp != c && *cp != toupper(c))
12131142Sedward break;
1221474Skas }
1231474Skas }
1241230Skas offset += count;
12531142Sedward this.m_size += count;
12631142Sedward this.m_lines++;
12731142Sedward maybe = linebuf[0] == 0;
1281230Skas }
1291230Skas }
1301230Skas
1311230Skas /*
1321230Skas * Drop the passed line onto the passed output buffer.
1331230Skas * If a write error occurs, return -1, else the count of
1341230Skas * characters written, including the newline.
1351230Skas */
13654505Sbostic int
putline(obuf,linebuf)1371230Skas putline(obuf, linebuf)
1381230Skas FILE *obuf;
1391230Skas char *linebuf;
1401230Skas {
1411230Skas register int c;
1421230Skas
1431230Skas c = strlen(linebuf);
14436554Sedward (void) fwrite(linebuf, sizeof *linebuf, c, obuf);
14536554Sedward (void) putc('\n', obuf);
1461230Skas if (ferror(obuf))
14731142Sedward return (-1);
14831142Sedward return (c + 1);
1491230Skas }
1501230Skas
1511230Skas /*
1521230Skas * Read up a line from the specified input into the line
1531230Skas * buffer. Return the number of characters read. Do not
1541230Skas * include the newline at the end.
1551230Skas */
15654505Sbostic int
readline(ibuf,linebuf,linesize)15736478Sedward readline(ibuf, linebuf, linesize)
1581230Skas FILE *ibuf;
1591230Skas char *linebuf;
16054505Sbostic int linesize;
1611230Skas {
16224536Sdonn register int n;
1631230Skas
16424536Sdonn clearerr(ibuf);
16536478Sedward if (fgets(linebuf, linesize, ibuf) == NULL)
16631142Sedward return -1;
16724536Sdonn n = strlen(linebuf);
16831142Sedward if (n > 0 && linebuf[n - 1] == '\n')
16931142Sedward linebuf[--n] = '\0';
17031142Sedward return n;
1711230Skas }
1721230Skas
1731230Skas /*
1741230Skas * Return a file buffer all ready to read up the
1751230Skas * passed message pointer.
1761230Skas */
1771230Skas FILE *
setinput(mp)1781230Skas setinput(mp)
1791230Skas register struct message *mp;
1801230Skas {
1811230Skas
1821230Skas fflush(otf);
18354505Sbostic if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) {
1841230Skas perror("fseek");
1851230Skas panic("temporary file seek");
1861230Skas }
18731142Sedward return (itf);
1881230Skas }
1891230Skas
1901230Skas /*
1911230Skas * Take the data out of the passed ghost file and toss it into
1921230Skas * a dynamically allocated message structure.
1931230Skas */
19454505Sbostic void
makemessage(f,omsgCount)195*68818Sdab makemessage(f, omsgCount)
19631142Sedward FILE *f;
197*68818Sdab int omsgCount;
1981230Skas {
19931142Sedward register size = (msgCount + 1) * sizeof (struct message);
2001230Skas
201*68818Sdab if (omsgCount) {
202*68818Sdab message = (struct message *)realloc(message, (unsigned) size);
203*68818Sdab if (message == 0)
204*68818Sdab panic("Insufficient memory for %d messages\n", msgCount);
205*68818Sdab } else {
206*68818Sdab if (message != 0)
207*68818Sdab free((char *) message);
208*68818Sdab if ((message = (struct message *) malloc((unsigned) size)) == 0)
209*68818Sdab panic("Insufficient memory for %d messages", msgCount);
210*68818Sdab dot = message;
211*68818Sdab }
212*68818Sdab size -= (omsgCount + 1) * sizeof (struct message);
21331142Sedward fflush(f);
21454505Sbostic (void) lseek(fileno(f), (off_t)sizeof *message, 0);
215*68818Sdab if (read(fileno(f), (char *) &message[omsgCount], size) != size)
21631142Sedward panic("Message temporary file corrupted");
21731142Sedward message[msgCount].m_size = 0;
2181230Skas message[msgCount].m_lines = 0;
21943865Sedward Fclose(f);
2201230Skas }
2211230Skas
2221230Skas /*
2231230Skas * Append the passed message descriptor onto the temp file.
2241230Skas * If the write fails, return 1, else 0
2251230Skas */
22654505Sbostic int
append(mp,f)2271230Skas append(mp, f)
2281230Skas struct message *mp;
22931142Sedward FILE *f;
2301230Skas {
23131142Sedward return fwrite((char *) mp, sizeof *mp, 1, f) != 1;
2321230Skas }
2331230Skas
2341230Skas /*
2351230Skas * Delete a file, but only if the file is a plain file.
2361230Skas */
23754505Sbostic int
rm(name)23846242Sbostic rm(name)
23946242Sbostic char *name;
2401230Skas {
24146242Sbostic struct stat sb;
2421230Skas
24346242Sbostic if (stat(name, &sb) < 0)
2441230Skas return(-1);
24546242Sbostic if (!S_ISREG(sb.st_mode)) {
2461230Skas errno = EISDIR;
2471230Skas return(-1);
2481230Skas }
24946242Sbostic return(unlink(name));
2501230Skas }
2511230Skas
25234965Sedward static int sigdepth; /* depth of holdsigs() */
25334965Sedward static int omask;
2541230Skas /*
25531142Sedward * Hold signals SIGHUP, SIGINT, and SIGQUIT.
2563411Skurt */
25754505Sbostic void
holdsigs()2583565Skurt holdsigs()
2593411Skurt {
2603411Skurt
26111285Sleres if (sigdepth++ == 0)
26217668Sralph omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
2633411Skurt }
2643411Skurt
2653411Skurt /*
26631142Sedward * Release signals SIGHUP, SIGINT, and SIGQUIT.
2673411Skurt */
26854505Sbostic void
relsesigs()2693565Skurt relsesigs()
2703411Skurt {
2713565Skurt
27211285Sleres if (--sigdepth == 0)
27317668Sralph sigsetmask(omask);
2743411Skurt }
2753411Skurt
2763411Skurt /*
2771230Skas * Determine the size of the file possessed by
2781230Skas * the passed buffer.
2791230Skas */
2801230Skas off_t
fsize(iob)2811230Skas fsize(iob)
2821230Skas FILE *iob;
2831230Skas {
2841230Skas struct stat sbuf;
2851230Skas
28631142Sedward if (fstat(fileno(iob), &sbuf) < 0)
28731142Sedward return 0;
28831142Sedward return sbuf.st_size;
2891230Skas }
2901230Skas
2911230Skas /*
29234753Sedward * Evaluate the string given as a new mailbox name.
29334753Sedward * Supported meta characters:
29434753Sedward * % for my system mail box
29534753Sedward * %user for user's system mail box
29634753Sedward * # for previous file
29734753Sedward * & invoker's mbox file
29834753Sedward * +file file in folder directory
29934753Sedward * any shell meta character
3001230Skas * Return the file name as a dynamic string.
3011230Skas */
3021230Skas char *
expand(name)3031230Skas expand(name)
30434754Sedward register char *name;
3051230Skas {
30634968Sedward char xname[PATHSIZE];
30734968Sedward char cmdbuf[PATHSIZE]; /* also used for file names */
30831142Sedward register int pid, l;
30934963Sedward register char *cp, *shell;
31031142Sedward int pivec[2];
3111230Skas struct stat sbuf;
31234976Sedward extern union wait wait_status;
3131230Skas
31435067Sedward /*
31535067Sedward * The order of evaluation is "%" and "#" expand into constants.
31635067Sedward * "&" can expand into "+". "+" can expand into shell meta characters.
31735067Sedward * Shell meta characters expand into constants.
31835067Sedward * This way, we make no recursive expansion.
31935067Sedward */
32034753Sedward switch (*name) {
32134753Sedward case '%':
32234968Sedward findmail(name[1] ? name + 1 : myname, xname);
32334968Sedward return savestr(xname);
32434753Sedward case '#':
32534753Sedward if (name[1] != 0)
32634753Sedward break;
32734753Sedward if (prevfile[0] == 0) {
32834753Sedward printf("No previous file\n");
32934753Sedward return NOSTR;
33034753Sedward }
33134754Sedward return savestr(prevfile);
33234753Sedward case '&':
33335351Sedward if (name[1] == 0 && (name = value("MBOX")) == NOSTR)
33435067Sedward name = "~/mbox";
33534753Sedward /* fall through */
33634753Sedward }
3376225Skurt if (name[0] == '+' && getfold(cmdbuf) >= 0) {
3386225Skurt sprintf(xname, "%s/%s", cmdbuf, name + 1);
33934753Sedward name = savestr(xname);
3406225Skurt }
34135067Sedward /* catch the most common shell meta character */
34235067Sedward if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) {
34335351Sedward sprintf(xname, "%s%s", homedir, name + 1);
34435067Sedward name = savestr(xname);
34535067Sedward }
3461230Skas if (!anyof(name, "~{[*?$`'\"\\"))
34734754Sedward return name;
3481230Skas if (pipe(pivec) < 0) {
3491230Skas perror("pipe");
35034754Sedward return name;
3511230Skas }
3521230Skas sprintf(cmdbuf, "echo %s", name);
35334963Sedward if ((shell = value("SHELL")) == NOSTR)
35437870Sbostic shell = _PATH_CSHELL;
35534963Sedward pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NOSTR);
35634963Sedward if (pid < 0) {
3571230Skas close(pivec[0]);
3581230Skas close(pivec[1]);
35934754Sedward return NOSTR;
3601230Skas }
3611230Skas close(pivec[1]);
3621230Skas l = read(pivec[0], xname, BUFSIZ);
3631230Skas close(pivec[0]);
36434976Sedward if (wait_child(pid) < 0 && wait_status.w_termsig != SIGPIPE) {
36535331Sbostic fprintf(stderr, "\"%s\": Expansion failed.\n", name);
36634754Sedward return NOSTR;
3671230Skas }
3681230Skas if (l < 0) {
3691230Skas perror("read");
37034754Sedward return NOSTR;
3711230Skas }
3721230Skas if (l == 0) {
37335067Sedward fprintf(stderr, "\"%s\": No match.\n", name);
37434754Sedward return NOSTR;
3751230Skas }
3761230Skas if (l == BUFSIZ) {
37735067Sedward fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name);
37834754Sedward return NOSTR;
3791230Skas }
3801230Skas xname[l] = 0;
3811230Skas for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
3821230Skas ;
38334754Sedward cp[1] = '\0';
38434987Sedward if (index(xname, ' ') && stat(xname, &sbuf) < 0) {
38535067Sedward fprintf(stderr, "\"%s\": Ambiguous.\n", name);
38634754Sedward return NOSTR;
3871230Skas }
38834754Sedward return savestr(xname);
3891230Skas }
3901230Skas
3911230Skas /*
3926225Skurt * Determine the current folder directory name.
3936225Skurt */
39454505Sbostic int
getfold(name)3956225Skurt getfold(name)
3966225Skurt char *name;
3976225Skurt {
3986225Skurt char *folder;
3996225Skurt
4006225Skurt if ((folder = value("folder")) == NOSTR)
40131142Sedward return (-1);
4026225Skurt if (*folder == '/')
4036225Skurt strcpy(name, folder);
4046225Skurt else
4056225Skurt sprintf(name, "%s/%s", homedir, folder);
40631142Sedward return (0);
4076225Skurt }
4086225Skurt
4096225Skurt /*
41035351Sedward * Return the name of the dead.letter file.
41135351Sedward */
41235351Sedward char *
getdeadletter()41335351Sedward getdeadletter()
41435351Sedward {
41535351Sedward register char *cp;
41635351Sedward
41735351Sedward if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR)
41835351Sedward cp = expand("~/dead.letter");
41935351Sedward else if (*cp != '/') {
42035351Sedward char buf[PATHSIZE];
42135351Sedward
42235351Sedward (void) sprintf(buf, "~/%s", cp);
42335351Sedward cp = expand(buf);
42435351Sedward }
42535351Sedward return cp;
42635351Sedward }
427