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