substr(): return bool_t
[s-mailx.git] / fio.c
blobed7a4a7c69e64b05f34c85f33a5e464b6e7ba554
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 struct message *
760 setdot(struct message *mp)
762 NYD_ENTER;
763 if (dot != mp) {
764 prevdot = dot;
765 did_print_dot = FAL0;
767 dot = mp;
768 uncollapse1(dot, 0);
769 NYD_LEAVE;
770 return dot;
773 FL int
774 rm(char const *name)
776 struct stat sb;
777 int rv = -1;
778 NYD_ENTER;
780 if (stat(name, &sb) < 0)
782 else if (!S_ISREG(sb.st_mode))
783 errno = EISDIR;
784 else
785 rv = unlink(name);
786 NYD_LEAVE;
787 return rv;
790 FL off_t
791 fsize(FILE *iob)
793 struct stat sbuf;
794 off_t rv;
795 NYD_ENTER;
797 rv = (fstat(fileno(iob), &sbuf) < 0) ? 0 : sbuf.st_size;
798 NYD_LEAVE;
799 return rv;
802 FL char *
803 fexpand(char const *name, enum fexp_mode fexpm)
805 char cbuf[PATH_MAX], *res;
806 struct str s;
807 struct shortcut *sh;
808 bool_t dyn;
809 NYD_ENTER;
811 /* The order of evaluation is "%" and "#" expand into constants.
812 * "&" can expand into "+". "+" can expand into shell meta characters.
813 * Shell meta characters expand into constants.
814 * This way, we make no recursive expansion */
815 res = UNCONST(name);
816 if (!(fexpm & FEXP_NSHORTCUT) && (sh = get_shortcut(res)) != NULL)
817 res = sh->sh_long;
819 if (fexpm & FEXP_SHELL) {
820 dyn = FAL0;
821 goto jshell;
823 jnext:
824 dyn = FAL0;
825 switch (*res) {
826 case '%':
827 if (res[1] == ':' && res[2] != '\0') {
828 res = &res[2];
829 goto jnext;
831 _findmail(cbuf, sizeof cbuf, (res[1] != '\0' ? res + 1 : myname),
832 (res[1] != '\0' || (options & OPT_u_FLAG)));
833 res = cbuf;
834 goto jislocal;
835 case '#':
836 if (res[1] != '\0')
837 break;
838 if (prevfile[0] == '\0') {
839 fprintf(stderr, tr(80, "No previous file\n"));
840 res = NULL;
841 goto jleave;
843 res = prevfile;
844 goto jislocal;
845 case '&':
846 if (res[1] == '\0') {
847 if ((res = ok_vlook(MBOX)) == NULL)
848 res = UNCONST("~/mbox");
849 else if (res[0] != '&' || res[1] != '\0')
850 goto jnext;
852 break;
855 if (res[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
856 res = str_concat_csvl(&s, protbase(mailname), "/", res + 1, NULL)->s;
857 dyn = TRU1;
860 if (res[0] == '+' && getfold(cbuf, sizeof cbuf)) {
861 size_t i = strlen(cbuf);
863 res = str_concat_csvl(&s, cbuf,
864 ((i > 0 && cbuf[i - 1] == '/') ? "" : "/"), res + 1, NULL)->s;
865 dyn = TRU1;
867 if (res[0] == '%' && res[1] == ':') {
868 res += 2;
869 goto jnext;
873 /* Catch the most common shell meta character */
874 jshell:
875 if (res[0] == '~' && (res[1] == '/' || res[1] == '\0')) {
876 res = str_concat_csvl(&s, homedir, res + 1, NULL)->s;
877 dyn = TRU1;
880 if (anyof(res, "|&;<>~{}()[]*?$`'\"\\") &&
881 which_protocol(res) == PROTO_FILE) {
882 res = _globname(res, fexpm);
883 dyn = TRU1;
884 goto jleave;
887 jislocal:
888 if (fexpm & FEXP_LOCAL)
889 switch (which_protocol(res)) {
890 case PROTO_FILE:
891 case PROTO_MAILDIR:
892 break;
893 default:
894 fprintf(stderr, tr(280,
895 "`%s': only a local file or directory may be used\n"), name);
896 res = NULL;
897 break;
899 jleave:
900 if (res && !dyn)
901 res = savestr(res);
902 NYD_LEAVE;
903 return res;
906 FL void
907 demail(void)
909 NYD_ENTER;
910 if (ok_blook(keep) || rm(mailname) < 0) {
911 int fd = open(mailname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
912 if (fd >= 0)
913 close(fd);
915 NYD_LEAVE;
918 FL bool_t
919 var_folder_updated(char const *name, char **store)
921 char rv = TRU1;
922 char *folder, *unres = NULL, *res = NULL;
923 NYD_ENTER;
925 if ((folder = UNCONST(name)) == NULL)
926 goto jleave;
928 /* Expand the *folder*; skip `%:' prefix for simplicity of use */
929 /* XXX This *only* works because we do NOT
930 * XXX update environment variables via the "set" mechanism */
931 if (folder[0] == '%' && folder[1] == ':')
932 folder += 2;
933 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
934 goto jleave;
936 switch (which_protocol(folder)) {
937 case PROTO_POP3:
938 /* Ooops. This won't work */
939 fprintf(stderr, tr(501,
940 "`folder' cannot be set to a flat, readonly POP3 account\n"));
941 rv = FAL0;
942 goto jleave;
943 case PROTO_IMAP:
944 /* Simply assign what we have, even including `%:' prefix */
945 if (folder != name)
946 goto jvcopy;
947 goto jleave;
948 default:
949 /* Further expansion desired */
950 break;
953 /* All non-absolute paths are relative to our home directory */
954 if (*folder != '/') {
955 size_t l1 = strlen(homedir), l2 = strlen(folder);
956 unres = ac_alloc(l1 + l2 + 1 +1);
957 memcpy(unres, homedir, l1);
958 unres[l1] = '/';
959 memcpy(unres + l1 + 1, folder, l2);
960 unres[l1 + 1 + l2] = '\0';
961 folder = unres;
964 /* Since lex.c:_update_mailname() uses realpath(3) if available to
965 * avoid that we loose track of our currently open folder in case we
966 * chdir away, but still checks the leading path portion against
967 * getfold() to be able to abbreviate to the +FOLDER syntax if
968 * possible, we need to realpath(3) the folder, too */
969 #ifdef HAVE_REALPATH
970 res = ac_alloc(PATH_MAX);
971 if (realpath(folder, res) == NULL)
972 fprintf(stderr, tr(151, "Can't canonicalize `%s'\n"), folder);
973 else
974 folder = res;
975 #endif
977 jvcopy:
978 *store = sstrdup(folder);
980 if (res != NULL)
981 ac_free(res);
982 if (unres != NULL)
983 ac_free(unres);
984 jleave:
985 NYD_LEAVE;
986 return rv;
989 FL bool_t
990 getfold(char *name, size_t size)
992 char const *folder;
993 NYD_ENTER;
995 if ((folder = ok_vlook(folder)) != NULL)
996 n_strlcpy(name, folder, size);
997 NYD_LEAVE;
998 return (folder != NULL);
1001 FL char const *
1002 getdeadletter(void) /* XXX should that be in auxlily.c? */
1004 char const *cp;
1005 NYD_ENTER;
1007 if ((cp = ok_vlook(DEAD)) == NULL || (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
1008 cp = fexpand("~/dead.letter", FEXP_LOCAL | FEXP_SHELL);
1009 else if (*cp != '/') {
1010 size_t sz = strlen(cp) + 3;
1011 char *buf = ac_alloc(sz);
1013 snprintf(buf, sz, "~/%s", cp);
1014 cp = fexpand(buf, FEXP_LOCAL | FEXP_SHELL);
1015 ac_free(buf);
1018 if (cp == NULL)
1019 cp = "dead.letter"; /* XXX magic -> nail.h (POSIX thing though) */
1020 NYD_LEAVE;
1021 return cp;
1024 FL enum okay
1025 get_body(struct message *mp)
1027 enum okay rv;
1028 NYD_ENTER;
1029 UNUSED(mp);
1031 switch (mb.mb_type) {
1032 case MB_FILE:
1033 case MB_MAILDIR:
1034 rv = OKAY;
1035 break;
1036 #ifdef HAVE_POP3
1037 case MB_POP3:
1038 rv = pop3_body(mp);
1039 break;
1040 #endif
1041 #ifdef HAVE_IMAP
1042 case MB_IMAP:
1043 case MB_CACHE:
1044 rv = imap_body(mp);
1045 break;
1046 #endif
1047 case MB_VOID:
1048 default:
1049 rv = STOP;
1050 break;
1052 NYD_LEAVE;
1053 return rv;
1056 #ifdef HAVE_SOCKETS
1057 FL int
1058 sclose(struct sock *sp)
1060 int i;
1061 NYD_ENTER;
1063 if (sp->s_fd > 0) {
1064 if (sp->s_onclose != NULL)
1065 (*sp->s_onclose)();
1066 # ifdef HAVE_OPENSSL
1067 if (sp->s_use_ssl) {
1068 void *s_ssl = sp->s_ssl, *s_ctx = sp->s_ctx;
1069 sp->s_ssl = sp->s_ctx = NULL;
1070 sp->s_use_ssl = 0;
1071 assert(s_ssl != NULL);
1072 while (!SSL_shutdown(s_ssl)) /* XXX */
1074 SSL_free(s_ssl);
1075 SSL_CTX_free(s_ctx);
1077 # endif
1078 i = close(sp->s_fd);
1079 sp->s_fd = -1;
1080 goto jleave;
1082 sp->s_fd = -1;
1083 i = 0;
1084 jleave:
1085 NYD_LEAVE;
1086 return i;
1089 FL enum okay
1090 swrite(struct sock *sp, char const *data)
1092 enum okay rv;
1093 NYD_ENTER;
1095 rv = swrite1(sp, data, strlen(data), 0);
1096 NYD_LEAVE;
1097 return rv;
1100 FL enum okay
1101 swrite1(struct sock *sp, char const *data, int sz, int use_buffer)
1103 enum okay rv = STOP;
1104 int x;
1105 NYD_ENTER;
1107 if (use_buffer > 0) {
1108 int di;
1110 if (sp->s_wbuf == NULL) {
1111 sp->s_wbufsize = 4096;
1112 sp->s_wbuf = smalloc(sp->s_wbufsize);
1113 sp->s_wbufpos = 0;
1115 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
1116 di = sp->s_wbufsize - sp->s_wbufpos;
1117 sz -= di;
1118 if (sp->s_wbufpos > 0) {
1119 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, di);
1120 rv = swrite1(sp, sp->s_wbuf, sp->s_wbufsize, -1);
1121 } else
1122 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1123 if (rv != OKAY)
1124 goto jleave;
1125 data += di;
1126 sp->s_wbufpos = 0;
1128 if (sz == sp->s_wbufsize) {
1129 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1130 if (rv != OKAY)
1131 goto jleave;
1132 } else if (sz) {
1133 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, sz);
1134 sp->s_wbufpos += sz;
1136 rv = OKAY;
1137 goto jleave;
1138 } else if (use_buffer == 0 && sp->s_wbuf != NULL && sp->s_wbufpos > 0) {
1139 x = sp->s_wbufpos;
1140 sp->s_wbufpos = 0;
1141 if ((rv = swrite1(sp, sp->s_wbuf, x, -1)) != OKAY)
1142 goto jleave;
1144 if (sz == 0) {
1145 rv = OKAY;
1146 goto jleave;
1149 # ifdef HAVE_OPENSSL
1150 if (sp->s_use_ssl) {
1151 jssl_retry:
1152 x = SSL_write(sp->s_ssl, data, sz);
1153 if (x < 0) {
1154 switch (SSL_get_error(sp->s_ssl, x)) {
1155 case SSL_ERROR_WANT_READ:
1156 case SSL_ERROR_WANT_WRITE:
1157 goto jssl_retry;
1160 } else
1161 # endif
1163 x = xwrite(sp->s_fd, data, sz);
1165 if (x != sz) {
1166 char o[512];
1167 snprintf(o, sizeof o, "%s write error",
1168 (sp->s_desc ? sp->s_desc : "socket"));
1169 # ifdef HAVE_OPENSSL
1170 sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o);
1171 # else
1172 perror(o);
1173 # endif
1174 if (x < 0)
1175 sclose(sp);
1176 rv = STOP;
1177 goto jleave;
1179 rv = OKAY;
1180 jleave:
1181 NYD_LEAVE;
1182 return rv;
1185 FL enum okay
1186 sopen(char const *xserver, struct sock *sp, int use_ssl, char const *uhp,
1187 char const *portstr)
1189 # ifdef HAVE_SO_SNDTIMEO
1190 struct timeval tv;
1191 # endif
1192 # ifdef HAVE_SO_LINGER
1193 struct linger li;
1194 # endif
1195 # ifdef HAVE_IPV6
1196 char hbuf[NI_MAXHOST];
1197 struct addrinfo hints, *res0, *res;
1198 # else
1199 struct sockaddr_in servaddr;
1200 struct in_addr **pptr;
1201 struct hostent *hp;
1202 struct servent *ep;
1203 unsigned short port = 0;
1204 # endif
1205 int sockfd;
1206 char *cp, *server = UNCONST(xserver);
1207 enum okay rv = STOP;
1208 NYD_ENTER;
1209 UNUSED(use_ssl);
1210 UNUSED(uhp);
1212 if ((cp = strchr(server, ':')) != NULL) { /* TODO URI parse! IPv6! */
1213 portstr = &cp[1];
1214 # ifndef HAVE_IPV6
1215 port = strtol(portstr, NULL, 10);
1216 # endif
1217 server = salloc(cp - xserver + 1);
1218 memcpy(server, xserver, cp - xserver);
1219 server[cp - xserver] = '\0';
1222 /* Connect timeouts after 30 seconds */
1223 # ifdef HAVE_SO_SNDTIMEO
1224 tv.tv_sec = 30;
1225 tv.tv_usec = 0;
1226 # endif
1228 # ifdef HAVE_IPV6
1229 if (options & OPT_VERBOSE)
1230 fprintf(stderr, "Resolving host %s . . .", server);
1231 memset(&hints, 0, sizeof hints);
1232 hints.ai_socktype = SOCK_STREAM;
1233 if (getaddrinfo(server, portstr, &hints, &res0) != 0) {
1234 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1235 goto jleave;
1236 } else if (options & OPT_VERBOSE)
1237 fprintf(stderr, tr(500, " done.\n"));
1239 sockfd = -1;
1240 for (res = res0; res != NULL && sockfd < 0; res = res->ai_next) {
1241 if (options & OPT_VERBOSE) {
1242 if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof hbuf,
1243 NULL, 0, NI_NUMERICHOST) != 0)
1244 strcpy(hbuf, "unknown host");
1245 fprintf(stderr, tr(192, "%sConnecting to %s:%s . . ."),
1246 (res == res0 ? "" : "\n"), hbuf, portstr);
1248 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1249 if (sockfd >= 0) {
1250 # ifdef HAVE_SO_SNDTIMEO
1251 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1252 # endif
1253 if (connect(sockfd, res->ai_addr, res->ai_addrlen) != 0) {
1254 close(sockfd);
1255 sockfd = -1;
1259 if (sockfd < 0) {
1260 perror(tr(254, " could not connect"));
1261 freeaddrinfo(res0);
1262 goto jleave;
1264 freeaddrinfo(res0);
1266 # else /* HAVE_IPV6 */
1267 if (port == 0) {
1268 if (!strcmp(portstr, "smtp"))
1269 port = htons(25);
1270 else if (!strcmp(portstr, "smtps"))
1271 port = htons(465);
1272 # ifdef HAVE_IMAP
1273 else if (!strcmp(portstr, "imap"))
1274 port = htons(143);
1275 else if (!strcmp(portstr, "imaps"))
1276 port = htons(993);
1277 # endif
1278 # ifdef HAVE_POP3
1279 else if (!strcmp(portstr, "pop3"))
1280 port = htons(110);
1281 else if (!strcmp(portstr, "pop3s"))
1282 port = htons(995);
1283 # endif
1284 else if ((ep = getservbyname(UNCONST(portstr), "tcp")) != NULL)
1285 port = ep->s_port;
1286 else {
1287 fprintf(stderr, tr(251, "Unknown service: %s\n"), portstr);
1288 rv = STOP;
1289 goto jleave;
1291 } else
1292 port = htons(port);
1294 if (options & OPT_VERBOSE)
1295 fprintf(stderr, "Resolving host %s . . .", server);
1296 if ((hp = gethostbyname(server)) == NULL) {
1297 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1298 goto jleave;
1299 } else if (options & OPT_VERBOSE)
1300 fprintf(stderr, tr(500, " done.\n"));
1302 pptr = (struct in_addr**)hp->h_addr_list;
1303 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1304 perror(tr(253, "could not create socket"));
1305 goto jleave;
1307 memset(&servaddr, 0, sizeof servaddr);
1308 servaddr.sin_family = AF_INET;
1309 servaddr.sin_port = port;
1310 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1311 if (options & OPT_VERBOSE)
1312 fprintf(stderr, tr(192, "%sConnecting to %s:%d . . ."),
1313 "", inet_ntoa(**pptr), ntohs(port));
1315 # ifdef HAVE_SO_SNDTIMEO
1316 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1317 # endif
1318 if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof servaddr) != 0) {
1319 perror(tr(254, " could not connect"));
1320 goto jleave;
1322 # endif /* !HAVE_IPV6 */
1323 if (options & OPT_VERBOSE)
1324 fputs(tr(193, " connected.\n"), stderr);
1326 /* And the regular timeouts */
1327 # ifdef HAVE_SO_SNDTIMEO
1328 tv.tv_sec = 42;
1329 tv.tv_usec = 0;
1330 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1331 setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
1332 # endif
1333 # ifdef HAVE_SO_LINGER
1334 li.l_onoff = 1;
1335 li.l_linger = 42;
1336 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
1337 # endif
1339 memset(sp, 0, sizeof *sp);
1340 sp->s_fd = sockfd;
1341 # ifdef HAVE_SSL
1342 if (use_ssl && ssl_open(server, sp, uhp) != OKAY) {
1343 sclose(sp);
1344 goto jleave;
1346 # endif
1347 rv = OKAY;
1348 jleave:
1349 NYD_LEAVE;
1350 return rv;
1353 FL int
1354 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
1355 SMALLOC_DEBUG_ARGS)
1357 int rv;
1358 size_t lsize = *linesize;
1359 char *lp_base = *line, *lp = lp_base;
1360 NYD_ENTER;
1362 if (sp->s_rsz < 0) {
1363 sclose(sp);
1364 rv = sp->s_rsz;
1365 goto jleave;
1368 do {
1369 if (lp_base == NULL || PTRCMP(lp, >, lp_base + lsize - 128)) {
1370 size_t diff = PTR2SIZE(lp - lp_base);
1371 *linesize = (lsize += 256); /* XXX magic */
1372 *line = lp_base = (srealloc)(lp_base, lsize SMALLOC_DEBUG_ARGSCALL);
1373 lp = lp_base + diff;
1376 if (sp->s_rbufptr == NULL ||
1377 PTRCMP(sp->s_rbufptr, >=, sp->s_rbuf + sp->s_rsz)) {
1378 # ifdef HAVE_OPENSSL
1379 if (sp->s_use_ssl) {
1380 jssl_retry:
1381 sp->s_rsz = SSL_read(sp->s_ssl, sp->s_rbuf, sizeof sp->s_rbuf);
1382 if (sp->s_rsz <= 0) {
1383 if (sp->s_rsz < 0) {
1384 char o[512];
1385 switch(SSL_get_error(sp->s_ssl, sp->s_rsz)) {
1386 case SSL_ERROR_WANT_READ:
1387 case SSL_ERROR_WANT_WRITE:
1388 goto jssl_retry;
1390 snprintf(o, sizeof o, "%s",
1391 (sp->s_desc ? sp->s_desc : "socket"));
1392 ssl_gen_err("%s", o);
1394 break;
1396 } else
1397 # endif
1399 jagain:
1400 sp->s_rsz = read(sp->s_fd, sp->s_rbuf, sizeof sp->s_rbuf);
1401 if (sp->s_rsz <= 0) {
1402 if (sp->s_rsz < 0) {
1403 char o[512];
1404 if (errno == EINTR)
1405 goto jagain;
1406 snprintf(o, sizeof o, "%s",
1407 (sp->s_desc ? sp->s_desc : "socket"));
1408 perror(o);
1410 break;
1413 sp->s_rbufptr = sp->s_rbuf;
1415 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
1416 *lp = '\0';
1417 lsize = PTR2SIZE(lp - lp_base);
1419 if (linelen)
1420 *linelen = lsize;
1421 rv = (int)lsize;
1422 jleave:
1423 NYD_LEAVE;
1424 return rv;
1426 #endif /* HAVE_SOCKETS */
1428 FL void
1429 load(char const *name)
1431 FILE *in, *oldin;
1432 NYD_ENTER;
1434 if (name == NULL || (in = Fopen(name, "r")) == NULL)
1435 goto jleave;
1436 oldin = _fio_input;
1437 _fio_input = in;
1438 loading = TRU1;
1439 sourcing = TRU1;
1440 commands();
1441 loading = FAL0;
1442 sourcing = FAL0;
1443 _fio_input = oldin;
1444 Fclose(in);
1445 jleave:
1446 NYD_LEAVE;
1449 FL int
1450 c_source(void *v)
1452 int rv = 1;
1453 char **arglist = v, *cp;
1454 FILE *fi;
1455 NYD_ENTER;
1457 if ((cp = fexpand(*arglist, FEXP_LOCAL)) == NULL)
1458 goto jleave;
1459 if ((fi = Fopen(cp, "r")) == NULL) {
1460 perror(cp);
1461 goto jleave;
1464 if (_fio_stack_size >= NELEM(_fio_stack)) {
1465 fprintf(stderr, tr(3, "Too much \"sourcing\" going on.\n"));
1466 Fclose(fi);
1467 goto jleave;
1470 _fio_stack[_fio_stack_size].s_file = _fio_input;
1471 _fio_stack[_fio_stack_size].s_cond = condstack_release();
1472 _fio_stack[_fio_stack_size].s_loading = loading;
1473 ++_fio_stack_size;
1474 loading = FAL0;
1475 _fio_input = fi;
1476 sourcing = TRU1;
1477 rv = 0;
1478 jleave:
1479 NYD_LEAVE;
1480 return rv;
1483 FL int
1484 unstack(void)
1486 int rv = 1;
1487 NYD_ENTER;
1489 if (_fio_stack_size == 0) {
1490 fprintf(stderr, tr(4, "\"Source\" stack over-pop.\n"));
1491 sourcing = FAL0;
1492 goto jleave;
1495 Fclose(_fio_input);
1497 --_fio_stack_size;
1498 if (!condstack_take(_fio_stack[_fio_stack_size].s_cond))
1499 fprintf(stderr, tr(5, "Unmatched \"if\"\n"));
1500 loading = _fio_stack[_fio_stack_size].s_loading;
1501 _fio_input = _fio_stack[_fio_stack_size].s_file;
1502 if (_fio_stack_size == 0)
1503 sourcing = loading;
1504 rv = 0;
1505 jleave:
1506 NYD_LEAVE;
1507 return rv;
1510 /* vim:set fenc=utf-8:s-it-mode */