cc-test.sh: do a simple \`Resend' test
[s-mailx.git] / accmacvar.c
blob76a1c095f14ee904d711a5c82674a0742e950fd8
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Account, macro and variable handling.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE accmacvar
38 #ifndef HAVE_AMALGAMATION
39 # define _ACCMACVAR_SOURCE /* For _features[] */
40 # include "nail.h"
41 #endif
44 * HOWTO add a new non-dynamic binary or value option:
45 * - add an entry to nail.h:enum okeys
46 * - run create-okey-map.pl
47 * - update nail.1
50 /* Note: changing the hash function must be reflected in create-okey-map.pl */
51 #define MA_PRIME HSHSIZE
52 #define MA_NAME2HASH(N) torek_hash(N)
53 #define MA_HASH2PRIME(H) ((H) % MA_PRIME)
55 enum ma_flags {
56 MA_NONE = 0,
57 MA_ACC = 1<<0,
58 MA_TYPE_MASK = MA_ACC,
59 MA_UNDEF = 1<<1, /* Unlink after lookup */
60 MA_DELETED = 1<<13 /* Only for _acc_curr: deleted while active */
63 enum var_map_flags {
64 VM_NONE = 0,
65 VM_BINARY = 1<<0, /* ok_b_* */
66 VM_RDONLY = 1<<1, /* May not be set by user */
67 VM_SPECIAL = 1<<2, /* Wants _var_check_specials() evaluation */
68 VM_VIRTUAL = 1<<3 /* "Stateless": no var* -- implies VM_RDONLY */
71 struct macro {
72 struct macro *ma_next;
73 char *ma_name;
74 struct mline *ma_contents;
75 ui32_t ma_maxlen; /* Maximum line length */
76 enum ma_flags ma_flags;
77 struct var *ma_localopts; /* `account' unroll list, for `localopts' */
80 struct mline {
81 struct mline *l_next;
82 ui32_t l_length;
83 ui32_t l_leadspaces; /* Number of leading SPC characters */
84 char l_line[VFIELD_SIZE(0)];
87 struct var {
88 struct var *v_link;
89 char *v_value;
90 char v_name[VFIELD_SIZE(0)];
93 struct var_virtual {
94 ui32_t vv_okey;
95 struct var const *vv_var;
98 struct var_map {
99 ui32_t vm_hash;
100 ui16_t vm_keyoff;
101 ui16_t vm_flags; /* var_map_flags bits */
104 struct var_carrier {
105 char const *vc_name;
106 ui32_t vc_hash;
107 ui32_t vc_prime;
108 struct var *vc_var;
109 struct var_map const *vc_vmap;
110 enum okeys vc_okey;
113 struct var_show {
114 struct var_carrier vs_vc; /* _var_revlookup() */
115 char const *vs_value; /* Value (from wherever it came) or NULL */
116 bool_t vs_isset; /* Managed by us and existent */
117 bool_t vs_isenv; /* Set, but managed by environ */
118 bool_t vs_isasm; /* Is an assembled variable */
119 ui8_t __pad[5];
122 struct lostack {
123 struct lostack *s_up; /* Outer context */
124 struct macro *s_mac; /* Context (`account' or `define') */
125 struct var *s_localopts;
126 bool_t s_unroll; /* Unroll? */
129 /* Include the constant create-okey-map.pl output */
130 #include "version.h"
131 #include "okeys.h"
133 static struct macro *_acc_curr; /* Currently active account */
134 static struct lostack *_localopts; /* Currently executing macro unroll list */
135 /* TODO We really deserve localopts support for *folder-hook*s, so hack it in
136 * TODO today via a static lostack, it should be a field in mailbox, once that
137 * TODO is a real multi-instance object */
138 static struct var *_folder_hook_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 int _ma_exec(struct macro const *mp, struct var **unroller);
200 /* User display helpers */
201 static int _ma_list(enum ma_flags mafl);
203 /* */
204 static bool_t _ma_define(char const *name, enum ma_flags mafl);
205 static void _ma_undefine(char const *name, enum ma_flags mafl);
206 static void _ma_freelines(struct mline *lp);
208 /* Update replay-log */
209 static void _localopts_add(struct lostack *losp, char const *name,
210 struct var *ovap);
211 static void _localopts_unroll(struct var **vapp);
213 static char *
214 _var_vcopy(char const *str)
216 char *news;
217 size_t len;
218 NYD2_ENTER;
220 if (*str == '\0')
221 news = UNCONST("");
222 else {
223 len = strlen(str) +1;
224 news = smalloc(len);
225 memcpy(news, str, len);
227 NYD2_LEAVE;
228 return news;
231 static void
232 _var_vfree(char *cp)
234 NYD2_ENTER;
235 if (*cp != '\0')
236 free(cp);
237 NYD2_LEAVE;
240 static bool_t
241 _var_check_specials(enum okeys okey, bool_t enable, char **val)
243 char *cp = NULL;
244 bool_t ok = TRU1;
245 int flag = 0;
246 NYD2_ENTER;
248 switch (okey) {
249 case ok_b_debug:
250 flag = OPT_DEBUG;
251 break;
252 case ok_b_header:
253 flag = OPT_N_FLAG;
254 enable = !enable;
255 break;
256 case ok_b_memdebug:
257 flag = OPT_MEMDEBUG;
258 break;
259 case ok_b_skipemptybody:
260 flag = OPT_E_FLAG;
261 break;
262 case ok_b_verbose:
263 flag = (enable && !(options & OPT_VERB))
264 ? OPT_VERB : OPT_VERB | OPT_VERBVERB;
265 break;
266 case ok_v_folder:
267 ok = (val != NULL && var_folder_updated(*val, &cp));
268 if (ok && cp != NULL) {
269 _var_vfree(*val);
270 /* It's smalloc()ed, but ensure we don't leak */
271 if (*cp == '\0') {
272 *val = UNCONST("");
273 free(cp);
274 } else
275 *val = cp;
277 break;
278 #ifdef HAVE_NCL
279 case ok_v_line_editor_cursor_right:
280 if ((ok = (val != NULL && *val != NULL))) {
281 /* Set with no value? TODO very guly */
282 if (*(cp = *val) != '\0') {
283 char const *x = cp;
284 int c;
285 do {
286 c = expand_shell_escape(&x, FAL0);
287 if (c < 0)
288 break;
289 *cp++ = (char)c;
290 } while (*x != '\0');
291 *cp = '\0';
294 break;
295 #endif
296 default:
297 break;
300 if (flag) {
301 if (enable)
302 options |= flag;
303 else
304 options &= ~flag;
306 NYD2_LEAVE;
307 return ok;
310 static char const *
311 _var_canonify(char const *vn)
313 NYD2_ENTER;
314 if (!upperchar(*vn)) {
315 char const *vp;
317 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
319 vn = (*vp == '@') ? i_strdup(vn) : vn;
321 NYD2_LEAVE;
322 return vn;
325 static bool_t
326 _var_revlookup(struct var_carrier *vcp, char const *name)
328 ui32_t hash, i, j;
329 struct var_map const *vmp;
330 NYD2_ENTER;
332 vcp->vc_name = name = _var_canonify(name);
333 vcp->vc_hash = hash = MA_NAME2HASH(name);
335 for (i = hash % _VAR_REV_PRIME, j = 0; j <= _VAR_REV_LONGEST; ++j) {
336 ui32_t x = _var_revmap[i];
337 if (x == _VAR_REV_ILL)
338 break;
339 vmp = _var_map + x;
340 if (vmp->vm_hash == hash && !strcmp(_var_keydat + vmp->vm_keyoff, name)) {
341 vcp->vc_vmap = vmp;
342 vcp->vc_okey = (enum okeys)x;
343 goto jleave;
345 if (++i == _VAR_REV_PRIME) {
346 #ifdef _VAR_REV_WRAPAROUND
347 i = 0;
348 #else
349 break;
350 #endif
353 vcp->vc_vmap = NULL;
354 vcp = NULL;
355 jleave:
356 NYD2_LEAVE;
357 return (vcp != NULL);
360 static bool_t
361 _var_lookup(struct var_carrier *vcp)
363 struct var **vap, *lvp, *vp;
364 NYD2_ENTER;
366 /* XXX _So_ unlikely that it should be checked if normal lookup fails! */
367 if (UNLIKELY(vcp->vc_vmap != NULL &&
368 (vcp->vc_vmap->vm_flags & VM_VIRTUAL) != 0)) {
369 struct var_virtual const *vvp;
371 for (vvp = _var_virtuals;
372 PTRCMP(vvp, <, _var_virtuals + NELEM(_var_virtuals)); ++vvp)
373 if (vvp->vv_okey == vcp->vc_okey) {
374 vp = UNCONST(vvp->vv_var);
375 goto jleave;
377 assert(0);
380 vap = _vars + (vcp->vc_prime = MA_HASH2PRIME(vcp->vc_hash));
382 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
383 if (!strcmp(vp->v_name, vcp->vc_name)) {
384 /* Relink as head, hope it "sorts on usage" over time.
385 * _var_clear() relies on this behaviour */
386 if (lvp != NULL) {
387 lvp->v_link = vp->v_link;
388 vp->v_link = *vap;
389 *vap = vp;
391 goto jleave;
393 vp = NULL;
394 jleave:
395 vcp->vc_var = vp;
396 NYD2_LEAVE;
397 return (vp != NULL);
400 static bool_t
401 _var_broadway(struct var_show *vsp, char const *name)
403 bool_t rv;
404 NYD2_ENTER;
406 memset(vsp, 0, sizeof *vsp);
408 vsp->vs_isasm = !_var_revlookup(&vsp->vs_vc, name);
410 if ((vsp->vs_isset = rv = _var_lookup(&vsp->vs_vc))) {
411 vsp->vs_value = vsp->vs_vc.vc_var->v_value;
412 vsp->vs_isenv = FAL0;
413 } else
414 vsp->vs_isenv = ((vsp->vs_value = getenv(vsp->vs_vc.vc_name)) != NULL);
416 NYD2_LEAVE;
417 return rv;
420 static bool_t
421 _var_set(struct var_carrier *vcp, char const *value)
423 struct var *vp;
424 char *oval;
425 bool_t ok = TRU1;
426 NYD2_ENTER;
428 if (value == NULL) {
429 ok = _var_clear(vcp);
430 goto jleave;
433 _var_lookup(vcp);
435 if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_RDONLY)) {
436 n_err(_("Variable readonly: \"%s\"\n"), vcp->vc_name);
437 ok = FAL0;
438 goto jleave;
441 /* Don't care what happens later on, store this in the unroll list */
442 if (_localopts != NULL)
443 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
445 if ((vp = vcp->vc_var) == NULL) {
446 size_t l = strlen(vcp->vc_name) + 1;
448 vcp->vc_var =
449 vp = smalloc(sizeof(*vp) - VFIELD_SIZEOF(struct var, v_name) + l);
450 vp->v_link = _vars[vcp->vc_prime];
451 _vars[vcp->vc_prime] = vp;
452 memcpy(vp->v_name, vcp->vc_name, l);
453 oval = UNCONST("");
454 } else
455 oval = vp->v_value;
457 if (vcp->vc_vmap == NULL)
458 vp->v_value = _var_vcopy(value);
459 else {
460 /* Via `set' etc. the user may give even binary options non-binary
461 * values, ignore that and force binary xxx error log? */
462 if (vcp->vc_vmap->vm_flags & VM_BINARY)
463 value = UNCONST("");
464 vp->v_value = _var_vcopy(value);
466 /* Check if update allowed XXX wasteful on error! */
467 if ((vcp->vc_vmap->vm_flags & VM_SPECIAL) &&
468 !(ok = _var_check_specials(vcp->vc_okey, TRU1, &vp->v_value))) {
469 char *cp = vp->v_value;
470 vp->v_value = oval;
471 oval = cp;
475 if (*oval != '\0')
476 _var_vfree(oval);
477 jleave:
478 NYD2_LEAVE;
479 return ok;
482 static bool_t
483 _var_clear(struct var_carrier *vcp)
485 bool_t ok = TRU1;
486 NYD2_ENTER;
488 if (!_var_lookup(vcp)) {
489 if (!(pstate & PS_IN_LOAD) && (options & OPT_D_V))
490 n_err(_("Variable undefined: \"%s\"\n"), vcp->vc_name);
491 } else if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_RDONLY)) {
492 n_err(_("Variable readonly: \"%s\"\n"), vcp->vc_name);
493 ok = FAL0;
494 } else {
495 if (_localopts != NULL)
496 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
498 /* Always listhead after _var_lookup() */
499 _vars[vcp->vc_prime] = _vars[vcp->vc_prime]->v_link;
500 _var_vfree(vcp->vc_var->v_value);
501 free(vcp->vc_var);
502 vcp->vc_var = NULL;
504 if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_SPECIAL))
505 ok = _var_check_specials(vcp->vc_okey, FAL0, NULL);
507 NYD2_LEAVE;
508 return ok;
511 static void
512 _var_list_all(void)
514 struct var_show vs;
515 FILE *fp;
516 size_t no, i;
517 struct var *vp;
518 char const **vacp, **cap;
519 NYD2_ENTER;
521 if ((fp = Ftmp(NULL, "listvars", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
522 NULL) {
523 n_perr(_("tmpfile"), 0);
524 goto jleave;
527 for (no = i = 0; i < MA_PRIME; ++i)
528 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
529 ++no;
530 no += NELEM(_var_virtuals);
532 vacp = salloc(no * sizeof(*vacp));
534 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
535 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
536 *cap++ = vp->v_name;
537 for (i = 0; i < NELEM(_var_virtuals); ++i)
538 *cap++ = _var_virtuals[i].vv_var->v_name;
540 if (no > 1)
541 qsort(vacp, no, sizeof *vacp, &__var_list_all_cmp);
543 i = (ok_blook(bsdcompat) || ok_blook(bsdset));
544 for (cap = vacp; no != 0; ++cap, --no) {
545 char const *asmis, *fmt;
547 if (!_var_broadway(&vs, *cap))
548 continue;
549 if (vs.vs_value == NULL)
550 vs.vs_value = "";
552 asmis = !(options & OPT_D_VV) ? ""
553 : vs.vs_isasm ? "*" : " ";
554 if (i)
555 fmt = "%s%s\t%s\n";
556 else {
557 if (vs.vs_vc.vc_vmap != NULL &&
558 (vs.vs_vc.vc_vmap->vm_flags & VM_BINARY))
559 fmt = "%sset %s\n";
560 else {
561 fmt = "%sset %s=\"%s\"\n";
562 if (*vs.vs_value != '\0')
563 vs.vs_value = __var_simple_quote(vs.vs_value);
566 /* Shall a code checker complain on that, i'm in holiday */
567 fprintf(fp, fmt, asmis, *cap, vs.vs_value);
570 page_or_print(fp, PTR2SIZE(cap - vacp));
571 Fclose(fp);
572 jleave:
573 NYD2_LEAVE;
576 static int
577 __var_list_all_cmp(void const *s1, void const *s2)
579 int rv;
580 NYD2_ENTER;
582 rv = strcmp(*(char**)UNCONST(s1), *(char**)UNCONST(s2));
583 NYD2_LEAVE;
584 return rv;
587 static char *
588 __var_simple_quote(char const *cp) /* TODO "unite" with string_quote(), etc.. */
590 size_t i;
591 char const *cp_base;
592 char c, *rv;
593 NYD2_ENTER;
595 for (i = 0, cp_base = cp; (c = *cp) != '\0'; ++i, ++cp)
596 if (c == '"')
597 ++i;
598 rv = salloc(i +1);
600 for (i = 0, cp = cp_base; (c = *cp) != '\0'; rv[i++] = c, ++cp)
601 if (c == '"')
602 rv[i++] = '\\';
603 rv[i] = '\0';
604 NYD2_LEAVE;
605 return rv;
608 static bool_t
609 _var_set_env(char **ap, bool_t issetenv)
611 char *cp, *cp2, *varbuf, c;
612 size_t errs = 0;
613 NYD2_ENTER;
615 for (; *ap != NULL; ++ap) {
616 /* Isolate key */
617 cp = *ap;
618 cp2 = varbuf = ac_alloc(strlen(cp) +1);
619 for (; (c = *cp) != '=' && c != '\0'; ++cp)
620 *cp2++ = c;
621 *cp2 = '\0';
622 if (c == '\0')
623 cp = UNCONST("");
624 else
625 ++cp;
626 if (varbuf == cp2) {
627 n_err(_("Non-null variable name required\n"));
628 ++errs;
629 goto jnext;
632 if (varbuf[0] == 'n' && varbuf[1] == 'o') {
633 char const *k = varbuf + 2;
635 if (issetenv && (!strcmp(k, "HOME") || /* TODO generic */
636 !strcmp(k, "USER") || !strcmp(k, "TMPDIR"))) {/* TODO approach */
637 if (options & OPT_D_V)
638 n_err(_("Cannot `unsetenv' \"%s\"\n"), k);
639 ++errs;
640 goto jnext;
643 errs += !_var_vokclear(k);
644 if (issetenv) {
645 #ifdef HAVE_SETENV
646 errs += (unsetenv(k) != 0);
647 #else
648 ++errs;
649 #endif
651 } else {
652 errs += !_var_vokset(varbuf, (uintptr_t)cp);
653 if (issetenv) {
654 do {
655 static char const *cp_buf[3];
656 char const **pl, **pe;
658 if (!strcmp(varbuf, "HOME")) /* TODO generic */
659 pl = cp_buf + 0, pe = &homedir;
660 else if (!strcmp(varbuf, "USER")) /* TODO approach */
661 pl = cp_buf + 1, pe = &myname;
662 else if (!strcmp(varbuf, "TMPDIR")) /* TODO also here */
663 pl = cp_buf + 2, pe = &tempdir;
664 else
665 break;
667 if (*pl != NULL)
668 free(UNCONST(*pl));
669 *pe = *pl = sstrdup(cp);
670 } while (0);
672 #ifdef HAVE_SETENV
673 errs += (setenv(varbuf, cp, 1) != 0);
674 #else
675 ++errs;
676 #endif
679 jnext:
680 ac_free(varbuf);
683 NYD2_LEAVE;
684 return (errs == 0);
687 static bool_t
688 _is_closing_angle(char const *cp)
690 bool_t rv = FAL0;
691 NYD2_ENTER;
693 while (spacechar(*cp))
694 ++cp;
695 if (*cp++ != '}')
696 goto jleave;
697 while (spacechar(*cp))
698 ++cp;
699 rv = (*cp == '\0');
700 jleave:
701 NYD2_LEAVE;
702 return rv;
705 static struct macro *
706 _ma_look(char const *name, struct macro *data, enum ma_flags mafl)
708 enum ma_flags save_mafl;
709 ui32_t h;
710 struct macro *lmp, *mp;
711 NYD2_ENTER;
713 save_mafl = mafl;
714 mafl &= MA_TYPE_MASK;
715 h = MA_NAME2HASH(name);
716 h = MA_HASH2PRIME(h);
718 for (lmp = NULL, mp = _macros[h]; mp != NULL; lmp = mp, mp = mp->ma_next) {
719 if ((mp->ma_flags & MA_TYPE_MASK) == mafl && !strcmp(mp->ma_name, name)) {
720 if (save_mafl & MA_UNDEF) {
721 if (lmp == NULL)
722 _macros[h] = mp->ma_next;
723 else
724 lmp->ma_next = mp->ma_next;
726 /* TODO it should also be possible to delete running macros */
727 if ((mafl & MA_ACC) &&
728 account_name != NULL && !strcmp(account_name, name)) {
729 mp->ma_flags |= MA_DELETED;
730 n_err(_("Delayed deletion of active account \"%s\"\n"), name);
731 } else {
732 _ma_freelines(mp->ma_contents);
733 free(mp->ma_name);
734 free(mp);
735 mp = (struct macro*)-1;
738 goto jleave;
742 if (data != NULL) {
743 data->ma_next = _macros[h];
744 _macros[h] = data;
745 mp = NULL;
747 jleave:
748 NYD2_LEAVE;
749 return mp;
752 static int
753 _ma_exec(struct macro const *mp, struct var **unroller)
755 struct lostack los;
756 char *buf;
757 struct n2 {struct n2 *up; struct lostack *lo;} *x; /* FIXME hack (sigman+) */
758 struct mline const *lp;
759 int rv = 0;
760 NYD2_ENTER;
762 los.s_up = _localopts;
763 los.s_mac = UNCONST(mp);
764 los.s_localopts = NULL;
765 los.s_unroll = FAL0;
766 _localopts = &los;
768 x = salloc(sizeof *x); /* FIXME intermediate hack (signal man+) */
769 x->up = temporary_localopts_store;
770 x->lo = _localopts;
771 temporary_localopts_store = x;
773 buf = ac_alloc(mp->ma_maxlen +1);
774 for (lp = mp->ma_contents; lp; lp = lp->l_next) {
775 memcpy(buf, lp->l_line, lp->l_length +1);
776 rv |= execute(buf, lp->l_length); /* XXX break if != 0 ? */
778 ac_free(buf);
780 temporary_localopts_store = x->up; /* FIXME intermediate hack */
782 _localopts = los.s_up;
783 if (unroller == NULL) {
784 if (los.s_localopts != NULL)
785 _localopts_unroll(&los.s_localopts);
786 } else
787 *unroller = los.s_localopts;
788 NYD2_LEAVE;
789 return rv;
792 static int
793 _ma_list(enum ma_flags mafl)
795 FILE *fp;
796 char const *typestr;
797 struct macro *mq;
798 ui32_t ti, mc, i;
799 struct mline *lp;
800 NYD2_ENTER;
802 if ((fp = Ftmp(NULL, "listmacs", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
803 NULL) {
804 n_perr(_("tmpfile"), 0);
805 mc = 1;
806 goto jleave;
809 mafl &= MA_TYPE_MASK;
810 typestr = (mafl & MA_ACC) ? "account" : "define";
812 for (ti = mc = 0; ti < MA_PRIME; ++ti)
813 for (mq = _macros[ti]; mq; mq = mq->ma_next)
814 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
815 if (++mc > 1)
816 putc('\n', fp);
817 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
818 for (lp = mq->ma_contents; lp != NULL; lp = lp->l_next) {
819 for (i = lp->l_leadspaces; i > 0; --i)
820 putc(' ', fp);
821 fputs(lp->l_line, fp);
822 putc('\n', fp);
824 fputs("}\n", fp);
826 if (mc)
827 page_or_print(fp, 0);
829 mc = (ui32_t)ferror(fp);
830 Fclose(fp);
831 jleave:
832 NYD2_LEAVE;
833 return (int)mc;
836 static bool_t
837 _ma_define(char const *name, enum ma_flags mafl)
839 bool_t rv = FAL0;
840 struct macro *mp;
841 struct mline *lp, *lst = NULL, *lnd = NULL;
842 char *linebuf = NULL, *cp;
843 size_t linesize = 0;
844 ui32_t maxlen = 0, leaspc;
845 union {int i; ui32_t ui;} n;
846 NYD2_ENTER;
848 mp = scalloc(1, sizeof *mp);
849 mp->ma_name = sstrdup(name);
850 mp->ma_flags = mafl;
852 for (;;) {
853 n.i = readline_input("", TRU1, &linebuf, &linesize, NULL);
854 if (n.ui == 0)
855 continue;
856 if (n.i < 0) {
857 n_err(_("Unterminated %s definition: \"%s\"\n"),
858 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
859 if ((pstate & PS_IN_LOAD) == PS_SOURCING)
860 unstack();
861 goto jerr;
863 if (_is_closing_angle(linebuf))
864 break;
866 /* Trim WS xxx we count tabs as one space here */
867 for (cp = linebuf, leaspc = 0; n.ui > 0 && whitechar(*cp); ++cp, --n.ui)
868 if (*cp == '\t')
869 leaspc = (leaspc + 8) & ~7;
870 else
871 ++leaspc;
872 if (n.ui == 0)
873 continue;
874 for (; whitechar(cp[n.ui - 1]); --n.ui)
875 assert(n.ui > 0);
876 assert(n.ui > 0);
878 maxlen = MAX(maxlen, n.ui);
879 cp[n.ui++] = '\0';
880 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n.ui);
881 memcpy(lp->l_line, cp, n.ui);
882 lp->l_length = --n.ui;
883 lp->l_leadspaces = leaspc;
885 if (lst != NULL) {
886 lnd->l_next = lp;
887 lnd = lp;
888 } else
889 lst = lnd = lp;
891 mp->ma_contents = lst;
892 mp->ma_maxlen = maxlen;
894 if (_ma_look(mp->ma_name, mp, mafl) != NULL) {
895 n_err(_("A %s named \"%s\" already exists\n"),
896 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
897 lst = mp->ma_contents;
898 goto jerr;
901 rv = TRU1;
902 jleave:
903 if (linebuf != NULL)
904 free(linebuf);
905 NYD2_LEAVE;
906 return rv;
908 jerr:
909 if (lst != NULL)
910 _ma_freelines(lst);
911 free(mp->ma_name);
912 free(mp);
913 goto jleave;
916 static void
917 _ma_undefine(char const *name, enum ma_flags mafl)
919 struct macro *mp;
920 NYD2_ENTER;
922 if (LIKELY(name[0] != '*' || name[1] != '\0')) {
923 if ((mp = _ma_look(name, NULL, mafl | MA_UNDEF)) == NULL)
924 n_err(_("%s \"%s\" is not defined\n"),
925 (mafl & MA_ACC ? "Account" : "Macro"), name);
926 } else {
927 struct macro **mpp, *lmp;
929 for (mpp = _macros; PTRCMP(mpp, <, _macros + NELEM(_macros)); ++mpp)
930 for (lmp = NULL, mp = *mpp; mp != NULL;) {
931 if ((mp->ma_flags & MA_TYPE_MASK) == mafl) {
932 /* xxx Expensive but rare: be simple */
933 _ma_look(mp->ma_name, NULL, mafl | MA_UNDEF);
934 mp = (lmp == NULL) ? *mpp : lmp->ma_next;
935 } else {
936 lmp = mp;
937 mp = mp->ma_next;
941 NYD2_LEAVE;
944 static void
945 _ma_freelines(struct mline *lp)
947 struct mline *lq;
948 NYD2_ENTER;
950 for (lq = NULL; lp != NULL; ) {
951 if (lq != NULL)
952 free(lq);
953 lq = lp;
954 lp = lp->l_next;
956 if (lq)
957 free(lq);
958 NYD2_LEAVE;
961 static void
962 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
964 struct var *vap;
965 size_t nl, vl;
966 NYD2_ENTER;
968 /* Propagate unrolling up the stack, as necessary */
969 while (!losp->s_unroll && (losp = losp->s_up) != NULL)
971 if (losp == NULL)
972 goto jleave;
974 /* We've found a level that wants to unroll; check wether it does it yet */
975 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
976 if (!strcmp(vap->v_name, name))
977 goto jleave;
979 nl = strlen(name) + 1;
980 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
981 vap = smalloc(sizeof(*vap) - VFIELD_SIZEOF(struct var, v_name) + nl + vl);
982 vap->v_link = losp->s_localopts;
983 losp->s_localopts = vap;
984 memcpy(vap->v_name, name, nl);
985 if (vl == 0)
986 vap->v_value = NULL;
987 else {
988 vap->v_value = vap->v_name + nl;
989 memcpy(vap->v_value, ovap->v_value, vl);
991 jleave:
992 NYD2_LEAVE;
995 static void
996 _localopts_unroll(struct var **vapp)
998 struct lostack *save_los;
999 struct var *x, *vap;
1000 NYD2_ENTER;
1002 vap = *vapp;
1003 *vapp = NULL;
1005 save_los = _localopts;
1006 _localopts = NULL;
1007 while (vap != NULL) {
1008 x = vap;
1009 vap = vap->v_link;
1010 vok_vset(x->v_name, x->v_value);
1011 free(x);
1013 _localopts = save_los;
1014 NYD2_LEAVE;
1017 FL char *
1018 _var_oklook(enum okeys okey)
1020 struct var_carrier vc;
1021 char *rv;
1022 NYD_ENTER;
1024 vc.vc_vmap = _var_map + okey;
1025 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1026 vc.vc_hash = _var_map[okey].vm_hash;
1027 vc.vc_okey = okey;
1029 if (!_var_lookup(&vc)) {
1030 if ((rv = getenv(vc.vc_name)) != NULL) {
1031 _var_set(&vc, rv);
1032 assert(vc.vc_var != NULL);
1033 goto jvar;
1035 } else
1036 jvar:
1037 rv = vc.vc_var->v_value;
1038 NYD_LEAVE;
1039 return rv;
1042 FL bool_t
1043 _var_okset(enum okeys okey, uintptr_t val)
1045 struct var_carrier vc;
1046 bool_t ok;
1047 NYD_ENTER;
1049 vc.vc_vmap = _var_map + okey;
1050 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1051 vc.vc_hash = _var_map[okey].vm_hash;
1052 vc.vc_okey = okey;
1054 ok = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
1055 NYD_LEAVE;
1056 return ok;
1059 FL bool_t
1060 _var_okclear(enum okeys okey)
1062 struct var_carrier vc;
1063 bool_t rv;
1064 NYD_ENTER;
1066 vc.vc_vmap = _var_map + okey;
1067 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1068 vc.vc_hash = _var_map[okey].vm_hash;
1069 vc.vc_okey = okey;
1071 rv = _var_clear(&vc);
1072 NYD_LEAVE;
1073 return rv;
1076 FL char *
1077 _var_voklook(char const *vokey)
1079 struct var_carrier vc;
1080 char *rv;
1081 NYD_ENTER;
1083 _var_revlookup(&vc, vokey);
1085 if (!_var_lookup(&vc)) {
1086 if ((rv = getenv(vc.vc_name)) != NULL) {
1087 _var_set(&vc, rv);
1088 assert(vc.vc_var != NULL);
1089 goto jvar;
1091 } else
1092 jvar:
1093 rv = vc.vc_var->v_value;
1094 NYD_LEAVE;
1095 return rv;
1098 FL bool_t
1099 _var_vokset(char const *vokey, uintptr_t val)
1101 struct var_carrier vc;
1102 bool_t ok;
1103 NYD_ENTER;
1105 _var_revlookup(&vc, vokey);
1107 ok = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
1108 NYD_LEAVE;
1109 return ok;
1112 FL bool_t
1113 _var_vokclear(char const *vokey)
1115 struct var_carrier vc;
1116 bool_t err;
1117 NYD_ENTER;
1119 _var_revlookup(&vc, vokey);
1121 err = !_var_clear(&vc);
1122 NYD_LEAVE;
1123 return !err;
1126 FL char *
1127 _env_look(char const *envkey, bool_t envonly) /* TODO rather dummy yet!! */
1129 char *rv;
1130 NYD_ENTER;
1132 if (envonly)
1133 rv = getenv(envkey); /* TODO rework vars: cache, output a la mimetypes */
1134 else
1135 rv = _var_voklook(envkey);
1136 NYD_LEAVE;
1137 return rv;
1140 #ifdef HAVE_SOCKETS
1141 FL char *
1142 _var_xoklook(enum okeys okey, struct url const *urlp, enum okey_xlook_mode oxm)
1144 struct var_carrier vc;
1145 struct str const *us;
1146 size_t nlen;
1147 char *nbuf = NULL /* CC happiness */, *rv;
1148 NYD_ENTER;
1150 assert(oxm & (OXM_PLAIN | OXM_H_P | OXM_U_H_P));
1152 /* For simplicity: allow this case too */
1153 if (!(oxm & (OXM_H_P | OXM_U_H_P)))
1154 goto jplain;
1156 vc.vc_vmap = _var_map + okey;
1157 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1158 vc.vc_okey = okey;
1160 us = (oxm & OXM_U_H_P) ? &urlp->url_u_h_p : &urlp->url_h_p;
1161 nlen = strlen(vc.vc_name);
1162 nbuf = ac_alloc(nlen + 1 + us->l +1);
1163 memcpy(nbuf, vc.vc_name, nlen);
1164 nbuf[nlen++] = '-';
1166 /* One of .url_u_h_p and .url_h_p we test in here */
1167 memcpy(nbuf + nlen, us->s, us->l +1);
1168 vc.vc_name = _var_canonify(nbuf);
1169 vc.vc_hash = MA_NAME2HASH(vc.vc_name);
1170 if (_var_lookup(&vc))
1171 goto jvar;
1173 /* The second */
1174 if (oxm & OXM_H_P) {
1175 us = &urlp->url_h_p;
1176 memcpy(nbuf + nlen, us->s, us->l +1);
1177 vc.vc_name = _var_canonify(nbuf);
1178 vc.vc_hash = MA_NAME2HASH(vc.vc_name);
1179 if (_var_lookup(&vc)) {
1180 jvar:
1181 rv = vc.vc_var->v_value;
1182 goto jleave;
1186 jplain:
1187 rv = (oxm & OXM_PLAIN) ? _var_oklook(okey) : NULL;
1188 jleave:
1189 if (oxm & (OXM_H_P | OXM_U_H_P))
1190 ac_free(nbuf);
1191 NYD_LEAVE;
1192 return rv;
1194 #endif /* HAVE_SOCKETS */
1196 FL int
1197 c_varshow(void *v)
1199 struct var_show vs;
1200 char const **argv = v;
1201 NYD_ENTER;
1203 if (*argv == NULL)
1204 v = NULL;
1205 else for (; *argv != NULL; ++argv) {
1206 _var_broadway(&vs, *argv);
1207 if (vs.vs_value == NULL)
1208 vs.vs_value = "NULL";
1210 if (vs.vs_vc.vc_vmap != NULL) {
1211 ui16_t f = vs.vs_vc.vc_vmap->vm_flags;
1213 if (f & VM_BINARY)
1214 printf(_("\"%s\": (%d) binary%s%s: set=%d (ENVIRON=%d)\n"),
1215 vs.vs_vc.vc_name, vs.vs_vc.vc_okey,
1216 (f & VM_RDONLY ? ", read-only" : ""),
1217 (f & VM_VIRTUAL ? ", virtual" : ""), vs.vs_isset, vs.vs_isenv);
1218 else
1219 printf(_("\"%s\": (%d) value%s%s: set=%d (ENVIRON=%d) value<%s>\n"),
1220 vs.vs_vc.vc_name, vs.vs_vc.vc_okey,
1221 (f & VM_RDONLY ? ", read-only" : ""),
1222 (f & VM_VIRTUAL ? ", virtual" : ""), vs.vs_isset, vs.vs_isenv,
1223 vs.vs_value);
1224 } else
1225 printf("\"%s\": (assembled): set=%d (ENVIRON=%d) value<%s>\n",
1226 vs.vs_vc.vc_name, vs.vs_isset, vs.vs_isenv, vs.vs_value);
1228 NYD_LEAVE;
1229 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1232 FL int
1233 c_set(void *v)
1235 char **ap = v;
1236 int err;
1237 NYD_ENTER;
1239 if (*ap == NULL) {
1240 _var_list_all();
1241 err = 0;
1242 } else
1243 err = !_var_set_env(ap, FAL0);
1244 NYD_LEAVE;
1245 return err;
1248 FL int
1249 c_setenv(void *v)
1251 char **ap = v;
1252 int err;
1253 NYD_ENTER;
1255 if (!(err = !(pstate & PS_STARTED)))
1256 err = !_var_set_env(ap, TRU1);
1257 NYD_LEAVE;
1258 return err;
1261 FL int
1262 c_unset(void *v)
1264 char **ap = v;
1265 int err = 0;
1266 NYD_ENTER;
1268 while (*ap != NULL)
1269 err |= !_var_vokclear(*ap++);
1270 NYD_LEAVE;
1271 return err;
1274 FL int
1275 c_unsetenv(void *v)
1277 int err;
1278 NYD_ENTER;
1280 if (!(err = !(pstate & PS_STARTED))) {
1281 char **ap;
1283 for (ap = v; *ap != NULL; ++ap) {
1284 bool_t bad;
1286 if (!strcmp(*ap, "HOME") || !strcmp(*ap, "USER") || /* TODO generic */
1287 !strcmp(*ap, "TMPDIR")) { /* TODO approach */
1288 if (options & OPT_D_V)
1289 n_err(_("Cannot `unsetenv' \"%s\"\n"), *ap);
1290 err = 1;
1291 continue;
1294 bad = !_var_vokclear(*ap);
1295 if (
1296 #ifdef HAVE_SETENV
1297 unsetenv(*ap) != 0 ||
1298 #endif
1301 err = 1;
1304 NYD_LEAVE;
1305 return err;
1308 FL int
1309 c_varedit(void *v)
1311 struct var_carrier vc;
1312 sighandler_type sigint;
1313 FILE *of, *nf;
1314 char *val, **argv = v;
1315 int err = 0;
1316 NYD_ENTER;
1318 sigint = safe_signal(SIGINT, SIG_IGN);
1320 while (*argv != NULL) {
1321 memset(&vc, 0, sizeof vc);
1323 _var_revlookup(&vc, *argv++);
1325 if (vc.vc_vmap != NULL) {
1326 if (vc.vc_vmap->vm_flags & VM_BINARY) {
1327 n_err(_("`varedit': can't edit binary variable \"%s\"\n"),
1328 vc.vc_name);
1329 continue;
1331 if (vc.vc_vmap->vm_flags & VM_RDONLY) {
1332 n_err(_("`varedit': can't edit readonly variable \"%s\"\n"),
1333 vc.vc_name);
1334 continue;
1338 _var_lookup(&vc);
1340 if ((of = Ftmp(NULL, "vared", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1341 NULL) {
1342 n_perr(_("`varedit': cannot create temporary file"), 0);
1343 err = 1;
1344 break;
1345 } else if (vc.vc_var != NULL && *(val = vc.vc_var->v_value) != '\0' &&
1346 sizeof *val != fwrite(val, strlen(val), sizeof *val, of)) {
1347 n_perr(_("`varedit' failed to write old value to temporary file"), 0);
1348 Fclose(of);
1349 err = 1;
1350 continue;
1353 fflush_rewind(of);
1354 nf = run_editor(of, (off_t)-1, 'e', FAL0, NULL, NULL, SEND_MBOX, sigint);
1355 Fclose(of);
1357 if (nf != NULL) {
1358 int c;
1359 char *base;
1360 off_t l = fsize(nf);
1362 assert(l >= 0);
1363 base = smalloc((size_t)l + 1);
1365 for (l = 0, val = base; (c = getc(nf)) != EOF; ++val)
1366 if (c == '\n' || c == '\r') {
1367 *val = ' ';
1368 ++l;
1369 } else {
1370 *val = (char)(uc_i)c;
1371 l = 0;
1373 val -= l;
1374 *val = '\0';
1376 if (!vok_vset(vc.vc_name, base))
1377 err = 1;
1379 free(base);
1381 Fclose(nf);
1384 safe_signal(SIGINT, sigint);
1385 NYD_LEAVE;
1386 return err;
1389 FL int
1390 c_define(void *v)
1392 int rv = 1;
1393 char **args = v;
1394 NYD_ENTER;
1396 if (args[0] == NULL) {
1397 rv = _ma_list(MA_NONE);
1398 goto jleave;
1401 if (args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
1402 args[2] != NULL) {
1403 n_err(_("Syntax is: define <name> {"));
1404 goto jleave;
1407 rv = !_ma_define(args[0], MA_NONE);
1408 jleave:
1409 NYD_LEAVE;
1410 return rv;
1413 FL int
1414 c_undefine(void *v)
1416 int rv = 1;
1417 char **args = v;
1418 NYD_ENTER;
1420 if (*args == NULL) {
1421 n_err(_("`undefine': required arguments are missing\n"));
1422 goto jleave;
1425 _ma_undefine(*args, MA_NONE);
1426 while (*++args != NULL);
1427 rv = 0;
1428 jleave:
1429 NYD_LEAVE;
1430 return rv;
1433 FL int
1434 c_call(void *v)
1436 int rv = 1;
1437 char **args = v;
1438 char const *errs, *name;
1439 struct macro *mp;
1440 NYD_ENTER;
1442 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
1443 errs = _("Syntax is: call <%s>\n");
1444 name = "name";
1445 goto jerr;
1448 if ((mp = _ma_look(*args, NULL, MA_NONE)) == NULL) {
1449 errs = _("Undefined macro called: \"%s\"\n");
1450 name = *args;
1451 goto jerr;
1454 rv = _ma_exec(mp, NULL);
1455 jleave:
1456 NYD_LEAVE;
1457 return rv;
1458 jerr:
1459 n_err(errs, name);
1460 goto jleave;
1463 FL bool_t
1464 check_folder_hook(bool_t nmail)
1466 size_t len;
1467 char *var, *cp;
1468 struct macro *mp;
1469 struct var **unroller;
1470 bool_t rv = TRU1;
1471 NYD_ENTER;
1473 var = ac_alloc(len = strlen(mailname) + sizeof("folder-hook-") -1 +1);
1475 /* First try the fully resolved path */
1476 snprintf(var, len, "folder-hook-%s", mailname);
1477 if ((cp = vok_vlook(var)) != NULL)
1478 goto jmac;
1480 /* If we are under *folder*, try the usual +NAME syntax, too */
1481 if (displayname[0] == '+') {
1482 char *x = mailname + len;
1484 for (; x > mailname; --x)
1485 if (x[-1] == '/') {
1486 snprintf(var, len, "folder-hook-+%s", x);
1487 if ((cp = vok_vlook(var)) != NULL)
1488 goto jmac;
1489 break;
1493 /* Plain *folder-hook* is our last try */
1494 if ((cp = ok_vlook(folder_hook)) == NULL)
1495 goto jleave;
1497 jmac:
1498 if ((mp = _ma_look(cp, NULL, MA_NONE)) == NULL) {
1499 n_err(_("Cannot call *folder-hook* for \"%s\": "
1500 "macro \"%s\" does not exist\n"), displayname, cp);
1501 rv = FAL0;
1502 goto jleave;
1505 pstate &= ~PS_HOOK_MASK;
1506 if (nmail) {
1507 pstate |= PS_HOOK_NEWMAIL;
1508 unroller = NULL;
1509 } else {
1510 pstate |= PS_HOOK;
1511 unroller = &_folder_hook_localopts;
1513 rv = (_ma_exec(mp, unroller) == 0);
1514 pstate &= ~PS_HOOK_MASK;
1516 jleave:
1517 ac_free(var);
1518 NYD_LEAVE;
1519 return rv;
1522 FL int
1523 c_account(void *v)
1525 char **args = v;
1526 struct macro *mp;
1527 int rv = 1, i, oqf, nqf;
1528 NYD_ENTER;
1530 if (args[0] == NULL) {
1531 rv = _ma_list(MA_ACC);
1532 goto jleave;
1535 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1536 if (args[2] != NULL) {
1537 n_err(_("Syntax is: account <name> {\n"));
1538 goto jleave;
1540 if (!asccasecmp(args[0], ACCOUNT_NULL)) {
1541 n_err(_("Error: \"%s\" is a reserved name\n"), ACCOUNT_NULL);
1542 goto jleave;
1544 rv = !_ma_define(args[0], MA_ACC);
1545 goto jleave;
1548 if (pstate & PS_HOOK_MASK) {
1549 n_err(_("Cannot change account from within a hook\n"));
1550 goto jleave;
1553 save_mbox_for_possible_quitstuff();
1555 mp = NULL;
1556 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
1557 (mp = _ma_look(args[0], NULL, MA_ACC)) == NULL) {
1558 n_err(_("Account \"%s\" does not exist\n"), args[0]);
1559 goto jleave;
1562 oqf = savequitflags();
1564 if (_acc_curr != NULL) {
1565 if (_acc_curr->ma_localopts != NULL)
1566 _localopts_unroll(&_acc_curr->ma_localopts);
1567 if (_acc_curr->ma_flags & MA_DELETED) { /* xxx can be made generic? */
1568 _ma_freelines(_acc_curr->ma_contents);
1569 free(_acc_curr->ma_name);
1570 free(_acc_curr);
1574 account_name = (mp != NULL) ? mp->ma_name : NULL;
1575 _acc_curr = mp;
1577 if (mp != NULL && _ma_exec(mp, &mp->ma_localopts) == CBAD) {
1578 /* XXX account switch incomplete, unroll? */
1579 n_err(_("Switching to account \"%s\" failed\n"), args[0]);
1580 goto jleave;
1583 if ((pstate & (PS_STARTED | PS_HOOK_MASK)) == PS_STARTED) {
1584 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1585 restorequitflags(oqf);
1586 if ((i = setfile("%", 0)) < 0)
1587 goto jleave;
1588 check_folder_hook(FAL0);
1589 if (i > 0 && !ok_blook(emptystart))
1590 goto jleave;
1591 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1592 restorequitflags(nqf);
1594 rv = 0;
1595 jleave:
1596 NYD_LEAVE;
1597 return rv;
1600 FL int
1601 c_unaccount(void *v)
1603 int rv = 1;
1604 char **args = v;
1605 NYD_ENTER;
1607 if (*args == NULL) {
1608 n_err(_("`unaccount': required arguments are missing\n"));
1609 goto jleave;
1612 _ma_undefine(*args, MA_ACC);
1613 while (*++args != NULL);
1614 rv = 0;
1615 jleave:
1616 NYD_LEAVE;
1617 return rv;
1620 FL int
1621 c_localopts(void *v)
1623 int rv = 1;
1624 char **c = v;
1625 NYD_ENTER;
1627 if (_localopts == NULL) {
1628 n_err(_("Cannot use `localopts' but from within a "
1629 "`define' or `account'\n"));
1630 goto jleave;
1633 _localopts->s_unroll = (boolify(*c, UIZ_MAX, FAL0) > 0);
1634 rv = 0;
1635 jleave:
1636 NYD_LEAVE;
1637 return rv;
1640 FL void
1641 temporary_localopts_free(void) /* XXX intermediate hack */
1643 struct n2 {struct n2 *up; struct lostack *lo;} *x;
1644 NYD_ENTER;
1646 x = temporary_localopts_store;
1647 temporary_localopts_store = NULL;
1648 _localopts = NULL;
1650 while (x != NULL) {
1651 struct lostack *losp = x->lo;
1652 x = x->up;
1653 if (losp->s_localopts != NULL)
1654 _localopts_unroll(&losp->s_localopts);
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 _localopts = save;
1669 NYD_LEAVE;
1672 /* s-it-mode */