INSTALL: update for v14.6
[s-mailx.git] / fio.c
blobe66f3893483f5d7eee28e6e2544c3eeb8d6a643c
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ File I/O.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <sys/wait.h>
46 #include <fcntl.h>
48 #ifdef HAVE_WORDEXP
49 # include <wordexp.h>
50 #endif
52 #ifdef HAVE_SOCKETS
53 # include <sys/socket.h>
55 # include <netdb.h>
57 # include <netinet/in.h>
59 # ifdef HAVE_ARPA_INET_H
60 # include <arpa/inet.h>
61 # endif
62 #endif
64 #ifdef HAVE_OPENSSL
65 # include <openssl/err.h>
66 # include <openssl/rand.h>
67 # include <openssl/ssl.h>
68 # include <openssl/x509v3.h>
69 # include <openssl/x509.h>
70 #endif
72 struct fio_stack {
73 FILE *s_file; /* File we were in. */
74 void *s_cond; /* Saved state of conditional stack */
75 int s_loading; /* Loading .mailrc, etc. */
78 static size_t _message_space; /* Slots in ::message */
79 static struct fio_stack _fio_stack[FIO_STACK_SIZE];
80 static size_t _fio_stack_size;
81 static FILE * _fio_input;
83 /* Locate the user's mailbox file (where new, unread mail is queued) */
84 static void _findmail(char *buf, size_t bufsize, char const *user,
85 bool_t force);
87 /* Perform shell meta character expansion */
88 static char * _globname(char const *name, enum fexp_mode fexpm);
90 /* line is a buffer with the result of fgets(). Returns the first newline or
91 * the last character read */
92 static size_t _length_of_line(char const *line, size_t linesize);
94 /* Read a line, one character at a time */
95 static char * _fgetline_byone(char **line, size_t *linesize, size_t *llen,
96 FILE *fp, int appendnl, size_t n SMALLOC_DEBUG_ARGS);
98 /* Take the data out of the passed ghost file and toss it into a dynamically
99 * allocated message structure */
100 static void makemessage(void);
102 static enum okay get_header(struct message *mp);
104 /* Write to socket fd, restarting on EINTR, unless anything is written */
105 #ifdef HAVE_SOCKETS
106 static long xwrite(int fd, char const *data, size_t sz);
107 #endif
109 static void
110 _findmail(char *buf, size_t bufsize, char const *user, bool_t force)
112 char *cp;
113 NYD_ENTER;
115 if (!strcmp(user, myname) && !force && (cp = ok_vlook(folder)) != NULL) {
116 switch (which_protocol(cp)) {
117 case PROTO_IMAP:
118 if (strcmp(cp, protbase(cp)))
119 goto jcopy;
120 snprintf(buf, bufsize, "%s/INBOX", cp);
121 goto jleave;
122 default:
123 break;
127 if (force || (cp = ok_vlook(MAIL)) == NULL)
128 snprintf(buf, bufsize, "%s/%s", MAILSPOOL, user);
129 else
130 jcopy:
131 n_strlcpy(buf, cp, bufsize);
132 jleave:
133 NYD_LEAVE;
136 static char *
137 _globname(char const *name, enum fexp_mode fexpm)
139 #ifdef HAVE_WORDEXP
140 wordexp_t we;
141 char *cp = NULL;
142 sigset_t nset;
143 int i;
144 NYD_ENTER;
146 /* Mac OS X Snow Leopard and Linux don't init fields on error, causing
147 * SIGSEGV in wordfree(3); so let's just always zero it ourselfs */
148 memset(&we, 0, sizeof we);
150 /* Some systems (notably Open UNIX 8.0.0) fork a shell for wordexp()
151 * and wait, which will fail if our SIGCHLD handler is active */
152 sigemptyset(&nset);
153 sigaddset(&nset, SIGCHLD);
154 sigprocmask(SIG_BLOCK, &nset, NULL);
155 i = wordexp(name, &we, 0);
156 sigprocmask(SIG_UNBLOCK, &nset, NULL);
158 switch (i) {
159 case 0:
160 break;
161 case WRDE_NOSPACE:
162 if (!(fexpm & FEXP_SILENT))
163 fprintf(stderr, tr(83, "\"%s\": Expansion buffer overflow.\n"), name);
164 goto jleave;
165 case WRDE_BADCHAR:
166 case WRDE_SYNTAX:
167 default:
168 if (!(fexpm & FEXP_SILENT))
169 fprintf(stderr, tr(242, "Syntax error in \"%s\"\n"), name);
170 goto jleave;
173 switch (we.we_wordc) {
174 case 1:
175 cp = savestr(we.we_wordv[0]);
176 break;
177 case 0:
178 if (!(fexpm & FEXP_SILENT))
179 fprintf(stderr, tr(82, "\"%s\": No match.\n"), name);
180 break;
181 default:
182 if (fexpm & FEXP_MULTIOK) {
183 size_t j, l;
185 for (l = 0, j = 0; j < we.we_wordc; ++j)
186 l += strlen(we.we_wordv[j]) + 1;
187 ++l;
188 cp = salloc(l);
189 for (l = 0, j = 0; j < we.we_wordc; ++j) {
190 size_t x = strlen(we.we_wordv[j]);
191 memcpy(cp + l, we.we_wordv[j], x);
192 l += x;
193 cp[l++] = ' ';
195 cp[l] = '\0';
196 } else if (!(fexpm & FEXP_SILENT))
197 fprintf(stderr, tr(84, "\"%s\": Ambiguous.\n"), name);
198 break;
200 jleave:
201 wordfree(&we);
202 NYD_LEAVE;
203 return cp;
205 #else /* HAVE_WORDEXP */
206 struct stat sbuf;
207 char xname[PATH_MAX], cmdbuf[PATH_MAX], /* also used for files */
208 *shellp, *cp = NULL;
209 int pivec[2], pid, l, waits;
210 NYD_ENTER;
212 if (pipe(pivec) < 0) {
213 perror("pipe");
214 goto jleave;
216 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
217 if ((shellp = ok_vlook(SHELL)) == NULL)
218 shellp = UNCONST(XSHELL);
219 pid = start_command(shellp, 0, -1, pivec[1], "-c", cmdbuf, NULL);
220 if (pid < 0) {
221 close(pivec[0]);
222 close(pivec[1]);
223 goto jleave;
225 close(pivec[1]);
227 jagain:
228 l = read(pivec[0], xname, sizeof xname);
229 if (l < 0) {
230 if (errno == EINTR)
231 goto jagain;
232 perror("read");
233 close(pivec[0]);
234 goto jleave;
236 close(pivec[0]);
237 if (!wait_child(pid, &waits) && WTERMSIG(waits) != SIGPIPE) {
238 if (!(fexpm & FEXP_SILENT))
239 fprintf(stderr, tr(81, "\"%s\": Expansion failed.\n"), name);
240 goto jleave;
242 if (l == 0) {
243 if (!(fexpm & FEXP_SILENT))
244 fprintf(stderr, tr(82, "\"%s\": No match.\n"), name);
245 goto jleave;
247 if (l == sizeof xname) {
248 if (!(fexpm & FEXP_SILENT))
249 fprintf(stderr, tr(83, "\"%s\": Expansion buffer overflow.\n"), name);
250 goto jleave;
252 xname[l] = 0;
253 for (cp = &xname[l - 1]; *cp == '\n' && cp > xname; --cp)
255 cp[1] = '\0';
256 if (!(fexpm & FEXP_MULTIOK) && strchr(xname, ' ') != NULL &&
257 stat(xname, &sbuf) < 0) {
258 if (!(fexpm & FEXP_SILENT))
259 fprintf(stderr, tr(84, "\"%s\": Ambiguous.\n"), name);
260 cp = NULL;
261 goto jleave;
263 cp = savestr(xname);
264 jleave:
265 NYD_LEAVE;
266 return cp;
267 #endif /* !HAVE_WORDEXP */
270 static size_t
271 _length_of_line(char const *line, size_t linesize)
273 size_t i;
274 NYD_ENTER;
276 /* Last character is always '\0' and was added by fgets() */
277 for (--linesize, i = 0; i < linesize; i++)
278 if (line[i] == '\n')
279 break;
280 i = (i < linesize) ? i + 1 : linesize;
281 NYD_LEAVE;
282 return i;
285 static char *
286 _fgetline_byone(char **line, size_t *linesize, size_t *llen, FILE *fp,
287 int appendnl, size_t n SMALLOC_DEBUG_ARGS)
289 char *rv;
290 int c;
291 NYD_ENTER;
293 assert(*linesize == 0 || *line != NULL);
294 for (rv = *line;;) {
295 if (*linesize <= LINESIZE || n >= *linesize - 128) {
296 *linesize += ((rv == NULL) ? LINESIZE + n + 1 : 256);
297 *line = rv = (srealloc)(rv, *linesize SMALLOC_DEBUG_ARGSCALL);
299 c = getc(fp);
300 if (c != EOF) {
301 rv[n++] = c;
302 rv[n] = '\0';
303 if (c == '\n')
304 break;
305 } else {
306 if (n > 0) {
307 if (appendnl) {
308 rv[n++] = '\n';
309 rv[n] = '\0';
311 break;
312 } else {
313 rv = NULL;
314 goto jleave;
318 if (llen)
319 *llen = n;
320 jleave:
321 NYD_LEAVE;
322 return rv;
325 static void
326 makemessage(void)
328 NYD_ENTER;
329 if (msgCount == 0)
330 message_append(NULL);
331 setdot(message);
332 message[msgCount].m_size = 0;
333 message[msgCount].m_lines = 0;
334 NYD_LEAVE;
337 static enum okay
338 get_header(struct message *mp)
340 enum okay rv;
341 NYD_ENTER;
342 UNUSED(mp);
344 switch (mb.mb_type) {
345 case MB_FILE:
346 case MB_MAILDIR:
347 rv = OKAY;
348 break;
349 #ifdef HAVE_POP3
350 case MB_POP3:
351 rv = pop3_header(mp);
352 break;
353 #endif
354 #ifdef HAVE_IMAP
355 case MB_IMAP:
356 case MB_CACHE:
357 rv = imap_header(mp);
358 break;
359 #endif
360 case MB_VOID:
361 default:
362 rv = STOP;
363 break;
365 NYD_LEAVE;
366 return rv;
369 #ifdef HAVE_SOCKETS
370 static long
371 xwrite(int fd, char const *data, size_t sz)
373 long rv = -1, wo;
374 size_t wt = 0;
375 NYD_ENTER;
377 do {
378 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
379 if (errno == EINTR)
380 continue;
381 else
382 goto jleave;
384 wt += wo;
385 } while (wt < sz);
386 rv = (long)sz;
387 jleave:
388 NYD_LEAVE;
389 return rv;
391 #endif /* HAVE_SOCKETS */
393 FL char *
394 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen, FILE *fp,
395 int appendnl SMALLOC_DEBUG_ARGS)
397 size_t i_llen, sz;
398 char *rv;
399 NYD_ENTER;
401 if (cnt == NULL) {
402 /* Without count, we can't determine where the chars returned by fgets()
403 * end if there's no newline. We have to read one character by one */
404 rv = _fgetline_byone(line, linesize, llen, fp, appendnl, 0
405 SMALLOC_DEBUG_ARGSCALL);
406 goto jleave;
409 if ((rv = *line) == NULL || *linesize < LINESIZE)
410 *line = rv = (srealloc)(rv, *linesize = LINESIZE SMALLOC_DEBUG_ARGSCALL);
411 sz = (*linesize <= *cnt) ? *linesize : *cnt + 1;
412 if (sz <= 1 || fgets(rv, sz, fp) == NULL) {
413 /* Leave llen untouched; it is used to determine whether the last line
414 * was \n-terminated in some callers */
415 rv = NULL;
416 goto jleave;
419 i_llen = _length_of_line(rv, sz);
420 *cnt -= i_llen;
421 while (rv[i_llen - 1] != '\n') {
422 *line = rv = (srealloc)(rv, *linesize += 256 SMALLOC_DEBUG_ARGSCALL);
423 sz = *linesize - i_llen;
424 sz = (sz <= *cnt) ? sz : *cnt + 1;
425 if (sz <= 1 || fgets(rv + i_llen, sz, fp) == NULL) {
426 if (appendnl) {
427 rv[i_llen++] = '\n';
428 rv[i_llen] = '\0';
430 break;
432 sz = _length_of_line(rv + i_llen, sz);
433 i_llen += sz;
434 *cnt -= sz;
436 if (llen)
437 *llen = i_llen;
438 jleave:
439 NYD_LEAVE;
440 return rv;
443 FL int
444 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
445 SMALLOC_DEBUG_ARGS)
447 /* TODO readline_restart(): always *appends* LF just to strip it again;
448 * TODO should be configurable just as for fgetline(); ..or whatevr.. */
449 int rv = -1;
450 long sz;
451 NYD_ENTER;
453 clearerr(ibuf);
455 /* Interrupts will cause trouble if we are inside a stdio call. As this is
456 * only relevant if input is from tty, bypass it by read(), then */
457 if (fileno(ibuf) == 0 && (options & OPT_TTYIN)) {
458 assert(*linesize == 0 || *linebuf != NULL);
459 for (;;) {
460 if (*linesize <= LINESIZE || n >= *linesize - 128) {
461 *linesize += ((*linebuf == NULL) ? LINESIZE + n + 1 : 256);
462 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
464 jagain:
465 sz = read(0, *linebuf + n, *linesize - n - 1);
466 if (sz > 0) {
467 n += sz;
468 (*linebuf)[n] = '\0';
469 if (n > 0 && (*linebuf)[n - 1] == '\n')
470 break;
471 } else {
472 if (sz < 0 && errno == EINTR)
473 goto jagain;
474 if (n > 0) {
475 if ((*linebuf)[n - 1] != '\n') {
476 (*linebuf)[n++] = '\n';
477 (*linebuf)[n] = '\0';
479 break;
480 } else
481 goto jleave;
484 } else {
485 /* Not reading from standard input or standard input not a terminal. We
486 * read one char at a time as it is the only way to get lines with
487 * embedded NUL characters in standard stdio */
488 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
489 SMALLOC_DEBUG_ARGSCALL) == NULL)
490 goto jleave;
492 if (n > 0 && (*linebuf)[n - 1] == '\n')
493 (*linebuf)[--n] = '\0';
494 rv = (int)n;
495 jleave:
496 NYD_LEAVE;
497 return rv;
500 FL int
501 (readline_input)(char const *prompt, bool_t nl_escape, char **linebuf,
502 size_t *linesize, char const *string SMALLOC_DEBUG_ARGS)
504 /* TODO readline: linebuf pool! */
505 FILE *ifile = (_fio_input != NULL) ? _fio_input : stdin;
506 bool_t doprompt, dotty;
507 int n;
508 NYD_ENTER;
510 doprompt = (!sourcing && (options & OPT_INTERACTIVE));
511 dotty = (doprompt && !ok_blook(line_editor_disable));
512 if (!doprompt)
513 prompt = NULL;
514 else if (prompt == NULL)
515 prompt = getprompt();
517 for (n = 0;;) {
518 if (dotty) {
519 assert(ifile == stdin);
520 if (string != NULL && (n = (int)strlen(string)) > 0) {
521 if (*linesize > 0)
522 *linesize += n +1;
523 else
524 *linesize = (size_t)n + LINESIZE +1;
525 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
526 memcpy(*linebuf, string, (size_t)n +1);
528 string = NULL;
529 n = (tty_readline)(prompt, linebuf, linesize, n
530 SMALLOC_DEBUG_ARGSCALL);
531 } else {
532 if (prompt != NULL && *prompt != '\0') {
533 fputs(prompt, stdout);
534 fflush(stdout);
536 n = (readline_restart)(ifile, linebuf, linesize, n
537 SMALLOC_DEBUG_ARGSCALL);
539 if (n <= 0)
540 break;
541 /* POSIX says:
542 * An unquoted <backslash> at the end of a command line shall
543 * be discarded and the next line shall continue the command */
544 if (nl_escape && (*linebuf)[n - 1] == '\\') {
545 (*linebuf)[--n] = '\0';
546 if (prompt != NULL && *prompt != '\0')
547 prompt = ".. "; /* XXX PS2 .. */
548 continue;
550 break;
552 NYD_LEAVE;
553 return n;
556 FL char *
557 readstr_input(char const *prompt, char const *string)
559 /* FIXME readstr_input: without linepool leaks on sigjmp */
560 size_t linesize = 0;
561 char *linebuf = NULL, *rv = NULL;
562 int n;
563 NYD_ENTER;
565 n = readline_input(prompt, FAL0, &linebuf, &linesize, string);
566 if (n > 0)
567 rv = savestrbuf(linebuf, (size_t)n + 1);
569 if (linebuf != NULL)
570 free(linebuf);
571 NYD_LEAVE;
572 return rv;
575 FL void
576 setptr(FILE *ibuf, off_t offset)
578 struct message self;
579 char *cp, *linebuf = NULL;
580 char const *cp2;
581 int c, maybe = 1, inhead = 0, selfcnt = 0;
582 size_t linesize = 0, filesize, cnt;
583 NYD_ENTER;
585 memset(&self, 0, sizeof self);
586 self.m_flag = MUSED | MNEW | MNEWEST;
587 filesize = mailsize - offset;
588 offset = ftell(mb.mb_otf);
590 for (;;) {
591 if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0) == NULL) {
592 self.m_xsize = self.m_size;
593 self.m_xlines = self.m_lines;
594 self.m_have = HAVE_HEADER | HAVE_BODY;
595 if (selfcnt > 0)
596 message_append(&self);
597 makemessage();
598 if (linebuf)
599 free(linebuf);
600 break;
603 #ifdef notdef
604 if (linebuf[0] == '\0')
605 linebuf[0] = '.';
606 #endif
607 /* XXX Convert CRLF to LF; this should be rethought in that
608 * XXX CRLF input should possibly end as CRLF output? */
609 if (cnt >= 2 && linebuf[cnt - 1] == '\n' && linebuf[cnt - 2] == '\r')
610 linebuf[--cnt - 1] = '\n';
611 fwrite(linebuf, sizeof *linebuf, cnt, mb.mb_otf);
612 if (ferror(mb.mb_otf)) {
613 perror("/tmp");
614 exit(1);
616 if (linebuf[cnt - 1] == '\n')
617 linebuf[cnt - 1] = '\0';
618 if (maybe && linebuf[0] == 'F' && is_head(linebuf, cnt)) {
619 /* TODO char date[FROM_DATEBUF];
620 * TODO extract_date_from_from_(linebuf, cnt, date);
621 * TODO self.m_time = 10000; */
622 self.m_xsize = self.m_size;
623 self.m_xlines = self.m_lines;
624 self.m_have = HAVE_HEADER | HAVE_BODY;
625 if (selfcnt++ > 0)
626 message_append(&self);
627 msgCount++;
628 self.m_flag = MUSED | MNEW | MNEWEST;
629 self.m_size = 0;
630 self.m_lines = 0;
631 self.m_block = mailx_blockof(offset);
632 self.m_offset = mailx_offsetof(offset);
633 inhead = 1;
634 } else if (linebuf[0] == 0) {
635 inhead = 0;
636 } else if (inhead) {
637 for (cp = linebuf, cp2 = "status";; ++cp) {
638 if ((c = *cp2++) == 0) {
639 while (c = *cp++, whitechar(c))
641 if (cp[-1] != ':')
642 break;
643 while ((c = *cp++) != '\0')
644 if (c == 'R')
645 self.m_flag |= MREAD;
646 else if (c == 'O')
647 self.m_flag &= ~MNEW;
648 break;
650 if (*cp != c && *cp != upperconv(c))
651 break;
653 for (cp = linebuf, cp2 = "x-status";; ++cp) {
654 if ((c = *cp2++) == 0) {
655 while ((c = *cp++, whitechar(c)))
657 if (cp[-1] != ':')
658 break;
659 while ((c = *cp++) != '\0')
660 if (c == 'F')
661 self.m_flag |= MFLAGGED;
662 else if (c == 'A')
663 self.m_flag |= MANSWERED;
664 else if (c == 'T')
665 self.m_flag |= MDRAFTED;
666 break;
668 if (*cp != c && *cp != upperconv(c))
669 break;
672 offset += cnt;
673 self.m_size += cnt;
674 ++self.m_lines;
675 maybe = linebuf[0] == 0;
677 NYD_LEAVE;
680 FL int
681 putline(FILE *obuf, char *linebuf, size_t cnt)
683 int rv = -1;
684 NYD_ENTER;
686 fwrite(linebuf, sizeof *linebuf, cnt, obuf);
687 putc('\n', obuf);
688 if (!ferror(obuf))
689 rv = (int)(cnt + 1);
690 NYD_LEAVE;
691 return rv;
694 FL FILE *
695 setinput(struct mailbox *mp, struct message *m, enum needspec need)
697 FILE *rv = NULL;
698 enum okay ok = STOP;
699 NYD_ENTER;
701 switch (need) {
702 case NEED_HEADER:
703 ok = (m->m_have & HAVE_HEADER) ? OKAY : get_header(m);
704 break;
705 case NEED_BODY:
706 ok = (m->m_have & HAVE_BODY) ? OKAY : get_body(m);
707 break;
708 case NEED_UNSPEC:
709 ok = OKAY;
710 break;
712 if (ok != OKAY)
713 goto jleave;
715 fflush(mp->mb_otf);
716 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
717 SEEK_SET) < 0) {
718 perror("fseek");
719 panic(tr(77, "temporary file seek"));
721 rv = mp->mb_itf;
722 jleave:
723 NYD_LEAVE;
724 return rv;
727 FL void
728 message_reset(void)
730 NYD_ENTER;
731 if (message != NULL) {
732 free(message);
733 message = NULL;
735 msgCount = 0;
736 _message_space = 0;
737 NYD_LEAVE;
740 FL void
741 message_append(struct message *mp)
743 NYD_ENTER;
744 if (UICMP(z, msgCount + 1, >=, _message_space)) {
745 /* XXX remove _message_space magics (or use s_Vector) */
746 _message_space = (_message_space >= 128 && _message_space <= 1000000)
747 ? _message_space << 1 : _message_space + 64;
748 message = srealloc(message, _message_space * sizeof *message);
750 if (msgCount > 0) {
751 if (mp != NULL)
752 message[msgCount - 1] = *mp;
753 else
754 memset(message + msgCount - 1, 0, sizeof *message);
756 NYD_LEAVE;
759 FL bool_t
760 message_match(struct message *mp, struct search_expr const *sep,
761 bool_t with_headers)
763 char **line;
764 size_t *linesize, cnt;
765 FILE *fp;
766 bool_t rv = FAL0;
767 NYD_ENTER;
769 if ((fp = Ftmp(NULL, "mpmatch", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
770 NULL)
771 goto j_leave;
773 if (sendmp(mp, fp, NULL, NULL, SEND_TOSRCH, NULL) < 0)
774 goto jleave;
775 fflush_rewind(fp);
777 cnt = fsize(fp);
778 line = &termios_state.ts_linebuf; /* XXX line pool */
779 linesize = &termios_state.ts_linesize; /* XXX line pool */
781 if (!with_headers)
782 while (fgetline(line, linesize, &cnt, NULL, fp, 0))
783 if (**line == '\n')
784 break;
786 while (fgetline(line, linesize, &cnt, NULL, fp, 0)) {
787 #ifdef HAVE_REGEX
788 if (sep->ss_sexpr == NULL) {
789 if (regexec(&sep->ss_reexpr, *line, 0,NULL, 0) == REG_NOMATCH)
790 continue;
791 } else
792 #endif
793 if (!substr(*line, sep->ss_sexpr))
794 continue;
795 rv = TRU1;
796 break;
799 jleave:
800 Fclose(fp);
801 j_leave:
802 NYD_LEAVE;
803 return rv;
806 FL struct message *
807 setdot(struct message *mp)
809 NYD_ENTER;
810 if (dot != mp) {
811 prevdot = dot;
812 did_print_dot = FAL0;
814 dot = mp;
815 uncollapse1(dot, 0);
816 NYD_LEAVE;
817 return dot;
820 FL int
821 rm(char const *name)
823 struct stat sb;
824 int rv = -1;
825 NYD_ENTER;
827 if (stat(name, &sb) < 0)
829 else if (!S_ISREG(sb.st_mode))
830 errno = EISDIR;
831 else
832 rv = unlink(name);
833 NYD_LEAVE;
834 return rv;
837 FL off_t
838 fsize(FILE *iob)
840 struct stat sbuf;
841 off_t rv;
842 NYD_ENTER;
844 rv = (fstat(fileno(iob), &sbuf) < 0) ? 0 : sbuf.st_size;
845 NYD_LEAVE;
846 return rv;
849 FL char *
850 fexpand(char const *name, enum fexp_mode fexpm)
852 char cbuf[PATH_MAX], *res;
853 struct str s;
854 struct shortcut *sh;
855 bool_t dyn;
856 NYD_ENTER;
858 /* The order of evaluation is "%" and "#" expand into constants.
859 * "&" can expand into "+". "+" can expand into shell meta characters.
860 * Shell meta characters expand into constants.
861 * This way, we make no recursive expansion */
862 res = UNCONST(name);
863 if (!(fexpm & FEXP_NSHORTCUT) && (sh = get_shortcut(res)) != NULL)
864 res = sh->sh_long;
866 if (fexpm & FEXP_SHELL) {
867 dyn = FAL0;
868 goto jshell;
870 jnext:
871 dyn = FAL0;
872 switch (*res) {
873 case '%':
874 if (res[1] == ':' && res[2] != '\0') {
875 res = &res[2];
876 goto jnext;
878 _findmail(cbuf, sizeof cbuf, (res[1] != '\0' ? res + 1 : myname),
879 (res[1] != '\0' || (options & OPT_u_FLAG)));
880 res = cbuf;
881 goto jislocal;
882 case '#':
883 if (res[1] != '\0')
884 break;
885 if (prevfile[0] == '\0') {
886 fprintf(stderr, tr(80, "No previous file\n"));
887 res = NULL;
888 goto jleave;
890 res = prevfile;
891 goto jislocal;
892 case '&':
893 if (res[1] == '\0') {
894 if ((res = ok_vlook(MBOX)) == NULL)
895 res = UNCONST("~/mbox");
896 else if (res[0] != '&' || res[1] != '\0')
897 goto jnext;
899 break;
902 if (res[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
903 res = str_concat_csvl(&s, protbase(mailname), "/", res + 1, NULL)->s;
904 dyn = TRU1;
907 if (res[0] == '+' && getfold(cbuf, sizeof cbuf)) {
908 size_t i = strlen(cbuf);
910 res = str_concat_csvl(&s, cbuf,
911 ((i > 0 && cbuf[i - 1] == '/') ? "" : "/"), res + 1, NULL)->s;
912 dyn = TRU1;
914 if (res[0] == '%' && res[1] == ':') {
915 res += 2;
916 goto jnext;
920 /* Catch the most common shell meta character */
921 jshell:
922 if (res[0] == '~' && (res[1] == '/' || res[1] == '\0')) {
923 res = str_concat_csvl(&s, homedir, res + 1, NULL)->s;
924 dyn = TRU1;
927 if (anyof(res, "|&;<>~{}()[]*?$`'\"\\") &&
928 which_protocol(res) == PROTO_FILE) {
929 res = _globname(res, fexpm);
930 dyn = TRU1;
931 goto jleave;
934 jislocal:
935 if (fexpm & FEXP_LOCAL)
936 switch (which_protocol(res)) {
937 case PROTO_FILE:
938 case PROTO_MAILDIR:
939 break;
940 default:
941 fprintf(stderr, tr(280,
942 "`%s': only a local file or directory may be used\n"), name);
943 res = NULL;
944 break;
946 jleave:
947 if (res && !dyn)
948 res = savestr(res);
949 NYD_LEAVE;
950 return res;
953 FL void
954 demail(void)
956 NYD_ENTER;
957 if (ok_blook(keep) || rm(mailname) < 0) {
958 int fd = open(mailname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
959 if (fd >= 0)
960 close(fd);
962 NYD_LEAVE;
965 FL bool_t
966 var_folder_updated(char const *name, char **store)
968 char rv = TRU1;
969 char *folder, *unres = NULL, *res = NULL;
970 NYD_ENTER;
972 if ((folder = UNCONST(name)) == NULL)
973 goto jleave;
975 /* Expand the *folder*; skip `%:' prefix for simplicity of use */
976 /* XXX This *only* works because we do NOT
977 * XXX update environment variables via the "set" mechanism */
978 if (folder[0] == '%' && folder[1] == ':')
979 folder += 2;
980 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
981 goto jleave;
983 switch (which_protocol(folder)) {
984 case PROTO_POP3:
985 /* Ooops. This won't work */
986 fprintf(stderr, tr(501,
987 "`folder' cannot be set to a flat, readonly POP3 account\n"));
988 rv = FAL0;
989 goto jleave;
990 case PROTO_IMAP:
991 /* Simply assign what we have, even including `%:' prefix */
992 if (folder != name)
993 goto jvcopy;
994 goto jleave;
995 default:
996 /* Further expansion desired */
997 break;
1000 /* All non-absolute paths are relative to our home directory */
1001 if (*folder != '/') {
1002 size_t l1 = strlen(homedir), l2 = strlen(folder);
1003 unres = ac_alloc(l1 + l2 + 1 +1);
1004 memcpy(unres, homedir, l1);
1005 unres[l1] = '/';
1006 memcpy(unres + l1 + 1, folder, l2);
1007 unres[l1 + 1 + l2] = '\0';
1008 folder = unres;
1011 /* Since lex.c:_update_mailname() uses realpath(3) if available to
1012 * avoid that we loose track of our currently open folder in case we
1013 * chdir away, but still checks the leading path portion against
1014 * getfold() to be able to abbreviate to the +FOLDER syntax if
1015 * possible, we need to realpath(3) the folder, too */
1016 #ifdef HAVE_REALPATH
1017 res = ac_alloc(PATH_MAX);
1018 if (realpath(folder, res) == NULL)
1019 fprintf(stderr, tr(151, "Can't canonicalize `%s'\n"), folder);
1020 else
1021 folder = res;
1022 #endif
1024 jvcopy:
1025 *store = sstrdup(folder);
1027 if (res != NULL)
1028 ac_free(res);
1029 if (unres != NULL)
1030 ac_free(unres);
1031 jleave:
1032 NYD_LEAVE;
1033 return rv;
1036 FL bool_t
1037 getfold(char *name, size_t size)
1039 char const *folder;
1040 NYD_ENTER;
1042 if ((folder = ok_vlook(folder)) != NULL)
1043 n_strlcpy(name, folder, size);
1044 NYD_LEAVE;
1045 return (folder != NULL);
1048 FL char const *
1049 getdeadletter(void) /* XXX should that be in auxlily.c? */
1051 char const *cp;
1052 NYD_ENTER;
1054 if ((cp = ok_vlook(DEAD)) == NULL || (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
1055 cp = fexpand("~/dead.letter", FEXP_LOCAL | FEXP_SHELL);
1056 else if (*cp != '/') {
1057 size_t sz = strlen(cp) + 3;
1058 char *buf = ac_alloc(sz);
1060 snprintf(buf, sz, "~/%s", cp);
1061 cp = fexpand(buf, FEXP_LOCAL | FEXP_SHELL);
1062 ac_free(buf);
1065 if (cp == NULL)
1066 cp = "dead.letter"; /* XXX magic -> nail.h (POSIX thing though) */
1067 NYD_LEAVE;
1068 return cp;
1071 FL enum okay
1072 get_body(struct message *mp)
1074 enum okay rv;
1075 NYD_ENTER;
1076 UNUSED(mp);
1078 switch (mb.mb_type) {
1079 case MB_FILE:
1080 case MB_MAILDIR:
1081 rv = OKAY;
1082 break;
1083 #ifdef HAVE_POP3
1084 case MB_POP3:
1085 rv = pop3_body(mp);
1086 break;
1087 #endif
1088 #ifdef HAVE_IMAP
1089 case MB_IMAP:
1090 case MB_CACHE:
1091 rv = imap_body(mp);
1092 break;
1093 #endif
1094 case MB_VOID:
1095 default:
1096 rv = STOP;
1097 break;
1099 NYD_LEAVE;
1100 return rv;
1103 #ifdef HAVE_SOCKETS
1104 FL int
1105 sclose(struct sock *sp)
1107 int i;
1108 NYD_ENTER;
1110 if (sp->s_fd > 0) {
1111 if (sp->s_onclose != NULL)
1112 (*sp->s_onclose)();
1113 # ifdef HAVE_OPENSSL
1114 if (sp->s_use_ssl) {
1115 void *s_ssl = sp->s_ssl, *s_ctx = sp->s_ctx;
1116 sp->s_ssl = sp->s_ctx = NULL;
1117 sp->s_use_ssl = 0;
1118 assert(s_ssl != NULL);
1119 while (!SSL_shutdown(s_ssl)) /* XXX */
1121 SSL_free(s_ssl);
1122 SSL_CTX_free(s_ctx);
1124 # endif
1125 i = close(sp->s_fd);
1126 sp->s_fd = -1;
1127 goto jleave;
1129 sp->s_fd = -1;
1130 i = 0;
1131 jleave:
1132 NYD_LEAVE;
1133 return i;
1136 FL enum okay
1137 swrite(struct sock *sp, char const *data)
1139 enum okay rv;
1140 NYD_ENTER;
1142 rv = swrite1(sp, data, strlen(data), 0);
1143 NYD_LEAVE;
1144 return rv;
1147 FL enum okay
1148 swrite1(struct sock *sp, char const *data, int sz, int use_buffer)
1150 enum okay rv = STOP;
1151 int x;
1152 NYD_ENTER;
1154 if (use_buffer > 0) {
1155 int di;
1157 if (sp->s_wbuf == NULL) {
1158 sp->s_wbufsize = 4096;
1159 sp->s_wbuf = smalloc(sp->s_wbufsize);
1160 sp->s_wbufpos = 0;
1162 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
1163 di = sp->s_wbufsize - sp->s_wbufpos;
1164 sz -= di;
1165 if (sp->s_wbufpos > 0) {
1166 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, di);
1167 rv = swrite1(sp, sp->s_wbuf, sp->s_wbufsize, -1);
1168 } else
1169 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1170 if (rv != OKAY)
1171 goto jleave;
1172 data += di;
1173 sp->s_wbufpos = 0;
1175 if (sz == sp->s_wbufsize) {
1176 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1177 if (rv != OKAY)
1178 goto jleave;
1179 } else if (sz) {
1180 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, sz);
1181 sp->s_wbufpos += sz;
1183 rv = OKAY;
1184 goto jleave;
1185 } else if (use_buffer == 0 && sp->s_wbuf != NULL && sp->s_wbufpos > 0) {
1186 x = sp->s_wbufpos;
1187 sp->s_wbufpos = 0;
1188 if ((rv = swrite1(sp, sp->s_wbuf, x, -1)) != OKAY)
1189 goto jleave;
1191 if (sz == 0) {
1192 rv = OKAY;
1193 goto jleave;
1196 # ifdef HAVE_OPENSSL
1197 if (sp->s_use_ssl) {
1198 jssl_retry:
1199 x = SSL_write(sp->s_ssl, data, sz);
1200 if (x < 0) {
1201 switch (SSL_get_error(sp->s_ssl, x)) {
1202 case SSL_ERROR_WANT_READ:
1203 case SSL_ERROR_WANT_WRITE:
1204 goto jssl_retry;
1207 } else
1208 # endif
1210 x = xwrite(sp->s_fd, data, sz);
1212 if (x != sz) {
1213 char o[512];
1214 snprintf(o, sizeof o, "%s write error",
1215 (sp->s_desc ? sp->s_desc : "socket"));
1216 # ifdef HAVE_OPENSSL
1217 sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o);
1218 # else
1219 perror(o);
1220 # endif
1221 if (x < 0)
1222 sclose(sp);
1223 rv = STOP;
1224 goto jleave;
1226 rv = OKAY;
1227 jleave:
1228 NYD_LEAVE;
1229 return rv;
1232 FL enum okay
1233 sopen(char const *xserver, struct sock *sp, int use_ssl, char const *uhp,
1234 char const *portstr)
1236 # ifdef HAVE_SO_SNDTIMEO
1237 struct timeval tv;
1238 # endif
1239 # ifdef HAVE_SO_LINGER
1240 struct linger li;
1241 # endif
1242 # ifdef HAVE_IPV6
1243 char hbuf[NI_MAXHOST];
1244 struct addrinfo hints, *res0, *res;
1245 # else
1246 struct sockaddr_in servaddr;
1247 struct in_addr **pptr;
1248 struct hostent *hp;
1249 struct servent *ep;
1250 unsigned short port = 0;
1251 # endif
1252 int sockfd;
1253 char *cp, *server = UNCONST(xserver);
1254 enum okay rv = STOP;
1255 NYD_ENTER;
1256 UNUSED(use_ssl);
1257 UNUSED(uhp);
1259 if ((cp = strchr(server, ':')) != NULL) { /* TODO URI parse! IPv6! */
1260 portstr = &cp[1];
1261 # ifndef HAVE_IPV6
1262 port = strtol(portstr, NULL, 10);
1263 # endif
1264 server = salloc(cp - xserver + 1);
1265 memcpy(server, xserver, cp - xserver);
1266 server[cp - xserver] = '\0';
1269 /* Connect timeouts after 30 seconds */
1270 # ifdef HAVE_SO_SNDTIMEO
1271 tv.tv_sec = 30;
1272 tv.tv_usec = 0;
1273 # endif
1275 # ifdef HAVE_IPV6
1276 if (options & OPT_VERBOSE)
1277 fprintf(stderr, "Resolving host %s . . .", server);
1278 memset(&hints, 0, sizeof hints);
1279 hints.ai_socktype = SOCK_STREAM;
1280 if (getaddrinfo(server, portstr, &hints, &res0) != 0) {
1281 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1282 goto jleave;
1283 } else if (options & OPT_VERBOSE)
1284 fprintf(stderr, tr(500, " done.\n"));
1286 sockfd = -1;
1287 for (res = res0; res != NULL && sockfd < 0; res = res->ai_next) {
1288 if (options & OPT_VERBOSE) {
1289 if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof hbuf,
1290 NULL, 0, NI_NUMERICHOST) != 0)
1291 strcpy(hbuf, "unknown host");
1292 fprintf(stderr, tr(192, "%sConnecting to %s:%s . . ."),
1293 (res == res0 ? "" : "\n"), hbuf, portstr);
1295 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1296 if (sockfd >= 0) {
1297 # ifdef HAVE_SO_SNDTIMEO
1298 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1299 # endif
1300 if (connect(sockfd, res->ai_addr, res->ai_addrlen) != 0) {
1301 close(sockfd);
1302 sockfd = -1;
1306 if (sockfd < 0) {
1307 perror(tr(254, " could not connect"));
1308 freeaddrinfo(res0);
1309 goto jleave;
1311 freeaddrinfo(res0);
1313 # else /* HAVE_IPV6 */
1314 if (port == 0) {
1315 if (!strcmp(portstr, "smtp"))
1316 port = htons(25);
1317 else if (!strcmp(portstr, "smtps"))
1318 port = htons(465);
1319 # ifdef HAVE_IMAP
1320 else if (!strcmp(portstr, "imap"))
1321 port = htons(143);
1322 else if (!strcmp(portstr, "imaps"))
1323 port = htons(993);
1324 # endif
1325 # ifdef HAVE_POP3
1326 else if (!strcmp(portstr, "pop3"))
1327 port = htons(110);
1328 else if (!strcmp(portstr, "pop3s"))
1329 port = htons(995);
1330 # endif
1331 else if ((ep = getservbyname(UNCONST(portstr), "tcp")) != NULL)
1332 port = ep->s_port;
1333 else {
1334 fprintf(stderr, tr(251, "Unknown service: %s\n"), portstr);
1335 rv = STOP;
1336 goto jleave;
1338 } else
1339 port = htons(port);
1341 if (options & OPT_VERBOSE)
1342 fprintf(stderr, "Resolving host %s . . .", server);
1343 if ((hp = gethostbyname(server)) == NULL) {
1344 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1345 goto jleave;
1346 } else if (options & OPT_VERBOSE)
1347 fprintf(stderr, tr(500, " done.\n"));
1349 pptr = (struct in_addr**)hp->h_addr_list;
1350 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1351 perror(tr(253, "could not create socket"));
1352 goto jleave;
1354 memset(&servaddr, 0, sizeof servaddr);
1355 servaddr.sin_family = AF_INET;
1356 servaddr.sin_port = port;
1357 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1358 if (options & OPT_VERBOSE)
1359 fprintf(stderr, tr(192, "%sConnecting to %s:%d . . ."),
1360 "", inet_ntoa(**pptr), ntohs(port));
1362 # ifdef HAVE_SO_SNDTIMEO
1363 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1364 # endif
1365 if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof servaddr) != 0) {
1366 perror(tr(254, " could not connect"));
1367 goto jleave;
1369 # endif /* !HAVE_IPV6 */
1370 if (options & OPT_VERBOSE)
1371 fputs(tr(193, " connected.\n"), stderr);
1373 /* And the regular timeouts */
1374 # ifdef HAVE_SO_SNDTIMEO
1375 tv.tv_sec = 42;
1376 tv.tv_usec = 0;
1377 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1378 setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
1379 # endif
1380 # ifdef HAVE_SO_LINGER
1381 li.l_onoff = 1;
1382 li.l_linger = 42;
1383 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
1384 # endif
1386 memset(sp, 0, sizeof *sp);
1387 sp->s_fd = sockfd;
1388 # ifdef HAVE_SSL
1389 if (use_ssl && ssl_open(server, sp, uhp) != OKAY) {
1390 sclose(sp);
1391 goto jleave;
1393 # endif
1394 rv = OKAY;
1395 jleave:
1396 NYD_LEAVE;
1397 return rv;
1400 FL int
1401 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
1402 SMALLOC_DEBUG_ARGS)
1404 int rv;
1405 size_t lsize;
1406 char *lp_base, *lp;
1407 NYD_ENTER;
1409 lsize = *linesize;
1410 lp_base = *line;
1411 lp = lp_base;
1413 if (sp->s_rsz < 0) {
1414 sclose(sp);
1415 rv = sp->s_rsz;
1416 goto jleave;
1419 do {
1420 if (lp_base == NULL || PTRCMP(lp, >, lp_base + lsize - 128)) {
1421 size_t diff = PTR2SIZE(lp - lp_base);
1422 *linesize = (lsize += 256); /* XXX magic */
1423 *line = lp_base = (srealloc)(lp_base, lsize SMALLOC_DEBUG_ARGSCALL);
1424 lp = lp_base + diff;
1427 if (sp->s_rbufptr == NULL ||
1428 PTRCMP(sp->s_rbufptr, >=, sp->s_rbuf + sp->s_rsz)) {
1429 # ifdef HAVE_OPENSSL
1430 if (sp->s_use_ssl) {
1431 jssl_retry:
1432 sp->s_rsz = SSL_read(sp->s_ssl, sp->s_rbuf, sizeof sp->s_rbuf);
1433 if (sp->s_rsz <= 0) {
1434 if (sp->s_rsz < 0) {
1435 char o[512];
1436 switch(SSL_get_error(sp->s_ssl, sp->s_rsz)) {
1437 case SSL_ERROR_WANT_READ:
1438 case SSL_ERROR_WANT_WRITE:
1439 goto jssl_retry;
1441 snprintf(o, sizeof o, "%s",
1442 (sp->s_desc ? sp->s_desc : "socket"));
1443 ssl_gen_err("%s", o);
1445 break;
1447 } else
1448 # endif
1450 jagain:
1451 sp->s_rsz = read(sp->s_fd, sp->s_rbuf, sizeof sp->s_rbuf);
1452 if (sp->s_rsz <= 0) {
1453 if (sp->s_rsz < 0) {
1454 char o[512];
1455 if (errno == EINTR)
1456 goto jagain;
1457 snprintf(o, sizeof o, "%s",
1458 (sp->s_desc ? sp->s_desc : "socket"));
1459 perror(o);
1461 break;
1464 sp->s_rbufptr = sp->s_rbuf;
1466 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
1467 *lp = '\0';
1468 lsize = PTR2SIZE(lp - lp_base);
1470 if (linelen)
1471 *linelen = lsize;
1472 rv = (int)lsize;
1473 jleave:
1474 NYD_LEAVE;
1475 return rv;
1477 #endif /* HAVE_SOCKETS */
1479 FL void
1480 load(char const *name)
1482 FILE *in, *oldin;
1483 NYD_ENTER;
1485 if (name == NULL || (in = Fopen(name, "r")) == NULL)
1486 goto jleave;
1487 oldin = _fio_input;
1488 _fio_input = in;
1489 loading = TRU1;
1490 sourcing = TRU1;
1491 commands();
1492 loading = FAL0;
1493 sourcing = FAL0;
1494 _fio_input = oldin;
1495 Fclose(in);
1496 jleave:
1497 NYD_LEAVE;
1500 FL int
1501 c_source(void *v)
1503 int rv = 1;
1504 char **arglist = v, *cp;
1505 FILE *fi;
1506 NYD_ENTER;
1508 if ((cp = fexpand(*arglist, FEXP_LOCAL)) == NULL)
1509 goto jleave;
1510 if ((fi = Fopen(cp, "r")) == NULL) {
1511 perror(cp);
1512 goto jleave;
1515 if (_fio_stack_size >= NELEM(_fio_stack)) {
1516 fprintf(stderr, tr(3, "Too much \"sourcing\" going on.\n"));
1517 Fclose(fi);
1518 goto jleave;
1521 _fio_stack[_fio_stack_size].s_file = _fio_input;
1522 _fio_stack[_fio_stack_size].s_cond = condstack_release();
1523 _fio_stack[_fio_stack_size].s_loading = loading;
1524 ++_fio_stack_size;
1525 loading = FAL0;
1526 _fio_input = fi;
1527 sourcing = TRU1;
1528 rv = 0;
1529 jleave:
1530 NYD_LEAVE;
1531 return rv;
1534 FL int
1535 unstack(void)
1537 int rv = 1;
1538 NYD_ENTER;
1540 if (_fio_stack_size == 0) {
1541 fprintf(stderr, tr(4, "\"Source\" stack over-pop.\n"));
1542 sourcing = FAL0;
1543 goto jleave;
1546 Fclose(_fio_input);
1548 --_fio_stack_size;
1549 if (!condstack_take(_fio_stack[_fio_stack_size].s_cond))
1550 fprintf(stderr, tr(5, "Unmatched \"if\"\n"));
1551 loading = _fio_stack[_fio_stack_size].s_loading;
1552 _fio_input = _fio_stack[_fio_stack_size].s_file;
1553 if (_fio_stack_size == 0)
1554 sourcing = loading;
1555 rv = 0;
1556 jleave:
1557 NYD_LEAVE;
1558 return rv;
1561 /* vim:set fenc=utf-8:s-it-mode */