page_or_print(): never use $PAGER unless startup complete
[s-mailx.git] / accmacvar.c
blobeea2da5aaea7e866a0ed375a4c5723d318cceb66
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Account, macro and variable handling.
3 *@ HOWTO add a new non-dynamic boolean or value option:
4 *@ - add an entry to nail.h:enum okeys
5 *@ - run mk-okey-map.pl
6 *@ - update the manual!
8 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
9 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
12 * Copyright (c) 1980, 1993
13 * The Regents of the University of California. All rights reserved.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 * 3. 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.
39 #undef n_FILE
40 #define n_FILE accmacvar
42 #ifndef HAVE_AMALGAMATION
43 # define _ACCMACVAR_SOURCE /* For _features[] */
44 # include "nail.h"
45 #endif
47 /* Note: changing the hash function must be reflected in mk-okey-map.pl */
48 #define MA_PRIME HSHSIZE
49 #define MA_NAME2HASH(N) torek_hash(N)
50 #define MA_HASH2PRIME(H) ((H) % 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 */
57 MA_DELETED = 1<<13 /* Only for _acc_curr: deleted while active */
60 enum var_map_flags {
61 VM_NONE = 0,
62 VM_BOOLEAN = 1<<0, /* ok_b_* */
63 VM_RDONLY = 1<<1, /* May not be set by user */
64 VM_SPECIAL = 1<<2, /* Wants _var_check_specials() evaluation */
65 VM_VIRTUAL = 1<<3 /* "Stateless": no var* -- implies VM_RDONLY */
68 struct macro {
69 struct macro *ma_next;
70 char *ma_name;
71 struct mline *ma_contents;
72 ui32_t ma_maxlen; /* Maximum line length */
73 enum ma_flags ma_flags;
74 struct var *ma_localopts; /* `account' unroll list, for `localopts' */
77 struct mline {
78 struct mline *l_next;
79 ui32_t l_length;
80 ui32_t l_leadspaces; /* Number of leading SPC characters */
81 char l_line[VFIELD_SIZE(0)];
84 struct var {
85 struct var *v_link;
86 char *v_value;
87 char v_name[VFIELD_SIZE(0)];
90 struct var_virtual {
91 ui32_t vv_okey;
92 struct var const *vv_var;
95 struct var_map {
96 ui32_t vm_hash;
97 ui16_t vm_keyoff;
98 ui16_t vm_flags; /* var_map_flags bits */
101 struct var_carrier {
102 char const *vc_name;
103 ui32_t vc_hash;
104 ui32_t vc_prime;
105 struct var *vc_var;
106 struct var_map const *vc_vmap;
107 enum okeys vc_okey;
110 struct var_show {
111 struct var_carrier vs_vc; /* _var_revlookup() */
112 char const *vs_value; /* Value (from wherever it came) or NULL */
113 bool_t vs_isset; /* Managed by us and existent */
114 bool_t vs_isenv; /* Set, but managed by environ */
115 bool_t vs_isasm; /* Is an assembled variable */
116 ui8_t __pad[5];
119 struct lostack {
120 struct lostack *s_up; /* Outer context */
121 struct macro *s_mac; /* Context (`account' or `define') */
122 struct var *s_localopts;
123 bool_t s_unroll; /* Unroll? */
126 /* Include the constant mk-okey-map.pl output */
127 #include "version.h"
128 #include "okeys.h"
130 static struct macro *_acc_curr; /* Currently active account */
131 static struct lostack *_localopts; /* Currently executing macro unroll list */
132 /* TODO We really deserve localopts support for *folder-hook*s, so hack it in
133 * TODO today via a static lostack, it should be a field in mailbox, once that
134 * TODO is a real multi-instance object */
135 static struct var *_folder_hook_localopts;
137 /* TODO once we have a dynamically sized hashtable we could unite _macros and
138 * TODO _variables into a single hashtable, stripping down fun interface;
139 * TODO also, setting and clearing a variable can be easily joined */
140 static struct var *_vars[MA_PRIME]; /* TODO dynamically spaced */
141 static struct macro *_macros[MA_PRIME]; /* TODO dynamically spaced */
143 /* Special cased value string allocation */
144 static char * _var_vcopy(char const *str);
145 static void _var_vfree(char *cp);
147 /* Check for special housekeeping. */
148 static bool_t _var_check_specials(enum okeys okey, bool_t enable,
149 char **val);
151 /* If a variable name begins with a lowercase-character and contains at
152 * least one '@', it is converted to all-lowercase. This is necessary
153 * for lookups of names based on email addresses.
154 * Following the standard, only the part following the last '@' should
155 * be lower-cased, but practice has established otherwise here.
156 * Return value may have been placed in string dope (salloc()) */
157 static char const * _var_canonify(char const *vn);
159 /* Try to reverse lookup an option name to an enum okeys mapping.
160 * Updates vcp.vc_name and vcp.vc_hash; vcp.vc_vmap is NULL if none found */
161 static bool_t _var_revlookup(struct var_carrier *vcp, char const *name);
163 /* Lookup a variable from vcp.vc_(vmap|name|hash), return wether it was found.
164 * Sets vcp.vc_prime; vcp.vc_var is NULL if not found */
165 static bool_t _var_lookup(struct var_carrier *vcp);
167 /* Completely fill vsp with data for name, return wether it was set as an
168 * internal variable (it may still have been set as an environment variable) */
169 static bool_t _var_broadway(struct var_show *vsp, char const *name);
171 /* Set variable from vcp.vc_(vmap|name|hash), return success */
172 static bool_t _var_set(struct var_carrier *vcp, char const *value);
174 /* Clear variable from vcp.vc_(vmap|name|hash); sets vcp.vc_var to NULL,
175 * return success */
176 static bool_t _var_clear(struct var_carrier *vcp);
178 /* List all variables */
179 static void _var_list_all(void);
181 static int __var_list_all_cmp(void const *s1, void const *s2);
182 static char * __var_simple_quote(char const *cp);
184 /* Shared c_set() and c_setenv() impl, return success */
185 static bool_t _var_set_env(char **ap, bool_t issetenv);
187 /* Does cp consist solely of WS and a } */
188 static bool_t _is_closing_angle(char const *cp);
190 /* Lookup for macros/accounts */
191 static struct macro *_ma_look(char const *name, struct macro *data,
192 enum ma_flags mafl);
194 /* Walk all lines of a macro and execute() them */
195 static int _ma_exec(struct macro const *mp, struct var **unroller);
197 /* User display helpers */
198 static int _ma_list(enum ma_flags mafl);
200 /* _ma_define() returns error for faulty definitions and already existing
201 * names, _ma_undefine() returns error if a named thing doesn't exist */
202 static bool_t _ma_define(char const *name, enum ma_flags mafl);
203 static bool_t _ma_undefine(char const *name, enum ma_flags mafl);
204 static void _ma_freelines(struct mline *lp);
206 /* Update replay-log */
207 static void _localopts_add(struct lostack *losp, char const *name,
208 struct var *ovap);
209 static void _localopts_unroll(struct var **vapp);
211 static char *
212 _var_vcopy(char const *str)
214 char *news;
215 size_t len;
216 NYD2_ENTER;
218 if (*str == '\0')
219 news = UNCONST("");
220 else {
221 len = strlen(str) +1;
222 news = smalloc(len);
223 memcpy(news, str, len);
225 NYD2_LEAVE;
226 return news;
229 static void
230 _var_vfree(char *cp)
232 NYD2_ENTER;
233 if (*cp != '\0')
234 free(cp);
235 NYD2_LEAVE;
238 static bool_t
239 _var_check_specials(enum okeys okey, bool_t enable, char **val)
241 char *cp = NULL;
242 bool_t ok = TRU1;
243 int flag = 0;
244 NYD2_ENTER;
246 switch (okey) {
247 case ok_b_debug:
248 flag = OPT_DEBUG;
249 break;
250 case ok_b_header:
251 flag = OPT_N_FLAG;
252 enable = !enable;
253 break;
254 case ok_b_memdebug:
255 flag = OPT_MEMDEBUG;
256 break;
257 case ok_b_skipemptybody:
258 flag = OPT_E_FLAG;
259 break;
260 case ok_b_verbose:
261 flag = (enable && !(options & OPT_VERB))
262 ? OPT_VERB : OPT_VERB | OPT_VERBVERB;
263 break;
264 case ok_v_folder:
265 ok = (val != NULL && var_folder_updated(*val, &cp));
266 if (ok && cp != NULL) {
267 _var_vfree(*val);
268 /* It's smalloc()ed, but ensure we don't leak */
269 if (*cp == '\0') {
270 *val = UNCONST("");
271 free(cp);
272 } else
273 *val = cp;
275 break;
276 #ifdef HAVE_NCL
277 case ok_v_line_editor_cursor_right:
278 if ((ok = (val != NULL && *val != NULL))) {
279 /* Set with no value? TODO very guly */
280 if (*(cp = *val) != '\0') {
281 char const *x = cp;
282 int c;
283 do {
284 c = n_shell_expand_escape(&x, FAL0);
285 if (c < 0)
286 break;
287 *cp++ = (char)c;
288 } while (*x != '\0');
289 *cp = '\0';
292 break;
293 #endif
294 default:
295 break;
298 if (flag) {
299 if (enable)
300 options |= flag;
301 else
302 options &= ~flag;
304 NYD2_LEAVE;
305 return ok;
308 static char const *
309 _var_canonify(char const *vn)
311 NYD2_ENTER;
312 if (!upperchar(*vn)) {
313 char const *vp;
315 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
317 vn = (*vp == '@') ? i_strdup(vn) : vn;
319 NYD2_LEAVE;
320 return vn;
323 static bool_t
324 _var_revlookup(struct var_carrier *vcp, char const *name)
326 ui32_t hash, i, j;
327 struct var_map const *vmp;
328 NYD2_ENTER;
330 vcp->vc_name = name = _var_canonify(name);
331 vcp->vc_hash = hash = MA_NAME2HASH(name);
333 for (i = hash % _VAR_REV_PRIME, j = 0; j <= _VAR_REV_LONGEST; ++j) {
334 ui32_t x = _var_revmap[i];
335 if (x == _VAR_REV_ILL)
336 break;
337 vmp = _var_map + x;
338 if (vmp->vm_hash == hash && !strcmp(_var_keydat + vmp->vm_keyoff, name)) {
339 vcp->vc_vmap = vmp;
340 vcp->vc_okey = (enum okeys)x;
341 goto jleave;
343 if (++i == _VAR_REV_PRIME) {
344 #ifdef _VAR_REV_WRAPAROUND
345 i = 0;
346 #else
347 break;
348 #endif
351 vcp->vc_vmap = NULL;
352 vcp = NULL;
353 jleave:
354 NYD2_LEAVE;
355 return (vcp != NULL);
358 static bool_t
359 _var_lookup(struct var_carrier *vcp)
361 struct var **vap, *lvp, *vp;
362 NYD2_ENTER;
364 /* XXX _So_ unlikely that it should be checked if normal lookup fails! */
365 if (UNLIKELY(vcp->vc_vmap != NULL &&
366 (vcp->vc_vmap->vm_flags & VM_VIRTUAL) != 0)) {
367 struct var_virtual const *vvp;
369 for (vvp = _var_virtuals;
370 PTRCMP(vvp, <, _var_virtuals + NELEM(_var_virtuals)); ++vvp)
371 if (vvp->vv_okey == vcp->vc_okey) {
372 vp = UNCONST(vvp->vv_var);
373 goto jleave;
375 assert(0);
378 vap = _vars + (vcp->vc_prime = MA_HASH2PRIME(vcp->vc_hash));
380 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
381 if (!strcmp(vp->v_name, vcp->vc_name)) {
382 /* Relink as head, hope it "sorts on usage" over time.
383 * _var_clear() relies on this behaviour */
384 if (lvp != NULL) {
385 lvp->v_link = vp->v_link;
386 vp->v_link = *vap;
387 *vap = vp;
389 goto jleave;
391 vp = NULL;
392 jleave:
393 vcp->vc_var = vp;
394 NYD2_LEAVE;
395 return (vp != NULL);
398 static bool_t
399 _var_broadway(struct var_show *vsp, char const *name)
401 bool_t rv;
402 NYD2_ENTER;
404 memset(vsp, 0, sizeof *vsp);
406 vsp->vs_isasm = !_var_revlookup(&vsp->vs_vc, name);
408 if ((vsp->vs_isset = rv = _var_lookup(&vsp->vs_vc))) {
409 vsp->vs_value = vsp->vs_vc.vc_var->v_value;
410 vsp->vs_isenv = FAL0;
411 } else
412 vsp->vs_isenv = ((vsp->vs_value = getenv(vsp->vs_vc.vc_name)) != NULL);
414 NYD2_LEAVE;
415 return rv;
418 static bool_t
419 _var_set(struct var_carrier *vcp, char const *value)
421 struct var *vp;
422 char *oval;
423 bool_t ok = TRU1;
424 NYD2_ENTER;
426 if (value == NULL) {
427 ok = _var_clear(vcp);
428 goto jleave;
431 _var_lookup(vcp);
433 if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_RDONLY)) {
434 n_err(_("Variable readonly: \"%s\"\n"), vcp->vc_name);
435 ok = FAL0;
436 goto jleave;
439 /* Don't care what happens later on, store this in the unroll list */
440 if (_localopts != NULL)
441 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
443 if ((vp = vcp->vc_var) == NULL) {
444 size_t l = strlen(vcp->vc_name) + 1;
446 vcp->vc_var =
447 vp = smalloc(sizeof(*vp) - VFIELD_SIZEOF(struct var, v_name) + l);
448 vp->v_link = _vars[vcp->vc_prime];
449 _vars[vcp->vc_prime] = vp;
450 memcpy(vp->v_name, vcp->vc_name, l);
451 oval = UNCONST("");
452 } else
453 oval = vp->v_value;
455 if (vcp->vc_vmap == NULL)
456 vp->v_value = _var_vcopy(value);
457 else {
458 /* Via `set' etc. the user may give even boolean options non-boolean
459 * values, ignore that and force boolean xxx error log? */
460 if (vcp->vc_vmap->vm_flags & VM_BOOLEAN)
461 value = UNCONST("");
462 vp->v_value = _var_vcopy(value);
464 /* Check if update allowed XXX wasteful on error! */
465 if ((vcp->vc_vmap->vm_flags & VM_SPECIAL) &&
466 !(ok = _var_check_specials(vcp->vc_okey, TRU1, &vp->v_value))) {
467 char *cp = vp->v_value;
468 vp->v_value = oval;
469 oval = cp;
473 if (*oval != '\0')
474 _var_vfree(oval);
475 jleave:
476 NYD2_LEAVE;
477 return ok;
480 static bool_t
481 _var_clear(struct var_carrier *vcp)
483 bool_t ok = TRU1;
484 NYD2_ENTER;
486 if (!_var_lookup(vcp)) {
487 if (!(pstate & PS_IN_LOAD) && (options & OPT_D_V))
488 n_err(_("Variable undefined: \"%s\"\n"), vcp->vc_name);
489 } else if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_RDONLY)) {
490 n_err(_("Variable readonly: \"%s\"\n"), vcp->vc_name);
491 ok = FAL0;
492 } else {
493 if (_localopts != NULL)
494 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
496 /* Always listhead after _var_lookup() */
497 _vars[vcp->vc_prime] = _vars[vcp->vc_prime]->v_link;
498 _var_vfree(vcp->vc_var->v_value);
499 free(vcp->vc_var);
500 vcp->vc_var = NULL;
502 if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_SPECIAL))
503 ok = _var_check_specials(vcp->vc_okey, FAL0, NULL);
505 NYD2_LEAVE;
506 return ok;
509 static void
510 _var_list_all(void)
512 struct var_show vs;
513 FILE *fp;
514 size_t no, i;
515 struct var *vp;
516 char const **vacp, **cap;
517 NYD2_ENTER;
519 if ((fp = Ftmp(NULL, "listvars", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
520 NULL) {
521 n_perr(_("tmpfile"), 0);
522 goto jleave;
525 for (no = i = 0; i < MA_PRIME; ++i)
526 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
527 ++no;
528 no += NELEM(_var_virtuals);
530 vacp = salloc(no * sizeof(*vacp));
532 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
533 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
534 *cap++ = vp->v_name;
535 for (i = 0; i < NELEM(_var_virtuals); ++i)
536 *cap++ = _var_virtuals[i].vv_var->v_name;
538 if (no > 1)
539 qsort(vacp, no, sizeof *vacp, &__var_list_all_cmp);
541 i = (ok_blook(bsdcompat) || ok_blook(bsdset));
542 for (cap = vacp; no != 0; ++cap, --no) {
543 char const *asmis, *fmt;
545 if (!_var_broadway(&vs, *cap))
546 continue;
547 if (vs.vs_value == NULL)
548 vs.vs_value = "";
550 asmis = !(options & OPT_D_VV) ? ""
551 : vs.vs_isasm ? "*" : " ";
552 if (i)
553 fmt = "%s%s\t%s\n";
554 else {
555 if (vs.vs_vc.vc_vmap != NULL &&
556 (vs.vs_vc.vc_vmap->vm_flags & VM_BOOLEAN))
557 fmt = "%sset %s\n";
558 else {
559 fmt = "%sset %s=\"%s\"\n";
560 if (*vs.vs_value != '\0')
561 vs.vs_value = __var_simple_quote(vs.vs_value);
564 /* Shall a code checker complain on that, i'm in holiday */
565 fprintf(fp, fmt, asmis, *cap, vs.vs_value);
568 page_or_print(fp, PTR2SIZE(cap - vacp));
569 Fclose(fp);
570 jleave:
571 NYD2_LEAVE;
574 static int
575 __var_list_all_cmp(void const *s1, void const *s2)
577 int rv;
578 NYD2_ENTER;
580 rv = strcmp(*(char**)UNCONST(s1), *(char**)UNCONST(s2));
581 NYD2_LEAVE;
582 return rv;
585 static char *
586 __var_simple_quote(char const *cp) /* TODO "unite" with string_quote(), etc.. */
588 bool_t esc;
589 size_t i;
590 char const *cp_base;
591 char c, *rv;
592 NYD2_ENTER;
594 for (i = 0, cp_base = cp; (c = *cp) != '\0'; ++i, ++cp)
595 if (c == '"')
596 ++i;
597 rv = salloc(i +1);
599 for (esc = FAL0, i = 0, cp = cp_base; (c = *cp) != '\0'; rv[i++] = c, ++cp) {
600 if (!esc) {
601 if (c == '"')
602 rv[i++] = '\\';
603 esc = (c == '\\');
604 } else
605 esc = FAL0;
607 rv[i] = '\0';
608 NYD2_LEAVE;
609 return rv;
612 static bool_t
613 _var_set_env(char **ap, bool_t issetenv)
615 char *cp, *cp2, *varbuf, c;
616 size_t errs = 0;
617 NYD2_ENTER;
619 for (; *ap != NULL; ++ap) {
620 /* Isolate key */
621 cp = *ap;
622 cp2 = varbuf = ac_alloc(strlen(cp) +1);
623 for (; (c = *cp) != '=' && c != '\0'; ++cp)
624 *cp2++ = c;
625 *cp2 = '\0';
626 if (c == '\0')
627 cp = UNCONST("");
628 else
629 ++cp;
630 if (varbuf == cp2) {
631 n_err(_("Non-null variable name required\n"));
632 ++errs;
633 goto jnext;
636 if (varbuf[0] == 'n' && varbuf[1] == 'o') {
637 char const *k = varbuf + 2;
639 if (issetenv && (!strcmp(k, "HOME") || /* TODO generic */
640 !strcmp(k, "LOGNAME") || !strcmp(k, "USER") || /* TODO .. */
641 !strcmp(k, "TMPDIR"))) {/* TODO approach */
642 if (options & OPT_D_V)
643 n_err(_("Cannot `unsetenv' \"%s\"\n"), k);
644 ++errs;
645 goto jnext;
648 errs += !_var_vokclear(k);
649 if (issetenv) {
650 #ifdef HAVE_SETENV
651 errs += (unsetenv(k) != 0);
652 #else
653 ++errs;
654 #endif
656 } else {
657 errs += !_var_vokset(varbuf, (uintptr_t)cp);
658 if (issetenv) {
659 do {
660 static char const *cp_buf[3];
661 char const **pl, **pe;
663 if (!strcmp(varbuf, "HOME")) /* TODO generic approach..*/
664 pl = cp_buf + 0, pe = &homedir;
665 else if (!strcmp(varbuf, "LOGNAME") || !strcmp(varbuf, "USER"))
666 pl = cp_buf + 1, pe = &myname;
667 else if (!strcmp(varbuf, "TMPDIR")) /* TODO ..until here */
668 pl = cp_buf + 2, pe = &tempdir;
669 else
670 break;
672 if (*pl != NULL)
673 free(UNCONST(*pl));
674 *pe = *pl = sstrdup(cp);
675 } while (0);
677 #ifdef HAVE_SETENV
678 errs += (setenv(varbuf, cp, 1) != 0);
679 #else
680 ++errs;
681 #endif
684 jnext:
685 ac_free(varbuf);
688 NYD2_LEAVE;
689 return (errs == 0);
692 static bool_t
693 _is_closing_angle(char const *cp)
695 bool_t rv = FAL0;
696 NYD2_ENTER;
698 while (spacechar(*cp))
699 ++cp;
700 if (*cp++ != '}')
701 goto jleave;
702 while (spacechar(*cp))
703 ++cp;
704 rv = (*cp == '\0');
705 jleave:
706 NYD2_LEAVE;
707 return rv;
710 static struct macro *
711 _ma_look(char const *name, struct macro *data, enum ma_flags mafl)
713 enum ma_flags save_mafl;
714 ui32_t h;
715 struct macro *lmp, *mp;
716 NYD2_ENTER;
718 save_mafl = mafl;
719 mafl &= MA_TYPE_MASK;
720 h = MA_NAME2HASH(name);
721 h = MA_HASH2PRIME(h);
723 for (lmp = NULL, mp = _macros[h]; mp != NULL; lmp = mp, mp = mp->ma_next) {
724 if ((mp->ma_flags & MA_TYPE_MASK) == mafl && !strcmp(mp->ma_name, name)) {
725 if (save_mafl & MA_UNDEF) {
726 if (lmp == NULL)
727 _macros[h] = mp->ma_next;
728 else
729 lmp->ma_next = mp->ma_next;
731 /* TODO it should also be possible to delete running macros */
732 if ((mafl & MA_ACC) &&
733 account_name != NULL && !strcmp(account_name, name)) {
734 mp->ma_flags |= MA_DELETED;
735 n_err(_("Delayed deletion of active account \"%s\"\n"), name);
736 } else {
737 _ma_freelines(mp->ma_contents);
738 free(mp->ma_name);
739 free(mp);
740 mp = (struct macro*)-1;
743 goto jleave;
747 if (data != NULL) {
748 data->ma_next = _macros[h];
749 _macros[h] = data;
750 mp = NULL;
752 jleave:
753 NYD2_LEAVE;
754 return mp;
757 static int
758 _ma_exec(struct macro const *mp, struct var **unroller)
760 struct lostack los;
761 char *buf;
762 struct n2 {struct n2 *up; struct lostack *lo;} *x; /* FIXME hack (sigman+) */
763 struct mline const *lp;
764 int rv = 0;
765 NYD2_ENTER;
767 los.s_up = _localopts;
768 los.s_mac = UNCONST(mp);
769 los.s_localopts = NULL;
770 los.s_unroll = FAL0;
771 _localopts = &los;
773 x = salloc(sizeof *x); /* FIXME intermediate hack (signal man+) */
774 x->up = temporary_localopts_store;
775 x->lo = _localopts;
776 temporary_localopts_store = x;
778 buf = ac_alloc(mp->ma_maxlen +1);
779 for (lp = mp->ma_contents; lp; lp = lp->l_next) {
780 memcpy(buf, lp->l_line, lp->l_length +1);
781 rv |= execute(buf, lp->l_length); /* XXX break if != 0 ? */
783 ac_free(buf);
785 temporary_localopts_store = x->up; /* FIXME intermediate hack */
787 _localopts = los.s_up;
788 if (unroller == NULL) {
789 if (los.s_localopts != NULL)
790 _localopts_unroll(&los.s_localopts);
791 } else
792 *unroller = los.s_localopts;
793 NYD2_LEAVE;
794 return rv;
797 static int
798 _ma_list(enum ma_flags mafl)
800 FILE *fp;
801 char const *typestr;
802 struct macro *mq;
803 ui32_t ti, mc, i;
804 struct mline *lp;
805 NYD2_ENTER;
807 if ((fp = Ftmp(NULL, "listmacs", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
808 NULL) {
809 n_perr(_("tmpfile"), 0);
810 mc = 1;
811 goto jleave;
814 mafl &= MA_TYPE_MASK;
815 typestr = (mafl & MA_ACC) ? "account" : "define";
817 for (ti = mc = 0; ti < MA_PRIME; ++ti)
818 for (mq = _macros[ti]; mq; mq = mq->ma_next)
819 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
820 if (++mc > 1)
821 putc('\n', fp);
822 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
823 for (lp = mq->ma_contents; lp != NULL; lp = lp->l_next) {
824 for (i = lp->l_leadspaces; i > 0; --i)
825 putc(' ', fp);
826 fputs(lp->l_line, fp);
827 putc('\n', fp);
829 fputs("}\n", fp);
831 if (mc)
832 page_or_print(fp, 0);
834 mc = (ui32_t)ferror(fp);
835 Fclose(fp);
836 jleave:
837 NYD2_LEAVE;
838 return (int)mc;
841 static bool_t
842 _ma_define(char const *name, enum ma_flags mafl)
844 bool_t rv = FAL0;
845 struct macro *mp;
846 struct mline *lp, *lst = NULL, *lnd = NULL;
847 char *linebuf = NULL, *cp;
848 size_t linesize = 0;
849 ui32_t maxlen = 0, leaspc;
850 union {int i; ui32_t ui;} n;
851 NYD2_ENTER;
853 mp = scalloc(1, sizeof *mp);
854 mp->ma_name = sstrdup(name);
855 mp->ma_flags = mafl;
857 for (;;) {
858 n.i = readline_input("", TRU1, &linebuf, &linesize, NULL);
859 if (n.ui == 0)
860 continue;
861 if (n.i < 0) {
862 n_err(_("Unterminated %s definition: \"%s\"\n"),
863 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
864 if ((pstate & PS_IN_LOAD) == PS_SOURCING)
865 unstack();
866 goto jerr;
868 if (_is_closing_angle(linebuf))
869 break;
871 /* Trim WS xxx we count tabs as one space here */
872 for (cp = linebuf, leaspc = 0; n.ui > 0 && whitechar(*cp); ++cp, --n.ui)
873 if (*cp == '\t')
874 leaspc = (leaspc + 8) & ~7;
875 else
876 ++leaspc;
877 if (n.ui == 0)
878 continue;
879 for (; whitechar(cp[n.ui - 1]); --n.ui)
880 assert(n.ui > 0);
881 assert(n.ui > 0);
883 maxlen = MAX(maxlen, n.ui);
884 cp[n.ui++] = '\0';
885 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n.ui);
886 memcpy(lp->l_line, cp, n.ui);
887 lp->l_length = --n.ui;
888 lp->l_leadspaces = leaspc;
890 if (lst != NULL) {
891 lnd->l_next = lp;
892 lnd = lp;
893 } else
894 lst = lnd = lp;
896 mp->ma_contents = lst;
897 mp->ma_maxlen = maxlen;
899 if (_ma_look(mp->ma_name, mp, mafl) != NULL) {
900 n_err(_("A %s named \"%s\" already exists\n"),
901 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
902 lst = mp->ma_contents;
903 goto jerr;
906 rv = TRU1;
907 jleave:
908 if (linebuf != NULL)
909 free(linebuf);
910 NYD2_LEAVE;
911 return rv;
913 jerr:
914 if (lst != NULL)
915 _ma_freelines(lst);
916 free(mp->ma_name);
917 free(mp);
918 goto jleave;
921 static bool_t
922 _ma_undefine(char const *name, enum ma_flags mafl)
924 struct macro *mp;
925 bool_t rv;
926 NYD2_ENTER;
928 rv = TRU1;
930 if (LIKELY(name[0] != '*' || name[1] != '\0')) {
931 if ((mp = _ma_look(name, NULL, mafl | MA_UNDEF)) == NULL) {
932 n_err(_("%s \"%s\" is not defined\n"),
933 (mafl & MA_ACC ? "Account" : "Macro"), name);
934 rv = FAL0;
936 } else {
937 struct macro **mpp, *lmp;
939 for (mpp = _macros; PTRCMP(mpp, <, _macros + NELEM(_macros)); ++mpp)
940 for (lmp = NULL, mp = *mpp; mp != NULL;) {
941 if ((mp->ma_flags & MA_TYPE_MASK) == mafl) {
942 /* xxx Expensive but rare: be simple */
943 _ma_look(mp->ma_name, NULL, mafl | MA_UNDEF);
944 mp = (lmp == NULL) ? *mpp : lmp->ma_next;
945 } else {
946 lmp = mp;
947 mp = mp->ma_next;
951 NYD2_LEAVE;
952 return rv;
955 static void
956 _ma_freelines(struct mline *lp)
958 struct mline *lq;
959 NYD2_ENTER;
961 for (lq = NULL; lp != NULL; ) {
962 if (lq != NULL)
963 free(lq);
964 lq = lp;
965 lp = lp->l_next;
967 if (lq)
968 free(lq);
969 NYD2_LEAVE;
972 static void
973 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
975 struct var *vap;
976 size_t nl, vl;
977 NYD2_ENTER;
979 /* Propagate unrolling up the stack, as necessary */
980 while (!losp->s_unroll && (losp = losp->s_up) != NULL)
982 if (losp == NULL)
983 goto jleave;
985 /* We've found a level that wants to unroll; check wether it does it yet */
986 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
987 if (!strcmp(vap->v_name, name))
988 goto jleave;
990 nl = strlen(name) + 1;
991 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
992 vap = smalloc(sizeof(*vap) - VFIELD_SIZEOF(struct var, v_name) + nl + vl);
993 vap->v_link = losp->s_localopts;
994 losp->s_localopts = vap;
995 memcpy(vap->v_name, name, nl);
996 if (vl == 0)
997 vap->v_value = NULL;
998 else {
999 vap->v_value = vap->v_name + nl;
1000 memcpy(vap->v_value, ovap->v_value, vl);
1002 jleave:
1003 NYD2_LEAVE;
1006 static void
1007 _localopts_unroll(struct var **vapp)
1009 struct lostack *save_los;
1010 struct var *x, *vap;
1011 NYD2_ENTER;
1013 vap = *vapp;
1014 *vapp = NULL;
1016 save_los = _localopts;
1017 _localopts = NULL;
1018 while (vap != NULL) {
1019 x = vap;
1020 vap = vap->v_link;
1021 vok_vset(x->v_name, x->v_value);
1022 free(x);
1024 _localopts = save_los;
1025 NYD2_LEAVE;
1028 FL char *
1029 _var_oklook(enum okeys okey)
1031 struct var_carrier vc;
1032 char *rv;
1033 NYD_ENTER;
1035 vc.vc_vmap = _var_map + okey;
1036 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1037 vc.vc_hash = _var_map[okey].vm_hash;
1038 vc.vc_okey = okey;
1040 if (!_var_lookup(&vc)) {
1041 if ((rv = getenv(vc.vc_name)) != NULL) {
1042 _var_set(&vc, rv);
1043 assert(vc.vc_var != NULL);
1044 goto jvar;
1046 } else
1047 jvar:
1048 rv = vc.vc_var->v_value;
1049 NYD_LEAVE;
1050 return rv;
1053 FL bool_t
1054 _var_okset(enum okeys okey, uintptr_t val)
1056 struct var_carrier vc;
1057 bool_t ok;
1058 NYD_ENTER;
1060 vc.vc_vmap = _var_map + okey;
1061 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1062 vc.vc_hash = _var_map[okey].vm_hash;
1063 vc.vc_okey = okey;
1065 ok = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
1066 NYD_LEAVE;
1067 return ok;
1070 FL bool_t
1071 _var_okclear(enum okeys okey)
1073 struct var_carrier vc;
1074 bool_t rv;
1075 NYD_ENTER;
1077 vc.vc_vmap = _var_map + okey;
1078 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1079 vc.vc_hash = _var_map[okey].vm_hash;
1080 vc.vc_okey = okey;
1082 rv = _var_clear(&vc);
1083 NYD_LEAVE;
1084 return rv;
1087 FL char *
1088 _var_voklook(char const *vokey)
1090 struct var_carrier vc;
1091 char *rv;
1092 NYD_ENTER;
1094 _var_revlookup(&vc, vokey);
1096 if (!_var_lookup(&vc)) {
1097 if ((rv = getenv(vc.vc_name)) != NULL) {
1098 _var_set(&vc, rv);
1099 assert(vc.vc_var != NULL);
1100 goto jvar;
1102 } else
1103 jvar:
1104 rv = vc.vc_var->v_value;
1105 NYD_LEAVE;
1106 return rv;
1109 FL bool_t
1110 _var_vokset(char const *vokey, uintptr_t val)
1112 struct var_carrier vc;
1113 bool_t ok;
1114 NYD_ENTER;
1116 _var_revlookup(&vc, vokey);
1118 ok = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
1119 NYD_LEAVE;
1120 return ok;
1123 FL bool_t
1124 _var_vokclear(char const *vokey)
1126 struct var_carrier vc;
1127 bool_t err;
1128 NYD_ENTER;
1130 _var_revlookup(&vc, vokey);
1132 err = !_var_clear(&vc);
1133 NYD_LEAVE;
1134 return !err;
1137 FL char *
1138 _env_look(char const *envkey, bool_t envonly) /* TODO rather dummy yet!! */
1140 char *rv;
1141 NYD_ENTER;
1143 if (envonly)
1144 rv = getenv(envkey); /* TODO rework vars: cache, output a la mimetypes */
1145 else
1146 rv = _var_voklook(envkey);
1147 NYD_LEAVE;
1148 return rv;
1151 #ifdef HAVE_SOCKETS
1152 FL char *
1153 _var_xoklook(enum okeys okey, struct url const *urlp, enum okey_xlook_mode oxm)
1155 struct var_carrier vc;
1156 struct str const *us;
1157 size_t nlen;
1158 char *nbuf = NULL /* CC happiness */, *rv;
1159 NYD_ENTER;
1161 assert(oxm & (OXM_PLAIN | OXM_H_P | OXM_U_H_P));
1163 /* For simplicity: allow this case too */
1164 if (!(oxm & (OXM_H_P | OXM_U_H_P)))
1165 goto jplain;
1167 vc.vc_vmap = _var_map + okey;
1168 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1169 vc.vc_okey = okey;
1171 us = (oxm & OXM_U_H_P) ? &urlp->url_u_h_p : &urlp->url_h_p;
1172 nlen = strlen(vc.vc_name);
1173 nbuf = ac_alloc(nlen + 1 + us->l +1);
1174 memcpy(nbuf, vc.vc_name, nlen);
1175 nbuf[nlen++] = '-';
1177 /* One of .url_u_h_p and .url_h_p we test in here */
1178 memcpy(nbuf + nlen, us->s, us->l +1);
1179 vc.vc_name = _var_canonify(nbuf);
1180 vc.vc_hash = MA_NAME2HASH(vc.vc_name);
1181 if (_var_lookup(&vc))
1182 goto jvar;
1184 /* The second */
1185 if (oxm & OXM_H_P) {
1186 us = &urlp->url_h_p;
1187 memcpy(nbuf + nlen, us->s, us->l +1);
1188 vc.vc_name = _var_canonify(nbuf);
1189 vc.vc_hash = MA_NAME2HASH(vc.vc_name);
1190 if (_var_lookup(&vc)) {
1191 jvar:
1192 rv = vc.vc_var->v_value;
1193 goto jleave;
1197 jplain:
1198 rv = (oxm & OXM_PLAIN) ? _var_oklook(okey) : NULL;
1199 jleave:
1200 if (oxm & (OXM_H_P | OXM_U_H_P))
1201 ac_free(nbuf);
1202 NYD_LEAVE;
1203 return rv;
1205 #endif /* HAVE_SOCKETS */
1207 FL int
1208 c_varshow(void *v)
1210 struct var_show vs;
1211 char const **argv = v;
1212 NYD_ENTER;
1214 if (*argv == NULL)
1215 v = NULL;
1216 else for (; *argv != NULL; ++argv) {
1217 _var_broadway(&vs, *argv);
1218 if (vs.vs_value == NULL)
1219 vs.vs_value = "NULL";
1221 if (vs.vs_vc.vc_vmap != NULL) {
1222 ui16_t f = vs.vs_vc.vc_vmap->vm_flags;
1224 if (f & VM_BOOLEAN)
1225 printf(_("\"%s\": (%d) boolean%s%s: set=%d (ENVIRON=%d)\n"),
1226 vs.vs_vc.vc_name, vs.vs_vc.vc_okey,
1227 (f & VM_RDONLY ? ", read-only" : ""),
1228 (f & VM_VIRTUAL ? ", virtual" : ""), vs.vs_isset, vs.vs_isenv);
1229 else
1230 printf(_("\"%s\": (%d) value%s%s: set=%d (ENVIRON=%d) value<%s>\n"),
1231 vs.vs_vc.vc_name, vs.vs_vc.vc_okey,
1232 (f & VM_RDONLY ? ", read-only" : ""),
1233 (f & VM_VIRTUAL ? ", virtual" : ""), vs.vs_isset, vs.vs_isenv,
1234 vs.vs_value);
1235 } else
1236 printf("\"%s\": (assembled): set=%d (ENVIRON=%d) value<%s>\n",
1237 vs.vs_vc.vc_name, vs.vs_isset, vs.vs_isenv, vs.vs_value);
1239 NYD_LEAVE;
1240 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1243 FL int
1244 c_set(void *v)
1246 char **ap = v;
1247 int err;
1248 NYD_ENTER;
1250 if (*ap == NULL) {
1251 _var_list_all();
1252 err = 0;
1253 } else
1254 err = !_var_set_env(ap, FAL0);
1255 NYD_LEAVE;
1256 return err;
1259 FL int
1260 c_setenv(void *v)
1262 char **ap = v;
1263 int err;
1264 NYD_ENTER;
1266 if (!(err = !(pstate & PS_STARTED)))
1267 err = !_var_set_env(ap, TRU1);
1268 NYD_LEAVE;
1269 return err;
1272 FL int
1273 c_unset(void *v)
1275 char **ap = v;
1276 int err = 0;
1277 NYD_ENTER;
1279 while (*ap != NULL)
1280 err |= !_var_vokclear(*ap++);
1281 NYD_LEAVE;
1282 return err;
1285 FL int
1286 c_unsetenv(void *v)
1288 int err;
1289 NYD_ENTER;
1291 if (!(err = !(pstate & PS_STARTED))) {
1292 char **ap;
1294 for (ap = v; *ap != NULL; ++ap) {
1295 bool_t bad;
1297 if (!strcmp(*ap, "HOME") || /* TODO generic */
1298 !strcmp(*ap, "LOGNAME") || !strcmp(*ap, "USER") || /* TODO .. */
1299 !strcmp(*ap, "TMPDIR")) { /* TODO approach */
1300 if (options & OPT_D_V)
1301 n_err(_("Cannot `unsetenv' \"%s\"\n"), *ap);
1302 err = 1;
1303 continue;
1306 bad = !_var_vokclear(*ap);
1307 if (
1308 #ifdef HAVE_SETENV
1309 unsetenv(*ap) != 0 ||
1310 #endif
1313 err = 1;
1316 NYD_LEAVE;
1317 return err;
1320 FL int
1321 c_varedit(void *v)
1323 struct var_carrier vc;
1324 sighandler_type sigint;
1325 FILE *of, *nf;
1326 char *val, **argv = v;
1327 int err = 0;
1328 NYD_ENTER;
1330 sigint = safe_signal(SIGINT, SIG_IGN);
1332 while (*argv != NULL) {
1333 memset(&vc, 0, sizeof vc);
1335 _var_revlookup(&vc, *argv++);
1337 if (vc.vc_vmap != NULL) {
1338 if (vc.vc_vmap->vm_flags & VM_BOOLEAN) {
1339 n_err(_("`varedit': can't edit boolean variable \"%s\"\n"),
1340 vc.vc_name);
1341 continue;
1343 if (vc.vc_vmap->vm_flags & VM_RDONLY) {
1344 n_err(_("`varedit': can't edit readonly variable \"%s\"\n"),
1345 vc.vc_name);
1346 continue;
1350 _var_lookup(&vc);
1352 if ((of = Ftmp(NULL, "vared", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1353 NULL) {
1354 n_perr(_("`varedit': can't create temporary file, bailing out"), 0);
1355 err = 1;
1356 break;
1357 } else if (vc.vc_var != NULL && *(val = vc.vc_var->v_value) != '\0' &&
1358 sizeof *val != fwrite(val, strlen(val), sizeof *val, of)) {
1359 n_perr(_("`varedit' failed to write old value to temporary file"), 0);
1360 Fclose(of);
1361 err = 1;
1362 continue;
1365 fflush_rewind(of);
1366 nf = run_editor(of, (off_t)-1, 'e', FAL0, NULL, NULL, SEND_MBOX, sigint);
1367 Fclose(of);
1369 if (nf != NULL) {
1370 int c;
1371 char *base;
1372 off_t l = fsize(nf);
1374 assert(l >= 0);
1375 base = smalloc((size_t)l + 1);
1377 for (l = 0, val = base; (c = getc(nf)) != EOF; ++val)
1378 if (c == '\n' || c == '\r') {
1379 *val = ' ';
1380 ++l;
1381 } else {
1382 *val = (char)(uc_i)c;
1383 l = 0;
1385 val -= l;
1386 *val = '\0';
1388 if (!vok_vset(vc.vc_name, base))
1389 err = 1;
1391 free(base);
1392 Fclose(nf);
1393 } else {
1394 n_err(_("`varedit': can't start $EDITOR, bailing out\n"));
1395 err = 1;
1396 break;
1400 safe_signal(SIGINT, sigint);
1401 NYD_LEAVE;
1402 return err;
1405 FL int
1406 c_define(void *v)
1408 int rv = 1;
1409 char **args = v;
1410 NYD_ENTER;
1412 if (args[0] == NULL) {
1413 rv = _ma_list(MA_NONE);
1414 goto jleave;
1417 if (args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
1418 args[2] != NULL) {
1419 n_err(_("Syntax is: define <name> {"));
1420 goto jleave;
1423 rv = !_ma_define(args[0], MA_NONE);
1424 jleave:
1425 NYD_LEAVE;
1426 return rv;
1429 FL int
1430 c_undefine(void *v)
1432 int rv;
1433 char **args;
1434 NYD_ENTER;
1436 rv = 0;
1437 args = v;
1439 rv |= !_ma_undefine(*args, MA_NONE);
1440 while (*++args != NULL);
1441 NYD_LEAVE;
1442 return rv;
1445 FL int
1446 c_call(void *v)
1448 int rv = 1;
1449 char **args = v;
1450 char const *errs, *name;
1451 struct macro *mp;
1452 NYD_ENTER;
1454 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
1455 errs = _("Syntax is: call <%s>\n");
1456 name = "name";
1457 goto jerr;
1460 if ((mp = _ma_look(*args, NULL, MA_NONE)) == NULL) {
1461 errs = _("Undefined macro called: \"%s\"\n");
1462 name = *args;
1463 goto jerr;
1466 rv = _ma_exec(mp, NULL);
1467 jleave:
1468 NYD_LEAVE;
1469 return rv;
1470 jerr:
1471 n_err(errs, name);
1472 goto jleave;
1475 FL bool_t
1476 check_folder_hook(bool_t nmail)
1478 size_t len;
1479 char *var, *cp;
1480 struct macro *mp;
1481 struct var **unroller;
1482 bool_t rv = TRU1;
1483 NYD_ENTER;
1485 var = ac_alloc(len = strlen(mailname) + sizeof("folder-hook-") -1 +1);
1487 /* First try the fully resolved path */
1488 snprintf(var, len, "folder-hook-%s", mailname);
1489 if ((cp = vok_vlook(var)) != NULL)
1490 goto jmac;
1492 /* If we are under *folder*, try the usual +NAME syntax, too */
1493 if (displayname[0] == '+') {
1494 char *x = mailname + len;
1496 for (; x > mailname; --x)
1497 if (x[-1] == '/') {
1498 snprintf(var, len, "folder-hook-+%s", x);
1499 if ((cp = vok_vlook(var)) != NULL)
1500 goto jmac;
1501 break;
1505 /* Plain *folder-hook* is our last try */
1506 if ((cp = ok_vlook(folder_hook)) == NULL)
1507 goto jleave;
1509 jmac:
1510 if ((mp = _ma_look(cp, NULL, MA_NONE)) == NULL) {
1511 n_err(_("Cannot call *folder-hook* for \"%s\": "
1512 "macro \"%s\" does not exist\n"), displayname, cp);
1513 rv = FAL0;
1514 goto jleave;
1517 pstate &= ~PS_HOOK_MASK;
1518 if (nmail) {
1519 pstate |= PS_HOOK_NEWMAIL;
1520 unroller = NULL;
1521 } else {
1522 pstate |= PS_HOOK;
1523 unroller = &_folder_hook_localopts;
1525 rv = (_ma_exec(mp, unroller) == 0);
1526 pstate &= ~PS_HOOK_MASK;
1528 jleave:
1529 ac_free(var);
1530 NYD_LEAVE;
1531 return rv;
1534 FL int
1535 c_account(void *v)
1537 char **args = v;
1538 struct macro *mp;
1539 int rv = 1, i, oqf, nqf;
1540 NYD_ENTER;
1542 if (args[0] == NULL) {
1543 rv = _ma_list(MA_ACC);
1544 goto jleave;
1547 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1548 if (args[2] != NULL) {
1549 n_err(_("Syntax is: account <name> {\n"));
1550 goto jleave;
1552 if (!asccasecmp(args[0], ACCOUNT_NULL)) {
1553 n_err(_("Error: \"%s\" is a reserved name\n"), ACCOUNT_NULL);
1554 goto jleave;
1556 rv = !_ma_define(args[0], MA_ACC);
1557 goto jleave;
1560 if (pstate & PS_HOOK_MASK) {
1561 n_err(_("Cannot change account from within a hook\n"));
1562 goto jleave;
1565 save_mbox_for_possible_quitstuff();
1567 mp = NULL;
1568 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
1569 (mp = _ma_look(args[0], NULL, MA_ACC)) == NULL) {
1570 n_err(_("Account \"%s\" does not exist\n"), args[0]);
1571 goto jleave;
1574 oqf = savequitflags();
1576 if (_acc_curr != NULL) {
1577 if (_acc_curr->ma_localopts != NULL)
1578 _localopts_unroll(&_acc_curr->ma_localopts);
1579 if (_acc_curr->ma_flags & MA_DELETED) { /* xxx can be made generic? */
1580 _ma_freelines(_acc_curr->ma_contents);
1581 free(_acc_curr->ma_name);
1582 free(_acc_curr);
1586 account_name = (mp != NULL) ? mp->ma_name : NULL;
1587 _acc_curr = mp;
1589 if (mp != NULL && _ma_exec(mp, &mp->ma_localopts) == CBAD) {
1590 /* XXX account switch incomplete, unroll? */
1591 n_err(_("Switching to account \"%s\" failed\n"), args[0]);
1592 goto jleave;
1595 if ((pstate & (PS_STARTED | PS_HOOK_MASK)) == PS_STARTED) {
1596 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1597 restorequitflags(oqf);
1598 if ((i = setfile("%", 0)) < 0)
1599 goto jleave;
1600 check_folder_hook(FAL0);
1601 if (i > 0 && !ok_blook(emptystart))
1602 goto jleave;
1603 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1604 restorequitflags(nqf);
1606 rv = 0;
1607 jleave:
1608 NYD_LEAVE;
1609 return rv;
1612 FL int
1613 c_unaccount(void *v)
1615 int rv;
1616 char **args;
1617 NYD_ENTER;
1619 rv = 0;
1620 args = v;
1622 rv |= !_ma_undefine(*args, MA_ACC);
1623 while (*++args != NULL);
1624 NYD_LEAVE;
1625 return rv;
1628 FL int
1629 c_localopts(void *v)
1631 int rv = 1;
1632 char **c = v;
1633 NYD_ENTER;
1635 if (_localopts == NULL) {
1636 n_err(_("Cannot use `localopts' but from within a "
1637 "`define' or `account'\n"));
1638 goto jleave;
1641 _localopts->s_unroll = (boolify(*c, UIZ_MAX, FAL0) > 0);
1642 rv = 0;
1643 jleave:
1644 NYD_LEAVE;
1645 return rv;
1648 FL void
1649 temporary_localopts_free(void) /* XXX intermediate hack */
1651 struct n2 {struct n2 *up; struct lostack *lo;} *x;
1652 NYD_ENTER;
1654 x = temporary_localopts_store;
1655 temporary_localopts_store = NULL;
1656 _localopts = NULL;
1658 while (x != NULL) {
1659 struct lostack *losp = x->lo;
1660 x = x->up;
1661 if (losp->s_localopts != NULL)
1662 _localopts_unroll(&losp->s_localopts);
1664 NYD_LEAVE;
1667 FL void
1668 temporary_localopts_folder_hook_unroll(void) /* XXX intermediate hack */
1670 NYD_ENTER;
1671 if (_folder_hook_localopts != NULL) {
1672 void *save = _localopts;
1673 _localopts = NULL;
1674 _localopts_unroll(&_folder_hook_localopts);
1675 _localopts = save;
1677 NYD_LEAVE;
1680 /* s-it-mode */