1 /* $NetBSD: fio.c,v 1.32 2009/04/10 13:08:24 christos Exp $ */
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95";
37 __RCSID("$NetBSD: fio.c,v 1.32 2009/04/10 13:08:24 christos Exp $");
47 * Mail -- a mail program
52 #ifndef THREAD_SUPPORT
53 /************************************************************************/
55 * If we have threading support, these routines live in thread.c.
57 static struct message
*message
; /* message structure array */
58 static int msgCount
; /* Count of messages read in */
60 PUBLIC
struct message
*
61 next_message(struct message
*mp
)
63 if (mp
+ 1 < message
|| mp
+ 1 >= message
+ msgCount
)
69 PUBLIC
struct message
*
70 prev_message(struct message
*mp
)
72 if (mp
- 1 < message
|| mp
- 1 >= message
+ msgCount
)
78 PUBLIC
struct message
*
79 get_message(int msgnum
)
81 if (msgnum
< 1 || msgnum
> msgCount
)
84 return message
+ msgnum
- 1;
88 get_msgnum(struct message
*mp
)
90 if (mp
< message
|| mp
>= message
+ msgCount
)
93 return mp
- message
+ 1;
101 #endif /* THREAD_SUPPORT */
102 /************************************************************************/
105 * Initialize a message structure.
108 message_init(struct message
*mp
, off_t offset
, short flags
)
110 /* use memset so new fields are always zeroed */
111 (void)memset(mp
, 0, sizeof(*mp
));
113 mp
->m_block
= blockof(offset
);
114 mp
->m_offset
= blkoffsetof(offset
);
118 * Take the data out of the passed ghost file and toss it into
119 * a dynamically allocated message structure.
122 makemessage(FILE *f
, int omsgCount
, int nmsgCount
)
125 struct message
*omessage
; /* old message structure array */
126 struct message
*nmessage
;
128 omessage
= get_abs_message(1);
130 size
= (nmsgCount
+ 1) * sizeof(*nmessage
);
131 nmessage
= realloc(omessage
, size
);
132 if (nmessage
== NULL
)
133 err(1, "Insufficient memory for %d messages", nmsgCount
);
134 if (omsgCount
== 0 || omessage
== NULL
)
137 dot
= nmessage
+ (dot
- omessage
);
139 thread_fix_old_links(nmessage
, omessage
, omsgCount
);
141 #ifndef THREAD_SUPPORT
144 size
-= (omsgCount
+ 1) * sizeof(*nmessage
);
146 (void)lseek(fileno(f
), (off_t
)sizeof(*nmessage
), SEEK_SET
);
147 if (read(fileno(f
), &nmessage
[omsgCount
], size
) != (ssize_t
)size
)
148 errx(EXIT_FAILURE
, "Message temporary file corrupted");
150 message_init(&nmessage
[nmsgCount
], (off_t
)0, 0); /* append a dummy */
152 thread_fix_new_links(nmessage
, omsgCount
, nmsgCount
);
158 * Append the passed message descriptor onto the temp file.
159 * If the write fails, return 1, else 0
162 append(struct message
*mp
, FILE *f
)
164 return fwrite(mp
, sizeof(*mp
), 1, f
) != 1;
168 * Set up the input pointers while copying the mail file into /tmp.
171 setptr(FILE *ibuf
, off_t offset
)
180 char linebuf
[LINESIZE
];
182 #ifdef THREAD_SUPPORT
185 # define nmsgCount msgCount
188 /* Get temporary file. */
189 (void)snprintf(linebuf
, LINESIZE
, "%s/mail.XXXXXX", tmpdir
);
190 if ((c
= mkstemp(linebuf
)) == -1 ||
191 (mestmp
= Fdopen(c
, "r+")) == NULL
) {
192 (void)fprintf(stderr
, "mail: can't open %s\n", linebuf
);
195 (void)unlink(linebuf
);
197 nmsgCount
= get_abs_msgCount();
201 /* Seek into the file to get to the new messages */
202 (void)fseeko(ibuf
, offset
, 0);
204 * We need to make "offset" a pointer to the end of
205 * the temp file that has the copy of the mail file.
206 * If any messages have been edited, this will be
207 * different from the offset into the mail file.
209 (void)fseek(otf
, 0L, SEEK_END
);
212 omsgCount
= nmsgCount
;
215 message_init(&this, (off_t
)0, MUSED
|MNEW
);
218 if (fgets(linebuf
, LINESIZE
, ibuf
) == NULL
) {
219 if (append(&this, mestmp
))
220 err(EXIT_FAILURE
, "temporary file");
221 makemessage(mestmp
, omsgCount
, nmsgCount
);
224 len
= strlen(linebuf
);
226 * Transforms lines ending in <CR><LF> to just <LF>.
227 * This allows mail to be able to read Eudora mailboxes
228 * that reside on a DOS partition.
230 if (len
>= 2 && linebuf
[len
- 1] == '\n' &&
231 linebuf
[len
- 2] == '\r') {
232 linebuf
[len
- 2] = '\n';
235 (void)fwrite(linebuf
, sizeof(*linebuf
), len
, otf
);
237 err(EXIT_FAILURE
, "/tmp");
239 linebuf
[len
- 1] = 0;
240 if (maybe
&& linebuf
[0] == 'F' && ishead(linebuf
)) {
242 if (append(&this, mestmp
))
243 err(EXIT_FAILURE
, "temporary file");
244 message_init(&this, offset
, MUSED
|MNEW
);
246 } else if (linebuf
[0] == 0) {
249 for (cp
= linebuf
, cp2
= "status";; cp
++) {
250 if ((c
= *cp2
++) == 0) {
251 while (isspace((unsigned char)*cp
++))
255 while ((c
= *cp
++) != '\0')
257 this.m_flag
|= MREAD
;
259 this.m_flag
&= ~MNEW
;
263 if (*cp
!= c
&& *cp
!= toupper(c
))
271 int lines_plus_wraps
= 1;
272 int linelen
= (int)strlen(linebuf
);
274 if (screenwidth
&& (int)linelen
> screenwidth
) {
275 lines_plus_wraps
= linelen
/ screenwidth
;
276 if (linelen
% screenwidth
!= 0)
279 this.m_blines
+= lines_plus_wraps
;
281 maybe
= linebuf
[0] == 0;
286 * Drop the passed line onto the passed output buffer.
287 * If a write error occurs, return -1, else the count of
288 * characters written, including the newline if requested.
291 putline(FILE *obuf
, const char *linebuf
, int outlf
)
296 (void)fwrite(linebuf
, sizeof(*linebuf
), c
, obuf
);
298 (void)putc('\n', obuf
);
307 * Read up a line from the specified input into the line
308 * buffer. Return the number of characters read. Do not
309 * include the newline at the end.
312 readline(FILE *ibuf
, char *linebuf
, int linesize
, int no_restart
)
314 struct sigaction osa_sigtstp
;
315 struct sigaction osa_sigttin
;
316 struct sigaction osa_sigttou
;
323 (void)sig_setflags(SIGTSTP
, 0, &osa_sigtstp
);
324 (void)sig_setflags(SIGTTIN
, 0, &osa_sigttin
);
325 (void)sig_setflags(SIGTTOU
, 0, &osa_sigttou
);
327 if (fgets(linebuf
, linesize
, ibuf
) == NULL
)
330 n
= (int)strlen(linebuf
);
331 if (n
> 0 && linebuf
[n
- 1] == '\n')
335 (void)sigaction(SIGTSTP
, &osa_sigtstp
, NULL
);
336 (void)sigaction(SIGTTIN
, &osa_sigttin
, NULL
);
337 (void)sigaction(SIGTTOU
, &osa_sigttou
, NULL
);
344 * Return a file buffer all ready to read up the
345 * passed message pointer.
348 setinput(const struct message
*mp
)
352 if (fseek(itf
, (long)positionof(mp
->m_block
, mp
->m_offset
), SEEK_SET
) < 0)
358 * Delete a file, but only if the file is a plain file.
365 if (stat(name
, &sb
) < 0)
367 if (!S_ISREG(sb
.st_mode
)) {
375 * Determine the size of the file possessed by
383 if (fstat(fileno(iob
), &sbuf
) < 0)
389 * Determine the current folder directory name.
392 getfold(char *name
, size_t namesize
)
396 if ((folder
= value(ENAME_FOLDER
)) == NULL
)
399 (void)strlcpy(name
, folder
, namesize
);
401 (void)snprintf(name
, namesize
, "%s/%s", homedir
, folder
);
406 * Evaluate the string given as a new mailbox name.
407 * Supported meta characters:
408 * % for my system mail box
409 * %user for user's system mail box
410 * # for previous file
411 * & invoker's mbox file
412 * +file file in folder directory
413 * any shell meta character
414 * Return the file name as a dynamic string.
417 expand(const char *name
)
419 char xname
[PATHSIZE
];
420 char cmdbuf
[PATHSIZE
]; /* also used for file names */
424 const char *shellcmd
;
429 * The order of evaluation is "%" and "#" expand into constants.
430 * "&" can expand into "+". "+" can expand into shell meta characters.
431 * Shell meta characters expand into constants.
432 * This way, we make no recursive expansion.
436 findmail(name
[1] ? name
+ 1 : myname
, xname
, sizeof(xname
));
437 return savestr(xname
);
441 if (prevfile
[0] == 0) {
442 (void)printf("No previous file\n");
445 return savestr(prevfile
);
447 if (name
[1] == 0 && (name
= value(ENAME_MBOX
)) == NULL
)
451 if (name
[0] == '+' && getfold(cmdbuf
, sizeof(cmdbuf
)) >= 0) {
452 (void)snprintf(xname
, sizeof(xname
), "%s/%s", cmdbuf
, name
+ 1);
453 name
= savestr(xname
);
455 /* catch the most common shell meta character */
456 if (name
[0] == '~' && (name
[1] == '/' || name
[1] == '\0')) {
457 (void)snprintf(xname
, sizeof(xname
), "%s%s", homedir
, name
+ 1);
458 name
= savestr(xname
);
460 if (strpbrk(name
, "~{[*?$`'\"\\") == NULL
)
462 if (pipe(pivec
) < 0) {
466 (void)snprintf(cmdbuf
, sizeof(cmdbuf
), "echo %s", name
);
467 if ((shellcmd
= value(ENAME_SHELL
)) == NULL
)
468 shellcmd
= _PATH_CSHELL
;
469 pid
= start_command(shellcmd
, NULL
, -1, pivec
[1], "-c", cmdbuf
, NULL
);
471 (void)close(pivec
[0]);
472 (void)close(pivec
[1]);
475 (void)close(pivec
[1]);
476 l
= read(pivec
[0], xname
, sizeof(xname
));
477 (void)close(pivec
[0]);
478 if (wait_child(pid
) < 0 && WTERMSIG(wait_status
) != SIGPIPE
) {
479 (void)fprintf(stderr
, "\"%s\": Expansion failed.\n", name
);
487 (void)fprintf(stderr
, "\"%s\": No match.\n", name
);
490 if (l
== sizeof(xname
)) {
491 (void)fprintf(stderr
, "\"%s\": Expansion buffer overflow.\n", name
);
495 for (cp
= &xname
[l
-1]; *cp
== '\n' && cp
> xname
; cp
--)
498 if (strchr(xname
, ' ') && stat(xname
, &sbuf
) < 0) {
499 (void)fprintf(stderr
, "\"%s\": Ambiguous.\n", name
);
502 return savestr(xname
);
506 * Return the name of the dead.letter file.
513 if ((cp
= value(ENAME_DEAD
)) == NULL
|| (cp
= expand(cp
)) == NULL
)
514 cp
= expand("~/dead.letter");
515 else if (*cp
!= '/') {
517 (void)snprintf(buf
, sizeof(buf
), "~/%s", cp
);