nail.1, COPYING: remove Open Group reference (no text included?)
[s-mailx.git] / fio.c
blob7f3e056eca2d82667892c58c280e8beb887b890c
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 int s_loading; /* Loading .mailrc, etc. */
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' */
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) {
138 switch (which_protocol(cp)) {
139 case PROTO_IMAP:
140 if (strcmp(cp, protbase(cp)))
141 goto jcopy;
142 snprintf(buf, bufsize, "%s/INBOX", cp);
143 goto jleave;
144 default:
145 break;
149 if (force || (cp = ok_vlook(MAIL)) == NULL)
150 snprintf(buf, bufsize, "%s/%s", MAILSPOOL, user);
151 else
152 jcopy:
153 n_strlcpy(buf, cp, bufsize);
154 jleave:
155 NYD_LEAVE;
158 static char *
159 _globname(char const *name, enum fexp_mode fexpm)
161 #ifdef HAVE_WORDEXP
162 wordexp_t we;
163 char *cp = NULL;
164 sigset_t nset;
165 int i;
166 NYD_ENTER;
168 /* Mac OS X Snow Leopard and Linux don't init fields on error, causing
169 * SIGSEGV in wordfree(3); so let's just always zero it ourselfs */
170 memset(&we, 0, sizeof we);
172 /* Some systems (notably Open UNIX 8.0.0) fork a shell for wordexp()
173 * and wait, which will fail if our SIGCHLD handler is active */
174 sigemptyset(&nset);
175 sigaddset(&nset, SIGCHLD);
176 sigprocmask(SIG_BLOCK, &nset, NULL);
177 # ifndef WRDE_NOCMD
178 # define WRDE_NOCMD 0
179 # endif
180 i = wordexp(name, &we, WRDE_NOCMD);
181 sigprocmask(SIG_UNBLOCK, &nset, NULL);
183 switch (i) {
184 case 0:
185 break;
186 #ifdef WRDE_CMDSUB
187 case WRDE_CMDSUB:
188 if (!(fexpm & FEXP_SILENT))
189 n_err(_("\"%s\": Command substitution not allowed\n"), name);
190 goto jleave;
191 #endif
192 case WRDE_NOSPACE:
193 if (!(fexpm & FEXP_SILENT))
194 n_err(_("\"%s\": Expansion buffer overflow\n"), name);
195 goto jleave;
196 case WRDE_BADCHAR:
197 case WRDE_SYNTAX:
198 default:
199 if (!(fexpm & FEXP_SILENT))
200 n_err(_("Syntax error in \"%s\"\n"), name);
201 goto jleave;
204 switch (we.we_wordc) {
205 case 1:
206 cp = savestr(we.we_wordv[0]);
207 break;
208 case 0:
209 if (!(fexpm & FEXP_SILENT))
210 n_err(_("\"%s\": No match\n"), name);
211 break;
212 default:
213 if (fexpm & FEXP_MULTIOK) {
214 size_t j, l;
216 for (l = 0, j = 0; j < we.we_wordc; ++j)
217 l += strlen(we.we_wordv[j]) + 1;
218 ++l;
219 cp = salloc(l);
220 for (l = 0, j = 0; j < we.we_wordc; ++j) {
221 size_t x = strlen(we.we_wordv[j]);
222 memcpy(cp + l, we.we_wordv[j], x);
223 l += x;
224 cp[l++] = ' ';
226 cp[l] = '\0';
227 } else if (!(fexpm & FEXP_SILENT))
228 n_err(_("\"%s\": Ambiguous\n"), name);
229 break;
231 jleave:
232 wordfree(&we);
233 NYD_LEAVE;
234 return cp;
236 #else /* HAVE_WORDEXP */
237 struct stat sbuf;
238 char xname[PATH_MAX +1], cmdbuf[PATH_MAX +1], /* also used for files */
239 *shellp, *cp = NULL;
240 int pivec[2], pid, l, waits;
241 NYD_ENTER;
243 if (pipe(pivec) < 0) {
244 n_perr(_("pipe"), 0);
245 goto jleave;
247 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
248 if ((shellp = ok_vlook(SHELL)) == NULL)
249 shellp = UNCONST(XSHELL);
250 pid = start_command(shellp, NULL, COMMAND_FD_NULL, pivec[1],
251 "-c", cmdbuf, NULL, NULL);
252 if (pid < 0) {
253 close(pivec[0]);
254 close(pivec[1]);
255 goto jleave;
257 close(pivec[1]);
259 jagain:
260 l = read(pivec[0], xname, sizeof xname);
261 if (l < 0) {
262 if (errno == EINTR)
263 goto jagain;
264 n_perr(_("read"), 0);
265 close(pivec[0]);
266 goto jleave;
268 close(pivec[0]);
269 if (!wait_child(pid, &waits) && WTERMSIG(waits) != SIGPIPE) {
270 if (!(fexpm & FEXP_SILENT))
271 n_err(_("\"%s\": Expansion failed\n"), name);
272 goto jleave;
274 if (l == 0) {
275 if (!(fexpm & FEXP_SILENT))
276 n_err(_("\"%s\": No match\n"), name);
277 goto jleave;
279 if (l == sizeof xname) {
280 if (!(fexpm & FEXP_SILENT))
281 n_err(_("\"%s\": Expansion buffer overflow\n"), name);
282 goto jleave;
284 xname[l] = 0;
285 for (cp = xname + l - 1; *cp == '\n' && cp > xname; --cp)
287 cp[1] = '\0';
288 if (!(fexpm & FEXP_MULTIOK) && strchr(xname, ' ') != NULL &&
289 stat(xname, &sbuf) < 0) {
290 if (!(fexpm & FEXP_SILENT))
291 n_err(_("\"%s\": Ambiguous\n"), name);
292 cp = NULL;
293 goto jleave;
295 cp = savestr(xname);
296 jleave:
297 NYD_LEAVE;
298 return cp;
299 #endif /* !HAVE_WORDEXP */
302 static size_t
303 _length_of_line(char const *line, size_t linesize)
305 size_t i;
306 NYD2_ENTER;
308 /* Last character is always '\0' and was added by fgets() */
309 for (--linesize, i = 0; i < linesize; i++)
310 if (line[i] == '\n')
311 break;
312 i = (i < linesize) ? i + 1 : linesize;
313 NYD2_LEAVE;
314 return i;
317 static char *
318 _fgetline_byone(char **line, size_t *linesize, size_t *llen, FILE *fp,
319 int appendnl, size_t n SMALLOC_DEBUG_ARGS)
321 char *rv;
322 int c;
323 NYD2_ENTER;
325 assert(*linesize == 0 || *line != NULL);
326 for (rv = *line;;) {
327 if (*linesize <= LINESIZE || n >= *linesize - 128) {
328 *linesize += ((rv == NULL) ? LINESIZE + n + 1 : 256);
329 *line = rv = (srealloc)(rv, *linesize SMALLOC_DEBUG_ARGSCALL);
331 c = getc(fp);
332 if (c != EOF) {
333 rv[n++] = c;
334 rv[n] = '\0';
335 if (c == '\n')
336 break;
337 } else {
338 if (n > 0) {
339 if (appendnl) {
340 rv[n++] = '\n';
341 rv[n] = '\0';
343 break;
344 } else {
345 rv = NULL;
346 goto jleave;
350 if (llen)
351 *llen = n;
352 jleave:
353 NYD2_LEAVE;
354 return rv;
357 static void
358 makemessage(void)
360 NYD_ENTER;
361 if (msgCount == 0)
362 message_append(NULL);
363 setdot(message);
364 message[msgCount].m_size = 0;
365 message[msgCount].m_lines = 0;
366 NYD_LEAVE;
369 static enum okay
370 get_header(struct message *mp)
372 enum okay rv;
373 NYD_ENTER;
374 UNUSED(mp);
376 switch (mb.mb_type) {
377 case MB_FILE:
378 case MB_MAILDIR:
379 rv = OKAY;
380 break;
381 #ifdef HAVE_POP3
382 case MB_POP3:
383 rv = pop3_header(mp);
384 break;
385 #endif
386 #ifdef HAVE_IMAP
387 case MB_IMAP:
388 case MB_CACHE:
389 rv = imap_header(mp);
390 break;
391 #endif
392 case MB_VOID:
393 default:
394 rv = STOP;
395 break;
397 NYD_LEAVE;
398 return rv;
401 static bool_t
402 _file_lock(int fd, enum file_lock_type flt, off_t off, off_t len)
404 struct flock flp;
405 bool_t rv;
406 NYD2_ENTER;
408 memset(&flp, 0, sizeof flp);
410 switch (flt) {
411 default:
412 case FLT_READ: rv = F_RDLCK; break;
413 case FLT_WRITE: rv = F_WRLCK; break;
415 flp.l_type = rv;
416 flp.l_start = off;
417 flp.l_whence = SEEK_SET;
418 flp.l_len = len;
420 rv = (fcntl(fd, F_SETLK, &flp) != -1);
421 NYD2_LEAVE;
422 return rv;
425 #ifdef HAVE_DOTLOCK
426 static int
427 _dotlock_main(void)
429 /* Use PATH_MAX not NAME_MAX to catch those "we proclaim the minimum value"
430 * problems (SunOS), since the pathconf(3) value comes too late! */
431 char name[PATH_MAX +1];
432 struct dotlock_info di;
433 struct stat stb, fdstb;
434 enum dotlock_state dls;
435 char const *cp;
436 int fd;
437 enum file_lock_type flt;
438 NYD_ENTER;
440 /* Ignore SIGPIPE, we'll see EPIPE and "fall through" */
441 safe_signal(SIGPIPE, SIG_IGN);
443 /* Get the arguments "passed to us" */
444 flt = _dotlock_flt;
445 fd = _dotlock_fd;
446 UNUSED(fd);
447 di = *_dotlock_dip;
449 /* chdir(2)? */
450 jislink:
451 dls = DLS_CANT_CHDIR | DLS_ABANDON;
453 if ((cp = strrchr(di.di_file_name, '/')) != NULL) {
454 char const *fname = cp + 1;
456 while (PTRCMP(cp - 1, >, di.di_file_name) && cp[-1] == '/')
457 --cp;
458 cp = savestrbuf(di.di_file_name, PTR2SIZE(cp - di.di_file_name));
459 if (chdir(cp))
460 goto jmsg;
462 di.di_file_name = fname;
465 /* So we're here, but then again the file can be a symbolic link!
466 * This is however only true if we do not have realpath(3) available since
467 * that'll have resolved the path already otherwise; nonetheless, let
468 * readlink(2) be a precondition for dotlocking and keep this code */
469 if (lstat(cp = di.di_file_name, &stb) == -1)
470 goto jmsg;
471 if (S_ISLNK(stb.st_mode)) {
472 /* Use salloc() and hope we stay in builtin buffer.. */
473 char *x;
474 size_t i;
475 ssize_t sr;
477 for (x = NULL, i = PATH_MAX;; i += PATH_MAX) {
478 x = salloc(i +1);
479 sr = readlink(cp, x, i);
480 if (sr <= 0) {
481 dls = DLS_FISHY | DLS_ABANDON;
482 goto jmsg;
484 if (UICMP(z, sr, <, i)) {
485 x[sr] = '\0';
486 i = (size_t)sr;
487 break;
490 di.di_file_name = x;
491 goto jislink;
494 dls = DLS_FISHY | DLS_ABANDON;
496 /* Bail out if the file has changed its identity in the meanwhile */
497 if (fstat(fd, &fdstb) == -1 ||
498 fdstb.st_dev != stb.st_dev || fdstb.st_ino != stb.st_ino ||
499 fdstb.st_uid != stb.st_uid || fdstb.st_gid != stb.st_gid ||
500 fdstb.st_mode != stb.st_mode)
501 goto jmsg;
503 /* Be aware, even if the error is false! Note the shared code in dotlock.h
504 * *requires* that it is possible to create a filename at least one byte
505 * longer than di_lock_name! */
506 do/* while(0) breaker */{
507 # ifdef HAVE_PATHCONF
508 long pc;
509 # endif
510 int i = snprintf(name, sizeof name, "%s.lock", di.di_file_name);
512 /* fd is a file, not portable to use for _PC_NAME_MAX */
513 if(i < 0){
514 jenametool:
515 dls = DLS_NAMETOOLONG | DLS_ABANDON;
516 goto jmsg;
518 # ifdef HAVE_PATHCONF
519 errno = 0;
520 if((pc = pathconf(".", _PC_NAME_MAX)) == -1){
521 /* errno unchanged: no limit */
522 if(errno == 0)
523 break;
524 }else if(pc - 1 >= (long)i)
525 break;
526 else
527 goto jenametool;
528 # endif
529 if(UICMP(z, NAME_MAX - 1, <, (uiz_t)i))
530 goto jenametool;
531 }while(0);
533 di.di_lock_name = name;
535 /* We are in the directory of the mailbox for which we have to create
536 * a dotlock file for. We don't know wether we have realpath(3) available,
537 * and manually resolving the path is due especially given that S-nail
538 * supports the special "%:" syntax to warp any file into a "system
539 * mailbox"; there may also be multiple system mailbox directories...
540 * So what we do is that we fstat(2) the mailbox and check its UID and
541 * GID against that of our own process: if any of those mismatch we must
542 * either assume a directory we are not allowed to write in, or that we run
543 * via -u/$USER/%USER as someone else, in which case we favour our
544 * privilege-separated dotlock process */
545 assert(cp != NULL); /* Ugly: avoid a useless var and reuse that one */
546 if (access(".", W_OK)) {
547 /* This may however also indicate a read-only filesystem, which is not
548 * really an error from our point of view since the mailbox will degrade
549 * to a readonly one for which no dotlock is needed, then, and errors
550 * may arise only due to actions which require box modifications */
551 if (errno == EROFS) {
552 dls = DLS_ROFS | DLS_ABANDON;
553 goto jmsg;
555 cp = NULL;
557 if (cp == NULL || stb.st_uid != user_id || stb.st_gid != group_id) {
558 char itoabuf[64];
559 char const *args[13];
561 snprintf(itoabuf, sizeof itoabuf, "%" PRIuZ, di.di_pollmsecs);
562 args[ 0] = PRIVSEP;
563 args[ 1] = (flt == FLT_READ ? "rdotlock" : "wdotlock");
564 args[ 2] = "mailbox"; args[ 3] = di.di_file_name;
565 args[ 4] = "name"; args[ 5] = di.di_lock_name;
566 args[ 6] = "hostname"; args[ 7] = di.di_hostname;
567 args[ 8] = "randstr"; args[ 9] = di.di_randstr;
568 args[10] = "pollmsecs"; args[11] = itoabuf;
569 args[12] = NULL;
570 execv(LIBEXECDIR "/" UAGENT "-privsep", UNCONST(args));
572 dls = DLS_NOEXEC;
573 write(STDOUT_FILENO, &dls, sizeof dls);
574 /* But fall through and try it with normal privileges! */
577 /* So let's try and call it ourselfs! Note that we don't block signals just
578 * like our privsep child does, the user will anyway be able to remove his
579 * file again, and if we're in -u/$USER mode then we are allowed to access
580 * the user's box: shall we leave behind a stale dotlock then at least we
581 * start a friendly human conversation. Since we cannot handle SIGKILL and
582 * SIGSTOP malicious things could happen whatever we do */
583 safe_signal(SIGHUP, SIG_IGN);
584 safe_signal(SIGINT, SIG_IGN);
585 safe_signal(SIGQUIT, SIG_IGN);
586 safe_signal(SIGTERM, SIG_IGN);
588 NYD;
589 dls = _dotlock_create(&di);
590 NYD;
592 /* Finally: notify our parent about the actual lock state.. */
593 jmsg:
594 write(STDOUT_FILENO, &dls, sizeof dls);
595 close(STDOUT_FILENO);
597 /* ..then eventually wait until we shall remove the lock again, which will
598 * be notified via the read returning */
599 if (dls == DLS_NONE) {
600 read(STDIN_FILENO, &dls, sizeof dls);
602 unlink(name);
604 NYD_LEAVE;
605 return EXIT_OK;
607 #endif /* HAVE_DOTLOCK */
609 #ifdef HAVE_SOCKETS
610 static long
611 xwrite(int fd, char const *data, size_t sz)
613 long rv = -1, wo;
614 size_t wt = 0;
615 NYD_ENTER;
617 do {
618 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
619 if (errno == EINTR)
620 continue;
621 else
622 goto jleave;
624 wt += wo;
625 } while (wt < sz);
626 rv = (long)sz;
627 jleave:
628 NYD_LEAVE;
629 return rv;
631 #endif /* HAVE_SOCKETS */
633 static bool_t
634 _source_file(char const *file, bool_t silent_error)
636 char *cp;
637 FILE *fi = NULL;
638 NYD_ENTER;
640 if ((cp = fexpand(file, FEXP_LOCAL)) == NULL)
641 goto jleave;
642 if ((fi = Fopen(cp, "r")) == NULL) {
643 if (!silent_error || (options & OPT_D_V))
644 n_perr(cp, 0);
645 goto jleave;
648 if (temporary_localopts_store != NULL) {
649 n_err(_("Before v15 you cannot `source' from within macros, sorry\n"));
650 goto jeclose;
652 if (_fio_stack_size >= NELEM(_fio_stack)) {
653 n_err(_("Too many `source' recursions\n"));
654 jeclose:
655 Fclose(fi);
656 fi = NULL;
657 goto jleave;
660 _fio_stack[_fio_stack_size].s_file = _fio_input;
661 _fio_stack[_fio_stack_size].s_cond = condstack_release();
662 _fio_stack[_fio_stack_size].s_loading = !!(pstate & PS_LOADING);
663 ++_fio_stack_size;
664 pstate &= ~PS_LOADING;
665 pstate |= PS_SOURCING;
666 _fio_input = fi;
667 jleave:
668 NYD_LEAVE;
669 return (fi != NULL);
672 FL char *
673 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen, FILE *fp,
674 int appendnl SMALLOC_DEBUG_ARGS)
676 size_t i_llen, sz;
677 char *rv;
678 NYD2_ENTER;
680 if (cnt == NULL) {
681 /* Without count, we can't determine where the chars returned by fgets()
682 * end if there's no newline. We have to read one character by one */
683 rv = _fgetline_byone(line, linesize, llen, fp, appendnl, 0
684 SMALLOC_DEBUG_ARGSCALL);
685 goto jleave;
688 if ((rv = *line) == NULL || *linesize < LINESIZE)
689 *line = rv = (srealloc)(rv, *linesize = LINESIZE SMALLOC_DEBUG_ARGSCALL);
690 sz = (*linesize <= *cnt) ? *linesize : *cnt + 1;
691 if (sz <= 1 || fgets(rv, sz, fp) == NULL) {
692 /* Leave llen untouched; it is used to determine whether the last line
693 * was \n-terminated in some callers */
694 rv = NULL;
695 goto jleave;
698 i_llen = _length_of_line(rv, sz);
699 *cnt -= i_llen;
700 while (rv[i_llen - 1] != '\n') {
701 *line = rv = (srealloc)(rv, *linesize += 256 SMALLOC_DEBUG_ARGSCALL);
702 sz = *linesize - i_llen;
703 sz = (sz <= *cnt) ? sz : *cnt + 1;
704 if (sz <= 1 || fgets(rv + i_llen, sz, fp) == NULL) {
705 if (appendnl) {
706 rv[i_llen++] = '\n';
707 rv[i_llen] = '\0';
709 break;
711 sz = _length_of_line(rv + i_llen, sz);
712 i_llen += sz;
713 *cnt -= sz;
715 if (llen)
716 *llen = i_llen;
717 jleave:
718 NYD2_LEAVE;
719 return rv;
722 FL int
723 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
724 SMALLOC_DEBUG_ARGS)
726 /* TODO readline_restart(): always *appends* LF just to strip it again;
727 * TODO should be configurable just as for fgetline(); ..or whatever.. */
728 int rv = -1;
729 long sz;
730 NYD2_ENTER;
732 clearerr(ibuf);
734 /* Interrupts will cause trouble if we are inside a stdio call. As this is
735 * only relevant if input is from tty, bypass it by read(), then */
736 if (fileno(ibuf) == 0 && (options & OPT_TTYIN)) {
737 assert(*linesize == 0 || *linebuf != NULL);
738 for (;;) {
739 if (*linesize <= LINESIZE || n >= *linesize - 128) {
740 *linesize += ((*linebuf == NULL) ? LINESIZE + n + 1 : 256);
741 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
743 jagain:
744 sz = read(0, *linebuf + n, *linesize - n - 1);
745 if (sz > 0) {
746 n += sz;
747 (*linebuf)[n] = '\0';
748 if (n > 0 && (*linebuf)[n - 1] == '\n')
749 break;
750 } else {
751 if (sz < 0 && errno == EINTR)
752 goto jagain;
753 if (n > 0) {
754 if ((*linebuf)[n - 1] != '\n') {
755 (*linebuf)[n++] = '\n';
756 (*linebuf)[n] = '\0';
758 break;
759 } else
760 goto jleave;
763 } else {
764 /* Not reading from standard input or standard input not a terminal. We
765 * read one char at a time as it is the only way to get lines with
766 * embedded NUL characters in standard stdio */
767 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
768 SMALLOC_DEBUG_ARGSCALL) == NULL)
769 goto jleave;
771 if (n > 0 && (*linebuf)[n - 1] == '\n')
772 (*linebuf)[--n] = '\0';
773 rv = (int)n;
774 jleave:
775 NYD2_LEAVE;
776 return rv;
779 FL int
780 (readline_input)(char const *prompt, bool_t nl_escape, char **linebuf,
781 size_t *linesize, char const *string SMALLOC_DEBUG_ARGS)
783 /* TODO readline: linebuf pool! */
784 FILE *ifile = (_fio_input != NULL) ? _fio_input : stdin;
785 bool_t doprompt, dotty;
786 int n, nold;
787 NYD2_ENTER;
789 doprompt = (!(pstate & PS_SOURCING) && (options & OPT_INTERACTIVE));
790 dotty = (doprompt && !ok_blook(line_editor_disable));
791 if (!doprompt)
792 prompt = NULL;
793 else if (prompt == NULL)
794 prompt = getprompt();
796 /* Ensure stdout is flushed first anyway */
797 if (!dotty && prompt == NULL)
798 fflush(stdout);
800 for (nold = n = 0;;) {
801 if (dotty) {
802 assert(ifile == stdin);
803 if (string != NULL && (n = (int)strlen(string)) > 0) {
804 if (*linesize > 0)
805 *linesize += n +1;
806 else
807 *linesize = (size_t)n + LINESIZE +1;
808 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
809 memcpy(*linebuf, string, (size_t)n +1);
811 string = NULL;
812 /* TODO if nold>0, don't redisplay the entire line!
813 * TODO needs complete redesign ... */
814 n = (tty_readline)(prompt, linebuf, linesize, n
815 SMALLOC_DEBUG_ARGSCALL);
816 } else {
817 if (prompt != NULL) {
818 if (*prompt != '\0')
819 fputs(prompt, stdout);
820 fflush(stdout);
822 n = (readline_restart)(ifile, linebuf, linesize, n
823 SMALLOC_DEBUG_ARGSCALL);
825 if (n > 0 && nold > 0) {
826 int i = 0;
827 char const *cp = *linebuf + nold;
829 while (blankspacechar(*cp) && nold + i < n)
830 ++cp, ++i;
831 if (i > 0) {
832 memmove(*linebuf + nold, cp, n - nold - i);
833 n -= i;
834 (*linebuf)[n] = '\0';
838 if (n <= 0)
839 break;
841 /* POSIX says:
842 * An unquoted <backslash> at the end of a command line shall
843 * be discarded and the next line shall continue the command */
844 if (!nl_escape || n == 0 || (*linebuf)[n - 1] != '\\')
845 break;
846 (*linebuf)[nold = --n] = '\0';
847 if (prompt != NULL && *prompt != '\0')
848 prompt = ".. "; /* XXX PS2 .. */
851 if (n >= 0 && (options & OPT_D_VV))
852 n_err(_("%s %d bytes <%.*s>\n"),
853 ((pstate & PS_LOADING) ? "LOAD"
854 : (pstate & PS_SOURCING) ? "SOURCE" : "READ"),
855 n, n, *linebuf);
856 NYD2_LEAVE;
857 return n;
860 FL char *
861 n_input_cp_addhist(char const *prompt, char const *string, bool_t isgabby)
863 /* FIXME n_input_cp_addhist(): leaks on sigjmp without linepool */
864 size_t linesize = 0;
865 char *linebuf = NULL, *rv = NULL;
866 int n;
867 NYD2_ENTER;
869 n = readline_input(prompt, FAL0, &linebuf, &linesize, string);
870 if (n > 0 && *(rv = savestrbuf(linebuf, (size_t)n)) != '\0' &&
871 (options & OPT_INTERACTIVE))
872 tty_addhist(rv, isgabby);
874 if (linebuf != NULL)
875 free(linebuf);
876 NYD2_LEAVE;
877 return rv;
880 FL void
881 setptr(FILE *ibuf, off_t offset)
883 struct message self;
884 char *cp, *linebuf = NULL;
885 char const *cp2;
886 int c, maybe = 1, inhead = 0, selfcnt = 0;
887 size_t linesize = 0, filesize, cnt;
888 NYD_ENTER;
890 memset(&self, 0, sizeof self);
891 self.m_flag = MUSED | MNEW | MNEWEST;
892 filesize = mailsize - offset;
893 offset = ftell(mb.mb_otf);
895 for (;;) {
896 if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0) == NULL) {
897 self.m_xsize = self.m_size;
898 self.m_xlines = self.m_lines;
899 self.m_have = HAVE_HEADER | HAVE_BODY;
900 if (selfcnt > 0)
901 message_append(&self);
902 makemessage();
903 if (linebuf)
904 free(linebuf);
905 break;
908 #ifdef notdef
909 if (linebuf[0] == '\0')
910 linebuf[0] = '.';
911 #endif
912 /* XXX Convert CRLF to LF; this should be rethought in that
913 * XXX CRLF input should possibly end as CRLF output? */
914 if (cnt >= 2 && linebuf[cnt - 1] == '\n' && linebuf[cnt - 2] == '\r')
915 linebuf[--cnt - 1] = '\n';
916 fwrite(linebuf, sizeof *linebuf, cnt, mb.mb_otf);
917 if (ferror(mb.mb_otf)) {
918 n_perr(_("/tmp"), 0);
919 exit(EXIT_ERR);
921 if (linebuf[cnt - 1] == '\n')
922 linebuf[cnt - 1] = '\0';
923 if (maybe && linebuf[0] == 'F' && is_head(linebuf, cnt, FAL0)) {
924 /* TODO char date[FROM_DATEBUF];
925 * TODO extract_date_from_from_(linebuf, cnt, date);
926 * TODO self.m_time = 10000; */
927 self.m_xsize = self.m_size;
928 self.m_xlines = self.m_lines;
929 self.m_have = HAVE_HEADER | HAVE_BODY;
930 if (selfcnt++ > 0)
931 message_append(&self);
932 msgCount++;
933 self.m_flag = MUSED | MNEW | MNEWEST;
934 self.m_size = 0;
935 self.m_lines = 0;
936 self.m_block = mailx_blockof(offset);
937 self.m_offset = mailx_offsetof(offset);
938 inhead = 1;
939 } else if (linebuf[0] == 0) {
940 inhead = 0;
941 } else if (inhead) {
942 for (cp = linebuf, cp2 = "status";; ++cp) {
943 if ((c = *cp2++) == 0) {
944 while (c = *cp++, whitechar(c))
946 if (cp[-1] != ':')
947 break;
948 while ((c = *cp++) != '\0')
949 if (c == 'R')
950 self.m_flag |= MREAD;
951 else if (c == 'O')
952 self.m_flag &= ~MNEW;
953 break;
955 if (*cp != c && *cp != upperconv(c))
956 break;
958 for (cp = linebuf, cp2 = "x-status";; ++cp) {
959 if ((c = *cp2++) == 0) {
960 while ((c = *cp++, whitechar(c)))
962 if (cp[-1] != ':')
963 break;
964 while ((c = *cp++) != '\0')
965 if (c == 'F')
966 self.m_flag |= MFLAGGED;
967 else if (c == 'A')
968 self.m_flag |= MANSWERED;
969 else if (c == 'T')
970 self.m_flag |= MDRAFTED;
971 break;
973 if (*cp != c && *cp != upperconv(c))
974 break;
977 offset += cnt;
978 self.m_size += cnt;
979 ++self.m_lines;
980 maybe = linebuf[0] == 0;
982 NYD_LEAVE;
985 FL int
986 putline(FILE *obuf, char *linebuf, size_t cnt)
988 int rv = -1;
989 NYD_ENTER;
991 fwrite(linebuf, sizeof *linebuf, cnt, obuf);
992 putc('\n', obuf);
993 if (!ferror(obuf))
994 rv = (int)(cnt + 1);
995 NYD_LEAVE;
996 return rv;
999 FL FILE *
1000 setinput(struct mailbox *mp, struct message *m, enum needspec need)
1002 FILE *rv = NULL;
1003 enum okay ok = STOP;
1004 NYD_ENTER;
1006 switch (need) {
1007 case NEED_HEADER:
1008 ok = (m->m_have & HAVE_HEADER) ? OKAY : get_header(m);
1009 break;
1010 case NEED_BODY:
1011 ok = (m->m_have & HAVE_BODY) ? OKAY : get_body(m);
1012 break;
1013 case NEED_UNSPEC:
1014 ok = OKAY;
1015 break;
1017 if (ok != OKAY)
1018 goto jleave;
1020 fflush(mp->mb_otf);
1021 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1022 SEEK_SET) == -1) {
1023 n_perr(_("fseek"), 0);
1024 n_panic(_("temporary file seek"));
1026 rv = mp->mb_itf;
1027 jleave:
1028 NYD_LEAVE;
1029 return rv;
1032 FL void
1033 message_reset(void)
1035 NYD_ENTER;
1036 if (message != NULL) {
1037 free(message);
1038 message = NULL;
1040 msgCount = 0;
1041 _message_space = 0;
1042 NYD_LEAVE;
1045 FL void
1046 message_append(struct message *mp)
1048 NYD_ENTER;
1049 if (UICMP(z, msgCount + 1, >=, _message_space)) {
1050 /* XXX remove _message_space magics (or use s_Vector) */
1051 _message_space = (_message_space >= 128 && _message_space <= 1000000)
1052 ? _message_space << 1 : _message_space + 64;
1053 message = srealloc(message, _message_space * sizeof *message);
1055 if (msgCount > 0) {
1056 if (mp != NULL)
1057 message[msgCount - 1] = *mp;
1058 else
1059 memset(message + msgCount - 1, 0, sizeof *message);
1061 NYD_LEAVE;
1064 FL bool_t
1065 message_match(struct message *mp, struct search_expr const *sep,
1066 bool_t with_headers)
1068 char **line;
1069 size_t *linesize, cnt;
1070 FILE *fp;
1071 bool_t rv = FAL0;
1072 NYD_ENTER;
1074 if ((fp = Ftmp(NULL, "mpmatch", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL)
1075 goto j_leave;
1077 if (sendmp(mp, fp, NULL, NULL, SEND_TOSRCH, NULL) < 0)
1078 goto jleave;
1079 fflush_rewind(fp);
1081 cnt = fsize(fp);
1082 line = &termios_state.ts_linebuf; /* XXX line pool */
1083 linesize = &termios_state.ts_linesize; /* XXX line pool */
1085 if (!with_headers)
1086 while (fgetline(line, linesize, &cnt, NULL, fp, 0))
1087 if (**line == '\n')
1088 break;
1090 while (fgetline(line, linesize, &cnt, NULL, fp, 0)) {
1091 #ifdef HAVE_REGEX
1092 if (sep->ss_sexpr == NULL) {
1093 if (regexec(&sep->ss_regex, *line, 0,NULL, 0) == REG_NOMATCH)
1094 continue;
1095 } else
1096 #endif
1097 if (!substr(*line, sep->ss_sexpr))
1098 continue;
1099 rv = TRU1;
1100 break;
1103 jleave:
1104 Fclose(fp);
1105 j_leave:
1106 NYD_LEAVE;
1107 return rv;
1110 FL struct message *
1111 setdot(struct message *mp)
1113 NYD_ENTER;
1114 if (dot != mp) {
1115 prevdot = dot;
1116 pstate &= ~PS_DID_PRINT_DOT;
1118 dot = mp;
1119 uncollapse1(dot, 0);
1120 NYD_LEAVE;
1121 return dot;
1124 FL int
1125 rm(char const *name)
1127 struct stat sb;
1128 int rv = -1;
1129 NYD_ENTER;
1131 if (stat(name, &sb) < 0)
1133 else if (!S_ISREG(sb.st_mode))
1134 errno = EISDIR;
1135 else
1136 rv = unlink(name);
1137 NYD_LEAVE;
1138 return rv;
1141 FL off_t
1142 fsize(FILE *iob)
1144 struct stat sbuf;
1145 off_t rv;
1146 NYD_ENTER;
1148 rv = (fstat(fileno(iob), &sbuf) == -1) ? 0 : sbuf.st_size;
1149 NYD_LEAVE;
1150 return rv;
1153 FL char *
1154 fexpand(char const *name, enum fexp_mode fexpm)
1156 char cbuf[PATH_MAX +1];
1157 char const *res;
1158 struct str s;
1159 bool_t dyn;
1160 NYD_ENTER;
1162 /* The order of evaluation is "%" and "#" expand into constants.
1163 * "&" can expand into "+". "+" can expand into shell meta characters.
1164 * Shell meta characters expand into constants.
1165 * This way, we make no recursive expansion */
1166 if ((fexpm & FEXP_NSHORTCUT) || (res = shortcut_expand(name)) == NULL)
1167 res = UNCONST(name);
1169 if (fexpm & FEXP_SHELL) {
1170 dyn = FAL0;
1171 goto jshell;
1173 jnext:
1174 dyn = FAL0;
1175 switch (*res) {
1176 case '%':
1177 if (res[1] == ':' && res[2] != '\0') {
1178 res = &res[2];
1179 goto jnext;
1181 _findmail(cbuf, sizeof cbuf, (res[1] != '\0' ? res + 1 : myname),
1182 (res[1] != '\0' || (options & OPT_u_FLAG)));
1183 res = cbuf;
1184 goto jislocal;
1185 case '#':
1186 if (res[1] != '\0')
1187 break;
1188 if (prevfile[0] == '\0') {
1189 n_err(_("No previous file\n"));
1190 res = NULL;
1191 goto jleave;
1193 res = prevfile;
1194 goto jislocal;
1195 case '&':
1196 if (res[1] == '\0') {
1197 if ((res = ok_vlook(MBOX)) == NULL)
1198 res = UNCONST("~/mbox"); /* XXX no magics (POSIX though) */
1199 else if (res[0] != '&' || res[1] != '\0')
1200 goto jnext;
1202 break;
1205 if (res[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
1206 res = str_concat_csvl(&s, protbase(mailname), "/", res + 1, NULL)->s;
1207 dyn = TRU1;
1210 if (res[0] == '+' && getfold(cbuf, sizeof cbuf)) {
1211 size_t i = strlen(cbuf);
1213 res = str_concat_csvl(&s, cbuf,
1214 ((i > 0 && cbuf[i - 1] == '/') ? "" : "/"), res + 1, NULL)->s;
1215 dyn = TRU1;
1217 if (res[0] == '%' && res[1] == ':') {
1218 res += 2;
1219 goto jnext;
1223 /* Catch the most common shell meta character */
1224 jshell:
1225 if (res[0] == '~') {
1226 res = n_shell_expand_tilde(res, NULL);
1227 dyn = TRU1;
1229 if (anyof(res, "|&;<>{}()[]*?$`'\"\\"))
1230 switch (which_protocol(res)) {
1231 case PROTO_FILE:
1232 case PROTO_MAILDIR:
1233 res = (fexpm & FEXP_NSHELL) ? n_shell_expand_var(res, TRU1, NULL)
1234 : _globname(res, fexpm);
1235 dyn = TRU1;
1236 goto jleave;
1237 default:
1238 break;
1240 jislocal:
1241 if (fexpm & FEXP_LOCAL)
1242 switch (which_protocol(res)) {
1243 case PROTO_FILE:
1244 case PROTO_MAILDIR:
1245 break;
1246 default:
1247 n_err(_("Not a local file or directory: \"%s\"\n"), name);
1248 res = NULL;
1249 break;
1251 jleave:
1252 if (res && !dyn)
1253 res = savestr(res);
1254 NYD_LEAVE;
1255 return UNCONST(res);
1258 FL char *
1259 fexpand_nshell_quote(char const *name)
1261 size_t i, j;
1262 char *rv, c;
1263 NYD_ENTER;
1265 for (i = j = 0; (c = name[i]) != '\0'; ++i)
1266 if (c == '\\')
1267 ++j;
1269 if (j == 0)
1270 rv = savestrbuf(name, i);
1271 else {
1272 rv = salloc(i + j +1);
1273 for (i = j = 0; (c = name[i]) != '\0'; ++i) {
1274 rv[j++] = c;
1275 if (c == '\\')
1276 rv[j++] = c;
1278 rv[j] = '\0';
1280 NYD_LEAVE;
1281 return rv;
1284 FL bool_t
1285 var_folder_updated(char const *name, char **store)
1287 char rv = TRU1;
1288 char *folder, *unres = NULL, *res = NULL;
1289 NYD_ENTER;
1291 if ((folder = UNCONST(name)) == NULL)
1292 goto jleave;
1294 /* Expand the *folder*; skip %: prefix for simplicity of use */
1295 /* XXX This *only* works because we do NOT
1296 * XXX update environment variables via the "set" mechanism */
1297 if (folder[0] == '%' && folder[1] == ':')
1298 folder += 2;
1299 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
1300 goto jleave;
1302 switch (which_protocol(folder)) {
1303 case PROTO_POP3:
1304 n_err(_("*folder* cannot be set to a flat, readonly POP3 account\n"));
1305 rv = FAL0;
1306 goto jleave;
1307 case PROTO_IMAP:
1308 /* Simply assign what we have, even including `%:' prefix */
1309 if (folder != name)
1310 goto jvcopy;
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 jvcopy:
1342 *store = sstrdup(folder);
1344 if (res != NULL)
1345 ac_free(res);
1346 if (unres != NULL)
1347 ac_free(unres);
1348 jleave:
1349 NYD_LEAVE;
1350 return rv;
1353 FL bool_t
1354 getfold(char *name, size_t size)
1356 char const *folder;
1357 NYD_ENTER;
1359 if ((folder = ok_vlook(folder)) != NULL)
1360 n_strlcpy(name, folder, size);
1361 NYD_LEAVE;
1362 return (folder != NULL);
1365 FL char const *
1366 getdeadletter(void) /* XXX should that be in auxlily.c? */
1368 char const *cp;
1369 NYD_ENTER;
1371 if ((cp = ok_vlook(DEAD)) == NULL || (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
1372 cp = fexpand("~/dead.letter", FEXP_LOCAL | FEXP_SHELL);
1373 else if (*cp != '/') {
1374 size_t sz = strlen(cp) + 2 +1;
1375 char *buf = ac_alloc(sz);
1377 snprintf(buf, sz, "~/%s", cp);
1378 cp = fexpand(buf, FEXP_LOCAL | FEXP_SHELL);
1379 ac_free(buf);
1382 if (cp == NULL)
1383 cp = "dead.letter"; /* XXX magic -> nail.h (POSIX thing though) */
1384 NYD_LEAVE;
1385 return cp;
1388 FL enum okay
1389 get_body(struct message *mp)
1391 enum okay rv;
1392 NYD_ENTER;
1393 UNUSED(mp);
1395 switch (mb.mb_type) {
1396 case MB_FILE:
1397 case MB_MAILDIR:
1398 rv = OKAY;
1399 break;
1400 #ifdef HAVE_POP3
1401 case MB_POP3:
1402 rv = pop3_body(mp);
1403 break;
1404 #endif
1405 #ifdef HAVE_IMAP
1406 case MB_IMAP:
1407 case MB_CACHE:
1408 rv = imap_body(mp);
1409 break;
1410 #endif
1411 case MB_VOID:
1412 default:
1413 rv = STOP;
1414 break;
1416 NYD_LEAVE;
1417 return rv;
1420 FL bool_t
1421 file_lock(int fd, enum file_lock_type flt, off_t off, off_t len,
1422 size_t pollmsecs)
1424 size_t tries;
1425 bool_t rv;
1426 NYD_ENTER;
1428 for (tries = 0; tries <= FILE_LOCK_TRIES; ++tries)
1429 if ((rv = _file_lock(fd, flt, off, len)) || pollmsecs == 0)
1430 break;
1431 else
1432 sleep(1); /* TODO pollmsecs -> use finer grain */
1433 NYD_LEAVE;
1434 return rv;
1437 FL FILE *
1438 dot_lock(char const *fname, int fd, enum file_lock_type flt,
1439 off_t off, off_t len, size_t pollmsecs)
1441 #undef _DOMSG
1442 #ifdef HAVE_DOTLOCK
1443 # define _DOMSG() n_err(_("Creating dotlock for \"%s\" "), fname)
1444 #else
1445 # define _DOMSG() n_err(_("Trying to lock file \"%s\" "), fname)
1446 #endif
1448 #ifdef HAVE_DOTLOCK
1449 int cpipe[2];
1450 struct dotlock_info di;
1451 enum dotlock_state dls;
1452 char const *emsg = NULL;
1453 #endif
1454 int UNINIT(serrno, 0);
1455 union {size_t tries; int (*ptf)(void); char const *sh; ssize_t r;} u;
1456 bool_t flocked, didmsg = FAL0;
1457 FILE *rv = NULL;
1458 NYD_ENTER;
1460 if (options & OPT_D_VV) {
1461 _DOMSG();
1462 didmsg = TRUM1;
1465 flocked = FAL0;
1466 for (u.tries = 0; !_file_lock(fd, flt, off, len);)
1467 switch ((serrno = errno)) {
1468 case EACCES:
1469 case EAGAIN:
1470 case ENOLCK:
1471 if (pollmsecs > 0 && ++u.tries < FILE_LOCK_TRIES) {
1472 if (!didmsg)
1473 _DOMSG();
1474 n_err(".");
1475 didmsg = TRUM1;
1476 sleep(1); /* TODO pollmsecs -> use finer grain */
1477 continue;
1479 /* FALLTHRU */
1480 default:
1481 goto jleave;
1483 flocked = TRU1;
1485 #ifndef HAVE_DOTLOCK
1486 jleave:
1487 if (didmsg == TRUM1)
1488 n_err("\n");
1489 if (flocked)
1490 rv = (FILE*)-1;
1491 else
1492 errno = serrno;
1493 NYD_LEAVE;
1494 return rv;
1496 #else
1497 /* Create control-pipe for our dot file locker process, which will remove
1498 * the lock and terminate once the pipe is closed, for whatever reason */
1499 if (pipe_cloexec(cpipe) == -1) {
1500 serrno = errno;
1501 emsg = N_(" Can't create file lock control pipe\n");
1502 goto jemsg;
1505 /* And the locker process itself; it'll be a (rather cheap) thread only
1506 * unless the lock has to be placed in the system spool and we have our
1507 * privilege-separated dotlock program available, in which case that will be
1508 * executed and do "it" with changed group-id */
1509 di.di_file_name = fname;
1510 di.di_pollmsecs = pollmsecs;
1511 /* Initialize some more stuff; query the two strings in the parent in order
1512 * to cache the result of the former and anyway minimalize child page-ins.
1513 * Especially uname(3) may hang for multiple seconds when it is called the
1514 * first time! */
1515 di.di_hostname = nodename(FAL0);
1516 di.di_randstr = getrandstring(16);
1517 _dotlock_flt = flt;
1518 _dotlock_fd = fd;
1519 _dotlock_dip = &di;
1521 u.ptf = &_dotlock_main;
1522 rv = Popen((char*)-1, "W", u.sh, NULL, cpipe[1]);
1523 serrno = errno;
1525 close(cpipe[1]);
1526 if (rv == NULL) {
1527 close(cpipe[0]);
1528 emsg = N_(" Can't create file lock process\n");
1529 goto jemsg;
1532 /* Let's check wether we were able to create the dotlock file */
1533 for (;;) {
1534 u.r = read(cpipe[0], &dls, sizeof dls);
1535 if (UICMP(z, u.r, !=, sizeof dls)) {
1536 serrno = (u.r != -1) ? EAGAIN : errno;
1537 dls = DLS_DUNNO | DLS_ABANDON;
1538 } else
1539 serrno = 0;
1541 if (dls == DLS_NONE || (dls & DLS_ABANDON))
1542 close(cpipe[0]);
1544 switch (dls & ~DLS_ABANDON) {
1545 case DLS_NONE:
1546 goto jleave;
1547 case DLS_CANT_CHDIR:
1548 if (options & OPT_D_V)
1549 emsg = N_(" Can't change to directory! Please check permissions\n");
1550 serrno = EACCES;
1551 break;
1552 case DLS_NAMETOOLONG:
1553 emsg = N_("Resulting dotlock filename would be too long\n");
1554 serrno = EACCES;
1555 break;
1556 case DLS_ROFS:
1557 assert(dls & DLS_ABANDON);
1558 if (options & OPT_D_V)
1559 emsg = N_(" Read-only filesystem, not creating lock file\n");
1560 serrno = EROFS;
1561 break;
1562 case DLS_NOPERM:
1563 if (options & OPT_D_V)
1564 emsg = N_(" Can't create a lock file! Please check permissions\n"
1565 " (Maybe setting *dotlock-ignore-error* variable helps.)\n");
1566 serrno = EACCES;
1567 break;
1568 case DLS_NOEXEC:
1569 if (options & OPT_D_V)
1570 emsg = N_(" Can't find privilege-separated file lock program\n");
1571 serrno = ENOENT;
1572 break;
1573 case DLS_PRIVFAILED:
1574 emsg = N_(" Privilege-separated file lock program can't change "
1575 "privileges\n");
1576 serrno = EPERM;
1577 break;
1578 case DLS_EXIST:
1579 emsg = N_(" It seems there is a stale dotlock file?\n"
1580 " Please remove the lock file manually, then retry\n");
1581 serrno = EEXIST;
1582 break;
1583 case DLS_FISHY:
1584 emsg = N_(" Fishy! Is someone trying to \"steal\" foreign files?\n"
1585 " Please check the mailbox file etc. manually, then retry\n");
1586 serrno = EAGAIN; /* ? Hack to ignore *dotlock-ignore-error* xxx */
1587 break;
1588 default:
1589 case DLS_DUNNO:
1590 emsg = N_(" Unspecified dotlock file control process error.\n"
1591 " Like broken I/O pipe; this one is unlikely to happen\n");
1592 if (serrno != EAGAIN)
1593 serrno = EINVAL;
1594 break;
1595 case DLS_PING:
1596 if (!didmsg)
1597 _DOMSG();
1598 n_err(".");
1599 didmsg = TRUM1;
1600 continue;
1603 if (emsg != NULL) {
1604 if (!didmsg) {
1605 _DOMSG();
1606 didmsg = TRUM1;
1608 if (didmsg == TRUM1)
1609 n_err("\n");
1610 didmsg = TRU1;
1611 n_err(V_(emsg));
1612 emsg = NULL;
1615 if (dls & DLS_ABANDON) {
1616 Pclose(rv, FAL0);
1617 rv = NULL;
1618 break;
1622 jleave:
1623 if (didmsg == TRUM1)
1624 n_err("\n");
1625 if (rv == NULL) {
1626 if (flocked && (serrno == EROFS ||
1627 (serrno != EAGAIN && serrno != EEXIST &&
1628 ok_blook(dotlock_ignore_error))))
1629 rv = (FILE*)-1;
1630 else
1631 errno = serrno;
1633 NYD_LEAVE;
1634 return rv;
1635 jemsg:
1636 if (!didmsg)
1637 _DOMSG();
1638 n_err("\n");
1639 didmsg = TRU1;
1640 n_err(V_(emsg));
1641 goto jleave;
1642 #endif /* HAVE_DOTLOCK */
1643 #undef _DOMSG
1646 #ifdef HAVE_SOCKETS
1647 FL int
1648 sclose(struct sock *sp)
1650 int i;
1651 NYD_ENTER;
1653 i = sp->s_fd;
1654 sp->s_fd = -1;
1655 /* TODO NOTE: we MUST NOT close the descriptor 0 here...
1656 * TODO of course this should be handled in a VMAILFS->open() .s_fd=-1,
1657 * TODO but unfortunately it isn't yet */
1658 if (i <= 0)
1659 i = 0;
1660 else {
1661 if (sp->s_onclose != NULL)
1662 (*sp->s_onclose)();
1663 if (sp->s_wbuf != NULL)
1664 free(sp->s_wbuf);
1665 # ifdef HAVE_OPENSSL
1666 if (sp->s_use_ssl) {
1667 void *s_ssl = sp->s_ssl;
1669 sp->s_ssl = NULL;
1670 sp->s_use_ssl = 0;
1671 while (!SSL_shutdown(s_ssl)) /* XXX proper error handling;signals! */
1673 SSL_free(s_ssl);
1675 # endif
1676 i = close(i);
1678 NYD_LEAVE;
1679 return i;
1682 FL enum okay
1683 swrite(struct sock *sp, char const *data)
1685 enum okay rv;
1686 NYD2_ENTER;
1688 rv = swrite1(sp, data, strlen(data), 0);
1689 NYD2_LEAVE;
1690 return rv;
1693 FL enum okay
1694 swrite1(struct sock *sp, char const *data, int sz, int use_buffer)
1696 enum okay rv = STOP;
1697 int x;
1698 NYD2_ENTER;
1700 if (use_buffer > 0) {
1701 int di;
1703 if (sp->s_wbuf == NULL) {
1704 sp->s_wbufsize = 4096;
1705 sp->s_wbuf = smalloc(sp->s_wbufsize);
1706 sp->s_wbufpos = 0;
1708 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
1709 di = sp->s_wbufsize - sp->s_wbufpos;
1710 sz -= di;
1711 if (sp->s_wbufpos > 0) {
1712 memcpy(sp->s_wbuf + sp->s_wbufpos, data, di);
1713 rv = swrite1(sp, sp->s_wbuf, sp->s_wbufsize, -1);
1714 } else
1715 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1716 if (rv != OKAY)
1717 goto jleave;
1718 data += di;
1719 sp->s_wbufpos = 0;
1721 if (sz == sp->s_wbufsize) {
1722 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1723 if (rv != OKAY)
1724 goto jleave;
1725 } else if (sz) {
1726 memcpy(sp->s_wbuf+ sp->s_wbufpos, data, sz);
1727 sp->s_wbufpos += sz;
1729 rv = OKAY;
1730 goto jleave;
1731 } else if (use_buffer == 0 && sp->s_wbuf != NULL && sp->s_wbufpos > 0) {
1732 x = sp->s_wbufpos;
1733 sp->s_wbufpos = 0;
1734 if ((rv = swrite1(sp, sp->s_wbuf, x, -1)) != OKAY)
1735 goto jleave;
1737 if (sz == 0) {
1738 rv = OKAY;
1739 goto jleave;
1742 # ifdef HAVE_OPENSSL
1743 if (sp->s_use_ssl) {
1744 jssl_retry:
1745 x = SSL_write(sp->s_ssl, data, sz);
1746 if (x < 0) {
1747 switch (SSL_get_error(sp->s_ssl, x)) {
1748 case SSL_ERROR_WANT_READ:
1749 case SSL_ERROR_WANT_WRITE:
1750 goto jssl_retry;
1753 } else
1754 # endif
1756 x = xwrite(sp->s_fd, data, sz);
1758 if (x != sz) {
1759 char o[512];
1760 snprintf(o, sizeof o, "%s write error",
1761 (sp->s_desc ? sp->s_desc : "socket"));
1762 # ifdef HAVE_OPENSSL
1763 if (sp->s_use_ssl)
1764 ssl_gen_err("%s", o);
1765 else
1766 # endif
1767 n_perr(o, 0);
1768 if (x < 0)
1769 sclose(sp);
1770 rv = STOP;
1771 goto jleave;
1773 rv = OKAY;
1774 jleave:
1775 NYD2_LEAVE;
1776 return rv;
1779 static sigjmp_buf __sopen_actjmp; /* TODO someday, we won't need it no more */
1780 static int __sopen_sig; /* TODO someday, we won't need it no more */
1781 static void
1782 __sopen_onsig(int sig) /* TODO someday, we won't need it no more */
1784 NYD_X; /* Signal handler */
1785 if (__sopen_sig < 0) {
1786 /* Of course the following doesn't belong into a signal handler XXX */
1787 int i, j;
1789 if (__sopen_sig == -1) {
1790 fprintf(stderr,
1791 _("\nInterrupting it could turn the (GNU/Linux+) DNS resolver "
1792 "unusable.\n"
1793 " Wait until it's done, or do terminate the program\n"));
1794 __sopen_sig = -2;
1795 } else if ((i = j = ABS(__sopen_sig)) + 15 < scrnwidth) {
1796 putc('\r', stderr);
1797 for (; j > 0; --j)
1798 putc(' ', stderr);
1799 fputs("___( o)", stderr);
1800 putc((i & 1) ? '=' : '>', stderr);
1801 putc(' ', stderr);
1802 putc(' ', stderr);
1803 ++i;
1804 __sopen_sig = -i;
1806 } else {
1807 __sopen_sig = sig;
1808 siglongjmp(__sopen_actjmp, 1);
1812 FL bool_t
1813 sopen(struct sock *sp, struct url *urlp) /* TODO sighandling; refactor */
1815 # ifdef HAVE_SO_SNDTIMEO
1816 struct timeval tv;
1817 # endif
1818 # ifdef HAVE_SO_LINGER
1819 struct linger li;
1820 # endif
1821 # ifdef HAVE_GETADDRINFO
1822 char hbuf[NI_MAXHOST];
1823 struct addrinfo hints, *res0 = NULL, *res;
1824 # else
1825 struct sockaddr_in servaddr;
1826 struct in_addr **pptr;
1827 struct hostent *hp;
1828 struct servent *ep;
1829 # endif
1830 sighandler_type volatile ohup, oint;
1831 char const * volatile serv;
1832 int volatile sofd = -1, errval;
1833 NYD_ENTER;
1835 UNINIT(errval, 0);
1837 /* Connect timeouts after 30 seconds XXX configurable */
1838 # ifdef HAVE_SO_SNDTIMEO
1839 tv.tv_sec = 30;
1840 tv.tv_usec = 0;
1841 # endif
1842 serv = (urlp->url_port != NULL) ? urlp->url_port : urlp->url_proto;
1844 if (options & OPT_VERB)
1845 n_err(_("Resolving host \"%s:%s\" ... "),
1846 urlp->url_host.s, serv);
1848 /* Signal handling (in respect to __sopen_sig dealing) is heavy, but no
1849 * healing until v15.0 and i want to end up with that functionality */
1850 hold_sigs();
1851 __sopen_sig = 0;
1852 ohup = safe_signal(SIGHUP, &__sopen_onsig);
1853 oint = safe_signal(SIGINT, &__sopen_onsig);
1854 if (sigsetjmp(__sopen_actjmp, 0)) {
1855 jpseudo_jump:
1856 n_err("%s\n",
1857 (__sopen_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
1858 if (sofd >= 0) {
1859 close(sofd);
1860 sofd = -1;
1862 goto jjumped;
1864 rele_sigs();
1866 # ifdef HAVE_GETADDRINFO
1867 for (;;) {
1868 memset(&hints, 0, sizeof hints);
1869 hints.ai_socktype = SOCK_STREAM;
1870 __sopen_sig = -1;
1871 errval = getaddrinfo(urlp->url_host.s, serv, &hints, &res0);
1872 if (__sopen_sig != -1) {
1873 __sopen_sig = SIGINT;
1874 goto jpseudo_jump;
1876 __sopen_sig = 0;
1877 if (errval == 0)
1878 break;
1880 if (options & OPT_VERB)
1881 n_err(_("failed\n"));
1882 n_err(_("Lookup of \"%s:%s\" failed: %s\n"),
1883 urlp->url_host.s, serv, gai_strerror(errval));
1885 /* Error seems to depend on how "smart" the /etc/service code is: is it
1886 * "able" to state wether the service as such is NONAME or does it only
1887 * check for the given ai_socktype.. */
1888 if (errval == EAI_NONAME || errval == EAI_SERVICE) {
1889 if (serv == urlp->url_proto &&
1890 (serv = url_servbyname(urlp, NULL)) != NULL) {
1891 n_err(_(" Trying standard protocol port \"%s\"\n"
1892 " If that succeeds consider including the port in the URL!\n"),
1893 serv);
1894 continue;
1896 if (serv != urlp->url_port)
1897 n_err(_(" Including a port number in the URL may "
1898 "circumvent this problem\n"));
1900 assert(sofd == -1);
1901 errval = 0;
1902 goto jjumped;
1904 if (options & OPT_VERB)
1905 n_err(_("done\n"));
1907 for (res = res0; res != NULL && sofd < 0; res = res->ai_next) {
1908 if (options & OPT_VERB) {
1909 if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof hbuf,
1910 NULL, 0, NI_NUMERICHOST))
1911 memcpy(hbuf, "unknown host", sizeof("unknown host"));
1912 n_err(_("%sConnecting to \"%s:%s\" ..."),
1913 (res == res0 ? "" : "\n"), hbuf, serv);
1916 sofd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1917 if (sofd >= 0) {
1918 # ifdef HAVE_SO_SNDTIMEO
1919 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1920 # endif
1921 if (connect(sofd, res->ai_addr, res->ai_addrlen)) {
1922 errval = errno;
1923 close(sofd);
1924 sofd = -1;
1929 jjumped:
1930 if (res0 != NULL) {
1931 freeaddrinfo(res0);
1932 res0 = NULL;
1935 # else /* HAVE_GETADDRINFO */
1936 if (serv == urlp->url_proto) {
1937 if ((ep = getservbyname(UNCONST(serv), "tcp")) != NULL)
1938 urlp->url_portno = ntohs(ep->s_port);
1939 else {
1940 if (options & OPT_VERB)
1941 n_err(_("failed\n"));
1942 if ((serv = url_servbyname(urlp, &urlp->url_portno)) != NULL)
1943 n_err(_(" Unknown service: \"%s\"\n"
1944 " Trying standard protocol port \"%s\"\n"
1945 " If that succeeds consider including the port in the URL!\n"),
1946 urlp->url_proto, serv);
1947 else {
1948 n_err(_(" Unknown service: \"%s\"\n"
1949 " Including a port in the URL may circumvent this problem\n"),
1950 urlp->url_proto);
1951 assert(sofd == -1 && errval == 0);
1952 goto jjumped;
1957 __sopen_sig = -1;
1958 hp = gethostbyname(urlp->url_host.s);
1959 if (__sopen_sig != -1) {
1960 __sopen_sig = SIGINT;
1961 goto jpseudo_jump;
1963 __sopen_sig = 0;
1965 if (hp == NULL) {
1966 char const *emsg;
1968 if (options & OPT_VERB)
1969 n_err(_("failed\n"));
1970 switch (h_errno) {
1971 case HOST_NOT_FOUND: emsg = N_("host not found"); break;
1972 default:
1973 case TRY_AGAIN: emsg = N_("(maybe) try again later"); break;
1974 case NO_RECOVERY: emsg = N_("non-recoverable server error"); break;
1975 case NO_DATA: emsg = N_("valid name without IP address"); break;
1977 n_err(_("Lookup of \"%s:%s\" failed: %s\n"),
1978 urlp->url_host.s, serv, V_(emsg));
1979 goto jjumped;
1980 } else if (options & OPT_VERB)
1981 n_err(_("done\n"));
1983 pptr = (struct in_addr**)hp->h_addr_list;
1984 if ((sofd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1985 n_perr(_("could not create socket"), 0);
1986 assert(sofd == -1 && errval == 0);
1987 goto jjumped;
1990 memset(&servaddr, 0, sizeof servaddr);
1991 servaddr.sin_family = AF_INET;
1992 servaddr.sin_port = htons(urlp->url_portno);
1993 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1994 if (options & OPT_VERB)
1995 n_err(_("%sConnecting to \"%s:%d\" ... "),
1996 "", inet_ntoa(**pptr), (int)urlp->url_portno);
1997 # ifdef HAVE_SO_SNDTIMEO
1998 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1999 # endif
2000 if (connect(sofd, (struct sockaddr*)&servaddr, sizeof servaddr)) {
2001 errval = errno;
2002 close(sofd);
2003 sofd = -1;
2005 jjumped:
2006 # endif /* !HAVE_GETADDRINFO */
2008 hold_sigs();
2009 safe_signal(SIGINT, oint);
2010 safe_signal(SIGHUP, ohup);
2011 rele_sigs();
2013 if (sofd < 0) {
2014 if (errval != 0) {
2015 errno = errval;
2016 n_perr(_("Could not connect"), 0);
2018 goto jleave;
2021 if (options & OPT_VERB)
2022 n_err(_("connected.\n"));
2024 /* And the regular timeouts XXX configurable */
2025 # ifdef HAVE_SO_SNDTIMEO
2026 tv.tv_sec = 42;
2027 tv.tv_usec = 0;
2028 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
2029 (void)setsockopt(sofd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
2030 # endif
2031 # ifdef HAVE_SO_LINGER
2032 li.l_onoff = 1;
2033 li.l_linger = 42;
2034 (void)setsockopt(sofd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
2035 # endif
2037 memset(sp, 0, sizeof *sp);
2038 sp->s_fd = sofd;
2040 /* SSL/TLS upgrade? */
2041 # ifdef HAVE_SSL
2042 if (urlp->url_needs_tls) {
2043 hold_sigs();
2044 ohup = safe_signal(SIGHUP, &__sopen_onsig);
2045 oint = safe_signal(SIGINT, &__sopen_onsig);
2046 if (sigsetjmp(__sopen_actjmp, 0)) {
2047 n_err(_("%s during SSL/TLS handshake\n"),
2048 (__sopen_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
2049 goto jsclose;
2051 rele_sigs();
2053 if (ssl_open(urlp, sp) != OKAY) {
2054 jsclose:
2055 sclose(sp);
2056 sofd = -1;
2059 hold_sigs();
2060 safe_signal(SIGINT, oint);
2061 safe_signal(SIGHUP, ohup);
2062 rele_sigs();
2064 # endif /* HAVE_SSL */
2066 jleave:
2067 /* May need to bounce the signal to the lex.c trampoline (or wherever) */
2068 if (__sopen_sig != 0) {
2069 sigset_t cset;
2070 sigemptyset(&cset);
2071 sigaddset(&cset, __sopen_sig);
2072 sigprocmask(SIG_UNBLOCK, &cset, NULL);
2073 n_raise(__sopen_sig);
2075 NYD_LEAVE;
2076 return (sofd >= 0);
2079 FL int
2080 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
2081 SMALLOC_DEBUG_ARGS)
2083 int rv;
2084 size_t lsize;
2085 char *lp_base, *lp;
2086 NYD2_ENTER;
2088 lsize = *linesize;
2089 lp_base = *line;
2090 lp = lp_base;
2092 if (sp->s_rsz < 0) {
2093 sclose(sp);
2094 rv = sp->s_rsz;
2095 goto jleave;
2098 do {
2099 if (lp_base == NULL || PTRCMP(lp, >, lp_base + lsize - 128)) {
2100 size_t diff = PTR2SIZE(lp - lp_base);
2101 *linesize = (lsize += 256); /* XXX magic */
2102 *line = lp_base = (srealloc)(lp_base, lsize SMALLOC_DEBUG_ARGSCALL);
2103 lp = lp_base + diff;
2106 if (sp->s_rbufptr == NULL ||
2107 PTRCMP(sp->s_rbufptr, >=, sp->s_rbuf + sp->s_rsz)) {
2108 # ifdef HAVE_OPENSSL
2109 if (sp->s_use_ssl) {
2110 jssl_retry:
2111 sp->s_rsz = SSL_read(sp->s_ssl, sp->s_rbuf, sizeof sp->s_rbuf);
2112 if (sp->s_rsz <= 0) {
2113 if (sp->s_rsz < 0) {
2114 char o[512];
2115 switch(SSL_get_error(sp->s_ssl, sp->s_rsz)) {
2116 case SSL_ERROR_WANT_READ:
2117 case SSL_ERROR_WANT_WRITE:
2118 goto jssl_retry;
2120 snprintf(o, sizeof o, "%s",
2121 (sp->s_desc ? sp->s_desc : "socket"));
2122 ssl_gen_err("%s", o);
2124 break;
2126 } else
2127 # endif
2129 jagain:
2130 sp->s_rsz = read(sp->s_fd, sp->s_rbuf, sizeof sp->s_rbuf);
2131 if (sp->s_rsz <= 0) {
2132 if (sp->s_rsz < 0) {
2133 char o[512];
2134 if (errno == EINTR)
2135 goto jagain;
2136 snprintf(o, sizeof o, "%s",
2137 (sp->s_desc ? sp->s_desc : "socket"));
2138 n_perr(o, 0);
2140 break;
2143 sp->s_rbufptr = sp->s_rbuf;
2145 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
2146 *lp = '\0';
2147 lsize = PTR2SIZE(lp - lp_base);
2149 if (linelen)
2150 *linelen = lsize;
2151 rv = (int)lsize;
2152 jleave:
2153 NYD2_LEAVE;
2154 return rv;
2156 #endif /* HAVE_SOCKETS */
2158 FL void
2159 load(char const *name)
2161 struct str n;
2162 void *cond;
2163 FILE *in, *oldin;
2164 NYD_ENTER;
2166 if (name == NULL || *name == '\0' || (in = Fopen(name, "r")) == NULL)
2167 goto jleave;
2169 oldin = _fio_input;
2170 _fio_input = in;
2171 pstate |= PS_IN_LOAD;
2172 /* commands() may sreset(), copy over file name */
2173 n.l = strlen(name);
2174 n.s = ac_alloc(n.l +1);
2175 memcpy(n.s, name, n.l +1);
2177 cond = condstack_release();
2178 if (!commands())
2179 n_err(_("Stopped loading \"%s\" due to errors "
2180 "(enable *debug* for trace)\n"), n.s);
2181 condstack_take(cond);
2183 ac_free(n.s);
2184 pstate &= ~PS_IN_LOAD;
2185 _fio_input = oldin;
2186 Fclose(in);
2187 jleave:
2188 NYD_LEAVE;
2191 FL int
2192 c_source(void *v)
2194 int rv;
2195 NYD_ENTER;
2197 rv = _source_file(*(char**)v, FAL0) ? 0 : 1;
2198 NYD_LEAVE;
2199 return rv;
2202 FL int
2203 c_source_if(void *v) /* XXX obsolete?, support file tests in `if' etc.! */
2205 int rv;
2206 NYD_ENTER;
2208 rv = _source_file(*(char**)v, TRU1) ? 0 : 1;
2209 rv = 0;
2210 NYD_LEAVE;
2211 return rv;
2214 FL int
2215 unstack(void)
2217 int rv = 1;
2218 NYD_ENTER;
2220 if (_fio_stack_size == 0) {
2221 n_err(_("`source' stack over-pop\n"));
2222 pstate &= ~PS_SOURCING;
2223 goto jleave;
2226 Fclose(_fio_input);
2228 --_fio_stack_size;
2229 if (!condstack_take(_fio_stack[_fio_stack_size].s_cond))
2230 n_err(_("Unmatched \"if\"\n"));
2231 if (_fio_stack[_fio_stack_size].s_loading)
2232 pstate |= PS_LOADING;
2233 else
2234 pstate &= ~PS_LOADING;
2235 _fio_input = _fio_stack[_fio_stack_size].s_file;
2236 if (_fio_stack_size == 0) {
2237 if (pstate & PS_LOADING)
2238 pstate |= PS_SOURCING;
2239 else
2240 pstate &= ~PS_SOURCING;
2242 rv = 0;
2243 jleave:
2244 NYD_LEAVE;
2245 return rv;
2248 /* s-it-mode */