create-okey-map.pl: added
[s-mailx.git] / acmava.c
blob2a2edc79729d8aa41044dd3fa6b8ec00f54d93ee
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 * HOWTO add a new non-dynamic binary or value option:
46 * - add an entry to nail.h:enum okeys
47 * - run create-okey-map.pl
48 * - update nail.1
51 /* Note: changing the hash function must be reflected in create-okey-map.pl */
52 #define MA_PRIME HSHSIZE
53 #define MA_HASH(S) (torek_hash(S) % MA_PRIME)
55 enum ma_flags {
56 MA_NONE = 0,
57 MA_ACC = 1<<0,
58 MA_TYPE_MASK = MA_ACC,
59 MA_UNDEF = 1<<1 /* Unlink after lookup */
62 struct macro {
63 struct macro *ma_next;
64 char *ma_name;
65 struct mline *ma_contents;
66 size_t ma_maxlen; /* Maximum line length */
67 enum ma_flags ma_flags;
68 struct var *ma_localopts; /* `account' unroll list, for `localopts' */
71 struct mline {
72 struct mline *l_next;
73 size_t l_length;
74 char l_line[VFIELD_SIZE(sizeof(size_t))];
77 struct var {
78 struct var *v_link;
79 char *v_name;
80 char *v_value;
83 struct var_map {
84 ui32_t vm_hash;
85 ui16_t vm_keyoff;
86 ui8_t vm_binary;
87 ui8_t vm_special;
90 struct lostack {
91 struct lostack *s_up; /* Outer context */
92 struct macro *s_mac; /* Context (`account' or `define') */
93 struct var *s_localopts;
94 bool_t s_unroll; /* Unroll? */
97 /* Include the constant create-okey-map.pl output */
98 #include "okeys.h"
100 static struct macro *_acc_curr; /* Currently active account */
101 static struct lostack *_localopts; /* Currently executing macro unroll list */
103 /* TODO once we have a dynamically sized hashtable we could unite _macros and
104 * TODO _variables into a single hashtable, stripping down fun interface;
105 * TODO also, setting and clearing a variable can be easily joined */
106 static struct macro *_macros[MA_PRIME]; /* TODO dynamically spaced */
107 static struct var *_vars[MA_PRIME]; /* TODO dynamically spaced */
109 /* Special cased value string allocation */
110 static char * _vcopy(char const *str);
111 static void _vfree(char *cp);
113 /* Check for special housekeeping. */
114 static bool_t _check_special_vars(char const *name, bool_t enable,
115 char **val);
117 /* If a variable name begins with a lowercase-character and contains at
118 * least one '@', it is converted to all-lowercase. This is necessary
119 * for lookups of names based on email addresses.
121 * Following the standard, only the part following the last '@' should
122 * be lower-cased, but practice has established otherwise here */
123 static char const * _canonify(char const *vn);
125 /* Locate a variable and return its variable node */
126 static struct var * _lookup(char const *name, ui_it h, bool_t hisset);
128 /* Legacy */
129 static bool_t __var_assign(char const *name, char const *val);
130 static bool_t __var_unset(char const *name);
131 static char * __var_lookup(char const *name, bool_t look_environ);
133 /* Line *cp* consists solely of WS and a } */
134 static bool_t _is_closing_angle(char const *cp);
136 /* Lookup for macros/accounts */
137 static struct macro *_malook(char const *name, struct macro *data,
138 enum ma_flags mafl);
140 /* Walk all lines of a macro and execute() them */
141 static int _maexec(struct macro const *mp, struct var **unroll_store);
143 /* User display helpers */
144 static int _list_macros(enum ma_flags mafl);
146 /* */
147 static bool_t _define1(char const *name, enum ma_flags mafl);
148 static void _undef1(char const *name, enum ma_flags mafl);
149 static void _freelines(struct mline *lp);
151 /* qsort(3) helper */
152 static int __var_list_all_cmp(void const *s1, void const *s2);
154 /* Update replay-log */
155 static void _localopts_add(struct lostack *losp, char const *name,
156 struct var *ovap);
157 static void _localopts_unroll(struct var **vapp);
159 static char *
160 _vcopy(char const *str)
162 char *news;
163 size_t len;
165 if (*str == '\0')
166 news = UNCONST("");
167 else {
168 len = strlen(str) + 1;
169 news = smalloc(len);
170 memcpy(news, str, len);
172 return news;
175 static void
176 _vfree(char *cp)
178 if (*cp != '\0')
179 free(cp);
182 static bool_t
183 _check_special_vars(char const *name, bool_t enable, char **val)
185 /* TODO _check_special_vars --> value cache
186 * TODO in general: some may not be unset etc. etc. All this shouldn't
187 * TODO be handled like this, but through a generic value-cache interface,
188 * TODO which may apply constaints *before* use; also see below */
189 char *cp = NULL;
190 bool_t rv = TRU1;
191 int flag = 0;
193 if (strcmp(name, "debug") == 0)
194 flag = OPT_DEBUG;
195 else if (strcmp(name, "header") == 0)
196 flag = OPT_N_FLAG, enable = !enable;
197 else if (strcmp(name, "skipemptybody") == 0)
198 flag = OPT_E_FLAG;
199 else if (strcmp(name, "verbose") == 0)
200 flag = OPT_VERBOSE;
201 else if (strcmp(name, "folder") == 0) {
202 rv = (val == NULL || var_folder_updated(*val, &cp));
203 if (rv && cp != NULL) {
204 _vfree(*val);
205 /* It's smalloc()ed, but ensure we don't leak */
206 if (*cp == '\0') {
207 *val = UNCONST("");
208 free(cp);
209 } else
210 *val = cp;
213 #ifdef HAVE_NCL
214 else if (strcmp(name, "line-editor-cursor-right") == 0 &&
215 (rv = (val != NULL && *val != NULL))) {
216 char const *x = cp = *val;
217 int c;
219 /* Set with no value? *//*TODO invalid,but no way to "re-unset";see above
220 * TODO adjust tty.c when line-editor-cursor-right is properly handled in
221 * TODO here */
222 if (*x != '\0') {
223 do {
224 c = expand_shell_escape(&x, FAL0);
225 if (c < 0)
226 break;
227 *cp++ = (char)c;
228 } while (*x != '\0');
229 *cp++ = '\0';
232 #endif
234 if (flag) {
235 if (enable)
236 options |= flag;
237 else
238 options &= ~flag;
240 return rv;
243 static char const *
244 _canonify(char const *vn)
246 if (! upperchar(*vn)) {
247 char const *vp;
249 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
251 vn = (*vp == '@') ? i_strdup(vn) : vn;
253 return vn;
256 static struct var *
257 _lookup(char const *name, ui_it h, bool_t hisset)
259 struct var **vap, *lvp, *vp;
261 if (! hisset)
262 h = MA_HASH(name);
263 vap = _vars + h;
265 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
266 if (*vp->v_name == *name && strcmp(vp->v_name, name) == 0) {
267 /* Relink as head, hope it "sorts on usage" over time */
268 if (lvp != NULL) {
269 lvp->v_link = vp->v_link;
270 vp->v_link = *vap;
271 *vap = vp;
273 goto jleave;
275 vp = NULL;
276 jleave:
277 return vp;
280 static bool_t
281 __var_assign(char const *name, char const *val)
283 bool_t err;
284 struct var *vp;
285 ui_it h;
286 char *oval;
288 if (val == NULL) {
289 bool_t tmp = var_clear_allow_undefined;
290 var_clear_allow_undefined = TRU1;
291 err = __var_unset(name);
292 var_clear_allow_undefined = tmp;
293 goto jleave;
296 name = _canonify(name);
297 h = MA_HASH(name);
298 vp = _lookup(name, h, TRU1);
300 /* Don't care what happens later on, store this in the unroll list */
301 if (_localopts != NULL)
302 _localopts_add(_localopts, name, vp);
304 if (vp == NULL) {
305 vp = (struct var*)scalloc(1, sizeof *vp);
306 vp->v_name = _vcopy(name);
307 vp->v_link = _vars[h];
308 _vars[h] = vp;
309 oval = UNCONST("");
310 } else
311 oval = vp->v_value;
312 vp->v_value = _vcopy(val);
314 /* Check if update allowed XXX wasteful on error! */
315 if ((err = !_check_special_vars(name, TRU1, &vp->v_value))) {
316 char *cp = vp->v_value;
317 vp->v_value = oval;
318 oval = cp;
320 if (*oval != '\0')
321 _vfree(oval);
322 jleave:
323 return err;
326 static bool_t
327 __var_unset(char const *name)
329 int err = TRU1;
330 ui_it h;
331 struct var *vp;
333 name = _canonify(name);
334 h = MA_HASH(name);
335 vp = _lookup(name, h, TRU1);
337 if (vp == NULL) {
338 if (!sourcing && !var_clear_allow_undefined) {
339 fprintf(stderr, tr(203, "\"%s\": undefined variable\n"), name);
340 goto jleave;
342 } else {
343 if (_localopts != NULL)
344 _localopts_add(_localopts, name, vp);
346 /* Always listhead after _lookup() */
347 _vars[h] = _vars[h]->v_link;
348 _vfree(vp->v_name);
349 _vfree(vp->v_value);
350 free(vp);
352 if (!_check_special_vars(name, FAL0, NULL))
353 goto jleave;
355 err = FAL0;
356 jleave:
357 return err;
360 static char *
361 __var_lookup(char const *name, bool_t look_environ)
363 struct var *vp;
364 char *rv;
366 name = _canonify(name);
367 if ((vp = _lookup(name, 0, FAL0)) != NULL)
368 rv = vp->v_value;
369 else if (! look_environ)
370 rv = NULL;
371 else if ((rv = getenv(name)) != NULL && *rv != '\0')
372 rv = savestr(rv);
373 return rv;
376 static bool_t
377 _is_closing_angle(char const *cp)
379 bool_t rv = FAL0;
380 while (spacechar(*cp))
381 ++cp;
382 if (*cp++ != '}')
383 goto jleave;
384 while (spacechar(*cp))
385 ++cp;
386 rv = (*cp == '\0');
387 jleave:
388 return rv;
391 static struct macro *
392 _malook(char const *name, struct macro *data, enum ma_flags mafl)
394 enum ma_flags save_mafl;
395 ui_it h;
396 struct macro *lmp, *mp;
398 save_mafl = mafl;
399 mafl &= MA_TYPE_MASK;
400 h = MA_HASH(name);
402 for (lmp = NULL, mp = _macros[h]; mp != NULL; lmp = mp, mp = mp->ma_next) {
403 if ((mp->ma_flags & MA_TYPE_MASK) == mafl &&
404 strcmp(mp->ma_name, name) == 0) {
405 if (save_mafl & MA_UNDEF) {
406 if (lmp == NULL)
407 _macros[h] = mp->ma_next;
408 else
409 lmp->ma_next = mp->ma_next;
411 goto jleave;
415 if (data != NULL) {
416 data->ma_next = _macros[h];
417 _macros[h] = data;
418 mp = NULL;
420 jleave:
421 return mp;
424 static int
425 _maexec(struct macro const *mp, struct var **unroll_store)
427 struct lostack los;
428 int rv = 0;
429 struct mline const *lp;
430 char *buf = ac_alloc(mp->ma_maxlen + 1);
432 los.s_up = _localopts;
433 los.s_mac = UNCONST(mp);
434 los.s_localopts = NULL;
435 los.s_unroll = FAL0;
436 _localopts = &los;
438 for (lp = mp->ma_contents; lp; lp = lp->l_next) {
439 var_clear_allow_undefined = TRU1;
440 memcpy(buf, lp->l_line, lp->l_length + 1);
441 rv |= execute(buf, 0, lp->l_length); /* XXX break if != 0 ? */
442 var_clear_allow_undefined = FAL0;
445 _localopts = los.s_up;
446 if (unroll_store == NULL)
447 _localopts_unroll(&los.s_localopts);
448 else
449 *unroll_store = los.s_localopts;
451 ac_free(buf);
452 return rv;
455 static int
456 _list_macros(enum ma_flags mafl)
458 FILE *fp;
459 char *cp;
460 char const *typestr;
461 struct macro *mq;
462 ui_it ti, mc;
463 struct mline *lp;
465 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
466 perror("tmpfile");
467 return 1;
469 rm(cp);
470 Ftfree(&cp);
472 mafl &= MA_TYPE_MASK;
473 typestr = (mafl & MA_ACC) ? "account" : "define";
475 for (ti = mc = 0; ti < MA_PRIME; ++ti)
476 for (mq = _macros[ti]; mq; mq = mq->ma_next)
477 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
478 if (++mc > 1)
479 fputc('\n', fp);
480 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
481 for (lp = mq->ma_contents; lp; lp = lp->l_next)
482 fprintf(fp, " %s\n", lp->l_line);
483 fputs("}\n", fp);
485 if (mc)
486 page_or_print(fp, 0);
488 mc = (ui_it)ferror(fp);
489 Fclose(fp);
490 return (int)mc;
493 static bool_t
494 _define1(char const *name, enum ma_flags mafl)
496 bool_t rv = FAL0;
497 struct macro *mp;
498 struct mline *lp, *lst = NULL, *lnd = NULL;
499 char *linebuf = NULL, *cp;
500 size_t linesize = 0, maxlen = 0;
501 int n, i;
503 mp = scalloc(1, sizeof *mp);
504 mp->ma_name = sstrdup(name);
505 mp->ma_flags = mafl;
507 for (;;) {
508 n = readline_input(LNED_LF_ESC, "", &linebuf, &linesize);
509 if (n <= 0) {
510 fprintf(stderr, tr(75, "Unterminated %s definition: \"%s\".\n"),
511 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
512 if (sourcing)
513 unstack();
514 goto jerr;
516 if (_is_closing_angle(linebuf))
517 break;
519 /* Trim WS */
520 for (cp = linebuf, i = 0; i < n; ++cp, ++i)
521 if (! whitechar(*cp))
522 break;
523 if (i == n)
524 continue;
525 n -= i;
526 while (whitechar(cp[n - 1]))
527 if (--n == 0)
528 break;
529 if (n == 0)
530 continue;
532 maxlen = MAX(maxlen, (size_t)n);
533 cp[n++] = '\0';
535 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n);
536 memcpy(lp->l_line, cp, n);
537 lp->l_length = (size_t)--n;
538 if (lst != NULL) {
539 lnd->l_next = lp;
540 lnd = lp;
541 } else
542 lst = lnd = lp;
544 mp->ma_contents = lst;
545 mp->ma_maxlen = maxlen;
547 if (_malook(mp->ma_name, mp, mafl) != NULL) {
548 if (! (mafl & MA_ACC)) {
549 fprintf(stderr, tr(76, "A macro named \"%s\" already exists.\n"),
550 mp->ma_name);
551 lst = mp->ma_contents;
552 goto jerr;
554 _undef1(mp->ma_name, MA_ACC);
555 _malook(mp->ma_name, mp, MA_ACC);
558 rv = TRU1;
559 jleave:
560 if (linebuf != NULL)
561 free(linebuf);
562 return rv;
563 jerr:
564 if (lst != NULL)
565 _freelines(lst);
566 free(mp->ma_name);
567 free(mp);
568 goto jleave;
571 static void
572 _undef1(char const *name, enum ma_flags mafl)
574 struct macro *mp;
576 if ((mp = _malook(name, NULL, mafl | MA_UNDEF)) != NULL) {
577 _freelines(mp->ma_contents);
578 free(mp->ma_name);
579 free(mp);
583 static void
584 _freelines(struct mline *lp)
586 struct mline *lq;
588 for (lq = NULL; lp != NULL; ) {
589 if (lq != NULL)
590 free(lq);
591 lq = lp;
592 lp = lp->l_next;
594 if (lq)
595 free(lq);
598 static int
599 __var_list_all_cmp(void const *s1, void const *s2)
601 return strcmp(*(char**)UNCONST(s1), *(char**)UNCONST(s2));
604 static void
605 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
607 struct var *vap;
608 size_t nl, vl;
610 /* Propagate unrolling up the stack, as necessary */
611 while (! losp->s_unroll && (losp = losp->s_up) != NULL)
613 if (losp == NULL)
614 goto jleave;
616 /* We have found a level that wants to unroll; check wether it does it yet */
617 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
618 if (strcmp(vap->v_name, name) == 0)
619 goto jleave;
621 nl = strlen(name) + 1;
622 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
623 vap = smalloc(sizeof(*vap) + nl + vl);
624 vap->v_link = losp->s_localopts;
625 losp->s_localopts = vap;
626 vap->v_name = (char*)(vap + 1);
627 memcpy(vap->v_name, name, nl);
628 if (vl == 0)
629 vap->v_value = NULL;
630 else {
631 vap->v_value = (char*)(vap + 1) + nl;
632 memcpy(vap->v_value, ovap->v_value, vl);
634 jleave:
638 static void
639 _localopts_unroll(struct var **vapp)
641 struct lostack *save_los;
642 struct var *x, *vap;
644 vap = *vapp;
645 *vapp = NULL;
647 save_los = _localopts;
648 _localopts = NULL;
649 while (vap != NULL) {
650 x = vap;
651 vap = vap->v_link;
652 __var_assign(x->v_name, x->v_value);
653 free(x);
655 _localopts = save_los;
658 FL char *
659 _var_oklook(enum okeys okey)
661 char const *k = _var_keydat + _var_map[okey].vm_keyoff;
663 return __var_lookup(k, TRU1);
666 FL bool_t
667 _var_okset(enum okeys okey, uintptr_t val)
669 char const *k = _var_keydat + _var_map[okey].vm_keyoff;
671 return __var_assign(k, (val == 0x1) ? "" : (char const*)val);
674 FL bool_t
675 _var_okclear(enum okeys okey)
677 char const *k = _var_keydat + _var_map[okey].vm_keyoff;
679 return __var_unset(k);
682 FL char *
683 _var_voklook(char const *vokey)
685 return __var_lookup(vokey, TRU1);
688 FL bool_t
689 _var_vokset(char const *vokey, uintptr_t val)
691 return __var_assign(vokey, (val == 0x1) ? "" : (char const*)val);
694 FL bool_t
695 _var_vokclear(char const *vokey)
697 return __var_unset(vokey);
700 FL void
701 var_list_all(void)
703 FILE *fp;
704 char *cp, **vacp, **cap;
705 struct var *vp;
706 size_t no, i;
707 char const *fmt;
709 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
710 perror("tmpfile");
711 goto jleave;
713 rm(cp);
714 Ftfree(&cp);
716 for (no = i = 0; i < MA_PRIME; ++i)
717 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
718 ++no;
719 vacp = salloc(no * sizeof(*vacp));
720 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
721 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
722 *cap++ = vp->v_name;
724 if (no > 1)
725 qsort(vacp, no, sizeof *vacp, &__var_list_all_cmp);
727 i = (ok_blook(bsdcompat) || ok_blook(bsdset));
728 fmt = (i != 0) ? "%s\t%s\n" : "%s=\"%s\"\n";
730 for (cap = vacp; no != 0; ++cap, --no) {
731 cp = vok_vlook(*cap); /* XXX when lookup checks value/binary, change */
732 if (cp == NULL)
733 cp = UNCONST("");
734 if (i || *cp != '\0')
735 fprintf(fp, fmt, *cap, cp);
736 else
737 fprintf(fp, "%s\n", *cap);
740 page_or_print(fp, (size_t)(cap - vacp));
741 Fclose(fp);
742 jleave:
746 FL int
747 cdefine(void *v)
749 int rv = 1;
750 char **args = v;
751 char const *errs;
753 if (args[0] == NULL) {
754 errs = tr(504, "Missing macro name to `define'");
755 goto jerr;
757 if (args[1] == NULL || strcmp(args[1], "{") || args[2] != NULL) {
758 errs = tr(505, "Syntax is: define <name> {");
759 goto jerr;
761 rv = ! _define1(args[0], MA_NONE);
762 jleave:
763 return rv;
764 jerr:
765 fprintf(stderr, "%s\n", errs);
766 goto jleave;
769 FL int
770 cundef(void *v)
772 int rv = 1;
773 char **args = v;
775 if (*args == NULL) {
776 fprintf(stderr, tr(506, "Missing macro name to `undef'\n"));
777 goto jleave;
780 _undef1(*args, MA_NONE);
781 while (*++args);
782 rv = 0;
783 jleave:
784 return rv;
787 FL int
788 ccall(void *v)
790 int rv = 1;
791 char **args = v;
792 char const *errs, *name;
793 struct macro *mp;
795 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
796 errs = tr(507, "Syntax is: call <%s>\n");
797 name = "name";
798 goto jerr;
801 if ((mp = _malook(*args, NULL, MA_NONE)) == NULL) {
802 errs = tr(508, "Undefined macro called: \"%s\"\n");
803 name = *args;
804 goto jerr;
807 rv = _maexec(mp, NULL);
808 jleave:
809 return rv;
810 jerr:
811 fprintf(stderr, errs, name);
812 goto jleave;
815 FL int
816 callhook(char const *name, int nmail)
818 int len, rv;
819 struct macro *mp;
820 char *var, *cp;
822 var = ac_alloc(len = strlen(name) + 13);
823 snprintf(var, len, "folder-hook-%s", name);
824 if ((cp = vok_vlook(var)) == NULL && (cp = ok_vlook(folder_hook)) == NULL) {
825 rv = 0;
826 goto jleave;
828 if ((mp = _malook(cp, NULL, MA_NONE)) == NULL) {
829 fprintf(stderr, tr(49, "Cannot call hook for folder \"%s\": "
830 "Macro \"%s\" does not exist.\n"), name, cp);
831 rv = 1;
832 goto jleave;
835 inhook = nmail ? 3 : 1;
836 rv = _maexec(mp, NULL);
837 inhook = 0;
838 jleave:
839 ac_free(var);
840 return rv;
843 FL int
844 cdefines(void *v)
846 (void)v;
847 return _list_macros(MA_NONE);
850 FL int
851 c_account(void *v)
853 char **args = v;
854 struct macro *mp;
855 int rv = 1, i, oqf, nqf;
857 if (args[0] == NULL) {
858 rv = _list_macros(MA_ACC);
859 goto jleave;
862 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
863 if (args[2] != NULL) {
864 fprintf(stderr, tr(517, "Syntax is: account <name> {\n"));
865 goto jleave;
867 if (asccasecmp(args[0], ACCOUNT_NULL) == 0) {
868 fprintf(stderr, tr(521, "Error: `%s' is a reserved name.\n"),
869 ACCOUNT_NULL);
870 goto jleave;
872 rv = ! _define1(args[0], MA_ACC);
873 goto jleave;
876 if (inhook) {
877 fprintf(stderr, tr(518, "Cannot change account from within a hook.\n"));
878 goto jleave;
881 save_mbox_for_possible_quitstuff();
883 mp = NULL;
884 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
885 (mp = _malook(args[0], NULL, MA_ACC)) == NULL) {
886 fprintf(stderr, tr(519, "Account `%s' does not exist.\n"), args[0]);
887 goto jleave;
890 oqf = savequitflags();
891 if (_acc_curr != NULL)
892 _localopts_unroll(&_acc_curr->ma_localopts);
893 account_name = (mp != NULL) ? mp->ma_name : NULL;
894 _acc_curr = mp;
896 if (mp != NULL && _maexec(mp, &mp->ma_localopts) == CBAD) {
897 /* XXX account switch incomplete, unroll? */
898 fprintf(stderr, tr(520, "Switching to account `%s' failed.\n"), args[0]);
899 goto jleave;
902 if (! starting && ! inhook) {
903 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
904 restorequitflags(oqf);
905 if ((i = setfile("%", 0)) < 0)
906 goto jleave;
907 callhook(mailname, 0);
908 if (i > 0 && !ok_blook(emptystart))
909 goto jleave;
910 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
911 restorequitflags(nqf);
913 rv = 0;
914 jleave:
915 return rv;
918 FL int
919 c_localopts(void *v)
921 int rv = 1;
922 char **c = v;
924 if (_localopts == NULL) {
925 fprintf(stderr, tr(522,
926 "Cannot use `localopts' but from within a `define' or `account'\n"));
927 goto jleave;
930 _localopts->s_unroll = (**c == '0') ? FAL0 : TRU1;
931 rv = 0;
932 jleave:
933 return rv;
936 /* vim:set fenc=utf-8:s-it-mode */