nail.h: add some OS_ constants
[s-mailx.git] / accmacvar.c
blob1920c2c067c9a957ca245a22193b7a79e9e82642
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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
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
48 * HOWTO add a new non-dynamic binary or value option:
49 * - add an entry to nail.h:enum okeys
50 * - run create-okey-map.pl
51 * - update nail.1
54 /* Note: changing the hash function must be reflected in create-okey-map.pl */
55 #define MA_PRIME HSHSIZE
56 #define MA_NAME2HASH(N) torek_hash(N)
57 #define MA_HASH2PRIME(H) ((H) % MA_PRIME)
59 enum ma_flags {
60 MA_NONE = 0,
61 MA_ACC = 1<<0,
62 MA_TYPE_MASK = MA_ACC,
63 MA_UNDEF = 1<<1, /* Unlink after lookup */
64 MA_DELETED = 1<<13 /* Only for _acc_curr: deleted while active */
67 enum var_map_flags {
68 VM_NONE = 0,
69 VM_BINARY = 1<<0, /* ok_b_* */
70 VM_RDONLY = 1<<1, /* May not be set by user */
71 VM_SPECIAL = 1<<2, /* Wants _var_check_specials() evaluation */
72 VM_VIRTUAL = 1<<3 /* "Stateless": no var* -- implies VM_RDONLY */
75 struct macro {
76 struct macro *ma_next;
77 char *ma_name;
78 struct mline *ma_contents;
79 size_t ma_maxlen; /* Maximum line length */
80 enum ma_flags ma_flags;
81 struct var *ma_localopts; /* `account' unroll list, for `localopts' */
84 struct mline {
85 struct mline *l_next;
86 size_t l_length;
87 char l_line[VFIELD_SIZE(0)];
90 struct var {
91 struct var *v_link;
92 char *v_value;
93 char v_name[VFIELD_SIZE(0)];
96 struct var_virtual {
97 ui32_t vv_okey;
98 struct var const *vv_var;
101 struct var_map {
102 ui32_t vm_hash;
103 ui16_t vm_keyoff;
104 ui16_t vm_flags; /* var_map_flags bits */
107 struct var_carrier {
108 char const *vc_name;
109 ui32_t vc_hash;
110 ui32_t vc_prime;
111 struct var *vc_var;
112 struct var_map const *vc_vmap;
113 enum okeys vc_okey;
116 struct var_show {
117 struct var_carrier vs_vc; /* _var_revlookup() */
118 char const *vs_value; /* Value (from wherever it came) or NULL */
119 bool_t vs_isset; /* Managed by us and existent */
120 bool_t vs_isenv; /* Set, but managed by environ */
121 ui8_t __pad[6];
124 struct lostack {
125 struct lostack *s_up; /* Outer context */
126 struct macro *s_mac; /* Context (`account' or `define') */
127 struct var *s_localopts;
128 bool_t s_unroll; /* Unroll? */
131 /* Include the constant create-okey-map.pl output */
132 #include "version.h"
133 #include "okeys.h"
135 static struct macro *_acc_curr; /* Currently active account */
136 static struct lostack *_localopts; /* Currently executing macro unroll list */
137 /* TODO We really deserve localopts support for *folder-hook*s, so hack it in
138 * TODO today via a static lostack, it should be a field in mailbox, once that
139 * TODO is a real multi-instance object */
140 static struct var *_folder_hook_localopts;
142 /* TODO once we have a dynamically sized hashtable we could unite _macros and
143 * TODO _variables into a single hashtable, stripping down fun interface;
144 * TODO also, setting and clearing a variable can be easily joined */
145 static struct var *_vars[MA_PRIME]; /* TODO dynamically spaced */
146 static struct macro *_macros[MA_PRIME]; /* TODO dynamically spaced */
148 /* Special cased value string allocation */
149 static char * _var_vcopy(char const *str);
150 static void _var_vfree(char *cp);
152 /* Check for special housekeeping. */
153 static bool_t _var_check_specials(enum okeys okey, bool_t enable,
154 char **val);
156 /* If a variable name begins with a lowercase-character and contains at
157 * least one '@', it is converted to all-lowercase. This is necessary
158 * for lookups of names based on email addresses.
159 * Following the standard, only the part following the last '@' should
160 * be lower-cased, but practice has established otherwise here.
161 * Return value may have been placed in string dope (salloc()) */
162 static char const * _var_canonify(char const *vn);
164 /* Try to reverse lookup an option name to an enum okeys mapping.
165 * Updates vcp.vc_name and vcp.vc_hash; vcp.vc_vmap is NULL if none found */
166 static bool_t _var_revlookup(struct var_carrier *vcp, char const *name);
168 /* Lookup a variable from vcp.vc_(vmap|name|hash), return wether it was found.
169 * Sets vcp.vc_prime; vcp.vc_var is NULL if not found */
170 static bool_t _var_lookup(struct var_carrier *vcp);
172 /* Completely fill vsp with data for name, return wether it was set as an
173 * internal variable (it may still have been set as an environment variable) */
174 static bool_t _var_broadway(struct var_show *vsp, char const *name);
176 /* Set variable from vcp.vc_(vmap|name|hash), return success */
177 static bool_t _var_set(struct var_carrier *vcp, char const *value);
179 /* Clear variable from vcp.vc_(vmap|name|hash); sets vcp.vc_var to NULL,
180 * return success */
181 static bool_t _var_clear(struct var_carrier *vcp);
183 /* List all variables */
184 static void _var_list_all(void);
186 static int __var_list_all_cmp(void const *s1, void const *s2);
187 static char * __var_simple_quote(char const *cp);
189 /* Shared c_set() and c_setenv() impl, return success */
190 static bool_t _var_set_env(char **ap, bool_t issetenv);
192 /* Does cp consist solely of WS and a } */
193 static bool_t _is_closing_angle(char const *cp);
195 /* Lookup for macros/accounts */
196 static struct macro *_ma_look(char const *name, struct macro *data,
197 enum ma_flags mafl);
199 /* Walk all lines of a macro and execute() them */
200 static int _ma_exec(struct macro const *mp, struct var **unroller);
202 /* User display helpers */
203 static int _ma_list(enum ma_flags mafl);
205 /* */
206 static bool_t _ma_define(char const *name, enum ma_flags mafl);
207 static void _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 #ifdef HAVE_NCL
281 case ok_v_line_editor_cursor_right:
282 if ((ok = (val != NULL && *val != NULL))) {
283 /* Set with no value? TODO very guly */
284 if (*(cp = *val) != '\0') {
285 char const *x = cp;
286 int c;
287 do {
288 c = expand_shell_escape(&x, FAL0);
289 if (c < 0)
290 break;
291 *cp++ = (char)c;
292 } while (*x != '\0');
293 *cp = '\0';
296 break;
297 #endif
298 default:
299 break;
302 if (flag) {
303 if (enable)
304 options |= flag;
305 else
306 options &= ~flag;
308 NYD2_LEAVE;
309 return ok;
312 static char const *
313 _var_canonify(char const *vn)
315 NYD2_ENTER;
316 if (!upperchar(*vn)) {
317 char const *vp;
319 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
321 vn = (*vp == '@') ? i_strdup(vn) : vn;
323 NYD2_LEAVE;
324 return vn;
327 static bool_t
328 _var_revlookup(struct var_carrier *vcp, char const *name)
330 ui32_t hash, i, j;
331 struct var_map const *vmp;
332 NYD2_ENTER;
334 vcp->vc_name = name = _var_canonify(name);
335 vcp->vc_hash = hash = MA_NAME2HASH(name);
337 for (i = hash % _VAR_REV_PRIME, j = 0; j <= _VAR_REV_LONGEST; ++j) {
338 ui32_t x = _var_revmap[i];
339 if (x == _VAR_REV_ILL)
340 break;
341 vmp = _var_map + x;
342 if (vmp->vm_hash == hash && !strcmp(_var_keydat + vmp->vm_keyoff, name)) {
343 vcp->vc_vmap = vmp;
344 vcp->vc_okey = (enum okeys)x;
345 goto jleave;
347 if (++i == _VAR_REV_PRIME) {
348 #ifdef _VAR_REV_WRAPAROUND
349 i = 0;
350 #else
351 break;
352 #endif
355 vcp->vc_vmap = NULL;
356 vcp = NULL;
357 jleave:
358 NYD2_LEAVE;
359 return (vcp != NULL);
362 static bool_t
363 _var_lookup(struct var_carrier *vcp)
365 struct var **vap, *lvp, *vp;
366 NYD2_ENTER;
368 /* XXX _So_ unlikely that it should be checked if normal lookup fails! */
369 if (UNLIKELY(vcp->vc_vmap != NULL &&
370 (vcp->vc_vmap->vm_flags & VM_VIRTUAL) != 0)) {
371 struct var_virtual const *vvp;
373 for (vvp = _var_virtuals;
374 PTRCMP(vvp, <, _var_virtuals + NELEM(_var_virtuals)); ++vvp)
375 if (vvp->vv_okey == vcp->vc_okey) {
376 vp = UNCONST(vvp->vv_var);
377 goto jleave;
379 assert(0);
382 vap = _vars + (vcp->vc_prime = MA_HASH2PRIME(vcp->vc_hash));
384 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
385 if (!strcmp(vp->v_name, vcp->vc_name)) {
386 /* Relink as head, hope it "sorts on usage" over time.
387 * _var_clear() relies on this behaviour */
388 if (lvp != NULL) {
389 lvp->v_link = vp->v_link;
390 vp->v_link = *vap;
391 *vap = vp;
393 goto jleave;
395 vp = NULL;
396 jleave:
397 vcp->vc_var = vp;
398 NYD2_LEAVE;
399 return (vp != NULL);
402 static bool_t
403 _var_broadway(struct var_show *vsp, char const *name)
405 bool_t rv;
406 NYD2_ENTER;
408 memset(vsp, 0, sizeof *vsp);
410 _var_revlookup(&vsp->vs_vc, name);
412 if ((vsp->vs_isset = rv = _var_lookup(&vsp->vs_vc))) {
413 vsp->vs_value = vsp->vs_vc.vc_var->v_value;
414 vsp->vs_isenv = FAL0;
415 } else
416 vsp->vs_isenv = ((vsp->vs_value = getenv(vsp->vs_vc.vc_name)) != NULL);
418 NYD2_LEAVE;
419 return rv;
422 static bool_t
423 _var_set(struct var_carrier *vcp, char const *value)
425 struct var *vp;
426 char *oval;
427 bool_t ok = TRU1;
428 NYD2_ENTER;
430 if (value == NULL) {
431 ok = _var_clear(vcp);
432 goto jleave;
435 _var_lookup(vcp);
437 if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_RDONLY)) {
438 fprintf(stderr, _("Variable readonly: \"%s\"\n"), vcp->vc_name);
439 ok = FAL0;
440 goto jleave;
443 /* Don't care what happens later on, store this in the unroll list */
444 if (_localopts != NULL)
445 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
447 if ((vp = vcp->vc_var) == NULL) {
448 size_t l = strlen(vcp->vc_name) + 1;
450 vcp->vc_var =
451 vp = smalloc(sizeof(*vp) - VFIELD_SIZEOF(struct var, v_name) + l);
452 vp->v_link = _vars[vcp->vc_prime];
453 _vars[vcp->vc_prime] = vp;
454 memcpy(vp->v_name, vcp->vc_name, l);
455 oval = UNCONST("");
456 } else
457 oval = vp->v_value;
459 if (vcp->vc_vmap == NULL)
460 vp->v_value = _var_vcopy(value);
461 else {
462 /* Via `set' etc. the user may give even binary options non-binary
463 * values, ignore that and force binary xxx error log? */
464 if (vcp->vc_vmap->vm_flags & VM_BINARY)
465 value = UNCONST("");
466 vp->v_value = _var_vcopy(value);
468 /* Check if update allowed XXX wasteful on error! */
469 if ((vcp->vc_vmap->vm_flags & VM_SPECIAL) &&
470 !(ok = _var_check_specials(vcp->vc_okey, TRU1, &vp->v_value))) {
471 char *cp = vp->v_value;
472 vp->v_value = oval;
473 oval = cp;
477 if (*oval != '\0')
478 _var_vfree(oval);
479 jleave:
480 NYD2_LEAVE;
481 return ok;
484 static bool_t
485 _var_clear(struct var_carrier *vcp)
487 bool_t ok = TRU1;
488 NYD2_ENTER;
490 if (!_var_lookup(vcp)) {
491 if (!(pstate & PS_IN_LOAD) && (options & OPT_D_V))
492 fprintf(stderr, _("Variable undefined: \"%s\"\n"), vcp->vc_name);
493 } else if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_RDONLY)) {
494 fprintf(stderr, _("Variable readonly: \"%s\"\n"), vcp->vc_name);
495 ok = FAL0;
496 } else {
497 if (_localopts != NULL)
498 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
500 /* Always listhead after _var_lookup() */
501 _vars[vcp->vc_prime] = _vars[vcp->vc_prime]->v_link;
502 _var_vfree(vcp->vc_var->v_value);
503 free(vcp->vc_var);
504 vcp->vc_var = NULL;
506 if (vcp->vc_vmap != NULL && (vcp->vc_vmap->vm_flags & VM_SPECIAL))
507 ok = _var_check_specials(vcp->vc_okey, FAL0, NULL);
509 NYD2_LEAVE;
510 return ok;
513 static void
514 _var_list_all(void)
516 struct var_show vs;
517 FILE *fp;
518 size_t no, i;
519 struct var *vp;
520 char const **vacp, **cap;
521 NYD2_ENTER;
523 if ((fp = Ftmp(NULL, "listvars", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
524 NULL) {
525 perror("tmpfile");
526 goto jleave;
529 for (no = i = 0; i < MA_PRIME; ++i)
530 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
531 ++no;
532 no += NELEM(_var_virtuals);
534 vacp = salloc(no * sizeof(*vacp));
536 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
537 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
538 *cap++ = vp->v_name;
539 for (i = 0; i < NELEM(_var_virtuals); ++i)
540 *cap++ = _var_virtuals[i].vv_var->v_name;
542 if (no > 1)
543 qsort(vacp, no, sizeof *vacp, &__var_list_all_cmp);
545 i = (ok_blook(bsdcompat) || ok_blook(bsdset));
546 for (cap = vacp; no != 0; ++cap, --no) {
547 char const *fmt;
549 if (!_var_broadway(&vs, *cap))
550 continue;
551 if (vs.vs_value == NULL)
552 vs.vs_value = "";
554 if (i)
555 fmt = "%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 = "set %s\n";
560 else {
561 fmt = "set %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, *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 fprintf(stderr, _("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 fprintf(stderr, _("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 fprintf(stderr, _("Delayed deletion of active account \"%s\"\n"),
731 name);
732 } else {
733 _ma_freelines(mp->ma_contents);
734 free(mp->ma_name);
735 free(mp);
736 mp = (struct macro*)-1;
739 goto jleave;
743 if (data != NULL) {
744 data->ma_next = _macros[h];
745 _macros[h] = data;
746 mp = NULL;
748 jleave:
749 NYD2_LEAVE;
750 return mp;
753 static int
754 _ma_exec(struct macro const *mp, struct var **unroller)
756 struct lostack los;
757 char *buf;
758 struct n2 {struct n2 *up; struct lostack *lo;} *x; /* FIXME hack (sigman+) */
759 struct mline const *lp;
760 int rv = 0;
761 NYD2_ENTER;
763 los.s_up = _localopts;
764 los.s_mac = UNCONST(mp);
765 los.s_localopts = NULL;
766 los.s_unroll = FAL0;
767 _localopts = &los;
769 x = salloc(sizeof *x); /* FIXME intermediate hack (signal man+) */
770 x->up = temporary_localopts_store;
771 x->lo = _localopts;
772 temporary_localopts_store = x;
774 buf = ac_alloc(mp->ma_maxlen +1);
775 for (lp = mp->ma_contents; lp; lp = lp->l_next) {
776 memcpy(buf, lp->l_line, lp->l_length +1);
777 rv |= execute(buf, 0, lp->l_length); /* XXX break if != 0 ? */
779 ac_free(buf);
781 temporary_localopts_store = x->up; /* FIXME intermediate hack */
783 _localopts = los.s_up;
784 if (unroller == NULL) {
785 if (los.s_localopts != NULL)
786 _localopts_unroll(&los.s_localopts);
787 } else
788 *unroller = los.s_localopts;
789 NYD2_LEAVE;
790 return rv;
793 static int
794 _ma_list(enum ma_flags mafl)
796 FILE *fp;
797 char const *typestr;
798 struct macro *mq;
799 ui32_t ti, mc;
800 struct mline *lp;
801 NYD2_ENTER;
803 if ((fp = Ftmp(NULL, "listmacs", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
804 NULL) {
805 perror("tmpfile");
806 mc = 1;
807 goto jleave;
810 mafl &= MA_TYPE_MASK;
811 typestr = (mafl & MA_ACC) ? "account" : "define";
813 for (ti = mc = 0; ti < MA_PRIME; ++ti)
814 for (mq = _macros[ti]; mq; mq = mq->ma_next)
815 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
816 if (++mc > 1)
817 putc('\n', fp);
818 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
819 for (lp = mq->ma_contents; lp != NULL; lp = lp->l_next)
820 fprintf(fp, " %s\n", lp->l_line);
821 fputs("}\n", fp);
823 if (mc)
824 page_or_print(fp, 0);
826 mc = (ui32_t)ferror(fp);
827 Fclose(fp);
828 jleave:
829 NYD2_LEAVE;
830 return (int)mc;
833 static bool_t
834 _ma_define(char const *name, enum ma_flags mafl)
836 bool_t rv = FAL0;
837 struct macro *mp;
838 struct mline *lp, *lst = NULL, *lnd = NULL;
839 char *linebuf = NULL, *cp;
840 size_t linesize = 0, maxlen = 0;
841 int n, i;
842 NYD2_ENTER;
844 mp = scalloc(1, sizeof *mp);
845 mp->ma_name = sstrdup(name);
846 mp->ma_flags = mafl;
848 for (;;) {
849 n = readline_input("", TRU1, &linebuf, &linesize, NULL);
850 if (n == 0)
851 continue;
852 if (n < 0) {
853 fprintf(stderr, _("Unterminated %s definition: \"%s\".\n"),
854 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
855 if ((pstate & PS_IN_LOAD) == PS_SOURCING)
856 unstack();
857 goto jerr;
859 if (_is_closing_angle(linebuf))
860 break;
862 /* Trim WS */
863 for (cp = linebuf, i = 0; i < n; ++cp, ++i)
864 if (!whitechar(*cp))
865 break;
866 if (i == n)
867 continue;
868 n -= i;
869 while (whitechar(cp[n - 1]))
870 if (--n == 0)
871 break;
872 if (n == 0)
873 continue;
875 maxlen = MAX(maxlen, (size_t)n);
876 cp[n++] = '\0';
878 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n);
879 memcpy(lp->l_line, cp, n);
880 lp->l_length = (size_t)--n;
881 if (lst != NULL) {
882 lnd->l_next = lp;
883 lnd = lp;
884 } else
885 lst = lnd = lp;
887 mp->ma_contents = lst;
888 mp->ma_maxlen = maxlen;
890 if (_ma_look(mp->ma_name, mp, mafl) != NULL) {
891 fprintf(stderr, _("A %s named \"%s\" already exists.\n"),
892 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
893 lst = mp->ma_contents;
894 goto jerr;
897 rv = TRU1;
898 jleave:
899 if (linebuf != NULL)
900 free(linebuf);
901 NYD2_LEAVE;
902 return rv;
904 jerr:
905 if (lst != NULL)
906 _ma_freelines(lst);
907 free(mp->ma_name);
908 free(mp);
909 goto jleave;
912 static void
913 _ma_undefine(char const *name, enum ma_flags mafl)
915 struct macro *mp;
916 NYD2_ENTER;
918 if (LIKELY(name[0] != '*' || name[1] != '\0')) {
919 if ((mp = _ma_look(name, NULL, mafl | MA_UNDEF)) == NULL)
920 fprintf(stderr, _("%s \"%s\" is not defined\n"),
921 (mafl & MA_ACC ? "Account" : "Macro"), name);
922 } else {
923 struct macro **mpp, *lmp;
925 for (mpp = _macros; PTRCMP(mpp, <, _macros + NELEM(_macros)); ++mpp)
926 for (lmp = NULL, mp = *mpp; mp != NULL;) {
927 if ((mp->ma_flags & MA_TYPE_MASK) == mafl) {
928 /* xxx Expensive but rare: be simple */
929 _ma_look(mp->ma_name, NULL, mafl | MA_UNDEF);
930 mp = (lmp == NULL) ? *mpp : lmp->ma_next;
931 } else {
932 lmp = mp;
933 mp = mp->ma_next;
937 NYD2_LEAVE;
940 static void
941 _ma_freelines(struct mline *lp)
943 struct mline *lq;
944 NYD2_ENTER;
946 for (lq = NULL; lp != NULL; ) {
947 if (lq != NULL)
948 free(lq);
949 lq = lp;
950 lp = lp->l_next;
952 if (lq)
953 free(lq);
954 NYD2_LEAVE;
957 static void
958 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
960 struct var *vap;
961 size_t nl, vl;
962 NYD2_ENTER;
964 /* Propagate unrolling up the stack, as necessary */
965 while (!losp->s_unroll && (losp = losp->s_up) != NULL)
967 if (losp == NULL)
968 goto jleave;
970 /* We've found a level that wants to unroll; check wether it does it yet */
971 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
972 if (!strcmp(vap->v_name, name))
973 goto jleave;
975 nl = strlen(name) + 1;
976 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
977 vap = smalloc(sizeof(*vap) - VFIELD_SIZEOF(struct var, v_name) + nl + vl);
978 vap->v_link = losp->s_localopts;
979 losp->s_localopts = vap;
980 memcpy(vap->v_name, name, nl);
981 if (vl == 0)
982 vap->v_value = NULL;
983 else {
984 vap->v_value = vap->v_name + nl;
985 memcpy(vap->v_value, ovap->v_value, vl);
987 jleave:
988 NYD2_LEAVE;
991 static void
992 _localopts_unroll(struct var **vapp)
994 struct lostack *save_los;
995 struct var *x, *vap;
996 NYD2_ENTER;
998 vap = *vapp;
999 *vapp = NULL;
1001 save_los = _localopts;
1002 _localopts = NULL;
1003 while (vap != NULL) {
1004 x = vap;
1005 vap = vap->v_link;
1006 vok_vset(x->v_name, x->v_value);
1007 free(x);
1009 _localopts = save_los;
1010 NYD2_LEAVE;
1013 FL char *
1014 _var_oklook(enum okeys okey)
1016 struct var_carrier vc;
1017 char *rv;
1018 NYD_ENTER;
1020 vc.vc_vmap = _var_map + okey;
1021 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1022 vc.vc_hash = _var_map[okey].vm_hash;
1023 vc.vc_okey = okey;
1025 if (!_var_lookup(&vc)) {
1026 if ((rv = getenv(vc.vc_name)) != NULL) {
1027 _var_set(&vc, rv);
1028 assert(vc.vc_var != NULL);
1029 goto jvar;
1031 } else
1032 jvar:
1033 rv = vc.vc_var->v_value;
1034 NYD_LEAVE;
1035 return rv;
1038 FL bool_t
1039 _var_okset(enum okeys okey, uintptr_t val)
1041 struct var_carrier vc;
1042 bool_t ok;
1043 NYD_ENTER;
1045 vc.vc_vmap = _var_map + okey;
1046 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1047 vc.vc_hash = _var_map[okey].vm_hash;
1048 vc.vc_okey = okey;
1050 ok = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
1051 NYD_LEAVE;
1052 return ok;
1055 FL bool_t
1056 _var_okclear(enum okeys okey)
1058 struct var_carrier vc;
1059 bool_t rv;
1060 NYD_ENTER;
1062 vc.vc_vmap = _var_map + okey;
1063 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1064 vc.vc_hash = _var_map[okey].vm_hash;
1065 vc.vc_okey = okey;
1067 rv = _var_clear(&vc);
1068 NYD_LEAVE;
1069 return rv;
1072 FL char *
1073 _var_voklook(char const *vokey)
1075 struct var_carrier vc;
1076 char *rv;
1077 NYD_ENTER;
1079 _var_revlookup(&vc, vokey);
1081 if (!_var_lookup(&vc)) {
1082 if ((rv = getenv(vc.vc_name)) != NULL) {
1083 _var_set(&vc, rv);
1084 assert(vc.vc_var != NULL);
1085 goto jvar;
1087 } else
1088 jvar:
1089 rv = vc.vc_var->v_value;
1090 NYD_LEAVE;
1091 return rv;
1094 FL bool_t
1095 _var_vokset(char const *vokey, uintptr_t val)
1097 struct var_carrier vc;
1098 bool_t ok;
1099 NYD_ENTER;
1101 _var_revlookup(&vc, vokey);
1103 ok = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
1104 NYD_LEAVE;
1105 return ok;
1108 FL bool_t
1109 _var_vokclear(char const *vokey)
1111 struct var_carrier vc;
1112 bool_t err;
1113 NYD_ENTER;
1115 _var_revlookup(&vc, vokey);
1117 err = !_var_clear(&vc);
1118 NYD_LEAVE;
1119 return !err;
1122 FL char *
1123 _env_look(char const *envkey, bool_t envonly) /* TODO rather dummy yet!! */
1125 char *rv;
1126 NYD_ENTER;
1128 if (envonly)
1129 rv = getenv(envkey); /* TODO rework vars: cache, output a la mimetypes */
1130 else
1131 rv = _var_voklook(envkey);
1132 NYD_LEAVE;
1133 return rv;
1136 #ifdef HAVE_SOCKETS
1137 FL char *
1138 _var_xoklook(enum okeys okey, struct url const *urlp, enum okey_xlook_mode oxm)
1140 struct var_carrier vc;
1141 struct str const *us;
1142 size_t nlen;
1143 char *nbuf = NULL /* CC happiness */, *rv;
1144 NYD_ENTER;
1146 assert(oxm & (OXM_PLAIN | OXM_H_P | OXM_U_H_P));
1148 /* For simplicity: allow this case too */
1149 if (!(oxm & (OXM_H_P | OXM_U_H_P)))
1150 goto jplain;
1152 vc.vc_vmap = _var_map + okey;
1153 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
1154 vc.vc_okey = okey;
1156 us = (oxm & OXM_U_H_P) ? &urlp->url_u_h_p : &urlp->url_h_p;
1157 nlen = strlen(vc.vc_name);
1158 nbuf = ac_alloc(nlen + 1 + us->l +1);
1159 memcpy(nbuf, vc.vc_name, nlen);
1160 nbuf[nlen++] = '-';
1162 /* One of .url_u_h_p and .url_h_p we test in here */
1163 memcpy(nbuf + nlen, us->s, us->l +1);
1164 vc.vc_name = _var_canonify(nbuf);
1165 vc.vc_hash = MA_NAME2HASH(vc.vc_name);
1166 if (_var_lookup(&vc))
1167 goto jvar;
1169 /* The second */
1170 if (oxm & OXM_H_P) {
1171 us = &urlp->url_h_p;
1172 memcpy(nbuf + nlen, us->s, us->l +1);
1173 vc.vc_name = _var_canonify(nbuf);
1174 vc.vc_hash = MA_NAME2HASH(vc.vc_name);
1175 if (_var_lookup(&vc)) {
1176 jvar:
1177 rv = vc.vc_var->v_value;
1178 goto jleave;
1182 jplain:
1183 rv = (oxm & OXM_PLAIN) ? _var_oklook(okey) : NULL;
1184 jleave:
1185 if (oxm & (OXM_H_P | OXM_U_H_P))
1186 ac_free(nbuf);
1187 NYD_LEAVE;
1188 return rv;
1190 #endif /* HAVE_SOCKETS */
1192 FL int
1193 c_varshow(void *v)
1195 struct var_show vs;
1196 char const **argv = v;
1197 NYD_ENTER;
1199 if (*argv == NULL)
1200 v = NULL;
1201 else for (; *argv != NULL; ++argv) {
1202 _var_broadway(&vs, *argv);
1203 if (vs.vs_value == NULL)
1204 vs.vs_value = "NULL";
1206 if (vs.vs_vc.vc_vmap != NULL) {
1207 ui16_t f = vs.vs_vc.vc_vmap->vm_flags;
1209 if (f & VM_BINARY)
1210 printf(_("\"%s\": (%d) binary%s%s: set=%d (ENVIRON=%d)\n"),
1211 vs.vs_vc.vc_name, vs.vs_vc.vc_okey,
1212 (f & VM_RDONLY ? ", read-only" : ""),
1213 (f & VM_VIRTUAL ? ", virtual" : ""), vs.vs_isset, vs.vs_isenv);
1214 else
1215 printf(_("\"%s\": (%d) value%s%s: set=%d (ENVIRON=%d) value<%s>\n"),
1216 vs.vs_vc.vc_name, vs.vs_vc.vc_okey,
1217 (f & VM_RDONLY ? ", read-only" : ""),
1218 (f & VM_VIRTUAL ? ", virtual" : ""), vs.vs_isset, vs.vs_isenv,
1219 vs.vs_value);
1220 } else
1221 printf("\"%s\": (assembled): set=%d (ENVIRON=%d) value<%s>\n",
1222 vs.vs_vc.vc_name, vs.vs_isset, vs.vs_isenv, vs.vs_value);
1224 NYD_LEAVE;
1225 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1228 FL int
1229 c_set(void *v)
1231 char **ap = v;
1232 int err;
1233 NYD_ENTER;
1235 if (*ap == NULL) {
1236 _var_list_all();
1237 err = 0;
1238 } else
1239 err = !_var_set_env(ap, FAL0);
1240 NYD_LEAVE;
1241 return err;
1244 FL int
1245 c_setenv(void *v)
1247 char **ap = v;
1248 int err;
1249 NYD_ENTER;
1251 if (!(err = !(pstate & PS_STARTED)))
1252 err = !_var_set_env(ap, TRU1);
1253 NYD_LEAVE;
1254 return err;
1257 FL int
1258 c_unset(void *v)
1260 char **ap = v;
1261 int err = 0;
1262 NYD_ENTER;
1264 while (*ap != NULL)
1265 err |= !_var_vokclear(*ap++);
1266 NYD_LEAVE;
1267 return err;
1270 FL int
1271 c_unsetenv(void *v)
1273 int err;
1274 NYD_ENTER;
1276 if (!(err = !(pstate & PS_STARTED))) {
1277 char **ap;
1279 for (ap = v; *ap != NULL; ++ap) {
1280 bool_t bad;
1282 if (!strcmp(*ap, "HOME") || !strcmp(*ap, "USER") || /* TODO generic */
1283 !strcmp(*ap, "TMPDIR")) { /* TODO approach */
1284 if (options & OPT_D_V)
1285 fprintf(stderr, _("Cannot `unsetenv' \"%s\"\n"), *ap);
1286 err = 1;
1287 continue;
1290 bad = !_var_vokclear(*ap);
1291 if (
1292 #ifdef HAVE_SETENV
1293 unsetenv(*ap) != 0 ||
1294 #endif
1297 err = 1;
1300 NYD_LEAVE;
1301 return err;
1304 FL int
1305 c_varedit(void *v)
1307 struct var_carrier vc;
1308 sighandler_type sigint;
1309 FILE *of, *nf;
1310 char *val, **argv = v;
1311 int err = 0;
1312 NYD_ENTER;
1314 sigint = safe_signal(SIGINT, SIG_IGN);
1316 while (*argv != NULL) {
1317 memset(&vc, 0, sizeof vc);
1319 _var_revlookup(&vc, *argv++);
1321 if (vc.vc_vmap != NULL) {
1322 if (vc.vc_vmap->vm_flags & VM_BINARY) {
1323 fprintf(stderr, _("`varedit': can't edit binary variable \"%s\"\n"),
1324 vc.vc_name);
1325 continue;
1327 if (vc.vc_vmap->vm_flags & VM_RDONLY) {
1328 fprintf(stderr,
1329 _("`varedit': can't edit readonly variable \"%s\"\n"),
1330 vc.vc_name);
1331 continue;
1335 _var_lookup(&vc);
1337 if ((of = Ftmp(NULL, "vared", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1338 NULL) {
1339 perror(_("`varedit': cannot create temporary file"));
1340 err = 1;
1341 break;
1342 } else if (vc.vc_var != NULL && *(val = vc.vc_var->v_value) != '\0' &&
1343 sizeof *val != fwrite(val, strlen(val), sizeof *val, of)) {
1344 perror(_("`varedit' failed to write an old value to temporary file"));
1345 Fclose(of);
1346 err = 1;
1347 continue;
1350 fflush_rewind(of);
1351 nf = run_editor(of, (off_t)-1, 'e', FAL0, NULL, NULL, SEND_MBOX, sigint);
1352 Fclose(of);
1354 if (nf != NULL) {
1355 int c;
1356 char *base;
1357 off_t l = fsize(nf);
1359 assert(l >= 0);
1360 base = smalloc((size_t)l + 1);
1362 for (l = 0, val = base; (c = getc(nf)) != EOF; ++val)
1363 if (c == '\n' || c == '\r') {
1364 *val = ' ';
1365 ++l;
1366 } else {
1367 *val = (char)(uc_i)c;
1368 l = 0;
1370 val -= l;
1371 *val = '\0';
1373 if (!vok_vset(vc.vc_name, base))
1374 err = 1;
1376 free(base);
1378 Fclose(nf);
1381 safe_signal(SIGINT, sigint);
1382 NYD_LEAVE;
1383 return err;
1386 FL int
1387 c_define(void *v)
1389 int rv = 1;
1390 char **args = v;
1391 NYD_ENTER;
1393 if (args[0] == NULL) {
1394 rv = _ma_list(MA_NONE);
1395 goto jleave;
1398 if (args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
1399 args[2] != NULL) {
1400 fprintf(stderr, _("Syntax is: define <name> {"));
1401 goto jleave;
1404 rv = !_ma_define(args[0], MA_NONE);
1405 jleave:
1406 NYD_LEAVE;
1407 return rv;
1410 FL int
1411 c_undefine(void *v)
1413 int rv = 1;
1414 char **args = v;
1415 NYD_ENTER;
1417 if (*args == NULL) {
1418 fprintf(stderr, _("`undefine': required arguments are missing\n"));
1419 goto jleave;
1422 _ma_undefine(*args, MA_NONE);
1423 while (*++args != NULL);
1424 rv = 0;
1425 jleave:
1426 NYD_LEAVE;
1427 return rv;
1430 FL int
1431 c_call(void *v)
1433 int rv = 1;
1434 char **args = v;
1435 char const *errs, *name;
1436 struct macro *mp;
1437 NYD_ENTER;
1439 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
1440 errs = _("Syntax is: call <%s>\n");
1441 name = "name";
1442 goto jerr;
1445 if ((mp = _ma_look(*args, NULL, MA_NONE)) == NULL) {
1446 errs = _("Undefined macro called: \"%s\"\n");
1447 name = *args;
1448 goto jerr;
1451 rv = _ma_exec(mp, NULL);
1452 jleave:
1453 NYD_LEAVE;
1454 return rv;
1455 jerr:
1456 fprintf(stderr, errs, name);
1457 goto jleave;
1460 FL bool_t
1461 check_folder_hook(bool_t nmail)
1463 size_t len;
1464 char *var, *cp;
1465 struct macro *mp;
1466 struct var **unroller;
1467 bool_t rv = TRU1;
1468 NYD_ENTER;
1470 var = ac_alloc(len = strlen(mailname) + sizeof("folder-hook-") -1 +1);
1472 /* First try the fully resolved path */
1473 snprintf(var, len, "folder-hook-%s", mailname);
1474 if ((cp = vok_vlook(var)) != NULL)
1475 goto jmac;
1477 /* If we are under *folder*, try the usual +NAME syntax, too */
1478 if (displayname[0] == '+') {
1479 char *x = mailname + len;
1481 for (; x > mailname; --x)
1482 if (x[-1] == '/') {
1483 snprintf(var, len, "folder-hook-+%s", x);
1484 if ((cp = vok_vlook(var)) != NULL)
1485 goto jmac;
1486 break;
1490 /* Plain *folder-hook* is our last try */
1491 if ((cp = ok_vlook(folder_hook)) == NULL)
1492 goto jleave;
1494 jmac:
1495 if ((mp = _ma_look(cp, NULL, MA_NONE)) == NULL) {
1496 fprintf(stderr, _("Cannot call *folder-hook* for \"%s\": "
1497 "macro \"%s\" does not exist.\n"), displayname, cp);
1498 rv = FAL0;
1499 goto jleave;
1502 pstate &= ~PS_HOOK_MASK;
1503 if (nmail) {
1504 pstate |= PS_HOOK_NEWMAIL;
1505 unroller = NULL;
1506 } else {
1507 pstate |= PS_HOOK;
1508 unroller = &_folder_hook_localopts;
1510 rv = (_ma_exec(mp, unroller) == 0);
1511 pstate &= ~PS_HOOK_MASK;
1513 jleave:
1514 ac_free(var);
1515 NYD_LEAVE;
1516 return rv;
1519 FL int
1520 c_account(void *v)
1522 char **args = v;
1523 struct macro *mp;
1524 int rv = 1, i, oqf, nqf;
1525 NYD_ENTER;
1527 if (args[0] == NULL) {
1528 rv = _ma_list(MA_ACC);
1529 goto jleave;
1532 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1533 if (args[2] != NULL) {
1534 fprintf(stderr, _("Syntax is: account <name> {\n"));
1535 goto jleave;
1537 if (!asccasecmp(args[0], ACCOUNT_NULL)) {
1538 fprintf(stderr, _("Error: \"%s\" is a reserved name.\n"),
1539 ACCOUNT_NULL);
1540 goto jleave;
1542 rv = !_ma_define(args[0], MA_ACC);
1543 goto jleave;
1546 if (pstate & PS_HOOK_MASK) {
1547 fprintf(stderr, _("Cannot change account from within a hook.\n"));
1548 goto jleave;
1551 save_mbox_for_possible_quitstuff();
1553 mp = NULL;
1554 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
1555 (mp = _ma_look(args[0], NULL, MA_ACC)) == NULL) {
1556 fprintf(stderr, _("Account \"%s\" does not exist.\n"), args[0]);
1557 goto jleave;
1560 oqf = savequitflags();
1562 if (_acc_curr != NULL) {
1563 if (_acc_curr->ma_localopts != NULL)
1564 _localopts_unroll(&_acc_curr->ma_localopts);
1565 if (_acc_curr->ma_flags & MA_DELETED) { /* xxx can be made generic? */
1566 _ma_freelines(_acc_curr->ma_contents);
1567 free(_acc_curr->ma_name);
1568 free(_acc_curr);
1572 account_name = (mp != NULL) ? mp->ma_name : NULL;
1573 _acc_curr = mp;
1575 if (mp != NULL && _ma_exec(mp, &mp->ma_localopts) == CBAD) {
1576 /* XXX account switch incomplete, unroll? */
1577 fprintf(stderr, _("Switching to account \"%s\" failed.\n"), args[0]);
1578 goto jleave;
1581 if ((pstate & (PS_STARTED | PS_HOOK_MASK)) == PS_STARTED) {
1582 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1583 restorequitflags(oqf);
1584 if ((i = setfile("%", 0)) < 0)
1585 goto jleave;
1586 check_folder_hook(FAL0);
1587 if (i > 0 && !ok_blook(emptystart))
1588 goto jleave;
1589 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1590 restorequitflags(nqf);
1592 rv = 0;
1593 jleave:
1594 NYD_LEAVE;
1595 return rv;
1598 FL int
1599 c_unaccount(void *v)
1601 int rv = 1;
1602 char **args = v;
1603 NYD_ENTER;
1605 if (*args == NULL) {
1606 fprintf(stderr, _("`unaccount': required arguments are missing\n"));
1607 goto jleave;
1610 _ma_undefine(*args, MA_ACC);
1611 while (*++args != NULL);
1612 rv = 0;
1613 jleave:
1614 NYD_LEAVE;
1615 return rv;
1618 FL int
1619 c_localopts(void *v)
1621 int rv = 1;
1622 char **c = v;
1623 NYD_ENTER;
1625 if (_localopts == NULL) {
1626 fprintf(stderr,
1627 _("Cannot use `localopts' but from within a `define' or `account'\n"));
1628 goto jleave;
1631 _localopts->s_unroll = (boolify(*c, UIZ_MAX, FAL0) > 0);
1632 rv = 0;
1633 jleave:
1634 NYD_LEAVE;
1635 return rv;
1638 FL void
1639 temporary_localopts_free(void) /* XXX intermediate hack */
1641 struct n2 {struct n2 *up; struct lostack *lo;} *x;
1642 NYD_ENTER;
1644 x = temporary_localopts_store;
1645 temporary_localopts_store = NULL;
1646 _localopts = NULL;
1648 while (x != NULL) {
1649 struct lostack *losp = x->lo;
1650 x = x->up;
1651 if (losp->s_localopts != NULL)
1652 _localopts_unroll(&losp->s_localopts);
1654 NYD_LEAVE;
1657 FL void
1658 temporary_localopts_folder_hook_unroll(void) /* XXX intermediate hack */
1660 NYD_ENTER;
1661 if (_folder_hook_localopts != NULL) {
1662 void *save = _localopts;
1663 _localopts = NULL;
1664 _localopts_unroll(&_folder_hook_localopts);
1665 _localopts = save;
1667 NYD_LEAVE;
1670 /* s-it-mode */