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