popen.c: some cleanup, use other wait_child() from now on
[s-mailx.git] / acmava.c
blob7225effa0e764ab94cc74210a4778a4f191b1815
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Account, macro and variable handling.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
45 * TODO in general it would be nice if it would be possible to define "macros"
46 * TODO etc. inside of other "macros"
49 #define MA_PRIME HSHSIZE
50 #define MA_HASH(S) (strhash(S) % MA_PRIME)
52 enum ma_flags {
53 MA_NONE = 0,
54 MA_ACC = 1<<0,
55 MA_TYPE_MASK = MA_ACC,
56 MA_UNDEF = 1<<1 /* Unlink after lookup */
59 struct macro {
60 struct macro *ma_next;
61 char *ma_name;
62 struct mline *ma_contents;
63 size_t ma_maxlen; /* Maximum line length */
64 enum ma_flags ma_flags;
65 struct var *ma_localopts; /* `account' unroll list, for `localopts' */
68 struct mline {
69 struct mline *l_next;
70 size_t l_length;
71 char l_line[VFIELD_SIZE(sizeof(size_t))];
74 struct var {
75 struct var *v_link;
76 char *v_name;
77 char *v_value;
80 struct lostack {
81 struct lostack *s_up; /* Outer context */
82 struct macro *s_mac; /* Context (`account' or `define') */
83 struct var *s_localopts;
84 bool_t s_unroll; /* Unroll? */
87 static struct macro *_acc_curr; /* Currently active account */
88 static struct lostack *_localopts; /* Currently executing macro unroll list */
90 /* TODO once we have a dynamically sized hashtable we could unite _macros and
91 * TODO _variables into a single hashtable, stripping down fun interface;
92 * TODO also, setting and clearing a variable can be easily joined */
93 static struct macro *_macros[MA_PRIME]; /* TODO dynamically spaced */
94 static struct var *_vars[MA_PRIME]; /* TODO dynamically spaced */
96 /* Special cased value string allocation */
97 static char * _vcopy(char const *str);
98 static void _vfree(char *cp);
100 /* Check for special housekeeping. */
101 static bool_t _check_special_vars(char const *name, bool_t enable,
102 char **val);
104 /* If a variable name begins with a lowercase-character and contains at
105 * least one '@', it is converted to all-lowercase. This is necessary
106 * for lookups of names based on email addresses.
108 * Following the standard, only the part following the last '@' should
109 * be lower-cased, but practice has established otherwise here */
110 static char const * _canonify(char const *vn);
112 /* Locate a variable and return its variable node */
113 static struct var * _lookup(char const *name, ui_it h, bool_t hisset);
115 /* Line *cp* consists solely of WS and a } */
116 static bool_t _is_closing_angle(char const *cp);
118 /* Lookup for macros/accounts */
119 static struct macro *_malook(char const *name, struct macro *data,
120 enum ma_flags mafl);
122 /* Walk all lines of a macro and execute() them */
123 static int _maexec(struct macro const *mp, struct var **unroll_store);
125 /* User display helpers */
126 static int _list_macros(enum ma_flags mafl);
128 /* */
129 static bool_t _define1(char const *name, enum ma_flags mafl);
130 static void _undef1(char const *name, enum ma_flags mafl);
131 static void _freelines(struct mline *lp);
133 /* qsort(3) helper */
134 static int __var_list_all_cmp(void const *s1, void const *s2);
136 /* Update replay-log */
137 static void _localopts_add(struct lostack *losp, char const *name,
138 struct var *ovap);
139 static void _localopts_unroll(struct var **vapp);
141 static char *
142 _vcopy(char const *str)
144 char *news;
145 size_t len;
147 if (*str == '\0')
148 news = UNCONST("");
149 else {
150 len = strlen(str) + 1;
151 news = smalloc(len);
152 memcpy(news, str, len);
154 return news;
157 static void
158 _vfree(char *cp)
160 if (*cp != '\0')
161 free(cp);
164 static bool_t
165 _check_special_vars(char const *name, bool_t enable, char **val)
167 /* TODO _check_special_vars --> value cache
168 * TODO in general: some may not be unset etc. etc. All this shouldn't
169 * TODO be handled like this, but through a generic value-cache interface,
170 * TODO which may apply constaints *before* use; also see below */
171 char *cp = NULL;
172 bool_t rv = TRU1;
173 int flag = 0;
175 if (strcmp(name, "debug") == 0)
176 flag = OPT_DEBUG;
177 else if (strcmp(name, "header") == 0)
178 flag = OPT_N_FLAG, enable = !enable;
179 else if (strcmp(name, "skipemptybody") == 0)
180 flag = OPT_E_FLAG;
181 else if (strcmp(name, "verbose") == 0)
182 flag = OPT_VERBOSE;
183 else if (strcmp(name, "prompt") == 0)
184 flag = OPT_NOPROMPT, enable = !enable;
185 else if (strcmp(name, "folder") == 0) {
186 rv = (val == NULL || var_folder_updated(*val, &cp));
187 if (rv && cp != NULL) {
188 _vfree(*val);
189 /* It's smalloc()ed, but ensure we don't leak */
190 if (*cp == '\0') {
191 *val = UNCONST("");
192 free(cp);
193 } else
194 *val = cp;
197 #ifdef HAVE_NCL
198 else if (strcmp(name, "line-editor-cursor-right") == 0 &&
199 (rv = (val != NULL && *val != NULL))) {
200 char const *x = cp = *val;
201 int c;
203 /* Set with no value? *//*TODO invalid,but no way to "re-unset";see above
204 * TODO adjust tty.c when line-editor-cursor-right is properly handled in
205 * TODO here */
206 if (*x != '\0') {
207 do {
208 c = expand_shell_escape(&x, FAL0);
209 if (c < 0)
210 break;
211 *cp++ = (char)c;
212 } while (*x != '\0');
213 *cp++ = '\0';
216 #endif
218 if (flag) {
219 if (enable)
220 options |= flag;
221 else
222 options &= ~flag;
224 return rv;
227 static char const *
228 _canonify(char const *vn)
230 if (! upperchar(*vn)) {
231 char const *vp;
233 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
235 vn = (*vp == '@') ? i_strdup(vn) : vn;
237 return vn;
240 static struct var *
241 _lookup(char const *name, ui_it h, bool_t hisset)
243 struct var **vap, *lvp, *vp;
245 if (! hisset)
246 h = MA_HASH(name);
247 vap = _vars + h;
249 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
250 if (*vp->v_name == *name && strcmp(vp->v_name, name) == 0) {
251 /* Relink as head, hope it "sorts on usage" over time */
252 if (lvp != NULL) {
253 lvp->v_link = vp->v_link;
254 vp->v_link = *vap;
255 *vap = vp;
257 goto jleave;
259 vp = NULL;
260 jleave:
261 return vp;
264 static bool_t
265 _is_closing_angle(char const *cp)
267 bool_t rv = FAL0;
268 while (spacechar(*cp))
269 ++cp;
270 if (*cp++ != '}')
271 goto jleave;
272 while (spacechar(*cp))
273 ++cp;
274 rv = (*cp == '\0');
275 jleave:
276 return rv;
279 static struct macro *
280 _malook(char const *name, struct macro *data, enum ma_flags mafl)
282 enum ma_flags save_mafl;
283 ui_it h;
284 struct macro *lmp, *mp;
286 save_mafl = mafl;
287 mafl &= MA_TYPE_MASK;
288 h = MA_HASH(name);
290 for (lmp = NULL, mp = _macros[h]; mp != NULL; lmp = mp, mp = mp->ma_next) {
291 if ((mp->ma_flags & MA_TYPE_MASK) == mafl &&
292 strcmp(mp->ma_name, name) == 0) {
293 if (save_mafl & MA_UNDEF) {
294 if (lmp == NULL)
295 _macros[h] = mp->ma_next;
296 else
297 lmp->ma_next = mp->ma_next;
299 goto jleave;
303 if (data != NULL) {
304 data->ma_next = _macros[h];
305 _macros[h] = data;
306 mp = NULL;
308 jleave:
309 return mp;
312 static int
313 _maexec(struct macro const *mp, struct var **unroll_store)
315 struct lostack los;
316 int rv = 0;
317 struct mline const *lp;
318 char *buf = ac_alloc(mp->ma_maxlen + 1);
320 los.s_up = _localopts;
321 los.s_mac = UNCONST(mp);
322 los.s_localopts = NULL;
323 los.s_unroll = FAL0;
324 _localopts = &los;
326 for (lp = mp->ma_contents; lp; lp = lp->l_next) {
327 unset_allow_undefined = TRU1;
328 memcpy(buf, lp->l_line, lp->l_length + 1);
329 rv |= execute(buf, 0, lp->l_length); /* XXX break if != 0 ? */
330 unset_allow_undefined = FAL0;
333 _localopts = los.s_up;
334 if (unroll_store == NULL)
335 _localopts_unroll(&los.s_localopts);
336 else
337 *unroll_store = los.s_localopts;
339 ac_free(buf);
340 return rv;
343 static int
344 _list_macros(enum ma_flags mafl)
346 FILE *fp;
347 char *cp;
348 char const *typestr;
349 struct macro *mq;
350 ui_it ti, mc;
351 struct mline *lp;
353 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
354 perror("tmpfile");
355 return 1;
357 rm(cp);
358 Ftfree(&cp);
360 mafl &= MA_TYPE_MASK;
361 typestr = (mafl & MA_ACC) ? "account" : "define";
363 for (ti = mc = 0; ti < MA_PRIME; ++ti)
364 for (mq = _macros[ti]; mq; mq = mq->ma_next)
365 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
366 if (++mc > 1)
367 fputc('\n', fp);
368 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
369 for (lp = mq->ma_contents; lp; lp = lp->l_next)
370 fprintf(fp, " %s\n", lp->l_line);
371 fputs("}\n", fp);
373 if (mc)
374 page_or_print(fp, 0);
376 mc = (ui_it)ferror(fp);
377 Fclose(fp);
378 return (int)mc;
381 static bool_t
382 _define1(char const *name, enum ma_flags mafl)
384 bool_t rv = FAL0;
385 struct macro *mp;
386 struct mline *lp, *lst = NULL, *lnd = NULL;
387 char *linebuf = NULL, *cp;
388 size_t linesize = 0, maxlen = 0;
389 int n, i;
391 mp = scalloc(1, sizeof *mp);
392 mp->ma_name = sstrdup(name);
393 mp->ma_flags = mafl;
395 for (;;) {
396 n = readline_input(LNED_LF_ESC, "", &linebuf, &linesize);
397 if (n <= 0) {
398 fprintf(stderr, tr(75, "Unterminated %s definition: \"%s\".\n"),
399 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
400 if (sourcing)
401 unstack();
402 goto jerr;
404 if (_is_closing_angle(linebuf))
405 break;
407 /* Trim WS */
408 for (cp = linebuf, i = 0; i < n; ++cp, ++i)
409 if (! whitechar(*cp))
410 break;
411 if (i == n)
412 continue;
413 n -= i;
414 while (whitechar(cp[n - 1]))
415 if (--n == 0)
416 break;
417 if (n == 0)
418 continue;
420 maxlen = MAX(maxlen, (size_t)n);
421 cp[n++] = '\0';
423 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n);
424 memcpy(lp->l_line, cp, n);
425 lp->l_length = (size_t)--n;
426 if (lst != NULL) {
427 lnd->l_next = lp;
428 lnd = lp;
429 } else
430 lst = lnd = lp;
432 mp->ma_contents = lst;
433 mp->ma_maxlen = maxlen;
435 if (_malook(mp->ma_name, mp, mafl) != NULL) {
436 if (! (mafl & MA_ACC)) {
437 fprintf(stderr, tr(76, "A macro named \"%s\" already exists.\n"),
438 mp->ma_name);
439 lst = mp->ma_contents;
440 goto jerr;
442 _undef1(mp->ma_name, MA_ACC);
443 _malook(mp->ma_name, mp, MA_ACC);
446 rv = TRU1;
447 jleave:
448 if (linebuf != NULL)
449 free(linebuf);
450 return rv;
451 jerr:
452 if (lst != NULL)
453 _freelines(lst);
454 free(mp->ma_name);
455 free(mp);
456 goto jleave;
459 static void
460 _undef1(char const *name, enum ma_flags mafl)
462 struct macro *mp;
464 if ((mp = _malook(name, NULL, mafl | MA_UNDEF)) != NULL) {
465 _freelines(mp->ma_contents);
466 free(mp->ma_name);
467 free(mp);
471 static void
472 _freelines(struct mline *lp)
474 struct mline *lq;
476 for (lq = NULL; lp != NULL; ) {
477 if (lq != NULL)
478 free(lq);
479 lq = lp;
480 lp = lp->l_next;
482 if (lq)
483 free(lq);
486 static int
487 __var_list_all_cmp(void const *s1, void const *s2)
489 return strcmp(*(char**)UNCONST(s1), *(char**)UNCONST(s2));
492 static void
493 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
495 struct var *vap;
496 size_t nl, vl;
498 /* Propagate unrolling up the stack, as necessary */
499 while (! losp->s_unroll && (losp = losp->s_up) != NULL)
501 if (losp == NULL)
502 goto jleave;
504 /* We have found a level that wants to unroll; check wether it does it yet */
505 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
506 if (strcmp(vap->v_name, name) == 0)
507 goto jleave;
509 nl = strlen(name) + 1;
510 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
511 vap = smalloc(sizeof(*vap) + nl + vl);
512 vap->v_link = losp->s_localopts;
513 losp->s_localopts = vap;
514 vap->v_name = (char*)(vap + 1);
515 memcpy(vap->v_name, name, nl);
516 if (vl == 0)
517 vap->v_value = NULL;
518 else {
519 vap->v_value = (char*)(vap + 1) + nl;
520 memcpy(vap->v_value, ovap->v_value, vl);
522 jleave:
526 static void
527 _localopts_unroll(struct var **vapp)
529 struct lostack *save_los;
530 struct var *x, *vap;
532 vap = *vapp;
533 *vapp = NULL;
535 save_los = _localopts;
536 _localopts = NULL;
537 while (vap != NULL) {
538 x = vap;
539 vap = vap->v_link;
540 var_assign(x->v_name, x->v_value);
541 free(x);
543 _localopts = save_los;
546 FL bool_t
547 var_assign(char const *name, char const *val)
549 bool_t err;
550 struct var *vp;
551 ui_it h;
552 char *oval;
554 if (val == NULL) {
555 bool_t tmp = unset_allow_undefined;
556 unset_allow_undefined = TRU1;
557 err = var_unset(name);
558 unset_allow_undefined = tmp;
559 goto jleave;
562 name = _canonify(name);
563 h = MA_HASH(name);
564 vp = _lookup(name, h, TRU1);
566 /* Don't care what happens later on, store this in the unroll list */
567 if (_localopts != NULL)
568 _localopts_add(_localopts, name, vp);
570 if (vp == NULL) {
571 vp = (struct var*)scalloc(1, sizeof *vp);
572 vp->v_name = _vcopy(name);
573 vp->v_link = _vars[h];
574 _vars[h] = vp;
575 oval = UNCONST("");
576 } else
577 oval = vp->v_value;
578 vp->v_value = _vcopy(val);
580 /* Check if update allowed XXX wasteful on error! */
581 if ((err = !_check_special_vars(name, TRU1, &vp->v_value))) {
582 char *cp = vp->v_value;
583 vp->v_value = oval;
584 oval = cp;
586 if (*oval != '\0')
587 _vfree(oval);
588 jleave:
589 return err;
592 FL bool_t
593 var_unset(char const *name)
595 int err = TRU1;
596 ui_it h;
597 struct var *vp;
599 name = _canonify(name);
600 h = MA_HASH(name);
601 vp = _lookup(name, h, TRU1);
603 if (vp == NULL) {
604 if (!sourcing && !unset_allow_undefined) {
605 fprintf(stderr, tr(203, "\"%s\": undefined variable\n"), name);
606 goto jleave;
608 } else {
609 if (_localopts != NULL)
610 _localopts_add(_localopts, name, vp);
612 /* Always listhead after _lookup() */
613 _vars[h] = _vars[h]->v_link;
614 _vfree(vp->v_name);
615 _vfree(vp->v_value);
616 free(vp);
618 if (!_check_special_vars(name, FAL0, NULL))
619 goto jleave;
621 err = FAL0;
622 jleave:
623 return err;
626 FL char *
627 var_lookup(char const *name, bool_t look_environ)
629 struct var *vp;
630 char *rv;
632 name = _canonify(name);
633 if ((vp = _lookup(name, 0, FAL0)) != NULL)
634 rv = vp->v_value;
635 else if (! look_environ)
636 rv = NULL;
637 else if ((rv = getenv(name)) != NULL && *rv != '\0')
638 rv = savestr(rv);
639 return rv;
642 FL void
643 var_list_all(void)
645 FILE *fp;
646 char *cp, **vacp, **cap;
647 struct var *vp;
648 size_t no, i;
649 char const *fmt;
651 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
652 perror("tmpfile");
653 goto jleave;
655 rm(cp);
656 Ftfree(&cp);
658 for (no = i = 0; i < MA_PRIME; ++i)
659 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
660 ++no;
661 vacp = salloc(no * sizeof(*vacp));
662 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
663 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
664 *cap++ = vp->v_name;
666 if (no > 1)
667 qsort(vacp, no, sizeof *vacp, &__var_list_all_cmp);
669 i = (boption("bsdcompat") || boption("bsdset"));
670 fmt = (i != 0) ? "%s\t%s\n" : "%s=\"%s\"\n";
672 for (cap = vacp; no != 0; ++cap, --no) {
673 cp = value(*cap); /* TODO internal lookup; binary? value? */
674 if (cp == NULL)
675 cp = UNCONST("");
676 if (i || *cp != '\0')
677 fprintf(fp, fmt, *cap, cp);
678 else
679 fprintf(fp, "%s\n", *cap);
682 page_or_print(fp, (size_t)(cap - vacp));
683 Fclose(fp);
684 jleave:
688 FL int
689 cdefine(void *v)
691 int rv = 1;
692 char **args = v;
693 char const *errs;
695 if (args[0] == NULL) {
696 errs = tr(504, "Missing macro name to `define'");
697 goto jerr;
699 if (args[1] == NULL || strcmp(args[1], "{") || args[2] != NULL) {
700 errs = tr(505, "Syntax is: define <name> {");
701 goto jerr;
703 rv = ! _define1(args[0], MA_NONE);
704 jleave:
705 return rv;
706 jerr:
707 fprintf(stderr, "%s\n", errs);
708 goto jleave;
711 FL int
712 cundef(void *v)
714 int rv = 1;
715 char **args = v;
717 if (*args == NULL) {
718 fprintf(stderr, tr(506, "Missing macro name to `undef'\n"));
719 goto jleave;
722 _undef1(*args, MA_NONE);
723 while (*++args);
724 rv = 0;
725 jleave:
726 return rv;
729 FL int
730 ccall(void *v)
732 int rv = 1;
733 char **args = v;
734 char const *errs, *name;
735 struct macro *mp;
737 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
738 errs = tr(507, "Syntax is: call <%s>\n");
739 name = "name";
740 goto jerr;
743 if ((mp = _malook(*args, NULL, MA_NONE)) == NULL) {
744 errs = tr(508, "Undefined macro called: \"%s\"\n");
745 name = *args;
746 goto jerr;
749 rv = _maexec(mp, NULL);
750 jleave:
751 return rv;
752 jerr:
753 fprintf(stderr, errs, name);
754 goto jleave;
757 FL int
758 callhook(char const *name, int nmail)
760 int len, rv;
761 struct macro *mp;
762 char *var, *cp;
764 var = ac_alloc(len = strlen(name) + 13);
765 snprintf(var, len, "folder-hook-%s", name);
766 if ((cp = value(var)) == NULL && (cp = value("folder-hook")) == NULL) {
767 rv = 0;
768 goto jleave;
770 if ((mp = _malook(cp, NULL, MA_NONE)) == NULL) {
771 fprintf(stderr, tr(49, "Cannot call hook for folder \"%s\": "
772 "Macro \"%s\" does not exist.\n"), name, cp);
773 rv = 1;
774 goto jleave;
777 inhook = nmail ? 3 : 1;
778 rv = _maexec(mp, NULL);
779 inhook = 0;
780 jleave:
781 ac_free(var);
782 return rv;
785 FL int
786 cdefines(void *v)
788 (void)v;
789 return _list_macros(MA_NONE);
792 FL int
793 c_account(void *v)
795 char **args = v;
796 struct macro *mp;
797 int rv = 1, i, oqf, nqf;
799 if (args[0] == NULL) {
800 rv = _list_macros(MA_ACC);
801 goto jleave;
804 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
805 if (args[2] != NULL) {
806 fprintf(stderr, tr(517, "Syntax is: account <name> {\n"));
807 goto jleave;
809 if (asccasecmp(args[0], ACCOUNT_NULL) == 0) {
810 fprintf(stderr, tr(521, "Error: `%s' is a reserved name.\n"),
811 ACCOUNT_NULL);
812 goto jleave;
814 rv = ! _define1(args[0], MA_ACC);
815 goto jleave;
818 if (inhook) {
819 fprintf(stderr, tr(518, "Cannot change account from within a hook.\n"));
820 goto jleave;
823 save_mbox_for_possible_quitstuff();
825 mp = NULL;
826 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
827 (mp = _malook(args[0], NULL, MA_ACC)) == NULL) {
828 fprintf(stderr, tr(519, "Account `%s' does not exist.\n"), args[0]);
829 goto jleave;
832 oqf = savequitflags();
833 if (_acc_curr != NULL)
834 _localopts_unroll(&_acc_curr->ma_localopts);
835 account_name = (mp != NULL) ? mp->ma_name : NULL;
836 _acc_curr = mp;
838 if (mp != NULL && _maexec(mp, &mp->ma_localopts) == CBAD) {
839 /* XXX account switch incomplete, unroll? */
840 fprintf(stderr, tr(520, "Switching to account `%s' failed.\n"), args[0]);
841 goto jleave;
844 if (! starting && ! inhook) {
845 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
846 restorequitflags(oqf);
847 if ((i = setfile("%", 0)) < 0)
848 goto jleave;
849 callhook(mailname, 0);
850 if (i > 0 && ! boption("emptystart"))
851 goto jleave;
852 announce(boption("bsdcompat") || boption("bsdannounce"));
853 restorequitflags(nqf);
855 rv = 0;
856 jleave:
857 return rv;
860 FL int
861 c_localopts(void *v)
863 int rv = 1;
864 char **c = v;
866 if (_localopts == NULL) {
867 fprintf(stderr, tr(522,
868 "Cannot use `localopts' but from within a `define' or `account'\n"));
869 goto jleave;
872 _localopts->s_unroll = (**c == '0') ? FAL0 : TRU1;
873 rv = 0;
874 jleave:
875 return rv;
878 /* vim:set fenc=utf-8:s-it-mode */