(BWDIC!) Allow `source' in `call'ed macros..
[s-mailx.git] / accmacvar.c
blobaa2a19c571f4c2c0016e746d2dc49542ea855449
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;
136 /* TODO Ditto, compose hooks */
137 static struct var *a_macvar_compose_localopts;
139 /* TODO once we have a dynamically sized hashtable we could unite _macros and
140 * TODO _variables into a single hashtable, stripping down fun interface;
141 * TODO also, setting and clearing a variable can be easily joined */
142 static struct var *_vars[MA_PRIME]; /* TODO dynamically spaced */
143 static struct macro *_macros[MA_PRIME]; /* TODO dynamically spaced */
145 /* Special cased value string allocation */
146 static char * _var_vcopy(char const *str);
147 static void _var_vfree(char *cp);
149 /* Check for special housekeeping. */
150 static bool_t _var_check_specials(enum okeys okey, bool_t enable,
151 char **val);
153 /* If a variable name begins with a lowercase-character and contains at
154 * least one '@', it is converted to all-lowercase. This is necessary
155 * for lookups of names based on email addresses.
156 * Following the standard, only the part following the last '@' should
157 * be lower-cased, but practice has established otherwise here.
158 * Return value may have been placed in string dope (salloc()) */
159 static char const * _var_canonify(char const *vn);
161 /* Try to reverse lookup an option name to an enum okeys mapping.
162 * Updates vcp.vc_name and vcp.vc_hash; vcp.vc_vmap is NULL if none found */
163 static bool_t _var_revlookup(struct var_carrier *vcp, char const *name);
165 /* Lookup a variable from vcp.vc_(vmap|name|hash), return wether it was found.
166 * Sets vcp.vc_prime; vcp.vc_var is NULL if not found */
167 static bool_t _var_lookup(struct var_carrier *vcp);
169 /* Completely fill vsp with data for name, return wether it was set as an
170 * internal variable (it may still have been set as an environment variable) */
171 static bool_t _var_broadway(struct var_show *vsp, char const *name);
173 /* Set variable from vcp.vc_(vmap|name|hash), return success */
174 static bool_t _var_set(struct var_carrier *vcp, char const *value);
176 /* Clear variable from vcp.vc_(vmap|name|hash); sets vcp.vc_var to NULL,
177 * return success */
178 static bool_t _var_clear(struct var_carrier *vcp);
180 /* List all variables */
181 static void _var_list_all(void);
183 static int __var_list_all_cmp(void const *s1, void const *s2);
184 static char * __var_simple_quote(char const *cp);
186 /* Shared c_set() and c_setenv() impl, return success */
187 static bool_t _var_set_env(char **ap, bool_t issetenv);
189 /* Does cp consist solely of WS and a } */
190 static bool_t _is_closing_angle(char const *cp);
192 /* Lookup for macros/accounts */
193 static struct macro *_ma_look(char const *name, struct macro *data,
194 enum ma_flags mafl);
196 /* Walk all lines of a macro and execute() them */
197 static bool_t _ma_exec(struct macro const *mp, struct var **unroller,
198 bool_t localopts);
200 /* User display helpers */
201 static int _ma_list(enum ma_flags mafl);
203 /* _ma_define() returns error for faulty definitions and already existing
204 * names, _ma_undefine() returns error if a named thing doesn't exist */
205 static bool_t _ma_define(char const *name, enum ma_flags mafl);
206 static bool_t _ma_undefine(char const *name, enum ma_flags mafl);
207 static void _ma_freelines(struct mline *lp);
209 /* Update replay-log */
210 static void _localopts_add(struct lostack *losp, char const *name,
211 struct var *ovap);
212 static void _localopts_unroll(struct var **vapp);
214 static char *
215 _var_vcopy(char const *str)
217 char *news;
218 size_t len;
219 NYD2_ENTER;
221 if (*str == '\0')
222 news = UNCONST("");
223 else {
224 len = strlen(str) +1;
225 news = smalloc(len);
226 memcpy(news, str, len);
228 NYD2_LEAVE;
229 return news;
232 static void
233 _var_vfree(char *cp)
235 NYD2_ENTER;
236 if (*cp != '\0')
237 free(cp);
238 NYD2_LEAVE;
241 static bool_t
242 _var_check_specials(enum okeys okey, bool_t enable, char **val)
244 char *cp = NULL;
245 bool_t ok = TRU1;
246 int flag = 0;
247 NYD2_ENTER;
249 switch (okey) {
250 case ok_b_debug:
251 flag = OPT_DEBUG;
252 break;
253 case ok_b_header:
254 flag = OPT_N_FLAG;
255 enable = !enable;
256 break;
257 case ok_b_memdebug:
258 flag = OPT_MEMDEBUG;
259 break;
260 case ok_b_skipemptybody:
261 flag = OPT_E_FLAG;
262 break;
263 case ok_b_verbose:
264 flag = (enable && !(options & OPT_VERB))
265 ? OPT_VERB : OPT_VERB | OPT_VERBVERB;
266 break;
267 case ok_v_folder:
268 ok = (val != NULL && var_folder_updated(*val, &cp));
269 if (ok && cp != NULL) {
270 _var_vfree(*val);
271 /* It's smalloc()ed, but ensure we don't leak */
272 if (*cp == '\0') {
273 *val = UNCONST("");
274 free(cp);
275 } else
276 *val = cp;
278 break;
279 default:
280 break;
283 if (flag) {
284 if (enable)
285 options |= flag;
286 else
287 options &= ~flag;
289 NYD2_LEAVE;
290 return ok;
293 static char const *
294 _var_canonify(char const *vn)
296 NYD2_ENTER;
297 if (!upperchar(*vn)) {
298 char const *vp;
300 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
302 vn = (*vp == '@') ? i_strdup(vn) : vn;
304 NYD2_LEAVE;
305 return vn;
308 static bool_t
309 _var_revlookup(struct var_carrier *vcp, char const *name)
311 ui32_t hash, i, j;
312 struct var_map const *vmp;
313 NYD2_ENTER;
315 vcp->vc_name = name = _var_canonify(name);
316 vcp->vc_hash = hash = MA_NAME2HASH(name);
318 for (i = hash % _VAR_REV_PRIME, j = 0; j <= _VAR_REV_LONGEST; ++j) {
319 ui32_t x = _var_revmap[i];
320 if (x == _VAR_REV_ILL)
321 break;
322 vmp = _var_map + x;
323 if (vmp->vm_hash == hash && !strcmp(_var_keydat + vmp->vm_keyoff, name)) {
324 vcp->vc_vmap = vmp;
325 vcp->vc_okey = (enum okeys)x;
326 goto jleave;
328 if (++i == _VAR_REV_PRIME) {
329 #ifdef _VAR_REV_WRAPAROUND
330 i = 0;
331 #else
332 break;
333 #endif
336 vcp->vc_vmap = NULL;
337 vcp = NULL;
338 jleave:
339 NYD2_LEAVE;
340 return (vcp != NULL);
343 static bool_t
344 _var_lookup(struct var_carrier *vcp)
346 struct var **vap, *lvp, *vp;
347 NYD2_ENTER;
349 /* XXX _So_ unlikely that it should be checked if normal lookup fails! */
350 if (UNLIKELY(vcp->vc_vmap != NULL &&
351 (vcp->vc_vmap->vm_flags & VM_VIRTUAL) != 0)) {
352 struct var_virtual const *vvp;
354 for (vvp = _var_virtuals;
355 PTRCMP(vvp, <, _var_virtuals + NELEM(_var_virtuals)); ++vvp)
356 if (vvp->vv_okey == vcp->vc_okey) {
357 vp = UNCONST(vvp->vv_var);
358 goto jleave;
360 assert(0);
363 vap = _vars + (vcp->vc_prime = MA_HASH2PRIME(vcp->vc_hash));
365 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
366 if (!strcmp(vp->v_name, vcp->vc_name)) {
367 /* Relink as head, hope it "sorts on usage" over time.
368 * _var_clear() relies on this behaviour */
369 if (lvp != NULL) {
370 lvp->v_link = vp->v_link;
371 vp->v_link = *vap;
372 *vap = vp;
374 goto jleave;
376 vp = NULL;
377 jleave:
378 vcp->vc_var = vp;
379 NYD2_LEAVE;
380 return (vp != NULL);
383 static bool_t
384 _var_broadway(struct var_show *vsp, char const *name)
386 bool_t rv;
387 NYD2_ENTER;
389 memset(vsp, 0, sizeof *vsp);
391 vsp->vs_isasm = !_var_revlookup(&vsp->vs_vc, name);
393 if ((vsp->vs_isset = rv = _var_lookup(&vsp->vs_vc))) {
394 vsp->vs_value = vsp->vs_vc.vc_var->v_value;
395 vsp->vs_isenv = FAL0;
396 } else
397 vsp->vs_isenv = ((vsp->vs_value = getenv(vsp->vs_vc.vc_name)) != NULL);
399 NYD2_LEAVE;
400 return rv;
403 static bool_t
404 _var_set(struct var_carrier *vcp, char const *value)
406 struct var *vp;
407 char *oval;
408 bool_t ok = TRU1;
409 NYD2_ENTER;
411 if (value == NULL) {
412 ok = _var_clear(vcp);
413 goto jleave;
416 _var_lookup(vcp);
418 if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_RDONLY)) {
419 n_err(_("Variable readonly: \"%s\"\n"), vcp->vc_name);
420 ok = FAL0;
421 goto jleave;
424 /* Don't care what happens later on, store this in the unroll list */
425 if (_localopts != NULL)
426 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
428 if ((vp = vcp->vc_var) == NULL) {
429 size_t l = strlen(vcp->vc_name) + 1;
431 vcp->vc_var =
432 vp = smalloc(sizeof(*vp) - VFIELD_SIZEOF(struct var, v_name) + l);
433 vp->v_link = _vars[vcp->vc_prime];
434 _vars[vcp->vc_prime] = vp;
435 memcpy(vp->v_name, vcp->vc_name, l);
436 oval = UNCONST("");
437 } else
438 oval = vp->v_value;
440 if (vcp->vc_vmap == NULL)
441 vp->v_value = _var_vcopy(value);
442 else {
443 /* Via `set' etc. the user may give even boolean options non-boolean
444 * values, ignore that and force boolean xxx error log? */
445 if (vcp->vc_vmap->vm_flags & VM_BOOLEAN)
446 value = UNCONST("");
447 vp->v_value = _var_vcopy(value);
449 /* Check if update allowed XXX wasteful on error! */
450 if ((vcp->vc_vmap->vm_flags & VM_SPECIAL) &&
451 !(ok = _var_check_specials(vcp->vc_okey, TRU1, &vp->v_value))) {
452 char *cp = vp->v_value;
453 vp->v_value = oval;
454 oval = cp;
458 if (*oval != '\0')
459 _var_vfree(oval);
460 jleave:
461 NYD2_LEAVE;
462 return ok;
465 static bool_t
466 _var_clear(struct var_carrier *vcp)
468 bool_t ok = TRU1;
469 NYD2_ENTER;
471 if (!_var_lookup(vcp)) {
472 if (!(pstate & PS_ROBOT) && (options & OPT_D_V))
473 n_err(_("Variable undefined: \"%s\"\n"), vcp->vc_name);
474 } else if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_RDONLY)) {
475 n_err(_("Variable readonly: \"%s\"\n"), vcp->vc_name);
476 ok = FAL0;
477 } else {
478 if (_localopts != NULL)
479 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
481 /* Always listhead after _var_lookup() */
482 _vars[vcp->vc_prime] = _vars[vcp->vc_prime]->v_link;
483 _var_vfree(vcp->vc_var->v_value);
484 free(vcp->vc_var);
485 vcp->vc_var = NULL;
487 if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_SPECIAL))
488 ok = _var_check_specials(vcp->vc_okey, FAL0, NULL);
490 NYD2_LEAVE;
491 return ok;
494 static void
495 _var_list_all(void)
497 struct var_show vs;
498 FILE *fp;
499 size_t no, i;
500 struct var *vp;
501 char const **vacp, **cap;
502 NYD2_ENTER;
504 if ((fp = Ftmp(NULL, "listvars", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
505 NULL) {
506 n_perr(_("tmpfile"), 0);
507 goto jleave;
510 for (no = i = 0; i < MA_PRIME; ++i)
511 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
512 ++no;
513 no += NELEM(_var_virtuals);
515 vacp = salloc(no * sizeof(*vacp));
517 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
518 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
519 *cap++ = vp->v_name;
520 for (i = 0; i < NELEM(_var_virtuals); ++i)
521 *cap++ = _var_virtuals[i].vv_var->v_name;
523 if (no > 1)
524 qsort(vacp, no, sizeof *vacp, &__var_list_all_cmp);
526 i = (ok_blook(bsdcompat) || ok_blook(bsdset));
527 for (cap = vacp; no != 0; ++cap, --no) {
528 char const *asmis, *fmt;
530 if (!_var_broadway(&vs, *cap))
531 continue;
532 if (vs.vs_value == NULL)
533 vs.vs_value = "";
535 asmis = !(options & OPT_D_VV) ? ""
536 : vs.vs_isasm ? "*" : " ";
537 if (i)
538 fmt = "%s%s\t%s\n";
539 else {
540 if (vs.vs_vc.vc_vmap != NULL &&
541 (vs.vs_vc.vc_vmap->vm_flags & VM_BOOLEAN))
542 fmt = "%sset %s\n";
543 else {
544 fmt = "%sset %s=\"%s\"\n";
545 if (*vs.vs_value != '\0')
546 vs.vs_value = __var_simple_quote(vs.vs_value);
549 /* Shall a code checker complain on that, i'm in holiday */
550 fprintf(fp, fmt, asmis, *cap, vs.vs_value);
553 page_or_print(fp, PTR2SIZE(cap - vacp));
554 Fclose(fp);
555 jleave:
556 NYD2_LEAVE;
559 static int
560 __var_list_all_cmp(void const *s1, void const *s2)
562 int rv;
563 NYD2_ENTER;
565 rv = strcmp(*(char**)UNCONST(s1), *(char**)UNCONST(s2));
566 NYD2_LEAVE;
567 return rv;
570 static char *
571 __var_simple_quote(char const *cp) /* TODO "unite" with string_quote(), etc.. */
573 bool_t esc;
574 size_t i;
575 char const *cp_base;
576 char c, *rv;
577 NYD2_ENTER;
579 for (i = 0, cp_base = cp; (c = *cp) != '\0'; ++i, ++cp)
580 if (c == '"')
581 ++i;
582 rv = salloc(i +1);
584 for (esc = FAL0, i = 0, cp = cp_base; (c = *cp) != '\0'; rv[i++] = c, ++cp) {
585 if (!esc) {
586 if (c == '"')
587 rv[i++] = '\\';
588 esc = (c == '\\');
589 } else
590 esc = FAL0;
592 rv[i] = '\0';
593 NYD2_LEAVE;
594 return rv;
597 static bool_t
598 _var_set_env(char **ap, bool_t issetenv)
600 char *cp, *cp2, *varbuf, c;
601 size_t errs = 0;
602 NYD2_ENTER;
604 for (; *ap != NULL; ++ap) {
605 /* Isolate key */
606 cp = *ap;
607 cp2 = varbuf = ac_alloc(strlen(cp) +1);
608 for (; (c = *cp) != '=' && c != '\0'; ++cp)
609 *cp2++ = c;
610 *cp2 = '\0';
611 if (c == '\0')
612 cp = UNCONST("");
613 else
614 ++cp;
615 if (varbuf == cp2) {
616 n_err(_("Non-null variable name required\n"));
617 ++errs;
618 goto jnext;
621 if (varbuf[0] == 'n' && varbuf[1] == 'o') {
622 char const *k = varbuf + 2;
624 if (issetenv && (!strcmp(k, "HOME") || /* TODO generic */
625 !strcmp(k, "LOGNAME") || !strcmp(k, "USER") || /* TODO .. */
626 !strcmp(k, "TMPDIR"))) {/* TODO approach */
627 if (options & OPT_D_V)
628 n_err(_("Cannot `unsetenv' \"%s\"\n"), k);
629 ++errs;
630 goto jnext;
633 errs += !_var_vokclear(k);
634 if (issetenv) {
635 #ifdef HAVE_SETENV
636 errs += (unsetenv(k) != 0);
637 #else
638 ++errs;
639 #endif
641 } else {
642 errs += !_var_vokset(varbuf, (uintptr_t)cp);
643 if (issetenv) {
644 do {
645 static char const *cp_buf[3];
646 char const **pl, **pe;
648 if (!strcmp(varbuf, "HOME")) /* TODO generic approach..*/
649 pl = cp_buf + 0, pe = &homedir;
650 else if (!strcmp(varbuf, "LOGNAME") || !strcmp(varbuf, "USER"))
651 pl = cp_buf + 1, pe = &myname;
652 else if (!strcmp(varbuf, "TMPDIR")) /* TODO ..until here */
653 pl = cp_buf + 2, pe = &tempdir;
654 else
655 break;
657 if (*pl != NULL)
658 free(UNCONST(*pl));
659 *pe = *pl = sstrdup(cp);
660 } while (0);
662 #ifdef HAVE_SETENV
663 errs += (setenv(varbuf, cp, 1) != 0);
664 #else
665 ++errs;
666 #endif
669 jnext:
670 ac_free(varbuf);
673 NYD2_LEAVE;
674 return (errs == 0);
677 static bool_t
678 _is_closing_angle(char const *cp)
680 bool_t rv = FAL0;
681 NYD2_ENTER;
683 while (spacechar(*cp))
684 ++cp;
685 if (*cp++ != '}')
686 goto jleave;
687 while (spacechar(*cp))
688 ++cp;
689 rv = (*cp == '\0');
690 jleave:
691 NYD2_LEAVE;
692 return rv;
695 static struct macro *
696 _ma_look(char const *name, struct macro *data, enum ma_flags mafl)
698 enum ma_flags save_mafl;
699 ui32_t h;
700 struct macro *lmp, *mp;
701 NYD2_ENTER;
703 save_mafl = mafl;
704 mafl &= MA_TYPE_MASK;
705 h = MA_NAME2HASH(name);
706 h = MA_HASH2PRIME(h);
708 for (lmp = NULL, mp = _macros[h]; mp != NULL; lmp = mp, mp = mp->ma_next) {
709 if ((mp->ma_flags & MA_TYPE_MASK) == mafl && !strcmp(mp->ma_name, name)) {
710 if (save_mafl & MA_UNDEF) {
711 if (lmp == NULL)
712 _macros[h] = mp->ma_next;
713 else
714 lmp->ma_next = mp->ma_next;
716 /* TODO it should also be possible to delete running macros */
717 if ((mafl & MA_ACC) &&
718 account_name != NULL && !strcmp(account_name, name)) {
719 mp->ma_flags |= MA_DELETED;
720 n_err(_("Delayed deletion of active account \"%s\"\n"), name);
721 } else {
722 _ma_freelines(mp->ma_contents);
723 free(mp->ma_name);
724 free(mp);
725 mp = (struct macro*)-1;
728 goto jleave;
732 if (data != NULL) {
733 data->ma_next = _macros[h];
734 _macros[h] = data;
735 mp = NULL;
737 jleave:
738 NYD2_LEAVE;
739 return mp;
742 static bool_t
743 _ma_exec(struct macro const *mp, struct var **unroller, bool_t localopts)
745 struct lostack los;
746 struct mline const *lp;
747 char **args;
748 size_t i;
749 bool_t rv;
750 NYD2_ENTER;
752 for (i = 0, lp = mp->ma_contents; lp != NULL; ++i, lp = lp->l_next)
754 args = smalloc(sizeof(*args) * ++i);
755 for (i = 0, lp = mp->ma_contents; lp != NULL; ++i, lp = lp->l_next)
756 args[i] = sbufdup(lp->l_line, lp->l_length);
757 args[i] = NULL;
759 los.s_up = _localopts;
760 los.s_mac = UNCONST(mp); /* But not used.. */
761 los.s_localopts = (unroller == NULL) ? NULL : *unroller;
762 los.s_unroll = localopts;
763 _localopts = &los;
764 rv = n_source_macro(mp->ma_name, args);
765 _localopts = los.s_up;
767 if (unroller == NULL) {
768 if (los.s_localopts != NULL)
769 _localopts_unroll(&los.s_localopts);
770 } else
771 *unroller = los.s_localopts;
773 NYD2_LEAVE;
774 return rv;
777 static int
778 _ma_list(enum ma_flags mafl)
780 FILE *fp;
781 char const *typestr;
782 struct macro *mq;
783 ui32_t ti, mc, i;
784 struct mline *lp;
785 NYD2_ENTER;
787 if ((fp = Ftmp(NULL, "listmacs", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
788 NULL) {
789 n_perr(_("tmpfile"), 0);
790 mc = 1;
791 goto jleave;
794 mafl &= MA_TYPE_MASK;
795 typestr = (mafl & MA_ACC) ? "account" : "define";
797 for (ti = mc = 0; ti < MA_PRIME; ++ti)
798 for (mq = _macros[ti]; mq; mq = mq->ma_next)
799 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
800 if (++mc > 1)
801 putc('\n', fp);
802 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
803 for (lp = mq->ma_contents; lp != NULL; lp = lp->l_next) {
804 for (i = lp->l_leadspaces; i > 0; --i)
805 putc(' ', fp);
806 fputs(lp->l_line, fp);
807 putc('\n', fp);
809 fputs("}\n", fp);
811 if (mc)
812 page_or_print(fp, 0);
814 mc = (ui32_t)ferror(fp);
815 Fclose(fp);
816 jleave:
817 NYD2_LEAVE;
818 return (int)mc;
821 static bool_t
822 _ma_define(char const *name, enum ma_flags mafl)
824 bool_t rv = FAL0;
825 struct macro *mp;
826 struct mline *lp, *lst = NULL, *lnd = NULL;
827 char *linebuf = NULL, *cp;
828 size_t linesize = 0;
829 ui32_t maxlen = 0, leaspc;
830 union {int i; ui32_t ui;} n;
831 NYD2_ENTER;
833 mp = scalloc(1, sizeof *mp);
834 mp->ma_name = sstrdup(name);
835 mp->ma_flags = mafl;
837 for (;;) {
838 n.i = n_lex_input("", TRU1, &linebuf, &linesize, NULL);
839 if (n.ui == 0)
840 continue;
841 if (n.i < 0) {
842 n_err(_("Unterminated %s definition: \"%s\"\n"),
843 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
844 goto jerr;
846 if (_is_closing_angle(linebuf))
847 break;
849 /* Trim WS xxx we count tabs as one space here */
850 for (cp = linebuf, leaspc = 0; n.ui > 0 && whitechar(*cp); ++cp, --n.ui)
851 if (*cp == '\t')
852 leaspc = (leaspc + 8) & ~7;
853 else
854 ++leaspc;
855 if (n.ui == 0)
856 continue;
857 for (; whitechar(cp[n.ui - 1]); --n.ui)
858 assert(n.ui > 0);
859 assert(n.ui > 0);
861 maxlen = MAX(maxlen, n.ui);
862 cp[n.ui++] = '\0';
863 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n.ui);
864 memcpy(lp->l_line, cp, n.ui);
865 lp->l_length = --n.ui;
866 lp->l_leadspaces = leaspc;
868 if (lst != NULL) {
869 lnd->l_next = lp;
870 lnd = lp;
871 } else
872 lst = lnd = lp;
874 mp->ma_contents = lst;
875 mp->ma_maxlen = maxlen;
877 if (_ma_look(mp->ma_name, mp, mafl) != NULL) {
878 n_err(_("A %s named \"%s\" already exists\n"),
879 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
880 lst = mp->ma_contents;
881 goto jerr;
884 rv = TRU1;
885 jleave:
886 if (linebuf != NULL)
887 free(linebuf);
888 NYD2_LEAVE;
889 return rv;
891 jerr:
892 if (lst != NULL)
893 _ma_freelines(lst);
894 free(mp->ma_name);
895 free(mp);
896 goto jleave;
899 static bool_t
900 _ma_undefine(char const *name, enum ma_flags mafl)
902 struct macro *mp;
903 bool_t rv;
904 NYD2_ENTER;
906 rv = TRU1;
908 if (LIKELY(name[0] != '*' || name[1] != '\0')) {
909 if ((mp = _ma_look(name, NULL, mafl | MA_UNDEF)) == NULL) {
910 n_err(_("%s \"%s\" is not defined\n"),
911 (mafl & MA_ACC ? "Account" : "Macro"), name);
912 rv = FAL0;
914 } else {
915 struct macro **mpp, *lmp;
917 for (mpp = _macros; PTRCMP(mpp, <, _macros + NELEM(_macros)); ++mpp)
918 for (lmp = NULL, mp = *mpp; mp != NULL;) {
919 if ((mp->ma_flags & MA_TYPE_MASK) == mafl) {
920 /* xxx Expensive but rare: be simple */
921 _ma_look(mp->ma_name, NULL, mafl | MA_UNDEF);
922 mp = (lmp == NULL) ? *mpp : lmp->ma_next;
923 } else {
924 lmp = mp;
925 mp = mp->ma_next;
929 NYD2_LEAVE;
930 return rv;
933 static void
934 _ma_freelines(struct mline *lp)
936 struct mline *lq;
937 NYD2_ENTER;
939 for (lq = NULL; lp != NULL; ) {
940 if (lq != NULL)
941 free(lq);
942 lq = lp;
943 lp = lp->l_next;
945 if (lq)
946 free(lq);
947 NYD2_LEAVE;
950 static void
951 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
953 struct var *vap;
954 size_t nl, vl;
955 NYD2_ENTER;
957 /* Propagate unrolling up the stack, as necessary */
958 while (!losp->s_unroll && (losp = losp->s_up) != NULL)
960 if (losp == NULL)
961 goto jleave;
963 /* We've found a level that wants to unroll; check wether it does it yet */
964 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
965 if (!strcmp(vap->v_name, name))
966 goto jleave;
968 nl = strlen(name) + 1;
969 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
970 vap = smalloc(sizeof(*vap) - VFIELD_SIZEOF(struct var, v_name) + nl + vl);
971 vap->v_link = losp->s_localopts;
972 losp->s_localopts = vap;
973 memcpy(vap->v_name, name, nl);
974 if (vl == 0)
975 vap->v_value = NULL;
976 else {
977 vap->v_value = vap->v_name + nl;
978 memcpy(vap->v_value, ovap->v_value, vl);
980 jleave:
981 NYD2_LEAVE;
984 static void
985 _localopts_unroll(struct var **vapp)
987 struct lostack *save_los;
988 struct var *x, *vap;
989 NYD2_ENTER;
991 vap = *vapp;
992 *vapp = NULL;
994 save_los = _localopts;
995 _localopts = NULL;
996 while (vap != NULL) {
997 x = vap;
998 vap = vap->v_link;
999 vok_vset(x->v_name, x->v_value);
1000 free(x);
1002 _localopts = save_los;
1003 NYD2_LEAVE;
1006 FL char *
1007 _var_oklook(enum okeys okey)
1009 struct var_carrier vc;
1010 char *rv;
1011 NYD_ENTER;
1013 vc.vc_vmap = _var_map + okey;
1014 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1015 vc.vc_hash = _var_map[okey].vm_hash;
1016 vc.vc_okey = okey;
1018 if (!_var_lookup(&vc)) {
1019 if ((rv = getenv(vc.vc_name)) != NULL) {
1020 _var_set(&vc, rv);
1021 assert(vc.vc_var != NULL);
1022 goto jvar;
1024 } else
1025 jvar:
1026 rv = vc.vc_var->v_value;
1027 NYD_LEAVE;
1028 return rv;
1031 FL bool_t
1032 _var_okset(enum okeys okey, uintptr_t val)
1034 struct var_carrier vc;
1035 bool_t ok;
1036 NYD_ENTER;
1038 vc.vc_vmap = _var_map + okey;
1039 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1040 vc.vc_hash = _var_map[okey].vm_hash;
1041 vc.vc_okey = okey;
1043 ok = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
1044 NYD_LEAVE;
1045 return ok;
1048 FL bool_t
1049 _var_okclear(enum okeys okey)
1051 struct var_carrier vc;
1052 bool_t rv;
1053 NYD_ENTER;
1055 vc.vc_vmap = _var_map + okey;
1056 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1057 vc.vc_hash = _var_map[okey].vm_hash;
1058 vc.vc_okey = okey;
1060 rv = _var_clear(&vc);
1061 NYD_LEAVE;
1062 return rv;
1065 FL char *
1066 _var_voklook(char const *vokey)
1068 struct var_carrier vc;
1069 char *rv;
1070 NYD_ENTER;
1072 _var_revlookup(&vc, vokey);
1074 if (!_var_lookup(&vc)) {
1075 if ((rv = getenv(vc.vc_name)) != NULL) {
1076 _var_set(&vc, rv);
1077 assert(vc.vc_var != NULL);
1078 goto jvar;
1080 } else
1081 jvar:
1082 rv = vc.vc_var->v_value;
1083 NYD_LEAVE;
1084 return rv;
1087 FL bool_t
1088 _var_vokset(char const *vokey, uintptr_t val)
1090 struct var_carrier vc;
1091 bool_t ok;
1092 NYD_ENTER;
1094 _var_revlookup(&vc, vokey);
1096 ok = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
1097 NYD_LEAVE;
1098 return ok;
1101 FL bool_t
1102 _var_vokclear(char const *vokey)
1104 struct var_carrier vc;
1105 bool_t err;
1106 NYD_ENTER;
1108 _var_revlookup(&vc, vokey);
1110 err = !_var_clear(&vc);
1111 NYD_LEAVE;
1112 return !err;
1115 FL char *
1116 _env_look(char const *envkey, bool_t envonly) /* TODO rather dummy yet!! */
1118 char *rv;
1119 NYD_ENTER;
1121 if (envonly)
1122 rv = getenv(envkey); /* TODO rework vars: cache, output a la mimetypes */
1123 else
1124 rv = _var_voklook(envkey);
1125 NYD_LEAVE;
1126 return rv;
1129 #ifdef HAVE_SOCKETS
1130 FL char *
1131 _var_xoklook(enum okeys okey, struct url const *urlp, enum okey_xlook_mode oxm)
1133 struct var_carrier vc;
1134 struct str const *us;
1135 size_t nlen;
1136 char *nbuf = NULL /* CC happiness */, *rv;
1137 NYD_ENTER;
1139 assert(oxm & (OXM_PLAIN | OXM_H_P | OXM_U_H_P));
1141 /* For simplicity: allow this case too */
1142 if (!(oxm & (OXM_H_P | OXM_U_H_P)))
1143 goto jplain;
1145 vc.vc_vmap = _var_map + okey;
1146 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1147 vc.vc_okey = okey;
1149 us = (oxm & OXM_U_H_P) ? &urlp->url_u_h_p : &urlp->url_h_p;
1150 nlen = strlen(vc.vc_name);
1151 nbuf = ac_alloc(nlen + 1 + us->l +1);
1152 memcpy(nbuf, vc.vc_name, nlen);
1153 nbuf[nlen++] = '-';
1155 /* One of .url_u_h_p and .url_h_p we test in here */
1156 memcpy(nbuf + nlen, us->s, us->l +1);
1157 vc.vc_name = _var_canonify(nbuf);
1158 vc.vc_hash = MA_NAME2HASH(vc.vc_name);
1159 if (_var_lookup(&vc))
1160 goto jvar;
1162 /* The second */
1163 if (oxm & OXM_H_P) {
1164 us = &urlp->url_h_p;
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 jvar:
1170 rv = vc.vc_var->v_value;
1171 goto jleave;
1175 jplain:
1176 rv = (oxm & OXM_PLAIN) ? _var_oklook(okey) : NULL;
1177 jleave:
1178 if (oxm & (OXM_H_P | OXM_U_H_P))
1179 ac_free(nbuf);
1180 NYD_LEAVE;
1181 return rv;
1183 #endif /* HAVE_SOCKETS */
1185 FL int
1186 c_varshow(void *v)
1188 struct var_show vs;
1189 char const **argv = v;
1190 NYD_ENTER;
1192 if (*argv == NULL)
1193 v = NULL;
1194 else for (; *argv != NULL; ++argv) {
1195 _var_broadway(&vs, *argv);
1196 if (vs.vs_value == NULL)
1197 vs.vs_value = "NULL";
1199 if (vs.vs_vc.vc_vmap != NULL) {
1200 ui16_t f = vs.vs_vc.vc_vmap->vm_flags;
1202 if (f & VM_BOOLEAN)
1203 printf(_("\"%s\": (%d) boolean%s%s: set=%d (ENVIRON=%d)\n"),
1204 vs.vs_vc.vc_name, vs.vs_vc.vc_okey,
1205 (f & VM_RDONLY ? ", read-only" : ""),
1206 (f & VM_VIRTUAL ? ", virtual" : ""), vs.vs_isset, vs.vs_isenv);
1207 else
1208 printf(_("\"%s\": (%d) value%s%s: set=%d (ENVIRON=%d) value<%s>\n"),
1209 vs.vs_vc.vc_name, vs.vs_vc.vc_okey,
1210 (f & VM_RDONLY ? ", read-only" : ""),
1211 (f & VM_VIRTUAL ? ", virtual" : ""), vs.vs_isset, vs.vs_isenv,
1212 vs.vs_value);
1213 } else
1214 printf("\"%s\": (assembled): set=%d (ENVIRON=%d) value<%s>\n",
1215 vs.vs_vc.vc_name, vs.vs_isset, vs.vs_isenv, vs.vs_value);
1217 NYD_LEAVE;
1218 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1221 FL int
1222 c_set(void *v)
1224 char **ap = v;
1225 int err;
1226 NYD_ENTER;
1228 if (*ap == NULL) {
1229 _var_list_all();
1230 err = 0;
1231 } else
1232 err = !_var_set_env(ap, FAL0);
1233 NYD_LEAVE;
1234 return err;
1237 FL int
1238 c_setenv(void *v)
1240 char **ap = v;
1241 int err;
1242 NYD_ENTER;
1244 if (!(err = !(pstate & PS_STARTED)))
1245 err = !_var_set_env(ap, TRU1);
1246 NYD_LEAVE;
1247 return err;
1250 FL int
1251 c_unset(void *v)
1253 char **ap = v;
1254 int err = 0;
1255 NYD_ENTER;
1257 while (*ap != NULL)
1258 err |= !_var_vokclear(*ap++);
1259 NYD_LEAVE;
1260 return err;
1263 FL int
1264 c_unsetenv(void *v)
1266 int err;
1267 NYD_ENTER;
1269 if (!(err = !(pstate & PS_STARTED))) {
1270 char **ap;
1272 for (ap = v; *ap != NULL; ++ap) {
1273 bool_t bad;
1275 if (!strcmp(*ap, "HOME") || /* TODO generic */
1276 !strcmp(*ap, "LOGNAME") || !strcmp(*ap, "USER") || /* TODO .. */
1277 !strcmp(*ap, "TMPDIR")) { /* TODO approach */
1278 if (options & OPT_D_V)
1279 n_err(_("Cannot `unsetenv' \"%s\"\n"), *ap);
1280 err = 1;
1281 continue;
1284 bad = !_var_vokclear(*ap);
1285 if (
1286 #ifdef HAVE_SETENV
1287 unsetenv(*ap) != 0 ||
1288 #endif
1291 err = 1;
1294 NYD_LEAVE;
1295 return err;
1298 FL int
1299 c_varedit(void *v)
1301 struct var_carrier vc;
1302 sighandler_type sigint;
1303 FILE *of, *nf;
1304 char *val, **argv = v;
1305 int err = 0;
1306 NYD_ENTER;
1308 sigint = safe_signal(SIGINT, SIG_IGN);
1310 while (*argv != NULL) {
1311 memset(&vc, 0, sizeof vc);
1313 _var_revlookup(&vc, *argv++);
1315 if (vc.vc_vmap != NULL) {
1316 if (vc.vc_vmap->vm_flags & VM_BOOLEAN) {
1317 n_err(_("`varedit': can't edit boolean variable \"%s\"\n"),
1318 vc.vc_name);
1319 continue;
1321 if (vc.vc_vmap->vm_flags & VM_RDONLY) {
1322 n_err(_("`varedit': can't edit readonly variable \"%s\"\n"),
1323 vc.vc_name);
1324 continue;
1328 _var_lookup(&vc);
1330 if ((of = Ftmp(NULL, "vared", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1331 NULL) {
1332 n_perr(_("`varedit': can't create temporary file, bailing out"), 0);
1333 err = 1;
1334 break;
1335 } else if (vc.vc_var != NULL && *(val = vc.vc_var->v_value) != '\0' &&
1336 sizeof *val != fwrite(val, strlen(val), sizeof *val, of)) {
1337 n_perr(_("`varedit' failed to write old value to temporary file"), 0);
1338 Fclose(of);
1339 err = 1;
1340 continue;
1343 fflush_rewind(of);
1344 nf = run_editor(of, (off_t)-1, 'e', FAL0, NULL, NULL, SEND_MBOX, sigint);
1345 Fclose(of);
1347 if (nf != NULL) {
1348 int c;
1349 char *base;
1350 off_t l = fsize(nf);
1352 assert(l >= 0);
1353 base = smalloc((size_t)l + 1);
1355 for (l = 0, val = base; (c = getc(nf)) != EOF; ++val)
1356 if (c == '\n' || c == '\r') {
1357 *val = ' ';
1358 ++l;
1359 } else {
1360 *val = (char)(uc_i)c;
1361 l = 0;
1363 val -= l;
1364 *val = '\0';
1366 if (!vok_vset(vc.vc_name, base))
1367 err = 1;
1369 free(base);
1370 Fclose(nf);
1371 } else {
1372 n_err(_("`varedit': can't start $EDITOR, bailing out\n"));
1373 err = 1;
1374 break;
1378 safe_signal(SIGINT, sigint);
1379 NYD_LEAVE;
1380 return err;
1383 FL int
1384 c_define(void *v)
1386 int rv = 1;
1387 char **args = v;
1388 NYD_ENTER;
1390 if (args[0] == NULL) {
1391 rv = _ma_list(MA_NONE);
1392 goto jleave;
1395 if (args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
1396 args[2] != NULL) {
1397 n_err(_("Syntax is: define <name> {"));
1398 goto jleave;
1401 rv = !_ma_define(args[0], MA_NONE);
1402 jleave:
1403 NYD_LEAVE;
1404 return rv;
1407 FL int
1408 c_undefine(void *v)
1410 int rv;
1411 char **args;
1412 NYD_ENTER;
1414 rv = 0;
1415 args = v;
1417 rv |= !_ma_undefine(*args, MA_NONE);
1418 while (*++args != NULL);
1419 NYD_LEAVE;
1420 return rv;
1423 FL int
1424 c_call(void *v)
1426 int rv = 1;
1427 char **args = v;
1428 char const *errs, *name;
1429 struct macro *mp;
1430 NYD_ENTER;
1432 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
1433 errs = _("Synopsis: call: <%s>\n");
1434 name = "name";
1435 goto jerr;
1438 if ((mp = _ma_look(name = *args, NULL, MA_NONE)) == NULL) {
1439 errs = _("Undefined macro `call'ed: \"%s\"\n");
1440 name = *args;
1441 goto jerr;
1444 rv = (_ma_exec(mp, NULL, FAL0) == FAL0);
1445 jleave:
1446 NYD_LEAVE;
1447 return rv;
1448 jerr:
1449 n_err(errs, name);
1450 goto jleave;
1453 FL bool_t
1454 check_folder_hook(bool_t nmail) /* TODO temporary, v15: drop */
1456 size_t len;
1457 char *var, *cp;
1458 struct macro *mp;
1459 struct var **unroller;
1460 bool_t rv = TRU1;
1461 NYD_ENTER;
1463 var = ac_alloc(len = strlen(mailname) + sizeof("folder-hook-") -1 +1);
1465 /* First try the fully resolved path */
1466 snprintf(var, len, "folder-hook-%s", mailname);
1467 if ((cp = vok_vlook(var)) != NULL)
1468 goto jmac;
1470 /* If we are under *folder*, try the usual +NAME syntax, too */
1471 if (displayname[0] == '+') {
1472 char *x = mailname + len;
1474 for (; x > mailname; --x)
1475 if (x[-1] == '/') {
1476 snprintf(var, len, "folder-hook-+%s", x);
1477 if ((cp = vok_vlook(var)) != NULL)
1478 goto jmac;
1479 break;
1483 /* Plain *folder-hook* is our last try */
1484 if ((cp = ok_vlook(folder_hook)) == NULL)
1485 goto jleave;
1487 jmac:
1488 if ((mp = _ma_look(cp, NULL, MA_NONE)) == NULL) {
1489 n_err(_("Cannot call *folder-hook* for \"%s\": "
1490 "macro \"%s\" does not exist\n"), displayname, cp);
1491 rv = FAL0;
1492 goto jleave;
1495 pstate &= ~PS_HOOK_MASK;
1496 if (nmail) {
1497 pstate |= PS_HOOK_NEWMAIL;
1498 unroller = NULL;
1499 } else {
1500 pstate |= PS_HOOK;
1501 unroller = &_folder_hook_localopts;
1503 rv = _ma_exec(mp, unroller, TRU1);
1504 pstate &= ~PS_HOOK_MASK;
1506 jleave:
1507 ac_free(var);
1508 NYD_LEAVE;
1509 return rv;
1512 FL void
1513 call_compose_mode_hook(char const *macname) /* TODO temporary, v15: drop */
1515 struct macro *mp;
1516 NYD_ENTER;
1518 if ((mp = _ma_look(macname, NULL, MA_NONE)) == NULL)
1519 n_err(_("Cannot call *on-compose-*-hook*: "
1520 "macro \"%s\" does not exist\n"), macname);
1521 else {
1522 pstate &= ~PS_HOOK_MASK;
1523 pstate |= PS_HOOK;
1524 _ma_exec(mp, &a_macvar_compose_localopts, TRU1);
1525 pstate &= ~PS_HOOK_MASK;
1527 NYD_LEAVE;
1530 FL int
1531 c_account(void *v)
1533 char **args = v;
1534 struct macro *mp;
1535 int rv = 1, i, oqf, nqf;
1536 NYD_ENTER;
1538 if (args[0] == NULL) {
1539 rv = _ma_list(MA_ACC);
1540 goto jleave;
1543 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1544 if (args[2] != NULL) {
1545 n_err(_("Syntax is: account <name> {\n"));
1546 goto jleave;
1548 if (!asccasecmp(args[0], ACCOUNT_NULL)) {
1549 n_err(_("Error: \"%s\" is a reserved name\n"), ACCOUNT_NULL);
1550 goto jleave;
1552 rv = !_ma_define(args[0], MA_ACC);
1553 goto jleave;
1556 if (pstate & PS_HOOK_MASK) {
1557 n_err(_("Cannot change account from within a hook\n"));
1558 goto jleave;
1561 save_mbox_for_possible_quitstuff();
1563 mp = NULL;
1564 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
1565 (mp = _ma_look(args[0], NULL, MA_ACC)) == NULL) {
1566 n_err(_("Account \"%s\" does not exist\n"), args[0]);
1567 goto jleave;
1570 oqf = savequitflags();
1572 if (_acc_curr != NULL) {
1573 if (_acc_curr->ma_localopts != NULL)
1574 _localopts_unroll(&_acc_curr->ma_localopts);
1575 if (_acc_curr->ma_flags & MA_DELETED) { /* xxx can be made generic? */
1576 _ma_freelines(_acc_curr->ma_contents);
1577 free(_acc_curr->ma_name);
1578 free(_acc_curr);
1582 account_name = (mp != NULL) ? mp->ma_name : NULL;
1583 _acc_curr = mp;
1585 if (mp != NULL && !_ma_exec(mp, &mp->ma_localopts, TRU1)) {
1586 /* XXX account switch incomplete, unroll? */
1587 n_err(_("Switching to account \"%s\" failed\n"), args[0]);
1588 goto jleave;
1591 if ((pstate & (PS_STARTED | PS_HOOK_MASK)) == PS_STARTED) {
1592 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1593 restorequitflags(oqf);
1594 if ((i = setfile("%", 0)) < 0)
1595 goto jleave;
1596 check_folder_hook(FAL0);
1597 if (i > 0 && !ok_blook(emptystart))
1598 goto jleave;
1599 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1600 restorequitflags(nqf);
1602 rv = 0;
1603 jleave:
1604 NYD_LEAVE;
1605 return rv;
1608 FL int
1609 c_unaccount(void *v)
1611 int rv;
1612 char **args;
1613 NYD_ENTER;
1615 rv = 0;
1616 args = v;
1618 rv |= !_ma_undefine(*args, MA_ACC);
1619 while (*++args != NULL);
1620 NYD_LEAVE;
1621 return rv;
1624 FL int
1625 c_localopts(void *v)
1627 int rv = 1;
1628 char **c = v;
1629 NYD_ENTER;
1631 if (_localopts == NULL) {
1632 n_err(_("Cannot use `localopts' but from within a "
1633 "`define' or `account'\n"));
1634 goto jleave;
1637 _localopts->s_unroll = (boolify(*c, UIZ_MAX, FAL0) > 0);
1638 rv = 0;
1639 jleave:
1640 NYD_LEAVE;
1641 return rv;
1644 FL void
1645 temporary_localopts_free(void) /* XXX intermediate hack */
1647 NYD_ENTER;
1649 if (a_macvar_compose_localopts != NULL) {
1650 void *save = _localopts;
1651 _localopts = NULL;
1652 _localopts_unroll(&a_macvar_compose_localopts);
1653 a_macvar_compose_localopts = NULL;
1654 _localopts = save;
1656 NYD_LEAVE;
1659 FL void
1660 temporary_localopts_folder_hook_unroll(void) /* XXX intermediate hack */
1662 NYD_ENTER;
1663 if (_folder_hook_localopts != NULL) {
1664 void *save = _localopts;
1665 _localopts = NULL;
1666 _localopts_unroll(&_folder_hook_localopts);
1667 _folder_hook_localopts = NULL;
1668 _localopts = save;
1670 NYD_LEAVE;
1673 /* s-it-mode */