NYD: names.c
[s-mailx.git] / fio.c
blobbf1409ca98e76538f9b1ca9a7b25292b286f213c
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 enum condition s_cond; /* Saved state of conditionals */
75 int s_loading; /* Loading .mailrc, etc. */
78 static struct fio_stack _fio_stack[FIO_STACK_SIZE];
79 static size_t _fio_stack_size;
80 static FILE * _fio_input;
82 /* Locate the user's mailbox file (where new, unread mail is queued) */
83 static void _findmail(char *buf, size_t bufsize, char const *user,
84 bool_t force);
86 /* Perform shell meta character expansion */
87 static char * _globname(char const *name, enum fexp_mode fexpm);
89 /* line is a buffer with the result of fgets(). Returns the first newline or
90 * the last character read */
91 static size_t _length_of_line(char const *line, size_t linesize);
93 /* Read a line, one character at a time */
94 static char * _fgetline_byone(char **line, size_t *linesize, size_t *llen,
95 FILE *fp, int appendnl, size_t n SMALLOC_DEBUG_ARGS);
97 /* Take the data out of the passed ghost file and toss it into a dynamically
98 * allocated message structure */
99 static void makemessage(void);
101 /* Append the passed message descriptor onto the message structure */
102 static void _fio_append(struct message *mp);
104 static enum okay get_header(struct message *mp);
106 /* Write to socket fd, restarting on EINTR, unless anything is written */
107 #ifdef HAVE_SOCKETS
108 static long xwrite(int fd, char const *data, size_t sz);
109 #endif
111 static void
112 _findmail(char *buf, size_t bufsize, char const *user, bool_t force)
114 char *cp;
115 NYD_ENTER;
117 if (!strcmp(user, myname) && !force && (cp = ok_vlook(folder)) != NULL) {
118 switch (which_protocol(cp)) {
119 case PROTO_IMAP:
120 if (strcmp(cp, protbase(cp)))
121 goto jcopy;
122 snprintf(buf, bufsize, "%s/INBOX", cp);
123 goto jleave;
124 default:
125 break;
129 if (force || (cp = ok_vlook(MAIL)) == NULL)
130 snprintf(buf, bufsize, "%s/%s", MAILSPOOL, user);
131 else
132 jcopy:
133 n_strlcpy(buf, cp, bufsize);
134 jleave:
135 NYD_LEAVE;
138 static char *
139 _globname(char const *name, enum fexp_mode fexpm)
141 #ifdef HAVE_WORDEXP
142 wordexp_t we;
143 char *cp = NULL;
144 sigset_t nset;
145 int i;
146 NYD_ENTER;
148 /* Mac OS X Snow Leopard and Linux don't init fields on error, causing
149 * SIGSEGV in wordfree(3); so let's just always zero it ourselfs */
150 memset(&we, 0, sizeof we);
152 /* Some systems (notably Open UNIX 8.0.0) fork a shell for wordexp()
153 * and wait, which will fail if our SIGCHLD handler is active */
154 sigemptyset(&nset);
155 sigaddset(&nset, SIGCHLD);
156 sigprocmask(SIG_BLOCK, &nset, NULL);
157 i = wordexp(name, &we, 0);
158 sigprocmask(SIG_UNBLOCK, &nset, NULL);
160 switch (i) {
161 case 0:
162 break;
163 case WRDE_NOSPACE:
164 if (!(fexpm & FEXP_SILENT))
165 fprintf(stderr, tr(83, "\"%s\": Expansion buffer overflow.\n"), name);
166 goto jleave;
167 case WRDE_BADCHAR:
168 case WRDE_SYNTAX:
169 default:
170 if (!(fexpm & FEXP_SILENT))
171 fprintf(stderr, tr(242, "Syntax error in \"%s\"\n"), name);
172 goto jleave;
175 switch (we.we_wordc) {
176 case 1:
177 cp = savestr(we.we_wordv[0]);
178 break;
179 case 0:
180 if (!(fexpm & FEXP_SILENT))
181 fprintf(stderr, tr(82, "\"%s\": No match.\n"), name);
182 break;
183 default:
184 if (fexpm & FEXP_MULTIOK) {
185 size_t j, l;
187 for (l = 0, j = 0; j < we.we_wordc; ++j)
188 l += strlen(we.we_wordv[j]) + 1;
189 ++l;
190 cp = salloc(l);
191 for (l = 0, j = 0; j < we.we_wordc; ++j) {
192 size_t x = strlen(we.we_wordv[j]);
193 memcpy(cp + l, we.we_wordv[j], x);
194 l += x;
195 cp[l++] = ' ';
197 cp[l] = '\0';
198 } else if (!(fexpm & FEXP_SILENT))
199 fprintf(stderr, tr(84, "\"%s\": Ambiguous.\n"), name);
200 break;
202 jleave:
203 wordfree(&we);
204 NYD_LEAVE;
205 return cp;
207 #else /* HAVE_WORDEXP */
208 struct stat sbuf;
209 char xname[PATH_MAX], cmdbuf[PATH_MAX], /* also used for files */
210 *shellp, *cp = NULL;
211 int pivec[2], pid, l, waits;
212 NYD_ENTER;
214 if (pipe(pivec) < 0) {
215 perror("pipe");
216 goto jleave;
218 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
219 if ((shellp = ok_vlook(SHELL)) == NULL)
220 shellp = UNCONST(XSHELL);
221 pid = start_command(shellp, 0, -1, pivec[1], "-c", cmdbuf, NULL);
222 if (pid < 0) {
223 close(pivec[0]);
224 close(pivec[1]);
225 goto jleave;
227 close(pivec[1]);
229 jagain:
230 l = read(pivec[0], xname, sizeof xname);
231 if (l < 0) {
232 if (errno == EINTR)
233 goto jagain;
234 perror("read");
235 close(pivec[0]);
236 goto jleave;
238 close(pivec[0]);
239 if (!wait_child(pid, &waits) && WTERMSIG(waits) != SIGPIPE) {
240 if (!(fexpm & FEXP_SILENT))
241 fprintf(stderr, tr(81, "\"%s\": Expansion failed.\n"), name);
242 goto jleave;
244 if (l == 0) {
245 if (!(fexpm & FEXP_SILENT))
246 fprintf(stderr, tr(82, "\"%s\": No match.\n"), name);
247 goto jleave;
249 if (l == sizeof xname) {
250 if (!(fexpm & FEXP_SILENT))
251 fprintf(stderr, tr(83, "\"%s\": Expansion buffer overflow.\n"), name);
252 goto jleave;
254 xname[l] = 0;
255 for (cp = &xname[l - 1]; *cp == '\n' && cp > xname; --cp)
257 cp[1] = '\0';
258 if (!(fexpm & FEXP_MULTIOK) && strchr(xname, ' ') != NULL &&
259 stat(xname, &sbuf) < 0) {
260 if (!(fexpm & FEXP_SILENT))
261 fprintf(stderr, tr(84, "\"%s\": Ambiguous.\n"), name);
262 cp = NULL;
263 goto jleave;
265 cp = savestr(xname);
266 jleave:
267 NYD_LEAVE;
268 return cp;
269 #endif /* !HAVE_WORDEXP */
272 static size_t
273 _length_of_line(char const *line, size_t linesize)
275 size_t i;
276 NYD_ENTER;
278 /* Last character is always '\0' and was added by fgets() */
279 for (--linesize, i = 0; i < linesize; i++)
280 if (line[i] == '\n')
281 break;
282 i = (i < linesize) ? i + 1 : linesize;
283 NYD_LEAVE;
284 return i;
287 static char *
288 _fgetline_byone(char **line, size_t *linesize, size_t *llen, FILE *fp,
289 int appendnl, size_t n SMALLOC_DEBUG_ARGS)
291 char *rv;
292 int c;
293 NYD_ENTER;
295 assert(*linesize == 0 || *line != NULL);
296 for (rv = *line;;) {
297 if (*linesize <= LINESIZE || n >= *linesize - 128) {
298 *linesize += ((rv == NULL) ? LINESIZE + n + 1 : 256);
299 *line = rv = (srealloc)(rv, *linesize SMALLOC_DEBUG_ARGSCALL);
301 c = getc(fp);
302 if (c != EOF) {
303 rv[n++] = c;
304 rv[n] = '\0';
305 if (c == '\n')
306 break;
307 } else {
308 if (n > 0) {
309 if (appendnl) {
310 rv[n++] = '\n';
311 rv[n] = '\0';
313 break;
314 } else {
315 rv = NULL;
316 goto jleave;
320 if (llen)
321 *llen = n;
322 jleave:
323 NYD_LEAVE;
324 return rv;
327 static void
328 makemessage(void)
330 NYD_ENTER;
331 if (msgCount == 0)
332 _fio_append(NULL);
333 setdot(message);
334 message[msgCount].m_size = 0;
335 message[msgCount].m_lines = 0;
336 NYD_LEAVE;
339 static void
340 _fio_append(struct message *mp)
342 NYD_ENTER;
343 if (msgCount + 1 >= msgspace)
344 message = srealloc(message, (msgspace += 64) * sizeof *message);
345 if (msgCount > 0)
346 message[msgCount - 1] = *mp;
347 NYD_LEAVE;
350 static enum okay
351 get_header(struct message *mp)
353 enum okay rv;
354 NYD_ENTER;
355 UNUSED(mp);
357 switch (mb.mb_type) {
358 case MB_FILE:
359 case MB_MAILDIR:
360 rv = OKAY;
361 break;
362 #ifdef HAVE_POP3
363 case MB_POP3:
364 rv = pop3_header(mp);
365 break;
366 #endif
367 #ifdef HAVE_IMAP
368 case MB_IMAP:
369 case MB_CACHE:
370 rv = imap_header(mp);
371 break;
372 #endif
373 case MB_VOID:
374 default:
375 rv = STOP;
376 break;
378 NYD_LEAVE;
379 return rv;
382 #ifdef HAVE_SOCKETS
383 static long
384 xwrite(int fd, char const *data, size_t sz)
386 long rv = -1, wo;
387 size_t wt = 0;
388 NYD_ENTER;
390 do {
391 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
392 if (errno == EINTR)
393 continue;
394 else
395 goto jleave;
397 wt += wo;
398 } while (wt < sz);
399 rv = (long)sz;
400 jleave:
401 NYD_LEAVE;
402 return rv;
404 #endif /* HAVE_SOCKETS */
406 FL char *
407 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen, FILE *fp,
408 int appendnl SMALLOC_DEBUG_ARGS)
410 size_t i_llen, sz;
411 char *rv;
412 NYD_ENTER;
414 if (cnt == NULL) {
415 /* Without count, we can't determine where the chars returned by fgets()
416 * end if there's no newline. We have to read one character by one */
417 rv = _fgetline_byone(line, linesize, llen, fp, appendnl, 0
418 SMALLOC_DEBUG_ARGSCALL);
419 goto jleave;
422 if ((rv = *line) == NULL || *linesize < LINESIZE)
423 *line = rv = (srealloc)(rv, *linesize = LINESIZE SMALLOC_DEBUG_ARGSCALL);
424 sz = (*linesize <= *cnt) ? *linesize : *cnt + 1;
425 if (sz <= 1 || fgets(rv, sz, fp) == NULL) {
426 /* Leave llen untouched; it is used to determine whether the last line
427 * was \n-terminated in some callers */
428 rv = NULL;
429 goto jleave;
432 i_llen = _length_of_line(rv, sz);
433 *cnt -= i_llen;
434 while (rv[i_llen - 1] != '\n') {
435 *line = rv = (srealloc)(rv, *linesize += 256 SMALLOC_DEBUG_ARGSCALL);
436 sz = *linesize - i_llen;
437 sz = (sz <= *cnt) ? sz : *cnt + 1;
438 if (sz <= 1 || fgets(rv + i_llen, sz, fp) == NULL) {
439 if (appendnl) {
440 rv[i_llen++] = '\n';
441 rv[i_llen] = '\0';
443 break;
445 sz = _length_of_line(rv + i_llen, sz);
446 i_llen += sz;
447 *cnt -= sz;
449 if (llen)
450 *llen = i_llen;
451 jleave:
452 NYD_LEAVE;
453 return rv;
456 FL int
457 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
458 SMALLOC_DEBUG_ARGS)
460 /* TODO readline_restart(): always *appends* LF just to strip it again;
461 * TODO should be configurable just as for fgetline(); ..or whatevr.. */
462 int rv = -1;
463 long sz;
464 NYD_ENTER;
466 clearerr(ibuf);
468 /* Interrupts will cause trouble if we are inside a stdio call. As this is
469 * only relevant if input is from tty, bypass it by read(), then */
470 if (fileno(ibuf) == 0 && (options & OPT_TTYIN)) {
471 assert(*linesize == 0 || *linebuf != NULL);
472 for (;;) {
473 if (*linesize <= LINESIZE || n >= *linesize - 128) {
474 *linesize += ((*linebuf == NULL) ? LINESIZE + n + 1 : 256);
475 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
477 jagain:
478 sz = read(0, *linebuf + n, *linesize - n - 1);
479 if (sz > 0) {
480 n += sz;
481 (*linebuf)[n] = '\0';
482 if (n > 0 && (*linebuf)[n - 1] == '\n')
483 break;
484 } else {
485 if (sz < 0 && errno == EINTR)
486 goto jagain;
487 if (n > 0) {
488 if ((*linebuf)[n - 1] != '\n') {
489 (*linebuf)[n++] = '\n';
490 (*linebuf)[n] = '\0';
492 break;
493 } else
494 goto jleave;
497 } else {
498 /* Not reading from standard input or standard input not a terminal. We
499 * read one char at a time as it is the only way to get lines with
500 * embedded NUL characters in standard stdio */
501 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
502 SMALLOC_DEBUG_ARGSCALL) == NULL)
503 goto jleave;
505 if (n > 0 && (*linebuf)[n - 1] == '\n')
506 (*linebuf)[--n] = '\0';
507 rv = (int)n;
508 jleave:
509 NYD_LEAVE;
510 return rv;
513 FL int
514 (readline_input)(char const *prompt, bool_t nl_escape, char **linebuf,
515 size_t *linesize, char const *string SMALLOC_DEBUG_ARGS)
517 /* TODO readline: linebuf pool! */
518 FILE *ifile = (_fio_input != NULL) ? _fio_input : stdin;
519 bool_t doprompt, dotty;
520 int n;
521 NYD_ENTER;
523 doprompt = (!sourcing && (options & OPT_INTERACTIVE));
524 dotty = (doprompt && !ok_blook(line_editor_disable));
525 if (!doprompt)
526 prompt = NULL;
527 else if (prompt == NULL)
528 prompt = getprompt();
530 for (n = 0;;) {
531 if (dotty) {
532 assert(ifile == stdin);
533 if (string != NULL && (n = (int)strlen(string)) > 0) {
534 if (*linesize > 0)
535 *linesize += n +1;
536 else
537 *linesize = (size_t)n + LINESIZE +1;
538 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
539 memcpy(*linebuf, string, (size_t)n +1);
541 string = NULL;
542 n = (tty_readline)(prompt, linebuf, linesize, n
543 SMALLOC_DEBUG_ARGSCALL);
544 } else {
545 if (prompt != NULL && *prompt != '\0') {
546 fputs(prompt, stdout);
547 fflush(stdout);
549 n = (readline_restart)(ifile, linebuf, linesize, n
550 SMALLOC_DEBUG_ARGSCALL);
552 if (n <= 0)
553 break;
554 /* POSIX says:
555 * An unquoted <backslash> at the end of a command line shall
556 * be discarded and the next line shall continue the command */
557 if (nl_escape && (*linebuf)[n - 1] == '\\') {
558 (*linebuf)[--n] = '\0';
559 if (prompt != NULL && *prompt != '\0')
560 prompt = ".. "; /* XXX PS2 .. */
561 continue;
563 break;
565 NYD_LEAVE;
566 return n;
569 FL char *
570 readstr_input(char const *prompt, char const *string)
572 /* FIXME readstr_input: without linepool leaks on sigjmp */
573 size_t linesize = 0;
574 char *linebuf = NULL, *rv = NULL;
575 int n;
576 NYD_ENTER;
578 n = readline_input(prompt, FAL0, &linebuf, &linesize, string);
579 if (n > 0)
580 rv = savestrbuf(linebuf, (size_t)n + 1);
582 if (linebuf != NULL)
583 free(linebuf);
584 NYD_LEAVE;
585 return rv;
588 FL void
589 setptr(FILE *ibuf, off_t offset)
591 struct message this;
592 char *cp, *linebuf = NULL;
593 char const *cp2;
594 int c, maybe = 1, inhead = 0, thiscnt = 0;
595 size_t linesize = 0, filesize, cnt;
596 NYD_ENTER;
598 memset(&this, 0, sizeof this);
599 this.m_flag = MUSED | MNEW | MNEWEST;
600 filesize = mailsize - offset;
601 offset = ftell(mb.mb_otf);
603 for (;;) {
604 if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0) == NULL) {
605 this.m_xsize = this.m_size;
606 this.m_xlines = this.m_lines;
607 this.m_have = HAVE_HEADER | HAVE_BODY;
608 if (thiscnt > 0)
609 _fio_append(&this);
610 makemessage();
611 if (linebuf)
612 free(linebuf);
613 break;
616 #ifdef notdef
617 if (linebuf[0] == '\0')
618 linebuf[0] = '.';
619 #endif
620 /* XXX Convert CRLF to LF; this should be rethought in that
621 * XXX CRLF input should possibly end as CRLF output? */
622 if (cnt >= 2 && linebuf[cnt - 1] == '\n' && linebuf[cnt - 2] == '\r')
623 linebuf[--cnt - 1] = '\n';
624 fwrite(linebuf, sizeof *linebuf, cnt, mb.mb_otf);
625 if (ferror(mb.mb_otf)) {
626 perror("/tmp");
627 exit(1);
629 if (linebuf[cnt - 1] == '\n')
630 linebuf[cnt - 1] = '\0';
631 if (maybe && linebuf[0] == 'F' && is_head(linebuf, cnt)) {
632 /* TODO char date[FROM_DATEBUF];
633 * TODO extract_date_from_from_(linebuf, cnt, date);
634 * TODO this.m_time = 10000; */
635 this.m_xsize = this.m_size;
636 this.m_xlines = this.m_lines;
637 this.m_have = HAVE_HEADER | HAVE_BODY;
638 if (thiscnt++ > 0)
639 _fio_append(&this);
640 msgCount++;
641 this.m_flag = MUSED | MNEW | MNEWEST;
642 this.m_size = 0;
643 this.m_lines = 0;
644 this.m_block = mailx_blockof(offset);
645 this.m_offset = mailx_offsetof(offset);
646 inhead = 1;
647 } else if (linebuf[0] == 0) {
648 inhead = 0;
649 } else if (inhead) {
650 for (cp = linebuf, cp2 = "status";; ++cp) {
651 if ((c = *cp2++) == 0) {
652 while (c = *cp++, whitechar(c))
654 if (cp[-1] != ':')
655 break;
656 while ((c = *cp++) != '\0')
657 if (c == 'R')
658 this.m_flag |= MREAD;
659 else if (c == 'O')
660 this.m_flag &= ~MNEW;
661 break;
663 if (*cp != c && *cp != upperconv(c))
664 break;
666 for (cp = linebuf, cp2 = "x-status";; ++cp) {
667 if ((c = *cp2++) == 0) {
668 while ((c = *cp++, whitechar(c)))
670 if (cp[-1] != ':')
671 break;
672 while ((c = *cp++) != '\0')
673 if (c == 'F')
674 this.m_flag |= MFLAGGED;
675 else if (c == 'A')
676 this.m_flag |= MANSWERED;
677 else if (c == 'T')
678 this.m_flag |= MDRAFTED;
679 break;
681 if (*cp != c && *cp != upperconv(c))
682 break;
685 offset += cnt;
686 this.m_size += cnt;
687 this.m_lines++;
688 maybe = linebuf[0] == 0;
690 NYD_LEAVE;
693 FL int
694 putline(FILE *obuf, char *linebuf, size_t cnt)
696 int rv = -1;
697 NYD_ENTER;
699 fwrite(linebuf, sizeof *linebuf, cnt, obuf);
700 putc('\n', obuf);
701 if (!ferror(obuf))
702 rv = (int)(cnt + 1);
703 NYD_LEAVE;
704 return rv;
707 FL FILE *
708 setinput(struct mailbox *mp, struct message *m, enum needspec need)
710 FILE *rv = NULL;
711 enum okay ok = STOP;
712 NYD_ENTER;
714 switch (need) {
715 case NEED_HEADER:
716 ok = (m->m_have & HAVE_HEADER) ? OKAY : get_header(m);
717 break;
718 case NEED_BODY:
719 ok = (m->m_have & HAVE_BODY) ? OKAY : get_body(m);
720 break;
721 case NEED_UNSPEC:
722 ok = OKAY;
723 break;
725 if (ok != OKAY)
726 goto jleave;
728 fflush(mp->mb_otf);
729 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
730 SEEK_SET) < 0) {
731 perror("fseek");
732 panic(tr(77, "temporary file seek"));
734 rv = mp->mb_itf;
735 jleave:
736 NYD_LEAVE;
737 return rv;
740 FL struct message *
741 setdot(struct message *mp)
743 NYD_ENTER;
744 if (dot != mp) {
745 prevdot = dot;
746 did_print_dot = FAL0;
748 dot = mp;
749 uncollapse1(dot, 0);
750 NYD_LEAVE;
751 return dot;
754 FL int
755 rm(char const *name)
757 struct stat sb;
758 int rv = -1;
759 NYD_ENTER;
761 if (stat(name, &sb) < 0)
763 else if (!S_ISREG(sb.st_mode))
764 errno = EISDIR;
765 else
766 rv = unlink(name);
767 NYD_LEAVE;
768 return rv;
771 FL off_t
772 fsize(FILE *iob)
774 struct stat sbuf;
775 off_t rv;
776 NYD_ENTER;
778 rv = (fstat(fileno(iob), &sbuf) < 0) ? 0 : sbuf.st_size;
779 NYD_LEAVE;
780 return rv;
783 FL char *
784 fexpand(char const *name, enum fexp_mode fexpm)
786 char cbuf[PATH_MAX], *res;
787 struct str s;
788 struct shortcut *sh;
789 bool_t dyn;
790 NYD_ENTER;
792 /* The order of evaluation is "%" and "#" expand into constants.
793 * "&" can expand into "+". "+" can expand into shell meta characters.
794 * Shell meta characters expand into constants.
795 * This way, we make no recursive expansion */
796 res = UNCONST(name);
797 if (!(fexpm & FEXP_NSHORTCUT) && (sh = get_shortcut(res)) != NULL)
798 res = sh->sh_long;
800 if (fexpm & FEXP_SHELL) {
801 dyn = FAL0;
802 goto jshell;
804 jnext:
805 dyn = FAL0;
806 switch (*res) {
807 case '%':
808 if (res[1] == ':' && res[2] != '\0') {
809 res = &res[2];
810 goto jnext;
812 _findmail(cbuf, sizeof cbuf, (res[1] != '\0' ? res + 1 : myname),
813 (res[1] != '\0' || (options & OPT_u_FLAG)));
814 res = cbuf;
815 goto jislocal;
816 case '#':
817 if (res[1] != '\0')
818 break;
819 if (prevfile[0] == '\0') {
820 fprintf(stderr, tr(80, "No previous file\n"));
821 res = NULL;
822 goto jleave;
824 res = prevfile;
825 goto jislocal;
826 case '&':
827 if (res[1] == '\0') {
828 if ((res = ok_vlook(MBOX)) == NULL)
829 res = UNCONST("~/mbox");
830 else if (res[0] != '&' || res[1] != '\0')
831 goto jnext;
833 break;
836 if (res[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
837 res = str_concat_csvl(&s, protbase(mailname), "/", res + 1, NULL)->s;
838 dyn = TRU1;
841 if (res[0] == '+' && getfold(cbuf, sizeof cbuf)) {
842 size_t i = strlen(cbuf);
844 res = str_concat_csvl(&s, cbuf,
845 ((i > 0 && cbuf[i - 1] == '/') ? "" : "/"), res + 1, NULL)->s;
846 dyn = TRU1;
848 if (res[0] == '%' && res[1] == ':') {
849 res += 2;
850 goto jnext;
854 /* Catch the most common shell meta character */
855 jshell:
856 if (res[0] == '~' && (res[1] == '/' || res[1] == '\0')) {
857 res = str_concat_csvl(&s, homedir, res + 1, NULL)->s;
858 dyn = TRU1;
861 if (anyof(res, "|&;<>~{}()[]*?$`'\"\\") &&
862 which_protocol(res) == PROTO_FILE) {
863 res = _globname(res, fexpm);
864 dyn = TRU1;
865 goto jleave;
868 jislocal:
869 if (fexpm & FEXP_LOCAL)
870 switch (which_protocol(res)) {
871 case PROTO_FILE:
872 case PROTO_MAILDIR:
873 break;
874 default:
875 fprintf(stderr, tr(280,
876 "`%s': only a local file or directory may be used\n"), name);
877 res = NULL;
878 break;
880 jleave:
881 if (res && !dyn)
882 res = savestr(res);
883 NYD_LEAVE;
884 return res;
887 FL void
888 demail(void)
890 NYD_ENTER;
891 if (ok_blook(keep) || rm(mailname) < 0) {
892 int fd = open(mailname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
893 if (fd >= 0)
894 close(fd);
896 NYD_LEAVE;
899 FL bool_t
900 var_folder_updated(char const *name, char **store)
902 char rv = TRU1;
903 char *folder, *unres = NULL, *res = NULL;
904 NYD_ENTER;
906 if ((folder = UNCONST(name)) == NULL)
907 goto jleave;
909 /* Expand the *folder*; skip `%:' prefix for simplicity of use */
910 /* XXX This *only* works because we do NOT
911 * XXX update environment variables via the "set" mechanism */
912 if (folder[0] == '%' && folder[1] == ':')
913 folder += 2;
914 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
915 goto jleave;
917 switch (which_protocol(folder)) {
918 case PROTO_POP3:
919 /* Ooops. This won't work */
920 fprintf(stderr, tr(501,
921 "`folder' cannot be set to a flat, readonly POP3 account\n"));
922 rv = FAL0;
923 goto jleave;
924 case PROTO_IMAP:
925 /* Simply assign what we have, even including `%:' prefix */
926 if (folder != name)
927 goto jvcopy;
928 goto jleave;
929 default:
930 /* Further expansion desired */
931 break;
934 /* All non-absolute paths are relative to our home directory */
935 if (*folder != '/') {
936 size_t l1 = strlen(homedir), l2 = strlen(folder);
937 unres = ac_alloc(l1 + l2 + 1 +1);
938 memcpy(unres, homedir, l1);
939 unres[l1] = '/';
940 memcpy(unres + l1 + 1, folder, l2);
941 unres[l1 + 1 + l2] = '\0';
942 folder = unres;
945 /* Since lex.c:_update_mailname() uses realpath(3) if available to
946 * avoid that we loose track of our currently open folder in case we
947 * chdir away, but still checks the leading path portion against
948 * getfold() to be able to abbreviate to the +FOLDER syntax if
949 * possible, we need to realpath(3) the folder, too */
950 #ifdef HAVE_REALPATH
951 res = ac_alloc(PATH_MAX);
952 if (realpath(folder, res) == NULL)
953 fprintf(stderr, tr(151, "Can't canonicalize `%s'\n"), folder);
954 else
955 folder = res;
956 #endif
958 jvcopy:
959 *store = sstrdup(folder);
961 if (res != NULL)
962 ac_free(res);
963 if (unres != NULL)
964 ac_free(unres);
965 jleave:
966 NYD_LEAVE;
967 return rv;
970 FL bool_t
971 getfold(char *name, size_t size)
973 char const *folder;
974 NYD_ENTER;
976 if ((folder = ok_vlook(folder)) != NULL)
977 n_strlcpy(name, folder, size);
978 NYD_LEAVE;
979 return (folder != NULL);
982 FL char const *
983 getdeadletter(void) /* XXX should that be in auxlily.c? */
985 char const *cp;
986 NYD_ENTER;
988 if ((cp = ok_vlook(DEAD)) == NULL || (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
989 cp = fexpand("~/dead.letter", FEXP_LOCAL | FEXP_SHELL);
990 else if (*cp != '/') {
991 size_t sz = strlen(cp) + 3;
992 char *buf = ac_alloc(sz);
994 snprintf(buf, sz, "~/%s", cp);
995 cp = fexpand(buf, FEXP_LOCAL | FEXP_SHELL);
996 ac_free(buf);
999 if (cp == NULL)
1000 cp = "dead.letter"; /* XXX magic -> nail.h (POSIX thing though) */
1001 NYD_LEAVE;
1002 return cp;
1005 FL enum okay
1006 get_body(struct message *mp)
1008 enum okay rv;
1009 NYD_ENTER;
1010 UNUSED(mp);
1012 switch (mb.mb_type) {
1013 case MB_FILE:
1014 case MB_MAILDIR:
1015 rv = OKAY;
1016 break;
1017 #ifdef HAVE_POP3
1018 case MB_POP3:
1019 rv = pop3_body(mp);
1020 break;
1021 #endif
1022 #ifdef HAVE_IMAP
1023 case MB_IMAP:
1024 case MB_CACHE:
1025 rv = imap_body(mp);
1026 break;
1027 #endif
1028 case MB_VOID:
1029 default:
1030 rv = STOP;
1031 break;
1033 NYD_LEAVE;
1034 return rv;
1037 #ifdef HAVE_SOCKETS
1038 FL int
1039 sclose(struct sock *sp)
1041 int i;
1042 NYD_ENTER;
1044 if (sp->s_fd > 0) {
1045 if (sp->s_onclose != NULL)
1046 (*sp->s_onclose)();
1047 # ifdef HAVE_OPENSSL
1048 if (sp->s_use_ssl) {
1049 void *s_ssl = sp->s_ssl, *s_ctx = sp->s_ctx;
1050 sp->s_ssl = sp->s_ctx = NULL;
1051 sp->s_use_ssl = 0;
1052 assert(s_ssl != NULL);
1053 while (!SSL_shutdown(s_ssl)) /* XXX */
1055 SSL_free(s_ssl);
1056 SSL_CTX_free(s_ctx);
1058 # endif
1059 i = close(sp->s_fd);
1060 sp->s_fd = -1;
1061 goto jleave;
1063 sp->s_fd = -1;
1064 i = 0;
1065 jleave:
1066 NYD_LEAVE;
1067 return i;
1070 FL enum okay
1071 swrite(struct sock *sp, char const *data)
1073 enum okay rv;
1074 NYD_ENTER;
1076 rv = swrite1(sp, data, strlen(data), 0);
1077 NYD_LEAVE;
1078 return rv;
1081 FL enum okay
1082 swrite1(struct sock *sp, char const *data, int sz, int use_buffer)
1084 enum okay rv = STOP;
1085 int x;
1086 NYD_ENTER;
1088 if (use_buffer > 0) {
1089 int di;
1091 if (sp->s_wbuf == NULL) {
1092 sp->s_wbufsize = 4096;
1093 sp->s_wbuf = smalloc(sp->s_wbufsize);
1094 sp->s_wbufpos = 0;
1096 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
1097 di = sp->s_wbufsize - sp->s_wbufpos;
1098 sz -= di;
1099 if (sp->s_wbufpos > 0) {
1100 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, di);
1101 rv = swrite1(sp, sp->s_wbuf, sp->s_wbufsize, -1);
1102 } else
1103 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1104 if (rv != OKAY)
1105 goto jleave;
1106 data += di;
1107 sp->s_wbufpos = 0;
1109 if (sz == sp->s_wbufsize) {
1110 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1111 if (rv != OKAY)
1112 goto jleave;
1113 } else if (sz) {
1114 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, sz);
1115 sp->s_wbufpos += sz;
1117 rv = OKAY;
1118 goto jleave;
1119 } else if (use_buffer == 0 && sp->s_wbuf != NULL && sp->s_wbufpos > 0) {
1120 x = sp->s_wbufpos;
1121 sp->s_wbufpos = 0;
1122 if ((rv = swrite1(sp, sp->s_wbuf, x, -1)) != OKAY)
1123 goto jleave;
1125 if (sz == 0) {
1126 rv = OKAY;
1127 goto jleave;
1130 # ifdef HAVE_OPENSSL
1131 if (sp->s_use_ssl) {
1132 jssl_retry:
1133 x = SSL_write(sp->s_ssl, data, sz);
1134 if (x < 0) {
1135 switch (SSL_get_error(sp->s_ssl, x)) {
1136 case SSL_ERROR_WANT_READ:
1137 case SSL_ERROR_WANT_WRITE:
1138 goto jssl_retry;
1141 } else
1142 # endif
1144 x = xwrite(sp->s_fd, data, sz);
1146 if (x != sz) {
1147 char o[512];
1148 snprintf(o, sizeof o, "%s write error",
1149 (sp->s_desc ? sp->s_desc : "socket"));
1150 # ifdef HAVE_OPENSSL
1151 sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o);
1152 # else
1153 perror(o);
1154 # endif
1155 if (x < 0)
1156 sclose(sp);
1157 rv = STOP;
1158 goto jleave;
1160 rv = OKAY;
1161 jleave:
1162 NYD_LEAVE;
1163 return rv;
1166 FL enum okay
1167 sopen(char const *xserver, struct sock *sp, int use_ssl, char const *uhp,
1168 char const *portstr)
1170 # ifdef HAVE_SO_SNDTIMEO
1171 struct timeval tv;
1172 # endif
1173 # ifdef HAVE_SO_LINGER
1174 struct linger li;
1175 # endif
1176 # ifdef HAVE_IPV6
1177 char hbuf[NI_MAXHOST];
1178 struct addrinfo hints, *res0, *res;
1179 # else
1180 struct sockaddr_in servaddr;
1181 struct in_addr **pptr;
1182 struct hostent *hp;
1183 struct servent *ep;
1184 unsigned short port = 0;
1185 # endif
1186 int sockfd;
1187 char *cp, *server = UNCONST(xserver);
1188 enum okay rv = STOP;
1189 NYD_ENTER;
1190 UNUSED(use_ssl);
1191 UNUSED(uhp);
1193 if ((cp = strchr(server, ':')) != NULL) { /* TODO URI parse! IPv6! */
1194 portstr = &cp[1];
1195 # ifndef HAVE_IPV6
1196 port = strtol(portstr, NULL, 10);
1197 # endif
1198 server = salloc(cp - xserver + 1);
1199 memcpy(server, xserver, cp - xserver);
1200 server[cp - xserver] = '\0';
1203 /* Connect timeouts after 30 seconds */
1204 # ifdef HAVE_SO_SNDTIMEO
1205 tv.tv_sec = 30;
1206 tv.tv_usec = 0;
1207 # endif
1209 # ifdef HAVE_IPV6
1210 if (options & OPT_VERBOSE)
1211 fprintf(stderr, "Resolving host %s . . .", server);
1212 memset(&hints, 0, sizeof hints);
1213 hints.ai_socktype = SOCK_STREAM;
1214 if (getaddrinfo(server, portstr, &hints, &res0) != 0) {
1215 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1216 goto jleave;
1217 } else if (options & OPT_VERBOSE)
1218 fprintf(stderr, tr(500, " done.\n"));
1220 sockfd = -1;
1221 for (res = res0; res != NULL && sockfd < 0; res = res->ai_next) {
1222 if (options & OPT_VERBOSE) {
1223 if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof hbuf,
1224 NULL, 0, NI_NUMERICHOST) != 0)
1225 strcpy(hbuf, "unknown host");
1226 fprintf(stderr, tr(192, "%sConnecting to %s:%s . . ."),
1227 (res == res0 ? "" : "\n"), hbuf, portstr);
1229 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1230 if (sockfd >= 0) {
1231 # ifdef HAVE_SO_SNDTIMEO
1232 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1233 # endif
1234 if (connect(sockfd, res->ai_addr, res->ai_addrlen) != 0) {
1235 close(sockfd);
1236 sockfd = -1;
1240 if (sockfd < 0) {
1241 perror(tr(254, " could not connect"));
1242 freeaddrinfo(res0);
1243 goto jleave;
1245 freeaddrinfo(res0);
1247 # else /* HAVE_IPV6 */
1248 if (port == 0) {
1249 if (!strcmp(portstr, "smtp"))
1250 port = htons(25);
1251 else if (!strcmp(portstr, "smtps"))
1252 port = htons(465);
1253 # ifdef HAVE_IMAP
1254 else if (!strcmp(portstr, "imap"))
1255 port = htons(143);
1256 else if (!strcmp(portstr, "imaps"))
1257 port = htons(993);
1258 # endif
1259 # ifdef HAVE_POP3
1260 else if (!strcmp(portstr, "pop3"))
1261 port = htons(110);
1262 else if (!strcmp(portstr, "pop3s"))
1263 port = htons(995);
1264 # endif
1265 else if ((ep = getservbyname(UNCONST(portstr), "tcp")) != NULL)
1266 port = ep->s_port;
1267 else {
1268 fprintf(stderr, tr(251, "Unknown service: %s\n"), portstr);
1269 rv = STOP;
1270 goto jleave;
1272 } else
1273 port = htons(port);
1275 if (options & OPT_VERBOSE)
1276 fprintf(stderr, "Resolving host %s . . .", server);
1277 if ((hp = gethostbyname(server)) == NULL) {
1278 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1279 goto jleave;
1280 } else if (options & OPT_VERBOSE)
1281 fprintf(stderr, tr(500, " done.\n"));
1283 pptr = (struct in_addr**)hp->h_addr_list;
1284 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1285 perror(tr(253, "could not create socket"));
1286 goto jleave;
1288 memset(&servaddr, 0, sizeof servaddr);
1289 servaddr.sin_family = AF_INET;
1290 servaddr.sin_port = port;
1291 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1292 if (options & OPT_VERBOSE)
1293 fprintf(stderr, tr(192, "%sConnecting to %s:%d . . ."),
1294 "", inet_ntoa(**pptr), ntohs(port));
1296 # ifdef HAVE_SO_SNDTIMEO
1297 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1298 # endif
1299 if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof servaddr) != 0) {
1300 perror(tr(254, " could not connect"));
1301 goto jleave;
1303 # endif /* !HAVE_IPV6 */
1304 if (options & OPT_VERBOSE)
1305 fputs(tr(193, " connected.\n"), stderr);
1307 /* And the regular timeouts */
1308 # ifdef HAVE_SO_SNDTIMEO
1309 tv.tv_sec = 42;
1310 tv.tv_usec = 0;
1311 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1312 setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
1313 # endif
1314 # ifdef HAVE_SO_LINGER
1315 li.l_onoff = 1;
1316 li.l_linger = 42;
1317 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
1318 # endif
1320 memset(sp, 0, sizeof *sp);
1321 sp->s_fd = sockfd;
1322 # ifdef HAVE_SSL
1323 if (use_ssl && ssl_open(server, sp, uhp) != OKAY) {
1324 sclose(sp);
1325 goto jleave;
1327 # endif
1328 rv = OKAY;
1329 jleave:
1330 NYD_LEAVE;
1331 return rv;
1334 FL int
1335 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
1336 SMALLOC_DEBUG_ARGS)
1338 int rv;
1339 size_t lsize = *linesize;
1340 char *lp_base = *line, *lp = lp_base;
1341 NYD_ENTER;
1343 if (sp->s_rsz < 0) {
1344 sclose(sp);
1345 rv = sp->s_rsz;
1346 goto jleave;
1349 do {
1350 if (lp_base == NULL || PTRCMP(lp, >, lp_base + lsize - 128)) {
1351 size_t diff = PTR2SIZE(lp - lp_base);
1352 *linesize = (lsize += 256); /* XXX magic */
1353 *line = lp_base = (srealloc)(lp_base, lsize SMALLOC_DEBUG_ARGSCALL);
1354 lp = lp_base + diff;
1357 if (sp->s_rbufptr == NULL ||
1358 PTRCMP(sp->s_rbufptr, >=, sp->s_rbuf + sp->s_rsz)) {
1359 # ifdef HAVE_OPENSSL
1360 if (sp->s_use_ssl) {
1361 jssl_retry:
1362 sp->s_rsz = SSL_read(sp->s_ssl, sp->s_rbuf, sizeof sp->s_rbuf);
1363 if (sp->s_rsz <= 0) {
1364 if (sp->s_rsz < 0) {
1365 char o[512];
1366 switch(SSL_get_error(sp->s_ssl, sp->s_rsz)) {
1367 case SSL_ERROR_WANT_READ:
1368 case SSL_ERROR_WANT_WRITE:
1369 goto jssl_retry;
1371 snprintf(o, sizeof o, "%s",
1372 (sp->s_desc ? sp->s_desc : "socket"));
1373 ssl_gen_err("%s", o);
1375 break;
1377 } else
1378 # endif
1380 jagain:
1381 sp->s_rsz = read(sp->s_fd, sp->s_rbuf, sizeof sp->s_rbuf);
1382 if (sp->s_rsz <= 0) {
1383 if (sp->s_rsz < 0) {
1384 char o[512];
1385 if (errno == EINTR)
1386 goto jagain;
1387 snprintf(o, sizeof o, "%s",
1388 (sp->s_desc ? sp->s_desc : "socket"));
1389 perror(o);
1391 break;
1394 sp->s_rbufptr = sp->s_rbuf;
1396 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
1397 *lp = '\0';
1398 lsize = PTR2SIZE(lp - lp_base);
1400 if (linelen)
1401 *linelen = lsize;
1402 rv = (int)lsize;
1403 jleave:
1404 NYD_LEAVE;
1405 return rv;
1407 #endif /* HAVE_SOCKETS */
1409 FL void
1410 load(char const *name)
1412 FILE *in, *oldin;
1413 NYD_ENTER;
1415 if (name == NULL || (in = Fopen(name, "r")) == NULL)
1416 goto jleave;
1417 oldin = _fio_input;
1418 _fio_input = in;
1419 loading = TRU1;
1420 sourcing = TRU1;
1421 commands();
1422 loading = FAL0;
1423 sourcing = FAL0;
1424 _fio_input = oldin;
1425 Fclose(in);
1426 jleave:
1427 NYD_LEAVE;
1430 FL int
1431 csource(void *v)
1433 int rv = 1;
1434 char **arglist = v, *cp;
1435 FILE *fi;
1436 NYD_ENTER;
1438 if ((cp = fexpand(*arglist, FEXP_LOCAL)) == NULL)
1439 goto jleave;
1440 if ((fi = Fopen(cp, "r")) == NULL) {
1441 perror(cp);
1442 goto jleave;
1445 if (_fio_stack_size >= NELEM(_fio_stack)) {
1446 fprintf(stderr, tr(3, "Too much \"sourcing\" going on.\n"));
1447 Fclose(fi);
1448 goto jleave;
1451 _fio_stack[_fio_stack_size].s_file = _fio_input;
1452 _fio_stack[_fio_stack_size].s_cond = cond_state;
1453 _fio_stack[_fio_stack_size].s_loading = loading;
1454 ++_fio_stack_size;
1455 loading = FAL0;
1456 cond_state = COND_ANY;
1457 _fio_input = fi;
1458 sourcing = TRU1;
1459 rv = 0;
1460 jleave:
1461 NYD_LEAVE;
1462 return rv;
1465 FL int
1466 unstack(void)
1468 int rv = 1;
1469 NYD_ENTER;
1471 if (_fio_stack_size == 0) {
1472 fprintf(stderr, tr(4, "\"Source\" stack over-pop.\n"));
1473 sourcing = FAL0;
1474 goto jleave;
1477 Fclose(_fio_input);
1478 if (cond_state != COND_ANY)
1479 fprintf(stderr, tr(5, "Unmatched \"if\"\n"));
1481 --_fio_stack_size;
1482 cond_state = _fio_stack[_fio_stack_size].s_cond;
1483 loading = _fio_stack[_fio_stack_size].s_loading;
1484 _fio_input = _fio_stack[_fio_stack_size].s_file;
1485 if (_fio_stack_size == 0)
1486 sourcing = loading;
1487 rv = 0;
1488 jleave:
1489 NYD_LEAVE;
1490 return rv;
1493 /* vim:set fenc=utf-8:s-it-mode */