MLE: add "mle-position" and "mle-prompt" colour mappings
[s-mailx.git] / fio.c
bloba7fa1046c9705edcf75b7c4a95aa0e6cbc5d7f24
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ File I/O, including resource file loading etc.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE fio
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <sys/wait.h>
44 #ifdef HAVE_SOCKETS
45 # include <sys/socket.h>
47 # include <netdb.h>
49 # include <netinet/in.h>
51 # ifdef HAVE_ARPA_INET_H
52 # include <arpa/inet.h>
53 # endif
54 #endif
56 #ifdef HAVE_WORDEXP
57 # include <wordexp.h>
58 #endif
60 #ifdef HAVE_OPENSSL
61 # include <openssl/err.h>
62 # include <openssl/rand.h>
63 # include <openssl/ssl.h>
64 # include <openssl/x509v3.h>
65 # include <openssl/x509.h>
66 #endif
68 #ifdef HAVE_DOTLOCK
69 # include "dotlock.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 ui32_t s_pstate; /* Copy of ::pstate */
78 /* Slots in ::message */
79 static size_t _message_space;
81 /* XXX Our Popen() main() takes void, temporary global data store */
82 #ifdef HAVE_DOTLOCK
83 static enum file_lock_type _dotlock_flt;
84 static int _dotlock_fd;
85 struct dotlock_info * _dotlock_dip;
86 #endif
88 /* */
89 static struct fio_stack _fio_stack[FIO_STACK_SIZE];
90 static size_t _fio_stack_size;
91 static FILE * _fio_input;
93 /* Locate the user's mailbox file (where new, unread mail is queued) */
94 static void _findmail(char *buf, size_t bufsize, char const *user,
95 bool_t force);
97 /* Perform shell meta character expansion TODO obsolete (INSECURE!) */
98 static char * _globname(char const *name, enum fexp_mode fexpm);
100 /* line is a buffer with the result of fgets(). Returns the first newline or
101 * the last character read */
102 static size_t _length_of_line(char const *line, size_t linesize);
104 /* Read a line, one character at a time */
105 static char * _fgetline_byone(char **line, size_t *linesize, size_t *llen,
106 FILE *fp, int appendnl, size_t n SMALLOC_DEBUG_ARGS);
108 /* Take the data out of the passed ghost file and toss it into a dynamically
109 * allocated message structure */
110 static void makemessage(void);
112 static enum okay get_header(struct message *mp);
114 /* Workhorse */
115 static bool_t _file_lock(int fd, enum file_lock_type ft,
116 off_t off, off_t len);
118 /* main() of fork(2)ed dot file locker */
119 #ifdef HAVE_DOTLOCK
120 static int _dotlock_main(void);
121 #endif
123 /* Write to socket fd, restarting on EINTR, unless anything is written */
124 #ifdef HAVE_SOCKETS
125 static long xwrite(int fd, char const *data, size_t sz);
126 #endif
128 /* `source' and `source_if' (if silent_error: no pipes allowed, then) */
129 static bool_t _source_file(char const *file, bool_t silent_error);
131 static void
132 _findmail(char *buf, size_t bufsize, char const *user, bool_t force)
134 char *cp;
135 NYD_ENTER;
137 if (!strcmp(user, myname) && !force && (cp = ok_vlook(folder)) != NULL) {
141 if (force || (cp = ok_vlook(MAIL)) == NULL)
142 snprintf(buf, bufsize, "%s/%s", MAILSPOOL, user); /* TODO */
143 else
144 n_strscpy(buf, cp, bufsize);
145 NYD_LEAVE;
148 static char *
149 _globname(char const *name, enum fexp_mode fexpm)
151 #ifdef HAVE_WORDEXP
152 wordexp_t we;
153 char *cp = NULL;
154 sigset_t nset;
155 int i;
156 NYD_ENTER;
158 /* Mac OS X Snow Leopard and Linux don't init fields on error, causing
159 * SIGSEGV in wordfree(3); so let's just always zero it ourselfs */
160 memset(&we, 0, sizeof we);
162 /* Some systems (notably Open UNIX 8.0.0) fork a shell for wordexp()
163 * and wait, which will fail if our SIGCHLD handler is active */
164 sigemptyset(&nset);
165 sigaddset(&nset, SIGCHLD);
166 sigprocmask(SIG_BLOCK, &nset, NULL);
167 # ifndef WRDE_NOCMD
168 # define WRDE_NOCMD 0
169 # endif
170 i = wordexp(name, &we, WRDE_NOCMD);
171 sigprocmask(SIG_UNBLOCK, &nset, NULL);
173 switch (i) {
174 case 0:
175 break;
176 #ifdef WRDE_CMDSUB
177 case WRDE_CMDSUB:
178 if (!(fexpm & FEXP_SILENT))
179 n_err(_("\"%s\": Command substitution not allowed\n"), name);
180 goto jleave;
181 #endif
182 case WRDE_NOSPACE:
183 if (!(fexpm & FEXP_SILENT))
184 n_err(_("\"%s\": Expansion buffer overflow\n"), name);
185 goto jleave;
186 case WRDE_BADCHAR:
187 case WRDE_SYNTAX:
188 default:
189 if (!(fexpm & FEXP_SILENT))
190 n_err(_("Syntax error in \"%s\"\n"), name);
191 goto jleave;
194 switch (we.we_wordc) {
195 case 1:
196 cp = savestr(we.we_wordv[0]);
197 break;
198 case 0:
199 if (!(fexpm & FEXP_SILENT))
200 n_err(_("\"%s\": No match\n"), name);
201 break;
202 default:
203 if (fexpm & FEXP_MULTIOK) {
204 size_t j, l;
206 for (l = 0, j = 0; j < we.we_wordc; ++j)
207 l += strlen(we.we_wordv[j]) + 1;
208 ++l;
209 cp = salloc(l);
210 for (l = 0, j = 0; j < we.we_wordc; ++j) {
211 size_t x = strlen(we.we_wordv[j]);
212 memcpy(cp + l, we.we_wordv[j], x);
213 l += x;
214 cp[l++] = ' ';
216 cp[l] = '\0';
217 } else if (!(fexpm & FEXP_SILENT))
218 n_err(_("\"%s\": Ambiguous\n"), name);
219 break;
221 jleave:
222 wordfree(&we);
223 NYD_LEAVE;
224 return cp;
226 #else /* HAVE_WORDEXP */
227 struct stat sbuf;
228 char xname[PATH_MAX +1], cmdbuf[PATH_MAX +1], /* also used for files */
229 *shellp, *cp = NULL;
230 int pivec[2], pid, l, waits;
231 NYD_ENTER;
233 if (pipe(pivec) < 0) {
234 n_perr(_("pipe"), 0);
235 goto jleave;
237 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
238 if ((shellp = ok_vlook(SHELL)) == NULL)
239 shellp = UNCONST(XSHELL);
240 pid = start_command(shellp, NULL, COMMAND_FD_NULL, pivec[1],
241 "-c", cmdbuf, NULL, NULL);
242 if (pid < 0) {
243 close(pivec[0]);
244 close(pivec[1]);
245 goto jleave;
247 close(pivec[1]);
249 jagain:
250 l = read(pivec[0], xname, sizeof xname);
251 if (l < 0) {
252 if (errno == EINTR)
253 goto jagain;
254 n_perr(_("read"), 0);
255 close(pivec[0]);
256 goto jleave;
258 close(pivec[0]);
259 if (!wait_child(pid, &waits) && WTERMSIG(waits) != SIGPIPE) {
260 if (!(fexpm & FEXP_SILENT))
261 n_err(_("\"%s\": Expansion failed\n"), name);
262 goto jleave;
264 if (l == 0) {
265 if (!(fexpm & FEXP_SILENT))
266 n_err(_("\"%s\": No match\n"), name);
267 goto jleave;
269 if (l == sizeof xname) {
270 if (!(fexpm & FEXP_SILENT))
271 n_err(_("\"%s\": Expansion buffer overflow\n"), name);
272 goto jleave;
274 xname[l] = 0;
275 for (cp = xname + l - 1; *cp == '\n' && cp > xname; --cp)
277 cp[1] = '\0';
278 if (!(fexpm & FEXP_MULTIOK) && strchr(xname, ' ') != NULL &&
279 stat(xname, &sbuf) < 0) {
280 if (!(fexpm & FEXP_SILENT))
281 n_err(_("\"%s\": Ambiguous\n"), name);
282 cp = NULL;
283 goto jleave;
285 cp = savestr(xname);
286 jleave:
287 NYD_LEAVE;
288 return cp;
289 #endif /* !HAVE_WORDEXP */
292 static size_t
293 _length_of_line(char const *line, size_t linesize)
295 size_t i;
296 NYD2_ENTER;
298 /* Last character is always '\0' and was added by fgets() */
299 for (--linesize, i = 0; i < linesize; i++)
300 if (line[i] == '\n')
301 break;
302 i = (i < linesize) ? i + 1 : linesize;
303 NYD2_LEAVE;
304 return i;
307 static char *
308 _fgetline_byone(char **line, size_t *linesize, size_t *llen, FILE *fp,
309 int appendnl, size_t n SMALLOC_DEBUG_ARGS)
311 char *rv;
312 int c;
313 NYD2_ENTER;
315 assert(*linesize == 0 || *line != NULL);
316 for (rv = *line;;) {
317 if (*linesize <= LINESIZE || n >= *linesize - 128) {
318 *linesize += ((rv == NULL) ? LINESIZE + n + 1 : 256);
319 *line = rv = (srealloc)(rv, *linesize SMALLOC_DEBUG_ARGSCALL);
321 c = getc(fp);
322 if (c != EOF) {
323 rv[n++] = c;
324 rv[n] = '\0';
325 if (c == '\n')
326 break;
327 } else {
328 if (n > 0) {
329 if (appendnl) {
330 rv[n++] = '\n';
331 rv[n] = '\0';
333 break;
334 } else {
335 rv = NULL;
336 goto jleave;
340 if (llen)
341 *llen = n;
342 jleave:
343 NYD2_LEAVE;
344 return rv;
347 static void
348 makemessage(void)
350 NYD_ENTER;
351 if (msgCount == 0)
352 message_append(NULL);
353 setdot(message);
354 message[msgCount].m_size = 0;
355 message[msgCount].m_lines = 0;
356 NYD_LEAVE;
359 static enum okay
360 get_header(struct message *mp)
362 enum okay rv;
363 NYD_ENTER;
364 UNUSED(mp);
366 switch (mb.mb_type) {
367 case MB_FILE:
368 case MB_MAILDIR:
369 rv = OKAY;
370 break;
371 #ifdef HAVE_POP3
372 case MB_POP3:
373 rv = pop3_header(mp);
374 break;
375 #endif
376 case MB_VOID:
377 default:
378 rv = STOP;
379 break;
381 NYD_LEAVE;
382 return rv;
385 static bool_t
386 _file_lock(int fd, enum file_lock_type flt, off_t off, off_t len)
388 struct flock flp;
389 bool_t rv;
390 NYD2_ENTER;
392 memset(&flp, 0, sizeof flp);
394 switch (flt) {
395 default:
396 case FLT_READ: rv = F_RDLCK; break;
397 case FLT_WRITE: rv = F_WRLCK; break;
399 flp.l_type = rv;
400 flp.l_start = off;
401 flp.l_whence = SEEK_SET;
402 flp.l_len = len;
404 rv = (fcntl(fd, F_SETLK, &flp) != -1);
405 NYD2_LEAVE;
406 return rv;
409 #ifdef HAVE_DOTLOCK
410 static int
411 _dotlock_main(void)
413 /* Use PATH_MAX not NAME_MAX to catch those "we proclaim the minimum value"
414 * problems (SunOS), since the pathconf(3) value comes too late! */
415 char name[PATH_MAX +1];
416 struct dotlock_info di;
417 struct stat stb, fdstb;
418 enum dotlock_state dls;
419 char const *cp;
420 int fd;
421 enum file_lock_type flt;
422 NYD_ENTER;
424 /* Ignore SIGPIPE, we'll see EPIPE and "fall through" */
425 safe_signal(SIGPIPE, SIG_IGN);
427 /* Get the arguments "passed to us" */
428 flt = _dotlock_flt;
429 fd = _dotlock_fd;
430 UNUSED(fd);
431 di = *_dotlock_dip;
433 /* chdir(2)? */
434 jislink:
435 dls = DLS_CANT_CHDIR | DLS_ABANDON;
437 if ((cp = strrchr(di.di_file_name, '/')) != NULL) {
438 char const *fname = cp + 1;
440 while (PTRCMP(cp - 1, >, di.di_file_name) && cp[-1] == '/')
441 --cp;
442 cp = savestrbuf(di.di_file_name, PTR2SIZE(cp - di.di_file_name));
443 if (chdir(cp))
444 goto jmsg;
446 di.di_file_name = fname;
449 /* So we're here, but then again the file can be a symbolic link!
450 * This is however only true if we do not have realpath(3) available since
451 * that'll have resolved the path already otherwise; nonetheless, let
452 * readlink(2) be a precondition for dotlocking and keep this code */
453 if (lstat(cp = di.di_file_name, &stb) == -1)
454 goto jmsg;
455 if (S_ISLNK(stb.st_mode)) {
456 /* Use salloc() and hope we stay in builtin buffer.. */
457 char *x;
458 size_t i;
459 ssize_t sr;
461 for (x = NULL, i = PATH_MAX;; i += PATH_MAX) {
462 x = salloc(i +1);
463 sr = readlink(cp, x, i);
464 if (sr <= 0) {
465 dls = DLS_FISHY | DLS_ABANDON;
466 goto jmsg;
468 if (UICMP(z, sr, <, i)) {
469 x[sr] = '\0';
470 i = (size_t)sr;
471 break;
474 di.di_file_name = x;
475 goto jislink;
478 dls = DLS_FISHY | DLS_ABANDON;
480 /* Bail out if the file has changed its identity in the meanwhile */
481 if (fstat(fd, &fdstb) == -1 ||
482 fdstb.st_dev != stb.st_dev || fdstb.st_ino != stb.st_ino ||
483 fdstb.st_uid != stb.st_uid || fdstb.st_gid != stb.st_gid ||
484 fdstb.st_mode != stb.st_mode)
485 goto jmsg;
487 /* Be aware, even if the error is false! Note the shared code in dotlock.h
488 * *requires* that it is possible to create a filename at least one byte
489 * longer than di_lock_name! */
490 do/* while(0) breaker */{
491 # ifdef HAVE_PATHCONF
492 long pc;
493 # endif
494 int i = snprintf(name, sizeof name, "%s.lock", di.di_file_name);
496 /* fd is a file, not portable to use for _PC_NAME_MAX */
497 if(i < 0){
498 jenametool:
499 dls = DLS_NAMETOOLONG | DLS_ABANDON;
500 goto jmsg;
502 # ifdef HAVE_PATHCONF
503 errno = 0;
504 if((pc = pathconf(".", _PC_NAME_MAX)) == -1){
505 /* errno unchanged: no limit */
506 if(errno == 0)
507 break;
508 }else if(pc - 1 >= (long)i)
509 break;
510 else
511 goto jenametool;
512 # endif
513 if(UICMP(z, NAME_MAX - 1, <, (uiz_t)i))
514 goto jenametool;
515 }while(0);
517 di.di_lock_name = name;
519 /* We are in the directory of the mailbox for which we have to create
520 * a dotlock file for. We don't know wether we have realpath(3) available,
521 * and manually resolving the path is due especially given that S-nail
522 * supports the special "%:" syntax to warp any file into a "system
523 * mailbox"; there may also be multiple system mailbox directories...
524 * So what we do is that we fstat(2) the mailbox and check its UID and
525 * GID against that of our own process: if any of those mismatch we must
526 * either assume a directory we are not allowed to write in, or that we run
527 * via -u/$USER/%USER as someone else, in which case we favour our
528 * privilege-separated dotlock process */
529 assert(cp != NULL); /* Ugly: avoid a useless var and reuse that one */
530 if (access(".", W_OK)) {
531 /* This may however also indicate a read-only filesystem, which is not
532 * really an error from our point of view since the mailbox will degrade
533 * to a readonly one for which no dotlock is needed, then, and errors
534 * may arise only due to actions which require box modifications */
535 if (errno == EROFS) {
536 dls = DLS_ROFS | DLS_ABANDON;
537 goto jmsg;
539 cp = NULL;
541 if (cp == NULL || stb.st_uid != user_id || stb.st_gid != group_id) {
542 char itoabuf[64];
543 char const *args[13];
545 snprintf(itoabuf, sizeof itoabuf, "%" PRIuZ, di.di_pollmsecs);
546 args[ 0] = PRIVSEP;
547 args[ 1] = (flt == FLT_READ ? "rdotlock" : "wdotlock");
548 args[ 2] = "mailbox"; args[ 3] = di.di_file_name;
549 args[ 4] = "name"; args[ 5] = di.di_lock_name;
550 args[ 6] = "hostname"; args[ 7] = di.di_hostname;
551 args[ 8] = "randstr"; args[ 9] = di.di_randstr;
552 args[10] = "pollmsecs"; args[11] = itoabuf;
553 args[12] = NULL;
554 execv(LIBEXECDIR "/" UAGENT "-privsep", UNCONST(args));
556 dls = DLS_NOEXEC;
557 write(STDOUT_FILENO, &dls, sizeof dls);
558 /* But fall through and try it with normal privileges! */
561 /* So let's try and call it ourselfs! Note that we don't block signals just
562 * like our privsep child does, the user will anyway be able to remove his
563 * file again, and if we're in -u/$USER mode then we are allowed to access
564 * the user's box: shall we leave behind a stale dotlock then at least we
565 * start a friendly human conversation. Since we cannot handle SIGKILL and
566 * SIGSTOP malicious things could happen whatever we do */
567 safe_signal(SIGHUP, SIG_IGN);
568 safe_signal(SIGINT, SIG_IGN);
569 safe_signal(SIGQUIT, SIG_IGN);
570 safe_signal(SIGTERM, SIG_IGN);
572 NYD;
573 dls = _dotlock_create(&di);
574 NYD;
576 /* Finally: notify our parent about the actual lock state.. */
577 jmsg:
578 write(STDOUT_FILENO, &dls, sizeof dls);
579 close(STDOUT_FILENO);
581 /* ..then eventually wait until we shall remove the lock again, which will
582 * be notified via the read returning */
583 if (dls == DLS_NONE) {
584 read(STDIN_FILENO, &dls, sizeof dls);
586 unlink(name);
588 NYD_LEAVE;
589 return EXIT_OK;
591 #endif /* HAVE_DOTLOCK */
593 #ifdef HAVE_SOCKETS
594 static long
595 xwrite(int fd, char const *data, size_t sz)
597 long rv = -1, wo;
598 size_t wt = 0;
599 NYD_ENTER;
601 do {
602 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
603 if (errno == EINTR)
604 continue;
605 else
606 goto jleave;
608 wt += wo;
609 } while (wt < sz);
610 rv = (long)sz;
611 jleave:
612 NYD_LEAVE;
613 return rv;
615 #endif /* HAVE_SOCKETS */
617 static bool_t
618 _source_file(char const *file, bool_t silent_error)
620 char *cp;
621 bool_t ispipe;
622 FILE *fi;
623 NYD_ENTER;
625 fi = NULL;
627 if ((ispipe = !silent_error)) {
628 size_t i = strlen(file);
630 while (i > 0 && spacechar(file[i - 1]))
631 --i;
632 if (i > 0 && file[i - 1] == '|')
633 cp = savestrbuf(file, --i);
634 else
635 ispipe = FAL0;
638 if (ispipe) {
639 char const *sh;
641 if ((sh = ok_vlook(SHELL)) == NULL)
642 sh = XSHELL;
643 if ((fi = Popen(cp, "r", sh, NULL, COMMAND_FD_NULL)) == NULL) {
644 n_perr(cp, 0);
645 goto jleave;
647 } else if ((cp = fexpand(file, FEXP_LOCAL)) == NULL)
648 goto jleave;
649 else if ((fi = Fopen(cp, "r")) == NULL) {
650 if (!silent_error || (options & OPT_D_V))
651 n_perr(cp, 0);
652 goto jleave;
655 if (temporary_localopts_store != NULL) {
656 n_err(_("Before v15 you cannot `source' from within macros, sorry\n"));
657 goto jeclose;
659 if (_fio_stack_size >= NELEM(_fio_stack)) {
660 n_err(_("Too many `source' recursions\n"));
661 jeclose:
662 if (ispipe)
663 Pclose(fi, TRU1);
664 else
665 Fclose(fi);
666 fi = NULL;
667 goto jleave;
670 _fio_stack[_fio_stack_size].s_file = _fio_input;
671 _fio_stack[_fio_stack_size].s_cond = condstack_release();
672 _fio_stack[_fio_stack_size].s_pstate = pstate;
673 ++_fio_stack_size;
674 pstate &= ~(PS_LOADING | PS_PIPING);
675 pstate |= PS_SOURCING | (ispipe ? PS_PIPING : PS_NONE);
676 _fio_input = fi;
677 jleave:
678 NYD_LEAVE;
679 return (fi != NULL);
682 FL char *
683 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen, FILE *fp,
684 int appendnl SMALLOC_DEBUG_ARGS)
686 size_t i_llen, sz;
687 char *rv;
688 NYD2_ENTER;
690 if (cnt == NULL) {
691 /* Without count, we can't determine where the chars returned by fgets()
692 * end if there's no newline. We have to read one character by one */
693 rv = _fgetline_byone(line, linesize, llen, fp, appendnl, 0
694 SMALLOC_DEBUG_ARGSCALL);
695 goto jleave;
698 if ((rv = *line) == NULL || *linesize < LINESIZE)
699 *line = rv = (srealloc)(rv, *linesize = LINESIZE SMALLOC_DEBUG_ARGSCALL);
700 sz = (*linesize <= *cnt) ? *linesize : *cnt + 1;
701 if (sz <= 1 || fgets(rv, sz, fp) == NULL) {
702 /* Leave llen untouched; it is used to determine whether the last line
703 * was \n-terminated in some callers */
704 rv = NULL;
705 goto jleave;
708 i_llen = _length_of_line(rv, sz);
709 *cnt -= i_llen;
710 while (rv[i_llen - 1] != '\n') {
711 *line = rv = (srealloc)(rv, *linesize += 256 SMALLOC_DEBUG_ARGSCALL);
712 sz = *linesize - i_llen;
713 sz = (sz <= *cnt) ? sz : *cnt + 1;
714 if (sz <= 1 || fgets(rv + i_llen, sz, fp) == NULL) {
715 if (appendnl) {
716 rv[i_llen++] = '\n';
717 rv[i_llen] = '\0';
719 break;
721 sz = _length_of_line(rv + i_llen, sz);
722 i_llen += sz;
723 *cnt -= sz;
725 if (llen)
726 *llen = i_llen;
727 jleave:
728 NYD2_LEAVE;
729 return rv;
732 FL int
733 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
734 SMALLOC_DEBUG_ARGS)
736 /* TODO readline_restart(): always *appends* LF just to strip it again;
737 * TODO should be configurable just as for fgetline(); ..or whatever.. */
738 int rv = -1;
739 long sz;
740 NYD2_ENTER;
742 clearerr(ibuf);
744 /* Interrupts will cause trouble if we are inside a stdio call. As this is
745 * only relevant if input is from tty, bypass it by read(), then */
746 if (fileno(ibuf) == 0 && (options & OPT_TTYIN)) {
747 assert(*linesize == 0 || *linebuf != NULL);
748 for (;;) {
749 if (*linesize <= LINESIZE || n >= *linesize - 128) {
750 *linesize += ((*linebuf == NULL) ? LINESIZE + n + 1 : 256);
751 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
753 jagain:
754 sz = read(0, *linebuf + n, *linesize - n - 1);
755 if (sz > 0) {
756 n += sz;
757 (*linebuf)[n] = '\0';
758 if (n > 0 && (*linebuf)[n - 1] == '\n')
759 break;
760 } else {
761 if (sz < 0 && errno == EINTR)
762 goto jagain;
763 if (n > 0) {
764 if ((*linebuf)[n - 1] != '\n') {
765 (*linebuf)[n++] = '\n';
766 (*linebuf)[n] = '\0';
768 break;
769 } else
770 goto jleave;
773 } else {
774 /* Not reading from standard input or standard input not a terminal. We
775 * read one char at a time as it is the only way to get lines with
776 * embedded NUL characters in standard stdio */
777 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
778 SMALLOC_DEBUG_ARGSCALL) == NULL)
779 goto jleave;
781 if (n > 0 && (*linebuf)[n - 1] == '\n')
782 (*linebuf)[--n] = '\0';
783 rv = (int)n;
784 jleave:
785 NYD2_LEAVE;
786 return rv;
789 FL int
790 (readline_input)(char const *prompt, bool_t nl_escape, char **linebuf,
791 size_t *linesize, char const *string SMALLOC_DEBUG_ARGS)
793 /* TODO readline: linebuf pool! */
794 FILE *ifile = (_fio_input != NULL) ? _fio_input : stdin;
795 bool_t doprompt, dotty;
796 int n, nold;
797 NYD2_ENTER;
799 doprompt = (!(pstate & PS_SOURCING) && (options & OPT_INTERACTIVE));
800 dotty = (doprompt && !ok_blook(line_editor_disable));
801 if (!doprompt)
802 prompt = NULL;
803 else if (prompt == NULL)
804 prompt = getprompt();
806 /* Ensure stdout is flushed first anyway */
807 if (!dotty && prompt == NULL)
808 fflush(stdout);
810 for (nold = n = 0;;) {
811 if (dotty) {
812 assert(ifile == stdin);
813 if (string != NULL && (n = (int)strlen(string)) > 0) {
814 if (*linesize > 0)
815 *linesize += n +1;
816 else
817 *linesize = (size_t)n + LINESIZE +1;
818 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
819 memcpy(*linebuf, string, (size_t)n +1);
821 string = NULL;
822 /* TODO if nold>0, don't redisplay the entire line!
823 * TODO needs complete redesign ... */
824 n = (n_tty_readline)(prompt, linebuf, linesize, n
825 SMALLOC_DEBUG_ARGSCALL);
826 } else {
827 if (prompt != NULL) {
828 if (*prompt != '\0')
829 fputs(prompt, stdout);
830 fflush(stdout);
832 n = (readline_restart)(ifile, linebuf, linesize, n
833 SMALLOC_DEBUG_ARGSCALL);
835 if (n > 0 && nold > 0) {
836 int i = 0;
837 char const *cp = *linebuf + nold;
839 while (blankspacechar(*cp) && nold + i < n)
840 ++cp, ++i;
841 if (i > 0) {
842 memmove(*linebuf + nold, cp, n - nold - i);
843 n -= i;
844 (*linebuf)[n] = '\0';
848 if (n <= 0)
849 break;
851 /* POSIX says:
852 * An unquoted <backslash> at the end of a command line shall
853 * be discarded and the next line shall continue the command */
854 if (!nl_escape || n == 0 || (*linebuf)[n - 1] != '\\')
855 break;
856 (*linebuf)[nold = --n] = '\0';
857 if (prompt != NULL && *prompt != '\0')
858 prompt = ".. "; /* XXX PS2 .. */
861 if (n >= 0 && (options & OPT_D_VV))
862 n_err(_("%s %d bytes <%.*s>\n"),
863 ((pstate & PS_LOADING) ? "LOAD"
864 : (pstate & PS_SOURCING) ? "SOURCE" : "READ"),
865 n, n, *linebuf);
866 NYD2_LEAVE;
867 return n;
870 FL char *
871 n_input_cp_addhist(char const *prompt, char const *string, bool_t isgabby)
873 /* FIXME n_input_cp_addhist(): leaks on sigjmp without linepool */
874 size_t linesize = 0;
875 char *linebuf = NULL, *rv = NULL;
876 int n;
877 NYD2_ENTER;
879 n = readline_input(prompt, FAL0, &linebuf, &linesize, string);
880 if (n > 0 && *(rv = savestrbuf(linebuf, (size_t)n)) != '\0' &&
881 (options & OPT_INTERACTIVE))
882 n_tty_addhist(rv, isgabby);
884 if (linebuf != NULL)
885 free(linebuf);
886 NYD2_LEAVE;
887 return rv;
890 FL void
891 setptr(FILE *ibuf, off_t offset)
893 struct message self;
894 char *cp, *linebuf = NULL;
895 char const *cp2;
896 int c, maybe = 1, inhead = 0, selfcnt = 0;
897 size_t linesize = 0, filesize, cnt;
898 NYD_ENTER;
900 memset(&self, 0, sizeof self);
901 self.m_flag = MUSED | MNEW | MNEWEST;
902 filesize = mailsize - offset;
903 offset = ftell(mb.mb_otf);
905 for (;;) {
906 if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0) == NULL) {
907 self.m_xsize = self.m_size;
908 self.m_xlines = self.m_lines;
909 self.m_have = HAVE_HEADER | HAVE_BODY;
910 if (selfcnt > 0)
911 message_append(&self);
912 makemessage();
913 if (linebuf)
914 free(linebuf);
915 break;
918 #ifdef notdef
919 if (linebuf[0] == '\0')
920 linebuf[0] = '.';
921 #endif
922 /* XXX Convert CRLF to LF; this should be rethought in that
923 * XXX CRLF input should possibly end as CRLF output? */
924 if (cnt >= 2 && linebuf[cnt - 1] == '\n' && linebuf[cnt - 2] == '\r')
925 linebuf[--cnt - 1] = '\n';
926 fwrite(linebuf, sizeof *linebuf, cnt, mb.mb_otf);
927 if (ferror(mb.mb_otf)) {
928 n_perr(_("/tmp"), 0);
929 exit(EXIT_ERR);
931 if (linebuf[cnt - 1] == '\n')
932 linebuf[cnt - 1] = '\0';
933 if (maybe && linebuf[0] == 'F' && is_head(linebuf, cnt, FAL0)) {
934 /* TODO char date[FROM_DATEBUF];
935 * TODO extract_date_from_from_(linebuf, cnt, date);
936 * TODO self.m_time = 10000; */
937 self.m_xsize = self.m_size;
938 self.m_xlines = self.m_lines;
939 self.m_have = HAVE_HEADER | HAVE_BODY;
940 if (selfcnt++ > 0)
941 message_append(&self);
942 msgCount++;
943 self.m_flag = MUSED | MNEW | MNEWEST;
944 self.m_size = 0;
945 self.m_lines = 0;
946 self.m_block = mailx_blockof(offset);
947 self.m_offset = mailx_offsetof(offset);
948 inhead = 1;
949 } else if (linebuf[0] == 0) {
950 inhead = 0;
951 } else if (inhead) {
952 for (cp = linebuf, cp2 = "status";; ++cp) {
953 if ((c = *cp2++) == 0) {
954 while (c = *cp++, whitechar(c))
956 if (cp[-1] != ':')
957 break;
958 while ((c = *cp++) != '\0')
959 if (c == 'R')
960 self.m_flag |= MREAD;
961 else if (c == 'O')
962 self.m_flag &= ~MNEW;
963 break;
965 if (*cp != c && *cp != upperconv(c))
966 break;
968 for (cp = linebuf, cp2 = "x-status";; ++cp) {
969 if ((c = *cp2++) == 0) {
970 while ((c = *cp++, whitechar(c)))
972 if (cp[-1] != ':')
973 break;
974 while ((c = *cp++) != '\0')
975 if (c == 'F')
976 self.m_flag |= MFLAGGED;
977 else if (c == 'A')
978 self.m_flag |= MANSWERED;
979 else if (c == 'T')
980 self.m_flag |= MDRAFTED;
981 break;
983 if (*cp != c && *cp != upperconv(c))
984 break;
987 offset += cnt;
988 self.m_size += cnt;
989 ++self.m_lines;
990 maybe = linebuf[0] == 0;
992 NYD_LEAVE;
995 FL int
996 putline(FILE *obuf, char *linebuf, size_t cnt)
998 int rv = -1;
999 NYD_ENTER;
1001 fwrite(linebuf, sizeof *linebuf, cnt, obuf);
1002 putc('\n', obuf);
1003 if (!ferror(obuf))
1004 rv = (int)(cnt + 1);
1005 NYD_LEAVE;
1006 return rv;
1009 FL FILE *
1010 setinput(struct mailbox *mp, struct message *m, enum needspec need)
1012 FILE *rv = NULL;
1013 enum okay ok = STOP;
1014 NYD_ENTER;
1016 switch (need) {
1017 case NEED_HEADER:
1018 ok = (m->m_have & HAVE_HEADER) ? OKAY : get_header(m);
1019 break;
1020 case NEED_BODY:
1021 ok = (m->m_have & HAVE_BODY) ? OKAY : get_body(m);
1022 break;
1023 case NEED_UNSPEC:
1024 ok = OKAY;
1025 break;
1027 if (ok != OKAY)
1028 goto jleave;
1030 fflush(mp->mb_otf);
1031 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1032 SEEK_SET) == -1) {
1033 n_perr(_("fseek"), 0);
1034 n_panic(_("temporary file seek"));
1036 rv = mp->mb_itf;
1037 jleave:
1038 NYD_LEAVE;
1039 return rv;
1042 FL void
1043 message_reset(void)
1045 NYD_ENTER;
1046 if (message != NULL) {
1047 free(message);
1048 message = NULL;
1050 msgCount = 0;
1051 _message_space = 0;
1052 NYD_LEAVE;
1055 FL void
1056 message_append(struct message *mp)
1058 NYD_ENTER;
1059 if (UICMP(z, msgCount + 1, >=, _message_space)) {
1060 /* XXX remove _message_space magics (or use s_Vector) */
1061 _message_space = (_message_space >= 128 && _message_space <= 1000000)
1062 ? _message_space << 1 : _message_space + 64;
1063 message = srealloc(message, _message_space * sizeof *message);
1065 if (msgCount > 0) {
1066 if (mp != NULL)
1067 message[msgCount - 1] = *mp;
1068 else
1069 memset(message + msgCount - 1, 0, sizeof *message);
1071 NYD_LEAVE;
1074 FL bool_t
1075 message_match(struct message *mp, struct search_expr const *sep,
1076 bool_t with_headers)
1078 char **line;
1079 size_t *linesize, cnt;
1080 FILE *fp;
1081 bool_t rv = FAL0;
1082 NYD_ENTER;
1084 if ((fp = Ftmp(NULL, "mpmatch", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL)
1085 goto j_leave;
1087 if (sendmp(mp, fp, NULL, NULL, SEND_TOSRCH, NULL) < 0)
1088 goto jleave;
1089 fflush_rewind(fp);
1091 cnt = fsize(fp);
1092 line = &termios_state.ts_linebuf; /* XXX line pool */
1093 linesize = &termios_state.ts_linesize; /* XXX line pool */
1095 if (!with_headers)
1096 while (fgetline(line, linesize, &cnt, NULL, fp, 0))
1097 if (**line == '\n')
1098 break;
1100 while (fgetline(line, linesize, &cnt, NULL, fp, 0)) {
1101 #ifdef HAVE_REGEX
1102 if (sep->ss_sexpr == NULL) {
1103 if (regexec(&sep->ss_regex, *line, 0,NULL, 0) == REG_NOMATCH)
1104 continue;
1105 } else
1106 #endif
1107 if (!substr(*line, sep->ss_sexpr))
1108 continue;
1109 rv = TRU1;
1110 break;
1113 jleave:
1114 Fclose(fp);
1115 j_leave:
1116 NYD_LEAVE;
1117 return rv;
1120 FL struct message *
1121 setdot(struct message *mp)
1123 NYD_ENTER;
1124 if (dot != mp) {
1125 prevdot = dot;
1126 pstate &= ~PS_DID_PRINT_DOT;
1128 dot = mp;
1129 uncollapse1(dot, 0);
1130 NYD_LEAVE;
1131 return dot;
1134 FL int
1135 rm(char const *name)
1137 struct stat sb;
1138 int rv = -1;
1139 NYD_ENTER;
1141 if (stat(name, &sb) < 0)
1143 else if (!S_ISREG(sb.st_mode))
1144 errno = EISDIR;
1145 else
1146 rv = unlink(name);
1147 NYD_LEAVE;
1148 return rv;
1151 FL off_t
1152 fsize(FILE *iob)
1154 struct stat sbuf;
1155 off_t rv;
1156 NYD_ENTER;
1158 rv = (fstat(fileno(iob), &sbuf) == -1) ? 0 : sbuf.st_size;
1159 NYD_LEAVE;
1160 return rv;
1163 FL char *
1164 fexpand(char const *name, enum fexp_mode fexpm)
1166 char cbuf[PATH_MAX +1];
1167 char const *res;
1168 struct str s;
1169 bool_t dyn;
1170 NYD_ENTER;
1172 /* The order of evaluation is "%" and "#" expand into constants.
1173 * "&" can expand into "+". "+" can expand into shell meta characters.
1174 * Shell meta characters expand into constants.
1175 * This way, we make no recursive expansion */
1176 if ((fexpm & FEXP_NSHORTCUT) || (res = shortcut_expand(name)) == NULL)
1177 res = UNCONST(name);
1179 if (fexpm & FEXP_SHELL) {
1180 dyn = FAL0;
1181 goto jshell;
1183 jnext:
1184 dyn = FAL0;
1185 switch (*res) {
1186 case '%':
1187 if (res[1] == ':' && res[2] != '\0') {
1188 res = &res[2];
1189 goto jnext;
1191 _findmail(cbuf, sizeof cbuf, (res[1] != '\0' ? res + 1 : myname),
1192 (res[1] != '\0' || (options & OPT_u_FLAG)));
1193 res = cbuf;
1194 goto jislocal;
1195 case '#':
1196 if (res[1] != '\0')
1197 break;
1198 if (prevfile[0] == '\0') {
1199 n_err(_("No previous file\n"));
1200 res = NULL;
1201 goto jleave;
1203 res = prevfile;
1204 goto jislocal;
1205 case '&':
1206 if (res[1] == '\0') {
1207 if ((res = ok_vlook(MBOX)) == NULL)
1208 res = UNCONST("~/mbox"); /* XXX no magics (POSIX though) */
1209 else if (res[0] != '&' || res[1] != '\0')
1210 goto jnext;
1212 break;
1215 if (res[0] == '+' && getfold(cbuf, sizeof cbuf)) {
1216 size_t i = strlen(cbuf);
1218 res = str_concat_csvl(&s, cbuf,
1219 ((i > 0 && cbuf[i - 1] == '/') ? "" : "/"), res + 1, NULL)->s;
1220 dyn = TRU1;
1222 if (res[0] == '%' && res[1] == ':') {
1223 res += 2;
1224 goto jnext;
1228 /* Catch the most common shell meta character */
1229 jshell:
1230 if (res[0] == '~') {
1231 res = n_shell_expand_tilde(res, NULL);
1232 dyn = TRU1;
1234 if (anyof(res, "|&;<>{}()[]*?$`'\"\\"))
1235 switch (which_protocol(res)) {
1236 case PROTO_FILE:
1237 case PROTO_MAILDIR:
1238 res = (fexpm & FEXP_NSHELL) ? n_shell_expand_var(res, TRU1, NULL)
1239 : _globname(res, fexpm);
1240 dyn = TRU1;
1241 goto jleave;
1242 default:
1243 break;
1245 jislocal:
1246 if (fexpm & FEXP_LOCAL)
1247 switch (which_protocol(res)) {
1248 case PROTO_FILE:
1249 case PROTO_MAILDIR:
1250 break;
1251 default:
1252 n_err(_("Not a local file or directory: \"%s\"\n"), name);
1253 res = NULL;
1254 break;
1256 jleave:
1257 if (res && !dyn)
1258 res = savestr(res);
1259 NYD_LEAVE;
1260 return UNCONST(res);
1263 FL char *
1264 fexpand_nshell_quote(char const *name)
1266 size_t i, j;
1267 char *rv, c;
1268 NYD_ENTER;
1270 for (i = j = 0; (c = name[i]) != '\0'; ++i)
1271 if (c == '\\')
1272 ++j;
1274 if (j == 0)
1275 rv = savestrbuf(name, i);
1276 else {
1277 rv = salloc(i + j +1);
1278 for (i = j = 0; (c = name[i]) != '\0'; ++i) {
1279 rv[j++] = c;
1280 if (c == '\\')
1281 rv[j++] = c;
1283 rv[j] = '\0';
1285 NYD_LEAVE;
1286 return rv;
1289 FL bool_t
1290 var_folder_updated(char const *name, char **store)
1292 char rv = TRU1;
1293 char *folder, *unres = NULL, *res = NULL;
1294 NYD_ENTER;
1296 if ((folder = UNCONST(name)) == NULL)
1297 goto jleave;
1299 /* Expand the *folder*; skip %: prefix for simplicity of use */
1300 /* XXX This *only* works because we do NOT
1301 * XXX update environment variables via the "set" mechanism */
1302 if (folder[0] == '%' && folder[1] == ':')
1303 folder += 2;
1304 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
1305 goto jleave;
1307 switch (which_protocol(folder)) {
1308 case PROTO_POP3:
1309 n_err(_("*folder* cannot be set to a flat, readonly POP3 account\n"));
1310 rv = FAL0;
1311 goto jleave;
1312 default:
1313 /* Further expansion desired */
1314 break;
1317 /* All non-absolute paths are relative to our home directory */
1318 if (*folder != '/') {
1319 size_t l1 = strlen(homedir), l2 = strlen(folder);
1320 unres = ac_alloc(l1 + l2 + 1 +1);
1321 memcpy(unres, homedir, l1);
1322 unres[l1] = '/';
1323 memcpy(unres + l1 + 1, folder, l2);
1324 unres[l1 + 1 + l2] = '\0';
1325 folder = unres;
1328 /* Since lex.c:_update_mailname() uses realpath(3) if available to
1329 * avoid that we loose track of our currently open folder in case we
1330 * chdir away, but still checks the leading path portion against
1331 * getfold() to be able to abbreviate to the +FOLDER syntax if
1332 * possible, we need to realpath(3) the folder, too */
1333 #ifdef HAVE_REALPATH
1334 res = ac_alloc(PATH_MAX +1);
1335 if (realpath(folder, res) == NULL)
1336 n_err(_("Can't canonicalize \"%s\"\n"), folder);
1337 else
1338 folder = res;
1339 #endif
1341 *store = sstrdup(folder);
1343 if (res != NULL)
1344 ac_free(res);
1345 if (unres != NULL)
1346 ac_free(unres);
1347 jleave:
1348 NYD_LEAVE;
1349 return rv;
1352 FL bool_t
1353 getfold(char *name, size_t size)
1355 char const *folder;
1356 NYD_ENTER;
1358 if ((folder = ok_vlook(folder)) != NULL)
1359 n_strscpy(name, folder, size);
1360 NYD_LEAVE;
1361 return (folder != NULL);
1364 FL char const *
1365 getdeadletter(void) /* XXX should that be in auxlily.c? */
1367 char const *cp;
1368 NYD_ENTER;
1370 if ((cp = ok_vlook(DEAD)) == NULL || (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
1371 cp = fexpand("~/dead.letter", FEXP_LOCAL | FEXP_SHELL);
1372 else if (*cp != '/') {
1373 size_t sz = strlen(cp) + 2 +1;
1374 char *buf = ac_alloc(sz);
1376 snprintf(buf, sz, "~/%s", cp);
1377 cp = fexpand(buf, FEXP_LOCAL | FEXP_SHELL);
1378 ac_free(buf);
1381 if (cp == NULL)
1382 cp = "dead.letter"; /* XXX magic -> nail.h (POSIX thing though) */
1383 NYD_LEAVE;
1384 return cp;
1387 FL enum okay
1388 get_body(struct message *mp)
1390 enum okay rv;
1391 NYD_ENTER;
1392 UNUSED(mp);
1394 switch (mb.mb_type) {
1395 case MB_FILE:
1396 case MB_MAILDIR:
1397 rv = OKAY;
1398 break;
1399 #ifdef HAVE_POP3
1400 case MB_POP3:
1401 rv = pop3_body(mp);
1402 break;
1403 #endif
1404 case MB_VOID:
1405 default:
1406 rv = STOP;
1407 break;
1409 NYD_LEAVE;
1410 return rv;
1413 FL bool_t
1414 file_lock(int fd, enum file_lock_type flt, off_t off, off_t len,
1415 size_t pollmsecs)
1417 size_t tries;
1418 bool_t rv;
1419 NYD_ENTER;
1421 for (tries = 0; tries <= FILE_LOCK_TRIES; ++tries)
1422 if ((rv = _file_lock(fd, flt, off, len)) || pollmsecs == 0)
1423 break;
1424 else
1425 n_msleep(pollmsecs, FAL0);
1426 NYD_LEAVE;
1427 return rv;
1430 FL FILE *
1431 dot_lock(char const *fname, int fd, enum file_lock_type flt,
1432 off_t off, off_t len, size_t pollmsecs)
1434 #undef _DOMSG
1435 #ifdef HAVE_DOTLOCK
1436 # define _DOMSG() n_err(_("Creating dotlock for \"%s\" "), fname)
1437 #else
1438 # define _DOMSG() n_err(_("Trying to lock file \"%s\" "), fname)
1439 #endif
1441 #ifdef HAVE_DOTLOCK
1442 int cpipe[2];
1443 struct dotlock_info di;
1444 enum dotlock_state dls;
1445 char const *emsg = NULL;
1446 #endif
1447 int UNINIT(serrno, 0);
1448 union {size_t tries; int (*ptf)(void); char const *sh; ssize_t r;} u;
1449 bool_t flocked, didmsg = FAL0;
1450 FILE *rv = NULL;
1451 NYD_ENTER;
1453 if (options & OPT_D_VV) {
1454 _DOMSG();
1455 didmsg = TRUM1;
1458 flocked = FAL0;
1459 for (u.tries = 0; !_file_lock(fd, flt, off, len);)
1460 switch ((serrno = errno)) {
1461 case EACCES:
1462 case EAGAIN:
1463 case ENOLCK:
1464 if (pollmsecs > 0 && ++u.tries < FILE_LOCK_TRIES) {
1465 if (!didmsg)
1466 _DOMSG();
1467 n_err(".");
1468 didmsg = TRUM1;
1469 n_msleep(pollmsecs, FAL0);
1470 continue;
1472 /* FALLTHRU */
1473 default:
1474 goto jleave;
1476 flocked = TRU1;
1478 #ifndef HAVE_DOTLOCK
1479 jleave:
1480 if (didmsg == TRUM1)
1481 n_err("\n");
1482 if (flocked)
1483 rv = (FILE*)-1;
1484 else
1485 errno = serrno;
1486 NYD_LEAVE;
1487 return rv;
1489 #else
1490 /* Create control-pipe for our dot file locker process, which will remove
1491 * the lock and terminate once the pipe is closed, for whatever reason */
1492 if (pipe_cloexec(cpipe) == -1) {
1493 serrno = errno;
1494 emsg = N_(" Can't create file lock control pipe\n");
1495 goto jemsg;
1498 /* And the locker process itself; it'll be a (rather cheap) thread only
1499 * unless the lock has to be placed in the system spool and we have our
1500 * privilege-separated dotlock program available, in which case that will be
1501 * executed and do "it" with changed group-id */
1502 di.di_file_name = fname;
1503 di.di_pollmsecs = pollmsecs;
1504 /* Initialize some more stuff; query the two strings in the parent in order
1505 * to cache the result of the former and anyway minimalize child page-ins.
1506 * Especially uname(3) may hang for multiple seconds when it is called the
1507 * first time! */
1508 di.di_hostname = nodename(FAL0);
1509 di.di_randstr = getrandstring(16);
1510 _dotlock_flt = flt;
1511 _dotlock_fd = fd;
1512 _dotlock_dip = &di;
1514 u.ptf = &_dotlock_main;
1515 rv = Popen((char*)-1, "W", u.sh, NULL, cpipe[1]);
1516 serrno = errno;
1518 close(cpipe[1]);
1519 if (rv == NULL) {
1520 close(cpipe[0]);
1521 emsg = N_(" Can't create file lock process\n");
1522 goto jemsg;
1525 /* Let's check wether we were able to create the dotlock file */
1526 for (;;) {
1527 u.r = read(cpipe[0], &dls, sizeof dls);
1528 if (UICMP(z, u.r, !=, sizeof dls)) {
1529 serrno = (u.r != -1) ? EAGAIN : errno;
1530 dls = DLS_DUNNO | DLS_ABANDON;
1531 } else
1532 serrno = 0;
1534 if (dls == DLS_NONE || (dls & DLS_ABANDON))
1535 close(cpipe[0]);
1537 switch (dls & ~DLS_ABANDON) {
1538 case DLS_NONE:
1539 goto jleave;
1540 case DLS_CANT_CHDIR:
1541 if (options & OPT_D_V)
1542 emsg = N_(" Can't change directory! Please check permissions\n");
1543 serrno = EACCES;
1544 break;
1545 case DLS_NAMETOOLONG:
1546 emsg = N_("Resulting dotlock filename would be too long\n");
1547 serrno = EACCES;
1548 break;
1549 case DLS_ROFS:
1550 assert(dls & DLS_ABANDON);
1551 if (options & OPT_D_V)
1552 emsg = N_(" Read-only filesystem, not creating lock file\n");
1553 serrno = EROFS;
1554 break;
1555 case DLS_NOPERM:
1556 if (options & OPT_D_V)
1557 emsg = N_(" Can't create a lock file! Please check permissions\n"
1558 " (Maybe setting *dotlock-ignore-error* variable helps.)\n");
1559 serrno = EACCES;
1560 break;
1561 case DLS_NOEXEC:
1562 if (options & OPT_D_V)
1563 emsg = N_(" Can't find privilege-separated file lock program\n");
1564 serrno = ENOENT;
1565 break;
1566 case DLS_PRIVFAILED:
1567 emsg = N_(" Privilege-separated file lock program can't change "
1568 "privileges\n");
1569 serrno = EPERM;
1570 break;
1571 case DLS_EXIST:
1572 emsg = N_(" It seems there is a stale dotlock file?\n"
1573 " Please remove the lock file manually, then retry\n");
1574 serrno = EEXIST;
1575 break;
1576 case DLS_FISHY:
1577 emsg = N_(" Fishy! Is someone trying to \"steal\" foreign files?\n"
1578 " Please check the mailbox file etc. manually, then retry\n");
1579 serrno = EAGAIN; /* ? Hack to ignore *dotlock-ignore-error* xxx */
1580 break;
1581 default:
1582 case DLS_DUNNO:
1583 emsg = N_(" Unspecified dotlock file control process error.\n"
1584 " Like broken I/O pipe; this one is unlikely to happen\n");
1585 if (serrno != EAGAIN)
1586 serrno = EINVAL;
1587 break;
1588 case DLS_PING:
1589 if (!didmsg)
1590 _DOMSG();
1591 n_err(".");
1592 didmsg = TRUM1;
1593 continue;
1596 if (emsg != NULL) {
1597 if (!didmsg) {
1598 _DOMSG();
1599 didmsg = TRUM1;
1601 if (didmsg == TRUM1)
1602 n_err("\n");
1603 didmsg = TRU1;
1604 n_err(V_(emsg));
1605 emsg = NULL;
1608 if (dls & DLS_ABANDON) {
1609 Pclose(rv, FAL0);
1610 rv = NULL;
1611 break;
1615 jleave:
1616 if (didmsg == TRUM1)
1617 n_err("\n");
1618 if (rv == NULL) {
1619 if (flocked && (serrno == EROFS ||
1620 (serrno != EAGAIN && serrno != EEXIST &&
1621 ok_blook(dotlock_ignore_error))))
1622 rv = (FILE*)-1;
1623 else
1624 errno = serrno;
1626 NYD_LEAVE;
1627 return rv;
1628 jemsg:
1629 if (!didmsg)
1630 _DOMSG();
1631 n_err("\n");
1632 didmsg = TRU1;
1633 n_err(V_(emsg));
1634 goto jleave;
1635 #endif /* HAVE_DOTLOCK */
1636 #undef _DOMSG
1639 #ifdef HAVE_SOCKETS
1640 FL int
1641 sclose(struct sock *sp)
1643 int i;
1644 NYD_ENTER;
1646 i = sp->s_fd;
1647 sp->s_fd = -1;
1648 /* TODO NOTE: we MUST NOT close the descriptor 0 here...
1649 * TODO of course this should be handled in a VMAILFS->open() .s_fd=-1,
1650 * TODO but unfortunately it isn't yet */
1651 if (i <= 0)
1652 i = 0;
1653 else {
1654 if (sp->s_onclose != NULL)
1655 (*sp->s_onclose)();
1656 if (sp->s_wbuf != NULL)
1657 free(sp->s_wbuf);
1658 # ifdef HAVE_OPENSSL
1659 if (sp->s_use_ssl) {
1660 void *s_ssl = sp->s_ssl;
1662 sp->s_ssl = NULL;
1663 sp->s_use_ssl = 0;
1664 while (!SSL_shutdown(s_ssl)) /* XXX proper error handling;signals! */
1666 SSL_free(s_ssl);
1668 # endif
1669 i = close(i);
1671 NYD_LEAVE;
1672 return i;
1675 FL enum okay
1676 swrite(struct sock *sp, char const *data)
1678 enum okay rv;
1679 NYD2_ENTER;
1681 rv = swrite1(sp, data, strlen(data), 0);
1682 NYD2_LEAVE;
1683 return rv;
1686 FL enum okay
1687 swrite1(struct sock *sp, char const *data, int sz, int use_buffer)
1689 enum okay rv = STOP;
1690 int x;
1691 NYD2_ENTER;
1693 if (use_buffer > 0) {
1694 int di;
1696 if (sp->s_wbuf == NULL) {
1697 sp->s_wbufsize = 4096;
1698 sp->s_wbuf = smalloc(sp->s_wbufsize);
1699 sp->s_wbufpos = 0;
1701 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
1702 di = sp->s_wbufsize - sp->s_wbufpos;
1703 sz -= di;
1704 if (sp->s_wbufpos > 0) {
1705 memcpy(sp->s_wbuf + sp->s_wbufpos, data, di);
1706 rv = swrite1(sp, sp->s_wbuf, sp->s_wbufsize, -1);
1707 } else
1708 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1709 if (rv != OKAY)
1710 goto jleave;
1711 data += di;
1712 sp->s_wbufpos = 0;
1714 if (sz == sp->s_wbufsize) {
1715 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1716 if (rv != OKAY)
1717 goto jleave;
1718 } else if (sz) {
1719 memcpy(sp->s_wbuf+ sp->s_wbufpos, data, sz);
1720 sp->s_wbufpos += sz;
1722 rv = OKAY;
1723 goto jleave;
1724 } else if (use_buffer == 0 && sp->s_wbuf != NULL && sp->s_wbufpos > 0) {
1725 x = sp->s_wbufpos;
1726 sp->s_wbufpos = 0;
1727 if ((rv = swrite1(sp, sp->s_wbuf, x, -1)) != OKAY)
1728 goto jleave;
1730 if (sz == 0) {
1731 rv = OKAY;
1732 goto jleave;
1735 # ifdef HAVE_OPENSSL
1736 if (sp->s_use_ssl) {
1737 jssl_retry:
1738 x = SSL_write(sp->s_ssl, data, sz);
1739 if (x < 0) {
1740 switch (SSL_get_error(sp->s_ssl, x)) {
1741 case SSL_ERROR_WANT_READ:
1742 case SSL_ERROR_WANT_WRITE:
1743 goto jssl_retry;
1746 } else
1747 # endif
1749 x = xwrite(sp->s_fd, data, sz);
1751 if (x != sz) {
1752 char o[512];
1753 snprintf(o, sizeof o, "%s write error",
1754 (sp->s_desc ? sp->s_desc : "socket"));
1755 # ifdef HAVE_OPENSSL
1756 if (sp->s_use_ssl)
1757 ssl_gen_err("%s", o);
1758 else
1759 # endif
1760 n_perr(o, 0);
1761 if (x < 0)
1762 sclose(sp);
1763 rv = STOP;
1764 goto jleave;
1766 rv = OKAY;
1767 jleave:
1768 NYD2_LEAVE;
1769 return rv;
1772 static sigjmp_buf __sopen_actjmp; /* TODO someday, we won't need it no more */
1773 static int __sopen_sig; /* TODO someday, we won't need it no more */
1774 static void
1775 __sopen_onsig(int sig) /* TODO someday, we won't need it no more */
1777 NYD_X; /* Signal handler */
1778 if (__sopen_sig < 0) {
1779 /* Of course the following doesn't belong into a signal handler XXX */
1780 int i, j;
1782 if (__sopen_sig == -1) {
1783 fprintf(stderr,
1784 _("\nInterrupting it could turn the (GNU/Linux+) DNS resolver "
1785 "unusable.\n"
1786 " Wait until it's done, or do terminate the program\n"));
1787 __sopen_sig = -2;
1788 } else if ((i = j = ABS(__sopen_sig)) + 15 < scrnwidth) {
1789 putc('\r', stderr);
1790 for (; j > 0; --j)
1791 putc(' ', stderr);
1792 fputs("___( o)", stderr);
1793 putc((i & 1) ? '=' : '>', stderr);
1794 putc(' ', stderr);
1795 putc(' ', stderr);
1796 ++i;
1797 __sopen_sig = -i;
1799 } else {
1800 __sopen_sig = sig;
1801 siglongjmp(__sopen_actjmp, 1);
1805 FL bool_t
1806 sopen(struct sock *sp, struct url *urlp) /* TODO sighandling; refactor */
1808 # ifdef HAVE_SO_SNDTIMEO
1809 struct timeval tv;
1810 # endif
1811 # ifdef HAVE_SO_LINGER
1812 struct linger li;
1813 # endif
1814 # ifdef HAVE_GETADDRINFO
1815 char hbuf[NI_MAXHOST];
1816 struct addrinfo hints, *res0 = NULL, *res;
1817 # else
1818 struct sockaddr_in servaddr;
1819 struct in_addr **pptr;
1820 struct hostent *hp;
1821 struct servent *ep;
1822 # endif
1823 sighandler_type volatile ohup, oint;
1824 char const * volatile serv;
1825 int volatile sofd = -1, errval;
1826 NYD_ENTER;
1828 UNINIT(errval, 0);
1830 /* Connect timeouts after 30 seconds XXX configurable */
1831 # ifdef HAVE_SO_SNDTIMEO
1832 tv.tv_sec = 30;
1833 tv.tv_usec = 0;
1834 # endif
1835 serv = (urlp->url_port != NULL) ? urlp->url_port : urlp->url_proto;
1837 if (options & OPT_VERB)
1838 n_err(_("Resolving host \"%s:%s\" ... "),
1839 urlp->url_host.s, serv);
1841 /* Signal handling (in respect to __sopen_sig dealing) is heavy, but no
1842 * healing until v15.0 and i want to end up with that functionality */
1843 hold_sigs();
1844 __sopen_sig = 0;
1845 ohup = safe_signal(SIGHUP, &__sopen_onsig);
1846 oint = safe_signal(SIGINT, &__sopen_onsig);
1847 if (sigsetjmp(__sopen_actjmp, 0)) {
1848 jpseudo_jump:
1849 n_err("%s\n",
1850 (__sopen_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
1851 if (sofd >= 0) {
1852 close(sofd);
1853 sofd = -1;
1855 goto jjumped;
1857 rele_sigs();
1859 # ifdef HAVE_GETADDRINFO
1860 for (;;) {
1861 memset(&hints, 0, sizeof hints);
1862 hints.ai_socktype = SOCK_STREAM;
1863 __sopen_sig = -1;
1864 errval = getaddrinfo(urlp->url_host.s, serv, &hints, &res0);
1865 if (__sopen_sig != -1) {
1866 __sopen_sig = SIGINT;
1867 goto jpseudo_jump;
1869 __sopen_sig = 0;
1870 if (errval == 0)
1871 break;
1873 if (options & OPT_VERB)
1874 n_err(_("failed\n"));
1875 n_err(_("Lookup of \"%s:%s\" failed: %s\n"),
1876 urlp->url_host.s, serv, gai_strerror(errval));
1878 /* Error seems to depend on how "smart" the /etc/service code is: is it
1879 * "able" to state wether the service as such is NONAME or does it only
1880 * check for the given ai_socktype.. */
1881 if (errval == EAI_NONAME || errval == EAI_SERVICE) {
1882 if (serv == urlp->url_proto &&
1883 (serv = url_servbyname(urlp, NULL)) != NULL) {
1884 n_err(_(" Trying standard protocol port \"%s\"\n"
1885 " If that succeeds consider including the port in the URL!\n"),
1886 serv);
1887 continue;
1889 if (serv != urlp->url_port)
1890 n_err(_(" Including a port number in the URL may "
1891 "circumvent this problem\n"));
1893 assert(sofd == -1);
1894 errval = 0;
1895 goto jjumped;
1897 if (options & OPT_VERB)
1898 n_err(_("done\n"));
1900 for (res = res0; res != NULL && sofd < 0; res = res->ai_next) {
1901 if (options & OPT_VERB) {
1902 if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof hbuf,
1903 NULL, 0, NI_NUMERICHOST))
1904 memcpy(hbuf, "unknown host", sizeof("unknown host"));
1905 n_err(_("%sConnecting to \"%s:%s\" ..."),
1906 (res == res0 ? "" : "\n"), hbuf, serv);
1909 sofd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1910 if (sofd >= 0) {
1911 # ifdef HAVE_SO_SNDTIMEO
1912 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1913 # endif
1914 if (connect(sofd, res->ai_addr, res->ai_addrlen)) {
1915 errval = errno;
1916 close(sofd);
1917 sofd = -1;
1922 jjumped:
1923 if (res0 != NULL) {
1924 freeaddrinfo(res0);
1925 res0 = NULL;
1928 # else /* HAVE_GETADDRINFO */
1929 if (serv == urlp->url_proto) {
1930 if ((ep = getservbyname(UNCONST(serv), "tcp")) != NULL)
1931 urlp->url_portno = ntohs(ep->s_port);
1932 else {
1933 if (options & OPT_VERB)
1934 n_err(_("failed\n"));
1935 if ((serv = url_servbyname(urlp, &urlp->url_portno)) != NULL)
1936 n_err(_(" Unknown service: \"%s\"\n"
1937 " Trying standard protocol port \"%s\"\n"
1938 " If that succeeds consider including the port in the URL!\n"),
1939 urlp->url_proto, serv);
1940 else {
1941 n_err(_(" Unknown service: \"%s\"\n"
1942 " Including a port in the URL may circumvent this problem\n"),
1943 urlp->url_proto);
1944 assert(sofd == -1 && errval == 0);
1945 goto jjumped;
1950 __sopen_sig = -1;
1951 hp = gethostbyname(urlp->url_host.s);
1952 if (__sopen_sig != -1) {
1953 __sopen_sig = SIGINT;
1954 goto jpseudo_jump;
1956 __sopen_sig = 0;
1958 if (hp == NULL) {
1959 char const *emsg;
1961 if (options & OPT_VERB)
1962 n_err(_("failed\n"));
1963 switch (h_errno) {
1964 case HOST_NOT_FOUND: emsg = N_("host not found"); break;
1965 default:
1966 case TRY_AGAIN: emsg = N_("(maybe) try again later"); break;
1967 case NO_RECOVERY: emsg = N_("non-recoverable server error"); break;
1968 case NO_DATA: emsg = N_("valid name without IP address"); break;
1970 n_err(_("Lookup of \"%s:%s\" failed: %s\n"),
1971 urlp->url_host.s, serv, V_(emsg));
1972 goto jjumped;
1973 } else if (options & OPT_VERB)
1974 n_err(_("done\n"));
1976 pptr = (struct in_addr**)hp->h_addr_list;
1977 if ((sofd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1978 n_perr(_("could not create socket"), 0);
1979 assert(sofd == -1 && errval == 0);
1980 goto jjumped;
1983 memset(&servaddr, 0, sizeof servaddr);
1984 servaddr.sin_family = AF_INET;
1985 servaddr.sin_port = htons(urlp->url_portno);
1986 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1987 if (options & OPT_VERB)
1988 n_err(_("%sConnecting to \"%s:%d\" ... "),
1989 "", inet_ntoa(**pptr), (int)urlp->url_portno);
1990 # ifdef HAVE_SO_SNDTIMEO
1991 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1992 # endif
1993 if (connect(sofd, (struct sockaddr*)&servaddr, sizeof servaddr)) {
1994 errval = errno;
1995 close(sofd);
1996 sofd = -1;
1998 jjumped:
1999 # endif /* !HAVE_GETADDRINFO */
2001 hold_sigs();
2002 safe_signal(SIGINT, oint);
2003 safe_signal(SIGHUP, ohup);
2004 rele_sigs();
2006 if (sofd < 0) {
2007 if (errval != 0) {
2008 errno = errval;
2009 n_perr(_("Could not connect"), 0);
2011 goto jleave;
2014 if (options & OPT_VERB)
2015 n_err(_("connected.\n"));
2017 /* And the regular timeouts XXX configurable */
2018 # ifdef HAVE_SO_SNDTIMEO
2019 tv.tv_sec = 42;
2020 tv.tv_usec = 0;
2021 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
2022 (void)setsockopt(sofd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
2023 # endif
2024 # ifdef HAVE_SO_LINGER
2025 li.l_onoff = 1;
2026 li.l_linger = 42;
2027 (void)setsockopt(sofd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
2028 # endif
2030 memset(sp, 0, sizeof *sp);
2031 sp->s_fd = sofd;
2033 /* SSL/TLS upgrade? */
2034 # ifdef HAVE_SSL
2035 if (urlp->url_needs_tls) {
2036 hold_sigs();
2037 ohup = safe_signal(SIGHUP, &__sopen_onsig);
2038 oint = safe_signal(SIGINT, &__sopen_onsig);
2039 if (sigsetjmp(__sopen_actjmp, 0)) {
2040 n_err(_("%s during SSL/TLS handshake\n"),
2041 (__sopen_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
2042 goto jsclose;
2044 rele_sigs();
2046 if (ssl_open(urlp, sp) != OKAY) {
2047 jsclose:
2048 sclose(sp);
2049 sofd = -1;
2052 hold_sigs();
2053 safe_signal(SIGINT, oint);
2054 safe_signal(SIGHUP, ohup);
2055 rele_sigs();
2057 # endif /* HAVE_SSL */
2059 jleave:
2060 /* May need to bounce the signal to the lex.c trampoline (or wherever) */
2061 if (__sopen_sig != 0) {
2062 sigset_t cset;
2063 sigemptyset(&cset);
2064 sigaddset(&cset, __sopen_sig);
2065 sigprocmask(SIG_UNBLOCK, &cset, NULL);
2066 n_raise(__sopen_sig);
2068 NYD_LEAVE;
2069 return (sofd >= 0);
2072 FL int
2073 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
2074 SMALLOC_DEBUG_ARGS)
2076 int rv;
2077 size_t lsize;
2078 char *lp_base, *lp;
2079 NYD2_ENTER;
2081 lsize = *linesize;
2082 lp_base = *line;
2083 lp = lp_base;
2085 if (sp->s_rsz < 0) {
2086 sclose(sp);
2087 rv = sp->s_rsz;
2088 goto jleave;
2091 do {
2092 if (lp_base == NULL || PTRCMP(lp, >, lp_base + lsize - 128)) {
2093 size_t diff = PTR2SIZE(lp - lp_base);
2094 *linesize = (lsize += 256); /* XXX magic */
2095 *line = lp_base = (srealloc)(lp_base, lsize SMALLOC_DEBUG_ARGSCALL);
2096 lp = lp_base + diff;
2099 if (sp->s_rbufptr == NULL ||
2100 PTRCMP(sp->s_rbufptr, >=, sp->s_rbuf + sp->s_rsz)) {
2101 # ifdef HAVE_OPENSSL
2102 if (sp->s_use_ssl) {
2103 jssl_retry:
2104 sp->s_rsz = SSL_read(sp->s_ssl, sp->s_rbuf, sizeof sp->s_rbuf);
2105 if (sp->s_rsz <= 0) {
2106 if (sp->s_rsz < 0) {
2107 char o[512];
2108 switch(SSL_get_error(sp->s_ssl, sp->s_rsz)) {
2109 case SSL_ERROR_WANT_READ:
2110 case SSL_ERROR_WANT_WRITE:
2111 goto jssl_retry;
2113 snprintf(o, sizeof o, "%s",
2114 (sp->s_desc ? sp->s_desc : "socket"));
2115 ssl_gen_err("%s", o);
2117 break;
2119 } else
2120 # endif
2122 jagain:
2123 sp->s_rsz = read(sp->s_fd, sp->s_rbuf, sizeof sp->s_rbuf);
2124 if (sp->s_rsz <= 0) {
2125 if (sp->s_rsz < 0) {
2126 char o[512];
2127 if (errno == EINTR)
2128 goto jagain;
2129 snprintf(o, sizeof o, "%s",
2130 (sp->s_desc ? sp->s_desc : "socket"));
2131 n_perr(o, 0);
2133 break;
2136 sp->s_rbufptr = sp->s_rbuf;
2138 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
2139 *lp = '\0';
2140 lsize = PTR2SIZE(lp - lp_base);
2142 if (linelen)
2143 *linelen = lsize;
2144 rv = (int)lsize;
2145 jleave:
2146 NYD2_LEAVE;
2147 return rv;
2149 #endif /* HAVE_SOCKETS */
2151 FL void
2152 load(char const *name)
2154 struct str n;
2155 void *cond;
2156 FILE *in, *oldin;
2157 NYD_ENTER;
2159 if (name == NULL || *name == '\0' || (in = Fopen(name, "r")) == NULL)
2160 goto jleave;
2162 oldin = _fio_input;
2163 _fio_input = in;
2164 pstate |= PS_IN_LOAD;
2165 /* commands() may sreset(), copy over file name */
2166 n.l = strlen(name);
2167 n.s = ac_alloc(n.l +1);
2168 memcpy(n.s, name, n.l +1);
2170 cond = condstack_release();
2171 if (!commands())
2172 n_err(_("Stopped loading \"%s\" due to errors "
2173 "(enable *debug* for trace)\n"), n.s);
2174 condstack_take(cond);
2176 ac_free(n.s);
2177 pstate &= ~PS_IN_LOAD;
2178 _fio_input = oldin;
2179 Fclose(in);
2180 jleave:
2181 NYD_LEAVE;
2184 FL int
2185 c_source(void *v)
2187 int rv;
2188 NYD_ENTER;
2190 rv = _source_file(*(char**)v, FAL0) ? 0 : 1;
2191 NYD_LEAVE;
2192 return rv;
2195 FL int
2196 c_source_if(void *v) /* XXX obsolete?, support file tests in `if' etc.! */
2198 int rv;
2199 NYD_ENTER;
2201 rv = _source_file(*(char**)v, TRU1) ? 0 : 1;
2202 rv = 0;
2203 NYD_LEAVE;
2204 return rv;
2207 FL int
2208 unstack(void)
2210 int rv = 1;
2211 NYD_ENTER;
2213 if (_fio_stack_size == 0) {
2214 n_err(_("`source' stack over-pop\n"));
2215 pstate &= ~PS_SOURCING;
2216 goto jleave;
2219 if (pstate & PS_PIPING)
2220 Pclose(_fio_input, TRU1);
2221 else
2222 Fclose(_fio_input);
2224 --_fio_stack_size;
2225 if (!condstack_take(_fio_stack[_fio_stack_size].s_cond))
2226 n_err(_("Unmatched \"if\"\n"));
2227 pstate &= ~(PS_LOADING | PS_PIPING);
2228 pstate |= _fio_stack[_fio_stack_size].s_pstate & (PS_LOADING | PS_PIPING);
2229 _fio_input = _fio_stack[_fio_stack_size].s_file;
2230 if (_fio_stack_size == 0) {
2231 if (pstate & PS_LOADING)
2232 pstate |= PS_SOURCING;
2233 else
2234 pstate &= ~PS_SOURCING;
2236 rv = 0;
2237 jleave:
2238 NYD_LEAVE;
2239 return rv;
2242 /* s-it-mode */