Bump S-nail v14.7.6
[s-mailx.git] / fio.c
bloba3cc48641911b9c79af565919592066618b9b621
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, _("\"%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, _("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, _("\"%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, _("\"%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, NULL, -1, pivec[1], "-c", cmdbuf, NULL, 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, _("\"%s\": Expansion failed.\n"), name);
240 goto jleave;
242 if (l == 0) {
243 if (!(fexpm & FEXP_SILENT))
244 fprintf(stderr, _("\"%s\": No match.\n"), name);
245 goto jleave;
247 if (l == sizeof xname) {
248 if (!(fexpm & FEXP_SILENT))
249 fprintf(stderr, _("\"%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, _("\"%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 NYD2_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 NYD2_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 NYD2_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 NYD2_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 NYD2_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 NYD2_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 whatever.. */
449 int rv = -1;
450 long sz;
451 NYD2_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 NYD2_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 NYD2_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 NYD2_LEAVE;
553 return n;
556 FL char *
557 readstr_input(char const *prompt, char const *string)
559 /* FIXME readstr_input: leaks on sigjmp without linepool */
560 size_t linesize = 0;
561 char *linebuf = NULL, *rv = NULL;
562 int n;
563 NYD2_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 NYD2_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(_("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, _("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"); /* XXX no magics (POSIX though) */
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;
926 if (anyof(res, "|&;<>~{}()[]*?$`'\"\\"))
927 switch (which_protocol(res)) {
928 case PROTO_FILE:
929 case PROTO_MAILDIR:
930 res = _globname(res, fexpm);
931 dyn = TRU1;
932 goto jleave;
933 default:
934 break;
936 jislocal:
937 if (fexpm & FEXP_LOCAL)
938 switch (which_protocol(res)) {
939 case PROTO_FILE:
940 case PROTO_MAILDIR:
941 break;
942 default:
943 fprintf(stderr, _(
944 "`%s': only a local file or directory may be used\n"), name);
945 res = NULL;
946 break;
948 jleave:
949 if (res && !dyn)
950 res = savestr(res);
951 NYD_LEAVE;
952 return res;
955 FL void
956 demail(void)
958 NYD_ENTER;
959 if (ok_blook(keep) || rm(mailname) < 0) {
960 int fd = open(mailname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
961 if (fd >= 0)
962 close(fd);
964 NYD_LEAVE;
967 FL bool_t
968 var_folder_updated(char const *name, char **store)
970 char rv = TRU1;
971 char *folder, *unres = NULL, *res = NULL;
972 NYD_ENTER;
974 if ((folder = UNCONST(name)) == NULL)
975 goto jleave;
977 /* Expand the *folder*; skip `%:' prefix for simplicity of use */
978 /* XXX This *only* works because we do NOT
979 * XXX update environment variables via the "set" mechanism */
980 if (folder[0] == '%' && folder[1] == ':')
981 folder += 2;
982 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
983 goto jleave;
985 switch (which_protocol(folder)) {
986 case PROTO_POP3:
987 fprintf(stderr, _(
988 "`folder' cannot be set to a flat, readonly POP3 account\n"));
989 rv = FAL0;
990 goto jleave;
991 case PROTO_IMAP:
992 /* Simply assign what we have, even including `%:' prefix */
993 if (folder != name)
994 goto jvcopy;
995 goto jleave;
996 default:
997 /* Further expansion desired */
998 break;
1001 /* All non-absolute paths are relative to our home directory */
1002 if (*folder != '/') {
1003 size_t l1 = strlen(homedir), l2 = strlen(folder);
1004 unres = ac_alloc(l1 + l2 + 1 +1);
1005 memcpy(unres, homedir, l1);
1006 unres[l1] = '/';
1007 memcpy(unres + l1 + 1, folder, l2);
1008 unres[l1 + 1 + l2] = '\0';
1009 folder = unres;
1012 /* Since lex.c:_update_mailname() uses realpath(3) if available to
1013 * avoid that we loose track of our currently open folder in case we
1014 * chdir away, but still checks the leading path portion against
1015 * getfold() to be able to abbreviate to the +FOLDER syntax if
1016 * possible, we need to realpath(3) the folder, too */
1017 #ifdef HAVE_REALPATH
1018 res = ac_alloc(PATH_MAX +1);
1019 if (realpath(folder, res) == NULL)
1020 fprintf(stderr, _("Can't canonicalize `%s'\n"), folder);
1021 else
1022 folder = res;
1023 #endif
1025 jvcopy:
1026 *store = sstrdup(folder);
1028 if (res != NULL)
1029 ac_free(res);
1030 if (unres != NULL)
1031 ac_free(unres);
1032 jleave:
1033 NYD_LEAVE;
1034 return rv;
1037 FL bool_t
1038 getfold(char *name, size_t size)
1040 char const *folder;
1041 NYD_ENTER;
1043 if ((folder = ok_vlook(folder)) != NULL)
1044 n_strlcpy(name, folder, size);
1045 NYD_LEAVE;
1046 return (folder != NULL);
1049 FL char const *
1050 getdeadletter(void) /* XXX should that be in auxlily.c? */
1052 char const *cp;
1053 NYD_ENTER;
1055 if ((cp = ok_vlook(DEAD)) == NULL || (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
1056 cp = fexpand("~/dead.letter", FEXP_LOCAL | FEXP_SHELL);
1057 else if (*cp != '/') {
1058 size_t sz = strlen(cp) + 2 +1;
1059 char *buf = ac_alloc(sz);
1061 snprintf(buf, sz, "~/%s", cp);
1062 cp = fexpand(buf, FEXP_LOCAL | FEXP_SHELL);
1063 ac_free(buf);
1066 if (cp == NULL)
1067 cp = "dead.letter"; /* XXX magic -> nail.h (POSIX thing though) */
1068 NYD_LEAVE;
1069 return cp;
1072 FL enum okay
1073 get_body(struct message *mp)
1075 enum okay rv;
1076 NYD_ENTER;
1077 UNUSED(mp);
1079 switch (mb.mb_type) {
1080 case MB_FILE:
1081 case MB_MAILDIR:
1082 rv = OKAY;
1083 break;
1084 #ifdef HAVE_POP3
1085 case MB_POP3:
1086 rv = pop3_body(mp);
1087 break;
1088 #endif
1089 #ifdef HAVE_IMAP
1090 case MB_IMAP:
1091 case MB_CACHE:
1092 rv = imap_body(mp);
1093 break;
1094 #endif
1095 case MB_VOID:
1096 default:
1097 rv = STOP;
1098 break;
1100 NYD_LEAVE;
1101 return rv;
1104 #ifdef HAVE_SOCKETS
1105 FL int
1106 sclose(struct sock *sp)
1108 int i;
1109 NYD_ENTER;
1111 i = sp->s_fd;
1112 sp->s_fd = -1;
1113 /* TODO NOTE: we MUST NOT close the descriptor `0' here...
1114 * TODO of course this should be handled in a VMAILFS->open() .s_fd=-1,
1115 * TODO but unfortunately it isn't yet */
1116 if (i <= 0)
1117 i = 0;
1118 else {
1119 if (sp->s_onclose != NULL)
1120 (*sp->s_onclose)();
1121 if (sp->s_wbuf != NULL)
1122 free(sp->s_wbuf);
1123 # ifdef HAVE_OPENSSL
1124 if (sp->s_use_ssl) {
1125 /* XXX Don't wonder: as of v14.6 there is a problem in the IMAP code in
1126 * XXX that if i connect via `file' to an IMAP account, that connection
1127 * XXX breaks, and i simply re-`file' to the same account, then it may
1128 * XXX happen that the OpenSSL library crashes, in SSL_CTX_free()?,
1129 * XXX but more checking is needed. That is true for 0* as well as for
1130 * XXX `OpenSSL 1.0.1f 6 Jan 2014'; i've reported that on @openssl-user
1131 * XXX somewhen in november 2013, i.e., the wrong list. What i still
1132 * XXX don't understand, and the reason for why all this NYD_X is here
1133 * XXX etc.: the socket is has not been closed, so these SSL_* funs
1134 * XXX below have not yet been called on the SSL objects */
1135 void *s_ssl = sp->s_ssl, *s_ctx = sp->s_ctx;
1136 sp->s_ssl = sp->s_ctx = NULL;
1137 sp->s_use_ssl = 0;
1138 NYD_X;
1139 while (!SSL_shutdown(s_ssl)) /* XXX proper error handling */
1141 NYD_X;
1142 SSL_free(s_ssl);
1143 NYD_X;
1144 SSL_CTX_free(s_ctx);
1145 NYD_X;
1147 # endif
1148 i = close(i);
1150 NYD_LEAVE;
1151 return i;
1154 FL enum okay
1155 swrite(struct sock *sp, char const *data)
1157 enum okay rv;
1158 NYD2_ENTER;
1160 rv = swrite1(sp, data, strlen(data), 0);
1161 NYD2_LEAVE;
1162 return rv;
1165 FL enum okay
1166 swrite1(struct sock *sp, char const *data, int sz, int use_buffer)
1168 enum okay rv = STOP;
1169 int x;
1170 NYD2_ENTER;
1172 if (use_buffer > 0) {
1173 int di;
1175 if (sp->s_wbuf == NULL) {
1176 sp->s_wbufsize = 4096;
1177 sp->s_wbuf = smalloc(sp->s_wbufsize);
1178 sp->s_wbufpos = 0;
1180 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
1181 di = sp->s_wbufsize - sp->s_wbufpos;
1182 sz -= di;
1183 if (sp->s_wbufpos > 0) {
1184 memcpy(sp->s_wbuf + sp->s_wbufpos, data, di);
1185 rv = swrite1(sp, sp->s_wbuf, sp->s_wbufsize, -1);
1186 } else
1187 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1188 if (rv != OKAY)
1189 goto jleave;
1190 data += di;
1191 sp->s_wbufpos = 0;
1193 if (sz == sp->s_wbufsize) {
1194 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1195 if (rv != OKAY)
1196 goto jleave;
1197 } else if (sz) {
1198 memcpy(sp->s_wbuf+ sp->s_wbufpos, data, sz);
1199 sp->s_wbufpos += sz;
1201 rv = OKAY;
1202 goto jleave;
1203 } else if (use_buffer == 0 && sp->s_wbuf != NULL && sp->s_wbufpos > 0) {
1204 x = sp->s_wbufpos;
1205 sp->s_wbufpos = 0;
1206 if ((rv = swrite1(sp, sp->s_wbuf, x, -1)) != OKAY)
1207 goto jleave;
1209 if (sz == 0) {
1210 rv = OKAY;
1211 goto jleave;
1214 # ifdef HAVE_OPENSSL
1215 if (sp->s_use_ssl) {
1216 jssl_retry:
1217 x = SSL_write(sp->s_ssl, data, sz);
1218 if (x < 0) {
1219 switch (SSL_get_error(sp->s_ssl, x)) {
1220 case SSL_ERROR_WANT_READ:
1221 case SSL_ERROR_WANT_WRITE:
1222 goto jssl_retry;
1225 } else
1226 # endif
1228 x = xwrite(sp->s_fd, data, sz);
1230 if (x != sz) {
1231 char o[512];
1232 snprintf(o, sizeof o, "%s write error",
1233 (sp->s_desc ? sp->s_desc : "socket"));
1234 # ifdef HAVE_OPENSSL
1235 sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o);
1236 # else
1237 perror(o);
1238 # endif
1239 if (x < 0)
1240 sclose(sp);
1241 rv = STOP;
1242 goto jleave;
1244 rv = OKAY;
1245 jleave:
1246 NYD2_LEAVE;
1247 return rv;
1250 FL bool_t
1251 sopen(struct sock *sp, struct url *urlp)
1253 # ifdef HAVE_SO_SNDTIMEO
1254 struct timeval tv;
1255 # endif
1256 # ifdef HAVE_SO_LINGER
1257 struct linger li;
1258 # endif
1259 # ifdef HAVE_IPV6
1260 char hbuf[NI_MAXHOST];
1261 struct addrinfo hints, *res0, *res;
1262 # else
1263 struct sockaddr_in servaddr;
1264 struct in_addr **pptr;
1265 struct hostent *hp;
1266 struct servent *ep;
1267 # endif
1268 char const *serv;
1269 int sofd = -1;
1270 NYD_ENTER;
1272 /* Connect timeouts after 30 seconds XXX configurable */
1273 # ifdef HAVE_SO_SNDTIMEO
1274 tv.tv_sec = 30;
1275 tv.tv_usec = 0;
1276 # endif
1277 serv = (urlp->url_port != NULL) ? urlp->url_port : urlp->url_proto;
1279 if (options & OPT_VERB)
1280 fprintf(stderr, _("Resolving host %s:%s ..."),
1281 urlp->url_host.s, serv);
1283 # ifdef HAVE_IPV6
1284 memset(&hints, 0, sizeof hints);
1285 hints.ai_socktype = SOCK_STREAM;
1286 if (getaddrinfo(urlp->url_host.s, serv, &hints, &res0)) {
1287 fprintf(stderr, _(" lookup of `%s' failed.\n"), urlp->url_host.s);
1288 goto jleave;
1289 } else if (options & OPT_VERB)
1290 fprintf(stderr, _(" done.\n"));
1292 for (res = res0; res != NULL && sofd < 0; res = res->ai_next) {
1293 if (options & OPT_VERB) {
1294 if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof hbuf,
1295 NULL, 0, NI_NUMERICHOST))
1296 strcpy(hbuf, "unknown host");
1297 fprintf(stderr, _("%sConnecting to %s:%s ..."),
1298 (res == res0 ? "" : "\n"), hbuf, serv);
1300 sofd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1301 if (sofd >= 0) {
1302 # ifdef HAVE_SO_SNDTIMEO
1303 setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1304 # endif
1305 if (connect(sofd, res->ai_addr, res->ai_addrlen)) {
1306 close(sofd);
1307 sofd = -1;
1311 freeaddrinfo(res0);
1312 if (sofd < 0) {
1313 perror(_(" could not connect"));
1314 goto jleave;
1317 # else /* HAVE_IPV6 */
1318 if (urlp->url_port == NULL && urlp->url_portno == 0) {
1319 if ((ep = getservbyname(UNCONST(urlp->url_proto), "tcp")) != NULL)
1320 urlp->url_portno = ep->s_port;
1321 else {
1322 fprintf(stderr, _("Unknown service: %s\n"), urlp->url_proto);
1323 goto jleave;
1327 if ((hp = gethostbyname(urlp->url_host.s)) == NULL) {
1328 fprintf(stderr, _(" lookup of `%s' failed.\n"), urlp->url_host.s);
1329 goto jleave;
1330 } else if (options & OPT_VERB)
1331 fprintf(stderr, _(" done.\n"));
1333 pptr = (struct in_addr**)hp->h_addr_list;
1334 if ((sofd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1335 perror(_("could not create socket"));
1336 goto jleave;
1339 memset(&servaddr, 0, sizeof servaddr);
1340 servaddr.sin_family = AF_INET;
1341 servaddr.sin_port = htons(urlp->url_portno);
1342 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1343 if (options & OPT_VERB)
1344 fprintf(stderr, _("%sConnecting to %s:%d ..."),
1345 "", inet_ntoa(**pptr), (int)urlp->url_portno);
1346 # ifdef HAVE_SO_SNDTIMEO
1347 setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1348 # endif
1349 if (connect(sofd, (struct sockaddr*)&servaddr, sizeof servaddr)) {
1350 perror(_(" could not connect"));
1351 close(sofd);
1352 sofd = -1;
1353 goto jleave;
1355 # endif /* !HAVE_IPV6 */
1357 if (options & OPT_VERB)
1358 fputs(_(" connected.\n"), stderr);
1360 /* And the regular timeouts XXX configurable */
1361 # ifdef HAVE_SO_SNDTIMEO
1362 tv.tv_sec = 42;
1363 tv.tv_usec = 0;
1364 setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1365 setsockopt(sofd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
1366 # endif
1367 # ifdef HAVE_SO_LINGER
1368 li.l_onoff = 1;
1369 li.l_linger = 42;
1370 setsockopt(sofd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
1371 # endif
1373 memset(sp, 0, sizeof *sp);
1374 sp->s_fd = sofd;
1375 # ifdef HAVE_SSL
1376 if (urlp->url_needs_tls &&
1377 ssl_open(urlp->url_host.s, sp, urlp->url_u_h_p.s) != OKAY) {
1378 sclose(sp);
1379 sofd = -1;
1381 # endif
1382 jleave:
1383 NYD_LEAVE;
1384 return (sofd >= 0);
1387 FL int
1388 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
1389 SMALLOC_DEBUG_ARGS)
1391 int rv;
1392 size_t lsize;
1393 char *lp_base, *lp;
1394 NYD2_ENTER;
1396 lsize = *linesize;
1397 lp_base = *line;
1398 lp = lp_base;
1400 if (sp->s_rsz < 0) {
1401 sclose(sp);
1402 rv = sp->s_rsz;
1403 goto jleave;
1406 do {
1407 if (lp_base == NULL || PTRCMP(lp, >, lp_base + lsize - 128)) {
1408 size_t diff = PTR2SIZE(lp - lp_base);
1409 *linesize = (lsize += 256); /* XXX magic */
1410 *line = lp_base = (srealloc)(lp_base, lsize SMALLOC_DEBUG_ARGSCALL);
1411 lp = lp_base + diff;
1414 if (sp->s_rbufptr == NULL ||
1415 PTRCMP(sp->s_rbufptr, >=, sp->s_rbuf + sp->s_rsz)) {
1416 # ifdef HAVE_OPENSSL
1417 if (sp->s_use_ssl) {
1418 jssl_retry:
1419 sp->s_rsz = SSL_read(sp->s_ssl, sp->s_rbuf, sizeof sp->s_rbuf);
1420 if (sp->s_rsz <= 0) {
1421 if (sp->s_rsz < 0) {
1422 char o[512];
1423 switch(SSL_get_error(sp->s_ssl, sp->s_rsz)) {
1424 case SSL_ERROR_WANT_READ:
1425 case SSL_ERROR_WANT_WRITE:
1426 goto jssl_retry;
1428 snprintf(o, sizeof o, "%s",
1429 (sp->s_desc ? sp->s_desc : "socket"));
1430 ssl_gen_err("%s", o);
1432 break;
1434 } else
1435 # endif
1437 jagain:
1438 sp->s_rsz = read(sp->s_fd, sp->s_rbuf, sizeof sp->s_rbuf);
1439 if (sp->s_rsz <= 0) {
1440 if (sp->s_rsz < 0) {
1441 char o[512];
1442 if (errno == EINTR)
1443 goto jagain;
1444 snprintf(o, sizeof o, "%s",
1445 (sp->s_desc ? sp->s_desc : "socket"));
1446 perror(o);
1448 break;
1451 sp->s_rbufptr = sp->s_rbuf;
1453 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
1454 *lp = '\0';
1455 lsize = PTR2SIZE(lp - lp_base);
1457 if (linelen)
1458 *linelen = lsize;
1459 rv = (int)lsize;
1460 jleave:
1461 NYD2_LEAVE;
1462 return rv;
1464 #endif /* HAVE_SOCKETS */
1466 FL void
1467 load(char const *name)
1469 struct str n;
1470 FILE *in, *oldin;
1471 NYD_ENTER;
1473 if (name == NULL || *name == '\0' || (in = Fopen(name, "r")) == NULL)
1474 goto jleave;
1476 oldin = _fio_input;
1477 _fio_input = in;
1478 loading = TRU1;
1479 sourcing = TRU1;
1480 /* commands() may sreset(), copy over file name */
1481 n.l = strlen(name);
1482 n.s = ac_alloc(n.l +1);
1483 memcpy(n.s, name, n.l +1);
1485 if (!commands())
1486 fprintf(stderr, _("Stopped loading `%s' due to errors\n"), n.s);
1488 ac_free(n.s);
1489 loading = FAL0;
1490 sourcing = FAL0;
1491 _fio_input = oldin;
1492 Fclose(in);
1493 jleave:
1494 NYD_LEAVE;
1497 FL int
1498 c_source(void *v)
1500 int rv = 1;
1501 char **arglist = v, *cp;
1502 FILE *fi;
1503 NYD_ENTER;
1505 if ((cp = fexpand(*arglist, FEXP_LOCAL)) == NULL)
1506 goto jleave;
1507 if ((fi = Fopen(cp, "r")) == NULL) {
1508 perror(cp);
1509 goto jleave;
1512 if (_fio_stack_size >= NELEM(_fio_stack)) {
1513 fprintf(stderr, _("Too much \"sourcing\" going on.\n"));
1514 Fclose(fi);
1515 goto jleave;
1518 _fio_stack[_fio_stack_size].s_file = _fio_input;
1519 _fio_stack[_fio_stack_size].s_cond = condstack_release();
1520 _fio_stack[_fio_stack_size].s_loading = loading;
1521 ++_fio_stack_size;
1522 loading = FAL0;
1523 _fio_input = fi;
1524 sourcing = TRU1;
1525 rv = 0;
1526 jleave:
1527 NYD_LEAVE;
1528 return rv;
1531 FL int
1532 unstack(void)
1534 int rv = 1;
1535 NYD_ENTER;
1537 if (_fio_stack_size == 0) {
1538 fprintf(stderr, _("\"Source\" stack over-pop.\n"));
1539 sourcing = FAL0;
1540 goto jleave;
1543 Fclose(_fio_input);
1545 --_fio_stack_size;
1546 if (!condstack_take(_fio_stack[_fio_stack_size].s_cond))
1547 fprintf(stderr, _("Unmatched \"if\"\n"));
1548 loading = _fio_stack[_fio_stack_size].s_loading;
1549 _fio_input = _fio_stack[_fio_stack_size].s_file;
1550 if (_fio_stack_size == 0)
1551 sourcing = loading;
1552 rv = 0;
1553 jleave:
1554 NYD_LEAVE;
1555 return rv;
1558 /* s-it-mode */