enum okeys: new flags, additions and removals
[s-mailx.git] / accmacvar.c
blob6f2c7e54fca13984e88b47048ec60408797394e9
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 */
66 VM_ENVIRON = 1<<4 /* Update environment on change */
69 struct macro {
70 struct macro *ma_next;
71 char *ma_name;
72 struct mline *ma_contents;
73 ui32_t ma_maxlen; /* Maximum line length */
74 enum ma_flags ma_flags;
75 struct var *ma_localopts; /* `account' unroll list, for `localopts' */
78 struct mline {
79 struct mline *l_next;
80 ui32_t l_length;
81 ui32_t l_leadspaces; /* Number of leading SPC characters */
82 char l_line[VFIELD_SIZE(0)];
85 struct var {
86 struct var *v_link;
87 char *v_value;
88 char v_name[VFIELD_SIZE(0)];
91 struct var_virtual {
92 ui32_t vv_okey;
93 struct var const *vv_var;
96 struct var_map {
97 ui32_t vm_hash;
98 ui16_t vm_keyoff;
99 ui16_t vm_flags; /* var_map_flags bits */
102 struct var_carrier {
103 char const *vc_name;
104 ui32_t vc_hash;
105 ui32_t vc_prime;
106 struct var *vc_var;
107 struct var_map const *vc_vmap;
108 enum okeys vc_okey;
111 struct var_show {
112 struct var_carrier vs_vc; /* _var_revlookup() */
113 char const *vs_value; /* Value (from wherever it came) or NULL */
114 bool_t vs_isset; /* Managed by us and existent */
115 bool_t vs_isenv; /* Set, but managed by environ */
116 bool_t vs_isasm; /* Is an assembled variable */
117 ui8_t __pad[5];
120 struct lostack {
121 struct lostack *s_up; /* Outer context */
122 struct macro *s_mac; /* Context (`account' or `define') */
123 struct var *s_localopts;
124 bool_t s_unroll; /* Unroll? */
127 /* Include the constant mk-okey-map.pl output */
128 #include "version.h"
129 #include "okeys.h"
131 static struct macro *_acc_curr; /* Currently active account */
132 static struct lostack *_localopts; /* Currently executing macro unroll list */
133 /* TODO We really deserve localopts support for *folder-hook*s, so hack it in
134 * TODO today via a static lostack, it should be a field in mailbox, once that
135 * TODO is a real multi-instance object */
136 static struct var *_folder_hook_localopts;
137 /* TODO Ditto, compose hooks */
138 static struct var *a_macvar_compose_localopts;
140 /* TODO once we have a dynamically sized hashtable we could unite _macros and
141 * TODO _variables into a single hashtable, stripping down fun interface;
142 * TODO also, setting and clearing a variable can be easily joined */
143 static struct var *_vars[MA_PRIME]; /* TODO dynamically spaced */
144 static struct macro *_macros[MA_PRIME]; /* TODO dynamically spaced */
146 /* Special cased value string allocation */
147 static char * _var_vcopy(char const *str);
148 static void _var_vfree(char *cp);
150 /* Check for special housekeeping. */
151 static bool_t _var_check_specials(enum okeys okey, bool_t enable,
152 char **val);
154 /* If a variable name begins with a lowercase-character and contains at
155 * least one '@', it is converted to all-lowercase. This is necessary
156 * for lookups of names based on email addresses.
157 * Following the standard, only the part following the last '@' should
158 * be lower-cased, but practice has established otherwise here.
159 * Return value may have been placed in string dope (salloc()) */
160 static char const * _var_canonify(char const *vn);
162 /* Try to reverse lookup an option name to an enum okeys mapping.
163 * Updates vcp.vc_name and vcp.vc_hash; vcp.vc_vmap is NULL if none found */
164 static bool_t _var_revlookup(struct var_carrier *vcp, char const *name);
166 /* Lookup a variable from vcp.vc_(vmap|name|hash), return wether it was found.
167 * Sets vcp.vc_prime; vcp.vc_var is NULL if not found */
168 static bool_t _var_lookup(struct var_carrier *vcp);
170 /* Completely fill vsp with data for name, return wether it was set as an
171 * internal variable (it may still have been set as an environment variable) */
172 static bool_t _var_broadway(struct var_show *vsp, char const *name);
174 /* Set variable from vcp.vc_(vmap|name|hash), return success */
175 static bool_t _var_set(struct var_carrier *vcp, char const *value);
177 /* Clear variable from vcp.vc_(vmap|name|hash); sets vcp.vc_var to NULL,
178 * return success */
179 static bool_t _var_clear(struct var_carrier *vcp);
181 /* List all variables */
182 static void _var_list_all(void);
184 static int __var_list_all_cmp(void const *s1, void const *s2);
185 static char * __var_simple_quote(char const *cp);
187 /* Shared c_set() and c_setenv() impl, return success */
188 static bool_t _var_set_env(char **ap, bool_t issetenv);
190 /* Does cp consist solely of WS and a } */
191 static bool_t _is_closing_angle(char const *cp);
193 /* Lookup for macros/accounts */
194 static struct macro *_ma_look(char const *name, struct macro *data,
195 enum ma_flags mafl);
197 /* Walk all lines of a macro and execute() them */
198 static bool_t _ma_exec(struct macro const *mp, struct var **unroller,
199 bool_t localopts);
201 /* User display helpers */
202 static int _ma_list(enum ma_flags mafl);
204 /* _ma_define() returns error for faulty definitions and already existing
205 * names, _ma_undefine() returns error if a named thing doesn't exist */
206 static bool_t _ma_define(char const *name, enum ma_flags mafl);
207 static bool_t _ma_undefine(char const *name, enum ma_flags mafl);
208 static void _ma_freelines(struct mline *lp);
210 /* Update replay-log */
211 static void _localopts_add(struct lostack *losp, char const *name,
212 struct var *ovap);
213 static void _localopts_unroll(struct var **vapp);
215 static char *
216 _var_vcopy(char const *str)
218 char *news;
219 size_t len;
220 NYD2_ENTER;
222 if (*str == '\0')
223 news = UNCONST("");
224 else {
225 len = strlen(str) +1;
226 news = smalloc(len);
227 memcpy(news, str, len);
229 NYD2_LEAVE;
230 return news;
233 static void
234 _var_vfree(char *cp)
236 NYD2_ENTER;
237 if (*cp != '\0')
238 free(cp);
239 NYD2_LEAVE;
242 static bool_t
243 _var_check_specials(enum okeys okey, bool_t enable, char **val)
245 char *cp = NULL;
246 bool_t ok = TRU1;
247 int flag = 0;
248 NYD2_ENTER;
250 switch (okey) {
251 case ok_b_debug:
252 flag = OPT_DEBUG;
253 break;
254 case ok_b_header:
255 flag = OPT_N_FLAG;
256 enable = !enable;
257 break;
258 case ok_b_memdebug:
259 flag = OPT_MEMDEBUG;
260 break;
261 case ok_b_skipemptybody:
262 flag = OPT_E_FLAG;
263 break;
264 case ok_b_verbose:
265 flag = (enable && !(options & OPT_VERB))
266 ? OPT_VERB : OPT_VERB | OPT_VERBVERB;
267 break;
268 case ok_v_folder:
269 ok = (val != NULL && var_folder_updated(*val, &cp));
270 if (ok && cp != NULL) {
271 _var_vfree(*val);
272 /* It's smalloc()ed, but ensure we don't leak */
273 if (*cp == '\0') {
274 *val = UNCONST("");
275 free(cp);
276 } else
277 *val = cp;
279 break;
280 default:
281 break;
284 if (flag) {
285 if (enable)
286 options |= flag;
287 else
288 options &= ~flag;
290 NYD2_LEAVE;
291 return ok;
294 static char const *
295 _var_canonify(char const *vn)
297 NYD2_ENTER;
298 if (!upperchar(*vn)) {
299 char const *vp;
301 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
303 vn = (*vp == '@') ? i_strdup(vn) : vn;
305 NYD2_LEAVE;
306 return vn;
309 static bool_t
310 _var_revlookup(struct var_carrier *vcp, char const *name)
312 ui32_t hash, i, j;
313 struct var_map const *vmp;
314 NYD2_ENTER;
316 vcp->vc_name = name = _var_canonify(name);
317 vcp->vc_hash = hash = MA_NAME2HASH(name);
319 for (i = hash % _VAR_REV_PRIME, j = 0; j <= _VAR_REV_LONGEST; ++j) {
320 ui32_t x = _var_revmap[i];
321 if (x == _VAR_REV_ILL)
322 break;
323 vmp = _var_map + x;
324 if (vmp->vm_hash == hash && !strcmp(_var_keydat + vmp->vm_keyoff, name)) {
325 vcp->vc_vmap = vmp;
326 vcp->vc_okey = (enum okeys)x;
327 goto jleave;
329 if (++i == _VAR_REV_PRIME) {
330 #ifdef _VAR_REV_WRAPAROUND
331 i = 0;
332 #else
333 break;
334 #endif
337 vcp->vc_vmap = NULL;
338 vcp = NULL;
339 jleave:
340 NYD2_LEAVE;
341 return (vcp != NULL);
344 static bool_t
345 _var_lookup(struct var_carrier *vcp)
347 struct var **vap, *lvp, *vp;
348 NYD2_ENTER;
350 /* XXX _So_ unlikely that it should be checked if normal lookup fails! */
351 if (UNLIKELY(vcp->vc_vmap != NULL &&
352 (vcp->vc_vmap->vm_flags & VM_VIRTUAL) != 0)) {
353 struct var_virtual const *vvp;
355 for (vvp = _var_virtuals;
356 PTRCMP(vvp, <, _var_virtuals + NELEM(_var_virtuals)); ++vvp)
357 if (vvp->vv_okey == vcp->vc_okey) {
358 vp = UNCONST(vvp->vv_var);
359 goto jleave;
361 assert(0);
364 vap = _vars + (vcp->vc_prime = MA_HASH2PRIME(vcp->vc_hash));
366 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
367 if (!strcmp(vp->v_name, vcp->vc_name)) {
368 /* Relink as head, hope it "sorts on usage" over time.
369 * _var_clear() relies on this behaviour */
370 if (lvp != NULL) {
371 lvp->v_link = vp->v_link;
372 vp->v_link = *vap;
373 *vap = vp;
375 goto jleave;
377 vp = NULL;
378 jleave:
379 vcp->vc_var = vp;
380 NYD2_LEAVE;
381 return (vp != NULL);
384 static bool_t
385 _var_broadway(struct var_show *vsp, char const *name)
387 bool_t rv;
388 NYD2_ENTER;
390 memset(vsp, 0, sizeof *vsp);
392 vsp->vs_isasm = !_var_revlookup(&vsp->vs_vc, name);
394 if ((vsp->vs_isset = rv = _var_lookup(&vsp->vs_vc))) {
395 vsp->vs_value = vsp->vs_vc.vc_var->v_value;
396 vsp->vs_isenv = FAL0;
397 } else
398 vsp->vs_isenv = ((vsp->vs_value = getenv(vsp->vs_vc.vc_name)) != NULL);
400 NYD2_LEAVE;
401 return rv;
404 static bool_t
405 _var_set(struct var_carrier *vcp, char const *value)
407 struct var *vp;
408 char *oval;
409 bool_t ok = TRU1;
410 NYD2_ENTER;
412 if (value == NULL) {
413 ok = _var_clear(vcp);
414 goto jleave;
417 _var_lookup(vcp);
419 if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_RDONLY)) {
420 n_err(_("Variable readonly: \"%s\"\n"), vcp->vc_name);
421 ok = FAL0;
422 goto jleave;
425 /* Don't care what happens later on, store this in the unroll list */
426 if (_localopts != NULL)
427 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
429 if ((vp = vcp->vc_var) == NULL) {
430 size_t l = strlen(vcp->vc_name) + 1;
432 vcp->vc_var =
433 vp = smalloc(sizeof(*vp) - VFIELD_SIZEOF(struct var, v_name) + l);
434 vp->v_link = _vars[vcp->vc_prime];
435 _vars[vcp->vc_prime] = vp;
436 memcpy(vp->v_name, vcp->vc_name, l);
437 oval = UNCONST("");
438 } else
439 oval = vp->v_value;
441 if (vcp->vc_vmap == NULL)
442 vp->v_value = _var_vcopy(value);
443 else {
444 /* Via `set' etc. the user may give even boolean options non-boolean
445 * values, ignore that and force boolean xxx error log? */
446 if (vcp->vc_vmap->vm_flags & VM_BOOLEAN)
447 value = UNCONST("");
448 vp->v_value = _var_vcopy(value);
450 /* Check if update allowed XXX wasteful on error! */
451 if ((vcp->vc_vmap->vm_flags & VM_SPECIAL) &&
452 !(ok = _var_check_specials(vcp->vc_okey, TRU1, &vp->v_value))) {
453 char *cp = vp->v_value;
454 vp->v_value = oval;
455 oval = cp;
456 } else if (vcp->vc_vmap->vm_flags & VM_ENVIRON) {
457 #ifdef HAVE_SETENV
458 ok = (setenv(vcp->vc_name, vp->v_value, 1) == 0);
459 #else
460 if(options & OPT_D_VV)
461 n_err(_("Cannot update environment variable \"%s\"\n"),
462 vcp->vc_name);
463 #endif
467 if (*oval != '\0')
468 _var_vfree(oval);
469 jleave:
470 NYD2_LEAVE;
471 return ok;
474 static bool_t
475 _var_clear(struct var_carrier *vcp)
477 bool_t ok = TRU1;
478 NYD2_ENTER;
480 if (!_var_lookup(vcp)) {
481 if (!(pstate & PS_ROBOT) && (options & OPT_D_V))
482 n_err(_("Variable undefined: \"%s\"\n"), vcp->vc_name);
483 } else if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_RDONLY)) {
484 n_err(_("Variable readonly: \"%s\"\n"), vcp->vc_name);
485 ok = FAL0;
486 } else {
487 if (_localopts != NULL)
488 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
490 /* Always listhead after _var_lookup() */
491 _vars[vcp->vc_prime] = _vars[vcp->vc_prime]->v_link;
492 _var_vfree(vcp->vc_var->v_value);
493 free(vcp->vc_var);
494 vcp->vc_var = NULL;
496 if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_SPECIAL))
497 ok = _var_check_specials(vcp->vc_okey, FAL0, NULL);
499 NYD2_LEAVE;
500 return ok;
503 static void
504 _var_list_all(void)
506 struct var_show vs;
507 FILE *fp;
508 size_t no, i;
509 struct var *vp;
510 char const **vacp, **cap;
511 NYD2_ENTER;
513 if ((fp = Ftmp(NULL, "listvars", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
514 NULL) {
515 n_perr(_("tmpfile"), 0);
516 goto jleave;
519 for (no = i = 0; i < MA_PRIME; ++i)
520 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
521 ++no;
522 no += NELEM(_var_virtuals);
524 vacp = salloc(no * sizeof(*vacp));
526 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
527 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
528 *cap++ = vp->v_name;
529 for (i = 0; i < NELEM(_var_virtuals); ++i)
530 *cap++ = _var_virtuals[i].vv_var->v_name;
532 if (no > 1)
533 qsort(vacp, no, sizeof *vacp, &__var_list_all_cmp);
535 i = (ok_blook(bsdcompat) || ok_blook(bsdset));
536 for (cap = vacp; no != 0; ++cap, --no) {
537 char const *asmis, *fmt;
539 if (!_var_broadway(&vs, *cap))
540 continue;
541 if (vs.vs_value == NULL)
542 vs.vs_value = "";
544 asmis = !(options & OPT_D_VV) ? ""
545 : vs.vs_isasm ? "*" : " ";
546 if (i)
547 fmt = "%s%s\t%s\n";
548 else {
549 if (vs.vs_vc.vc_vmap != NULL &&
550 (vs.vs_vc.vc_vmap->vm_flags & VM_BOOLEAN))
551 fmt = "%sset %s\n";
552 else {
553 fmt = "%sset %s=\"%s\"\n";
554 if (*vs.vs_value != '\0')
555 vs.vs_value = __var_simple_quote(vs.vs_value);
558 /* Shall a code checker complain on that, i'm in holiday */
559 fprintf(fp, fmt, asmis, *cap, vs.vs_value);
562 page_or_print(fp, PTR2SIZE(cap - vacp));
563 Fclose(fp);
564 jleave:
565 NYD2_LEAVE;
568 static int
569 __var_list_all_cmp(void const *s1, void const *s2)
571 int rv;
572 NYD2_ENTER;
574 rv = strcmp(*(char**)UNCONST(s1), *(char**)UNCONST(s2));
575 NYD2_LEAVE;
576 return rv;
579 static char *
580 __var_simple_quote(char const *cp) /* TODO "unite" with string_quote(), etc.. */
582 bool_t esc;
583 size_t i;
584 char const *cp_base;
585 char c, *rv;
586 NYD2_ENTER;
588 for (i = 0, cp_base = cp; (c = *cp) != '\0'; ++i, ++cp)
589 if (c == '"')
590 ++i;
591 rv = salloc(i +1);
593 for (esc = FAL0, i = 0, cp = cp_base; (c = *cp) != '\0'; rv[i++] = c, ++cp) {
594 if (!esc) {
595 if (c == '"')
596 rv[i++] = '\\';
597 esc = (c == '\\');
598 } else
599 esc = FAL0;
601 rv[i] = '\0';
602 NYD2_LEAVE;
603 return rv;
606 static bool_t
607 _var_set_env(char **ap, bool_t issetenv)
609 char *cp, *cp2, *varbuf, c;
610 size_t errs = 0;
611 NYD2_ENTER;
613 for (; *ap != NULL; ++ap) {
614 /* Isolate key */
615 cp = *ap;
616 cp2 = varbuf = ac_alloc(strlen(cp) +1);
617 for (; (c = *cp) != '=' && c != '\0'; ++cp)
618 *cp2++ = c;
619 *cp2 = '\0';
620 if (c == '\0')
621 cp = UNCONST("");
622 else
623 ++cp;
624 if (varbuf == cp2) {
625 n_err(_("Non-null variable name required\n"));
626 ++errs;
627 goto jnext;
630 if (varbuf[0] == 'n' && varbuf[1] == 'o') {
631 char const *k = varbuf + 2;
633 if (issetenv && (!strcmp(k, "HOME") || /* TODO generic */
634 !strcmp(k, "LOGNAME") || !strcmp(k, "USER") || /* TODO .. */
635 !strcmp(k, "TMPDIR"))) {/* TODO approach */
636 if (options & OPT_D_V)
637 n_err(_("Cannot `unsetenv' \"%s\"\n"), k);
638 ++errs;
639 goto jnext;
642 errs += !_var_vokclear(k);
643 if (issetenv) {
644 #ifdef HAVE_SETENV
645 errs += (unsetenv(k) != 0);
646 #else
647 ++errs;
648 #endif
650 } else {
651 errs += !_var_vokset(varbuf, (uintptr_t)cp);
652 if (issetenv) {
653 do {
654 static char const *cp_buf[3];
655 char const **pl, **pe;
657 if (!strcmp(varbuf, "HOME")) /* TODO generic approach..*/
658 pl = cp_buf + 0, pe = &homedir;
659 else if (!strcmp(varbuf, "LOGNAME") || !strcmp(varbuf, "USER"))
660 pl = cp_buf + 1, pe = &myname;
661 else if (!strcmp(varbuf, "TMPDIR")) /* TODO ..until here */
662 pl = cp_buf + 2, pe = &tempdir;
663 else
664 break;
666 if (*pl != NULL)
667 free(UNCONST(*pl));
668 *pe = *pl = sstrdup(cp);
669 } while (0);
671 #ifdef HAVE_SETENV
672 errs += (setenv(varbuf, cp, 1) != 0);
673 #else
674 ++errs;
675 #endif
678 jnext:
679 ac_free(varbuf);
682 NYD2_LEAVE;
683 return (errs == 0);
686 static bool_t
687 _is_closing_angle(char const *cp)
689 bool_t rv = FAL0;
690 NYD2_ENTER;
692 while (spacechar(*cp))
693 ++cp;
694 if (*cp++ != '}')
695 goto jleave;
696 while (spacechar(*cp))
697 ++cp;
698 rv = (*cp == '\0');
699 jleave:
700 NYD2_LEAVE;
701 return rv;
704 static struct macro *
705 _ma_look(char const *name, struct macro *data, enum ma_flags mafl)
707 enum ma_flags save_mafl;
708 ui32_t h;
709 struct macro *lmp, *mp;
710 NYD2_ENTER;
712 save_mafl = mafl;
713 mafl &= MA_TYPE_MASK;
714 h = MA_NAME2HASH(name);
715 h = MA_HASH2PRIME(h);
717 for (lmp = NULL, mp = _macros[h]; mp != NULL; lmp = mp, mp = mp->ma_next) {
718 if ((mp->ma_flags & MA_TYPE_MASK) == mafl && !strcmp(mp->ma_name, name)) {
719 if (save_mafl & MA_UNDEF) {
720 if (lmp == NULL)
721 _macros[h] = mp->ma_next;
722 else
723 lmp->ma_next = mp->ma_next;
725 /* TODO it should also be possible to delete running macros */
726 if ((mafl & MA_ACC) &&
727 account_name != NULL && !strcmp(account_name, name)) {
728 mp->ma_flags |= MA_DELETED;
729 n_err(_("Delayed deletion of active account \"%s\"\n"), name);
730 } else {
731 _ma_freelines(mp->ma_contents);
732 free(mp->ma_name);
733 free(mp);
734 mp = (struct macro*)-1;
737 goto jleave;
741 if (data != NULL) {
742 data->ma_next = _macros[h];
743 _macros[h] = data;
744 mp = NULL;
746 jleave:
747 NYD2_LEAVE;
748 return mp;
751 static bool_t
752 _ma_exec(struct macro const *mp, struct var **unroller, bool_t localopts)
754 struct lostack los;
755 struct mline const *lp;
756 char **args;
757 size_t i;
758 bool_t rv;
759 NYD2_ENTER;
761 for (i = 0, lp = mp->ma_contents; lp != NULL; ++i, lp = lp->l_next)
763 args = smalloc(sizeof(*args) * ++i);
764 for (i = 0, lp = mp->ma_contents; lp != NULL; ++i, lp = lp->l_next)
765 args[i] = sbufdup(lp->l_line, lp->l_length);
766 args[i] = NULL;
768 los.s_up = _localopts;
769 los.s_mac = UNCONST(mp); /* But not used.. */
770 los.s_localopts = (unroller == NULL) ? NULL : *unroller;
771 los.s_unroll = localopts;
772 _localopts = &los;
773 rv = n_source_macro(mp->ma_name, args);
774 _localopts = los.s_up;
776 if (unroller == NULL) {
777 if (los.s_localopts != NULL)
778 _localopts_unroll(&los.s_localopts);
779 } else
780 *unroller = los.s_localopts;
782 NYD2_LEAVE;
783 return rv;
786 static int
787 _ma_list(enum ma_flags mafl)
789 FILE *fp;
790 char const *typestr;
791 struct macro *mq;
792 ui32_t ti, mc, i;
793 struct mline *lp;
794 NYD2_ENTER;
796 if ((fp = Ftmp(NULL, "listmacs", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
797 NULL) {
798 n_perr(_("tmpfile"), 0);
799 mc = 1;
800 goto jleave;
803 mafl &= MA_TYPE_MASK;
804 typestr = (mafl & MA_ACC) ? "account" : "define";
806 for (ti = mc = 0; ti < MA_PRIME; ++ti)
807 for (mq = _macros[ti]; mq; mq = mq->ma_next)
808 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
809 if (++mc > 1)
810 putc('\n', fp);
811 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
812 for (lp = mq->ma_contents; lp != NULL; lp = lp->l_next) {
813 for (i = lp->l_leadspaces; i > 0; --i)
814 putc(' ', fp);
815 fputs(lp->l_line, fp);
816 putc('\n', fp);
818 fputs("}\n", fp);
820 if (mc)
821 page_or_print(fp, 0);
823 mc = (ui32_t)ferror(fp);
824 Fclose(fp);
825 jleave:
826 NYD2_LEAVE;
827 return (int)mc;
830 static bool_t
831 _ma_define(char const *name, enum ma_flags mafl)
833 bool_t rv = FAL0;
834 struct macro *mp;
835 struct mline *lp, *lst = NULL, *lnd = NULL;
836 char *linebuf = NULL, *cp;
837 size_t linesize = 0;
838 ui32_t maxlen = 0, leaspc;
839 union {int i; ui32_t ui;} n;
840 NYD2_ENTER;
842 mp = scalloc(1, sizeof *mp);
843 mp->ma_name = sstrdup(name);
844 mp->ma_flags = mafl;
846 for (;;) {
847 n.i = n_lex_input("", TRU1, &linebuf, &linesize, NULL);
848 if (n.ui == 0)
849 continue;
850 if (n.i < 0) {
851 n_err(_("Unterminated %s definition: \"%s\"\n"),
852 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
853 goto jerr;
855 if (_is_closing_angle(linebuf))
856 break;
858 /* Trim WS xxx we count tabs as one space here */
859 for (cp = linebuf, leaspc = 0; n.ui > 0 && whitechar(*cp); ++cp, --n.ui)
860 if (*cp == '\t')
861 leaspc = (leaspc + 8) & ~7;
862 else
863 ++leaspc;
864 if (n.ui == 0)
865 continue;
866 for (; whitechar(cp[n.ui - 1]); --n.ui)
867 assert(n.ui > 0);
868 assert(n.ui > 0);
870 maxlen = MAX(maxlen, n.ui);
871 cp[n.ui++] = '\0';
872 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n.ui);
873 memcpy(lp->l_line, cp, n.ui);
874 lp->l_length = --n.ui;
875 lp->l_leadspaces = leaspc;
877 if (lst != NULL) {
878 lnd->l_next = lp;
879 lnd = lp;
880 } else
881 lst = lnd = lp;
883 mp->ma_contents = lst;
884 mp->ma_maxlen = maxlen;
886 if (_ma_look(mp->ma_name, mp, mafl) != NULL) {
887 n_err(_("A %s named \"%s\" already exists\n"),
888 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
889 lst = mp->ma_contents;
890 goto jerr;
893 rv = TRU1;
894 jleave:
895 if (linebuf != NULL)
896 free(linebuf);
897 NYD2_LEAVE;
898 return rv;
900 jerr:
901 if (lst != NULL)
902 _ma_freelines(lst);
903 free(mp->ma_name);
904 free(mp);
905 goto jleave;
908 static bool_t
909 _ma_undefine(char const *name, enum ma_flags mafl)
911 struct macro *mp;
912 bool_t rv;
913 NYD2_ENTER;
915 rv = TRU1;
917 if (LIKELY(name[0] != '*' || name[1] != '\0')) {
918 if ((mp = _ma_look(name, NULL, mafl | MA_UNDEF)) == NULL) {
919 n_err(_("%s \"%s\" is not defined\n"),
920 (mafl & MA_ACC ? "Account" : "Macro"), name);
921 rv = FAL0;
923 } else {
924 struct macro **mpp, *lmp;
926 for (mpp = _macros; PTRCMP(mpp, <, _macros + NELEM(_macros)); ++mpp)
927 for (lmp = NULL, mp = *mpp; mp != NULL;) {
928 if ((mp->ma_flags & MA_TYPE_MASK) == mafl) {
929 /* xxx Expensive but rare: be simple */
930 _ma_look(mp->ma_name, NULL, mafl | MA_UNDEF);
931 mp = (lmp == NULL) ? *mpp : lmp->ma_next;
932 } else {
933 lmp = mp;
934 mp = mp->ma_next;
938 NYD2_LEAVE;
939 return rv;
942 static void
943 _ma_freelines(struct mline *lp)
945 struct mline *lq;
946 NYD2_ENTER;
948 for (lq = NULL; lp != NULL; ) {
949 if (lq != NULL)
950 free(lq);
951 lq = lp;
952 lp = lp->l_next;
954 if (lq)
955 free(lq);
956 NYD2_LEAVE;
959 static void
960 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
962 struct var *vap;
963 size_t nl, vl;
964 NYD2_ENTER;
966 /* Propagate unrolling up the stack, as necessary */
967 while (!losp->s_unroll && (losp = losp->s_up) != NULL)
969 if (losp == NULL)
970 goto jleave;
972 /* We've found a level that wants to unroll; check wether it does it yet */
973 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
974 if (!strcmp(vap->v_name, name))
975 goto jleave;
977 nl = strlen(name) + 1;
978 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
979 vap = smalloc(sizeof(*vap) - VFIELD_SIZEOF(struct var, v_name) + nl + vl);
980 vap->v_link = losp->s_localopts;
981 losp->s_localopts = vap;
982 memcpy(vap->v_name, name, nl);
983 if (vl == 0)
984 vap->v_value = NULL;
985 else {
986 vap->v_value = vap->v_name + nl;
987 memcpy(vap->v_value, ovap->v_value, vl);
989 jleave:
990 NYD2_LEAVE;
993 static void
994 _localopts_unroll(struct var **vapp)
996 struct lostack *save_los;
997 struct var *x, *vap;
998 NYD2_ENTER;
1000 vap = *vapp;
1001 *vapp = NULL;
1003 save_los = _localopts;
1004 _localopts = NULL;
1005 while (vap != NULL) {
1006 x = vap;
1007 vap = vap->v_link;
1008 vok_vset(x->v_name, x->v_value);
1009 free(x);
1011 _localopts = save_los;
1012 NYD2_LEAVE;
1015 FL char *
1016 _var_oklook(enum okeys okey)
1018 struct var_carrier vc;
1019 char *rv;
1020 NYD_ENTER;
1022 vc.vc_vmap = _var_map + okey;
1023 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1024 vc.vc_hash = _var_map[okey].vm_hash;
1025 vc.vc_okey = okey;
1027 if (!_var_lookup(&vc)) {
1028 if ((rv = getenv(vc.vc_name)) != NULL) {
1029 _var_set(&vc, rv);
1030 assert(vc.vc_var != NULL);
1031 goto jvar;
1033 } else
1034 jvar:
1035 rv = vc.vc_var->v_value;
1036 NYD_LEAVE;
1037 return rv;
1040 FL bool_t
1041 _var_okset(enum okeys okey, uintptr_t val)
1043 struct var_carrier vc;
1044 bool_t ok;
1045 NYD_ENTER;
1047 vc.vc_vmap = _var_map + okey;
1048 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1049 vc.vc_hash = _var_map[okey].vm_hash;
1050 vc.vc_okey = okey;
1052 ok = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
1053 NYD_LEAVE;
1054 return ok;
1057 FL bool_t
1058 _var_okclear(enum okeys okey)
1060 struct var_carrier vc;
1061 bool_t rv;
1062 NYD_ENTER;
1064 vc.vc_vmap = _var_map + okey;
1065 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1066 vc.vc_hash = _var_map[okey].vm_hash;
1067 vc.vc_okey = okey;
1069 rv = _var_clear(&vc);
1070 NYD_LEAVE;
1071 return rv;
1074 FL char *
1075 _var_voklook(char const *vokey)
1077 struct var_carrier vc;
1078 char *rv;
1079 NYD_ENTER;
1081 _var_revlookup(&vc, vokey);
1083 if (!_var_lookup(&vc)) {
1084 if ((rv = getenv(vc.vc_name)) != NULL) {
1085 _var_set(&vc, rv);
1086 assert(vc.vc_var != NULL);
1087 goto jvar;
1089 } else
1090 jvar:
1091 rv = vc.vc_var->v_value;
1092 NYD_LEAVE;
1093 return rv;
1096 FL bool_t
1097 _var_vokset(char const *vokey, uintptr_t val)
1099 struct var_carrier vc;
1100 bool_t ok;
1101 NYD_ENTER;
1103 _var_revlookup(&vc, vokey);
1105 ok = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
1106 NYD_LEAVE;
1107 return ok;
1110 FL bool_t
1111 _var_vokclear(char const *vokey)
1113 struct var_carrier vc;
1114 bool_t err;
1115 NYD_ENTER;
1117 _var_revlookup(&vc, vokey);
1119 err = !_var_clear(&vc);
1120 NYD_LEAVE;
1121 return !err;
1124 FL char *
1125 _env_look(char const *envkey, bool_t envonly) /* TODO rather dummy yet!! */
1127 char *rv;
1128 NYD_ENTER;
1130 if (envonly)
1131 rv = getenv(envkey); /* TODO rework vars: cache, output a la mimetypes */
1132 else
1133 rv = _var_voklook(envkey);
1134 NYD_LEAVE;
1135 return rv;
1138 #ifdef HAVE_SOCKETS
1139 FL char *
1140 _var_xoklook(enum okeys okey, struct url const *urlp, enum okey_xlook_mode oxm)
1142 struct var_carrier vc;
1143 struct str const *us;
1144 size_t nlen;
1145 char *nbuf = NULL /* CC happiness */, *rv;
1146 NYD_ENTER;
1148 assert(oxm & (OXM_PLAIN | OXM_H_P | OXM_U_H_P));
1150 /* For simplicity: allow this case too */
1151 if (!(oxm & (OXM_H_P | OXM_U_H_P)))
1152 goto jplain;
1154 vc.vc_vmap = _var_map + okey;
1155 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1156 vc.vc_okey = okey;
1158 us = (oxm & OXM_U_H_P) ? &urlp->url_u_h_p : &urlp->url_h_p;
1159 nlen = strlen(vc.vc_name);
1160 nbuf = ac_alloc(nlen + 1 + us->l +1);
1161 memcpy(nbuf, vc.vc_name, nlen);
1162 nbuf[nlen++] = '-';
1164 /* One of .url_u_h_p and .url_h_p we test in here */
1165 memcpy(nbuf + nlen, us->s, us->l +1);
1166 vc.vc_name = _var_canonify(nbuf);
1167 vc.vc_hash = MA_NAME2HASH(vc.vc_name);
1168 if (_var_lookup(&vc))
1169 goto jvar;
1171 /* The second */
1172 if (oxm & OXM_H_P) {
1173 us = &urlp->url_h_p;
1174 memcpy(nbuf + nlen, us->s, us->l +1);
1175 vc.vc_name = _var_canonify(nbuf);
1176 vc.vc_hash = MA_NAME2HASH(vc.vc_name);
1177 if (_var_lookup(&vc)) {
1178 jvar:
1179 rv = vc.vc_var->v_value;
1180 goto jleave;
1184 jplain:
1185 rv = (oxm & OXM_PLAIN) ? _var_oklook(okey) : NULL;
1186 jleave:
1187 if (oxm & (OXM_H_P | OXM_U_H_P))
1188 ac_free(nbuf);
1189 NYD_LEAVE;
1190 return rv;
1192 #endif /* HAVE_SOCKETS */
1194 FL int
1195 c_varshow(void *v)
1197 struct var_show vs;
1198 char const **argv = v;
1199 NYD_ENTER;
1201 if (*argv == NULL)
1202 v = NULL;
1203 else for (; *argv != NULL; ++argv) {
1204 _var_broadway(&vs, *argv);
1205 if (vs.vs_value == NULL)
1206 vs.vs_value = "NULL";
1208 if (vs.vs_vc.vc_vmap != NULL) {
1209 ui16_t f = vs.vs_vc.vc_vmap->vm_flags;
1211 if (f & VM_BOOLEAN)
1212 printf(_("\"%s\": (%d) boolean%s%s%s: set=%d%s\n"),
1213 vs.vs_vc.vc_name, vs.vs_vc.vc_okey,
1214 (f & VM_RDONLY ? ",read-only" : ""),
1215 (f & VM_VIRTUAL ? ",virtual" : ""),
1216 (f & VM_ENVIRON ? ",environ-sync" : ""),
1217 vs.vs_isset, (vs.vs_isenv ? _(" (in extern environ)") : ""));
1218 else
1219 printf(
1220 _("\"%s\": (%d) value%s%s%s: set=%d%s value<%s>\n"),
1221 vs.vs_vc.vc_name, vs.vs_vc.vc_okey,
1222 (f & VM_RDONLY ? ",read-only" : ""),
1223 (f & VM_VIRTUAL ? ",virtual" : ""),
1224 (f & VM_ENVIRON ? ",environ-sync" : ""),
1225 vs.vs_isset, (vs.vs_isenv ? _(" (in extern environ)") : ""),
1226 vs.vs_value);
1227 } else
1228 printf("\"%s\": (assembled): set=%d%s value<%s>\n",
1229 vs.vs_vc.vc_name, vs.vs_isset,
1230 (vs.vs_isenv ? _(" (in extern environ)") : ""), vs.vs_value);
1232 NYD_LEAVE;
1233 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1236 FL int
1237 c_set(void *v)
1239 char **ap = v;
1240 int err;
1241 NYD_ENTER;
1243 if (*ap == NULL) {
1244 _var_list_all();
1245 err = 0;
1246 } else
1247 err = !_var_set_env(ap, FAL0);
1248 NYD_LEAVE;
1249 return err;
1252 FL int
1253 c_setenv(void *v)
1255 char **ap = v;
1256 int err;
1257 NYD_ENTER;
1259 if (!(err = !(pstate & PS_STARTED)))
1260 err = !_var_set_env(ap, TRU1);
1261 NYD_LEAVE;
1262 return err;
1265 FL int
1266 c_unset(void *v)
1268 char **ap = v;
1269 int err = 0;
1270 NYD_ENTER;
1272 while (*ap != NULL)
1273 err |= !_var_vokclear(*ap++);
1274 NYD_LEAVE;
1275 return err;
1278 FL int
1279 c_unsetenv(void *v)
1281 int err;
1282 NYD_ENTER;
1284 if (!(err = !(pstate & PS_STARTED))) {
1285 char **ap;
1287 for (ap = v; *ap != NULL; ++ap) {
1288 bool_t bad;
1290 if (!strcmp(*ap, "HOME") || /* TODO generic */
1291 !strcmp(*ap, "LOGNAME") || !strcmp(*ap, "USER") || /* TODO .. */
1292 !strcmp(*ap, "TMPDIR")) { /* TODO approach */
1293 if (options & OPT_D_V)
1294 n_err(_("Cannot `unsetenv' \"%s\"\n"), *ap);
1295 err = 1;
1296 continue;
1299 bad = !_var_vokclear(*ap);
1300 if (
1301 #ifdef HAVE_SETENV
1302 unsetenv(*ap) != 0 ||
1303 #endif
1306 err = 1;
1309 NYD_LEAVE;
1310 return err;
1313 FL int
1314 c_varedit(void *v)
1316 struct var_carrier vc;
1317 sighandler_type sigint;
1318 FILE *of, *nf;
1319 char *val, **argv = v;
1320 int err = 0;
1321 NYD_ENTER;
1323 sigint = safe_signal(SIGINT, SIG_IGN);
1325 while (*argv != NULL) {
1326 memset(&vc, 0, sizeof vc);
1328 _var_revlookup(&vc, *argv++);
1330 if (vc.vc_vmap != NULL) {
1331 if (vc.vc_vmap->vm_flags & VM_BOOLEAN) {
1332 n_err(_("`varedit': can't edit boolean variable \"%s\"\n"),
1333 vc.vc_name);
1334 continue;
1336 if (vc.vc_vmap->vm_flags & VM_RDONLY) {
1337 n_err(_("`varedit': can't edit readonly variable \"%s\"\n"),
1338 vc.vc_name);
1339 continue;
1343 _var_lookup(&vc);
1345 if ((of = Ftmp(NULL, "vared", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1346 NULL) {
1347 n_perr(_("`varedit': can't create temporary file, bailing out"), 0);
1348 err = 1;
1349 break;
1350 } else if (vc.vc_var != NULL && *(val = vc.vc_var->v_value) != '\0' &&
1351 sizeof *val != fwrite(val, strlen(val), sizeof *val, of)) {
1352 n_perr(_("`varedit' failed to write old value to temporary file"), 0);
1353 Fclose(of);
1354 err = 1;
1355 continue;
1358 fflush_rewind(of);
1359 nf = run_editor(of, (off_t)-1, 'e', FAL0, NULL, NULL, SEND_MBOX, sigint);
1360 Fclose(of);
1362 if (nf != NULL) {
1363 int c;
1364 char *base;
1365 off_t l = fsize(nf);
1367 assert(l >= 0);
1368 base = smalloc((size_t)l + 1);
1370 for (l = 0, val = base; (c = getc(nf)) != EOF; ++val)
1371 if (c == '\n' || c == '\r') {
1372 *val = ' ';
1373 ++l;
1374 } else {
1375 *val = (char)(uc_i)c;
1376 l = 0;
1378 val -= l;
1379 *val = '\0';
1381 if (!vok_vset(vc.vc_name, base))
1382 err = 1;
1384 free(base);
1385 Fclose(nf);
1386 } else {
1387 n_err(_("`varedit': can't start $EDITOR, bailing out\n"));
1388 err = 1;
1389 break;
1393 safe_signal(SIGINT, sigint);
1394 NYD_LEAVE;
1395 return err;
1398 FL int
1399 c_define(void *v)
1401 int rv = 1;
1402 char **args = v;
1403 NYD_ENTER;
1405 if (args[0] == NULL) {
1406 rv = _ma_list(MA_NONE);
1407 goto jleave;
1410 if (args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
1411 args[2] != NULL) {
1412 n_err(_("Syntax is: define <name> {"));
1413 goto jleave;
1416 rv = !_ma_define(args[0], MA_NONE);
1417 jleave:
1418 NYD_LEAVE;
1419 return rv;
1422 FL int
1423 c_undefine(void *v)
1425 int rv;
1426 char **args;
1427 NYD_ENTER;
1429 rv = 0;
1430 args = v;
1432 rv |= !_ma_undefine(*args, MA_NONE);
1433 while (*++args != NULL);
1434 NYD_LEAVE;
1435 return rv;
1438 FL int
1439 c_call(void *v)
1441 int rv = 1;
1442 char **args = v;
1443 char const *errs, *name;
1444 struct macro *mp;
1445 NYD_ENTER;
1447 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
1448 errs = _("Synopsis: call: <%s>\n");
1449 name = "name";
1450 goto jerr;
1453 if ((mp = _ma_look(name = *args, NULL, MA_NONE)) == NULL) {
1454 errs = _("Undefined macro `call'ed: \"%s\"\n");
1455 name = *args;
1456 goto jerr;
1459 rv = (_ma_exec(mp, NULL, FAL0) == FAL0);
1460 jleave:
1461 NYD_LEAVE;
1462 return rv;
1463 jerr:
1464 n_err(errs, name);
1465 goto jleave;
1468 FL bool_t
1469 check_folder_hook(bool_t nmail) /* TODO temporary, v15: drop */
1471 size_t len;
1472 char *var, *cp;
1473 struct macro *mp;
1474 struct var **unroller;
1475 bool_t rv = TRU1;
1476 NYD_ENTER;
1478 var = ac_alloc(len = strlen(mailname) + sizeof("folder-hook-") -1 +1);
1480 /* First try the fully resolved path */
1481 snprintf(var, len, "folder-hook-%s", mailname);
1482 if ((cp = vok_vlook(var)) != NULL)
1483 goto jmac;
1485 /* If we are under *folder*, try the usual +NAME syntax, too */
1486 if (displayname[0] == '+') {
1487 char *x = mailname + len;
1489 for (; x > mailname; --x)
1490 if (x[-1] == '/') {
1491 snprintf(var, len, "folder-hook-+%s", x);
1492 if ((cp = vok_vlook(var)) != NULL)
1493 goto jmac;
1494 break;
1498 /* Plain *folder-hook* is our last try */
1499 if ((cp = ok_vlook(folder_hook)) == NULL)
1500 goto jleave;
1502 jmac:
1503 if ((mp = _ma_look(cp, NULL, MA_NONE)) == NULL) {
1504 n_err(_("Cannot call *folder-hook* for \"%s\": "
1505 "macro \"%s\" does not exist\n"), displayname, cp);
1506 rv = FAL0;
1507 goto jleave;
1510 pstate &= ~PS_HOOK_MASK;
1511 if (nmail) {
1512 pstate |= PS_HOOK_NEWMAIL;
1513 unroller = NULL;
1514 } else {
1515 pstate |= PS_HOOK;
1516 unroller = &_folder_hook_localopts;
1518 rv = _ma_exec(mp, unroller, TRU1);
1519 pstate &= ~PS_HOOK_MASK;
1521 jleave:
1522 ac_free(var);
1523 NYD_LEAVE;
1524 return rv;
1527 FL void
1528 call_compose_mode_hook(char const *macname) /* TODO temporary, v15: drop */
1530 struct macro *mp;
1531 NYD_ENTER;
1533 if ((mp = _ma_look(macname, NULL, MA_NONE)) == NULL)
1534 n_err(_("Cannot call *on-compose-*-hook*: "
1535 "macro \"%s\" does not exist\n"), macname);
1536 else {
1537 pstate &= ~PS_HOOK_MASK;
1538 pstate |= PS_HOOK;
1539 _ma_exec(mp, &a_macvar_compose_localopts, TRU1);
1540 pstate &= ~PS_HOOK_MASK;
1542 NYD_LEAVE;
1545 FL int
1546 c_account(void *v)
1548 char **args = v;
1549 struct macro *mp;
1550 int rv = 1, i, oqf, nqf;
1551 NYD_ENTER;
1553 if (args[0] == NULL) {
1554 rv = _ma_list(MA_ACC);
1555 goto jleave;
1558 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1559 if (args[2] != NULL) {
1560 n_err(_("Syntax is: account <name> {\n"));
1561 goto jleave;
1563 if (!asccasecmp(args[0], ACCOUNT_NULL)) {
1564 n_err(_("Error: \"%s\" is a reserved name\n"), ACCOUNT_NULL);
1565 goto jleave;
1567 rv = !_ma_define(args[0], MA_ACC);
1568 goto jleave;
1571 if (pstate & PS_HOOK_MASK) {
1572 n_err(_("Cannot change account from within a hook\n"));
1573 goto jleave;
1576 save_mbox_for_possible_quitstuff();
1578 mp = NULL;
1579 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
1580 (mp = _ma_look(args[0], NULL, MA_ACC)) == NULL) {
1581 n_err(_("Account \"%s\" does not exist\n"), args[0]);
1582 goto jleave;
1585 oqf = savequitflags();
1587 if (_acc_curr != NULL) {
1588 if (_acc_curr->ma_localopts != NULL)
1589 _localopts_unroll(&_acc_curr->ma_localopts);
1590 if (_acc_curr->ma_flags & MA_DELETED) { /* xxx can be made generic? */
1591 _ma_freelines(_acc_curr->ma_contents);
1592 free(_acc_curr->ma_name);
1593 free(_acc_curr);
1597 account_name = (mp != NULL) ? mp->ma_name : NULL;
1598 _acc_curr = mp;
1600 if (mp != NULL && !_ma_exec(mp, &mp->ma_localopts, TRU1)) {
1601 /* XXX account switch incomplete, unroll? */
1602 n_err(_("Switching to account \"%s\" failed\n"), args[0]);
1603 goto jleave;
1606 if ((pstate & (PS_STARTED | PS_HOOK_MASK)) == PS_STARTED) {
1607 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1608 restorequitflags(oqf);
1609 if ((i = setfile("%", 0)) < 0)
1610 goto jleave;
1611 check_folder_hook(FAL0);
1612 if (i > 0 && !ok_blook(emptystart))
1613 goto jleave;
1614 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1615 restorequitflags(nqf);
1617 rv = 0;
1618 jleave:
1619 NYD_LEAVE;
1620 return rv;
1623 FL int
1624 c_unaccount(void *v)
1626 int rv;
1627 char **args;
1628 NYD_ENTER;
1630 rv = 0;
1631 args = v;
1633 rv |= !_ma_undefine(*args, MA_ACC);
1634 while (*++args != NULL);
1635 NYD_LEAVE;
1636 return rv;
1639 FL int
1640 c_localopts(void *v)
1642 int rv = 1;
1643 char **c = v;
1644 NYD_ENTER;
1646 if (_localopts == NULL) {
1647 n_err(_("Cannot use `localopts' but from within a "
1648 "`define' or `account'\n"));
1649 goto jleave;
1652 _localopts->s_unroll = (boolify(*c, UIZ_MAX, FAL0) > 0);
1653 rv = 0;
1654 jleave:
1655 NYD_LEAVE;
1656 return rv;
1659 FL void
1660 temporary_localopts_free(void) /* XXX intermediate hack */
1662 NYD_ENTER;
1664 if (a_macvar_compose_localopts != NULL) {
1665 void *save = _localopts;
1666 _localopts = NULL;
1667 _localopts_unroll(&a_macvar_compose_localopts);
1668 a_macvar_compose_localopts = NULL;
1669 _localopts = save;
1671 NYD_LEAVE;
1674 FL void
1675 temporary_localopts_folder_hook_unroll(void) /* XXX intermediate hack */
1677 NYD_ENTER;
1678 if (_folder_hook_localopts != NULL) {
1679 void *save = _localopts;
1680 _localopts = NULL;
1681 _localopts_unroll(&_folder_hook_localopts);
1682 _folder_hook_localopts = NULL;
1683 _localopts = save;
1685 NYD_LEAVE;
1688 /* s-it-mode */