`localopts': handle recursive macro calls..
[s-mailx.git] / acmava.c
blob55f80c395d10869f12d28b7842075c5ee687849d
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 - 2014 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.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
45 * HOWTO add a new non-dynamic binary or value option:
46 * - add an entry to nail.h:enum okeys
47 * - run create-okey-map.pl
48 * - update nail.1
51 /* Note: changing the hash function must be reflected in create-okey-map.pl */
52 #define MA_PRIME HSHSIZE
53 #define MA_NAME2HASH(N) torek_hash(N)
54 #define MA_HASH2PRIME(H) ((H) % MA_PRIME)
56 enum ma_flags {
57 MA_NONE = 0,
58 MA_ACC = 1<<0,
59 MA_TYPE_MASK = MA_ACC,
60 MA_UNDEF = 1<<1 /* Unlink after lookup */
63 struct macro {
64 struct macro *ma_next;
65 char *ma_name;
66 struct mline *ma_contents;
67 size_t ma_maxlen; /* Maximum line length */
68 enum ma_flags ma_flags;
69 struct var *ma_localopts; /* `account' unroll list, for `localopts' */
72 struct mline {
73 struct mline *l_next;
74 size_t l_length;
75 char l_line[VFIELD_SIZE(sizeof(size_t))];
78 struct var {
79 struct var *v_link;
80 char *v_value;
81 char v_name[VFIELD_SIZE(sizeof(size_t))];
84 struct var_map {
85 ui32_t vm_hash;
86 ui16_t vm_keyoff;
87 ui8_t vm_binary;
88 ui8_t vm_special;
91 struct var_carrier {
92 char const *vc_name;
93 ui32_t vc_hash;
94 ui32_t vc_prime;
95 struct var *vc_var;
96 struct var_map const *vc_vmap;
97 enum okeys vc_okey;
100 struct lostack {
101 struct lostack *s_up; /* Outer context */
102 struct macro *s_mac; /* Context (`account' or `define') */
103 struct var *s_localopts;
104 bool_t s_unroll; /* Unroll? */
107 /* Include the constant create-okey-map.pl output */
108 #include "okeys.h"
110 static struct macro *_acc_curr; /* Currently active account */
111 static struct lostack *_localopts; /* Currently executing macro unroll list */
113 /* TODO once we have a dynamically sized hashtable we could unite _macros and
114 * TODO _variables into a single hashtable, stripping down fun interface;
115 * TODO also, setting and clearing a variable can be easily joined */
116 static struct var *_vars[MA_PRIME]; /* TODO dynamically spaced */
117 static struct macro *_macros[MA_PRIME]; /* TODO dynamically spaced */
119 /* Special cased value string allocation */
120 static char * _var_vcopy(char const *str);
121 static void _var_vfree(char *cp);
123 /* Check for special housekeeping. */
124 static bool_t _var_check_specials(enum okeys okey, bool_t enable,
125 char **val);
127 /* If a variable name begins with a lowercase-character and contains at
128 * least one '@', it is converted to all-lowercase. This is necessary
129 * for lookups of names based on email addresses.
130 * Following the standard, only the part following the last '@' should
131 * be lower-cased, but practice has established otherwise here.
132 * Return value may have been placed in string dope (salloc()) */
133 static char const * _var_canonify(char const *vn);
135 /* Try to reverse lookup an option name to an enum okeys mapping.
136 * Updates vcp.vc_name and vcp.vc_hash; vcp.vc_vmap is NULL if none found */
137 static bool_t _var_revlookup(struct var_carrier *vcp, char const *name);
139 /* Lookup a variable from vcp.vc_(vmap|name|hash), return wether it was found.
140 * Sets vcp.vc_prime; vcp.vc_var is NULL if not found */
141 static bool_t _var_lookup(struct var_carrier *vcp);
143 /* Set variable from vcp.vc_(vmap|name|hash), return wether error occurred */
144 static bool_t _var_set(struct var_carrier *vcp, char const *value);
146 /* Clear variable from vcp.vc_(vmap|name|hash); sets vcp.vc_var to NULL,
147 * return wether error occurred */
148 static bool_t _var_clear(struct var_carrier *vcp);
150 /* var_list_all(): qsort(3) helper */
151 static int _var_list_all_cmp(void const *s1, void const *s2);
153 /* Shared c_set() and c_setenv() impl, return wether error(s) occurred */
154 static bool_t _var_set_env(char **ap, bool_t issetenv);
156 /* Does cp consist solely of WS and a } */
157 static bool_t _is_closing_angle(char const *cp);
159 /* Lookup for macros/accounts */
160 static struct macro *_ma_look(char const *name, struct macro *data,
161 enum ma_flags mafl);
163 /* Walk all lines of a macro and execute() them */
164 static int _ma_exec(struct macro const *mp, struct var **unroller);
166 /* User display helpers */
167 static int _ma_list(enum ma_flags mafl);
169 /* */
170 static bool_t _ma_define1(char const *name, enum ma_flags mafl);
171 static void _ma_undef1(char const *name, enum ma_flags mafl);
172 static void _ma_freelines(struct mline *lp);
174 /* Update replay-log */
175 static void _localopts_add(struct lostack *losp, char const *name,
176 struct var *ovap);
177 static void _localopts_unroll(struct var **vapp);
179 static char *
180 _var_vcopy(char const *str)
182 char *news;
183 size_t len;
184 NYD_ENTER;
186 if (*str == '\0')
187 news = UNCONST("");
188 else {
189 len = strlen(str) +1;
190 news = smalloc(len);
191 memcpy(news, str, len);
193 NYD_LEAVE;
194 return news;
197 static void
198 _var_vfree(char *cp)
200 NYD_ENTER;
201 if (*cp != '\0')
202 free(cp);
203 NYD_LEAVE;
206 static bool_t
207 _var_check_specials(enum okeys okey, bool_t enable, char **val)
209 char *cp = NULL;
210 bool_t rv = TRU1;
211 int flag = 0;
212 NYD_ENTER;
214 switch (okey) {
215 case ok_b_debug:
216 flag = OPT_DEBUG;
217 break;
218 case ok_b_header:
219 flag = OPT_N_FLAG;
220 enable = !enable;
221 break;
222 case ok_b_skipemptybody:
223 flag = OPT_E_FLAG;
224 break;
225 case ok_b_verbose:
226 flag = (enable && !(options & OPT_VERB))
227 ? OPT_VERB : OPT_VERB | OPT_VERBVERB;
228 break;
229 case ok_v_folder:
230 rv = (val == NULL || var_folder_updated(*val, &cp));
231 if (rv && cp != NULL) {
232 _var_vfree(*val);
233 /* It's smalloc()ed, but ensure we don't leak */
234 if (*cp == '\0') {
235 *val = UNCONST("");
236 free(cp);
237 } else
238 *val = cp;
240 break;
241 #ifdef HAVE_NCL
242 case ok_v_line_editor_cursor_right:
243 if ((rv = (val != NULL && *val != NULL))) {
244 /* Set with no value? TODO very guly */
245 if (*(cp = *val) != '\0') {
246 char const *x = cp;
247 int c;
248 do {
249 c = expand_shell_escape(&x, FAL0);
250 if (c < 0)
251 break;
252 *cp++ = (char)c;
253 } while (*x != '\0');
254 *cp = '\0';
257 break;
258 #endif
259 default:
260 break;
263 if (flag) {
264 if (enable)
265 options |= flag;
266 else
267 options &= ~flag;
269 NYD_LEAVE;
270 return rv;
273 static char const *
274 _var_canonify(char const *vn)
276 NYD_ENTER;
277 if (!upperchar(*vn)) {
278 char const *vp;
280 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
282 vn = (*vp == '@') ? i_strdup(vn) : vn;
284 NYD_LEAVE;
285 return vn;
288 static bool_t
289 _var_revlookup(struct var_carrier *vcp, char const *name)
291 ui32_t hash, i, j;
292 struct var_map const *vmp;
293 NYD_ENTER;
295 vcp->vc_name = name = _var_canonify(name);
296 vcp->vc_hash = hash = MA_NAME2HASH(name);
298 for (i = hash % _VAR_REV_PRIME, j = 0; j <= _VAR_REV_LONGEST; ++j) {
299 ui32_t x = _var_revmap[i];
300 if (x == _VAR_REV_ILL)
301 break;
302 vmp = _var_map + x;
303 if (vmp->vm_hash == hash && !strcmp(_var_keydat + vmp->vm_keyoff, name)) {
304 vcp->vc_vmap = vmp;
305 vcp->vc_okey = (enum okeys)x;
306 goto jleave;
308 if (++i == _VAR_REV_PRIME) {
309 #ifdef _VAR_REV_WRAPAROUND
310 i = 0;
311 #else
312 break;
313 #endif
316 vcp->vc_vmap = NULL;
317 vcp = NULL;
318 jleave:
319 NYD_LEAVE;
320 return (vcp != NULL);
323 static bool_t
324 _var_lookup(struct var_carrier *vcp)
326 struct var **vap, *lvp, *vp;
327 NYD_ENTER;
329 vap = _vars + (vcp->vc_prime = MA_HASH2PRIME(vcp->vc_hash));
331 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
332 if (!strcmp(vp->v_name, vcp->vc_name)) {
333 /* Relink as head, hope it "sorts on usage" over time.
334 * _var_clear() relies on this behaviour */
335 if (lvp != NULL) {
336 lvp->v_link = vp->v_link;
337 vp->v_link = *vap;
338 *vap = vp;
340 goto jleave;
342 vp = NULL;
343 jleave:
344 vcp->vc_var = vp;
345 NYD_LEAVE;
346 return (vp != NULL);
349 static bool_t
350 _var_set(struct var_carrier *vcp, char const *value)
352 bool_t err;
353 struct var *vp;
354 char *oval;
355 NYD_ENTER;
357 if (value == NULL) {
358 bool_t tmp = var_clear_allow_undefined;
359 var_clear_allow_undefined = TRU1;
360 err = _var_clear(vcp);
361 var_clear_allow_undefined = tmp;
362 goto jleave;
365 _var_lookup(vcp);
367 /* Don't care what happens later on, store this in the unroll list */
368 if (_localopts != NULL)
369 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
371 if ((vp = vcp->vc_var) == NULL) {
372 size_t l = strlen(vcp->vc_name) + 1;
374 vcp->vc_var =
375 vp = smalloc(sizeof(*vp) - VFIELD_SIZEOF(struct var, v_name) + l);
376 vp->v_link = _vars[vcp->vc_prime];
377 _vars[vcp->vc_prime] = vp;
378 memcpy(vp->v_name, vcp->vc_name, l);
379 oval = UNCONST("");
380 } else
381 oval = vp->v_value;
382 vp->v_value = _var_vcopy(value);
384 /* Check if update allowed XXX wasteful on error! */
385 if (vcp->vc_vmap != NULL && vcp->vc_vmap->vm_special &&
386 (err = !_var_check_specials(vcp->vc_okey, TRU1, &vp->v_value))) {
387 char *cp = vp->v_value;
388 vp->v_value = oval;
389 oval = cp;
391 if (*oval != '\0')
392 _var_vfree(oval);
393 err = FAL0;
394 jleave:
395 NYD_LEAVE;
396 return err;
399 static bool_t
400 _var_clear(struct var_carrier *vcp)
402 bool_t err = TRU1;
403 NYD_ENTER;
405 if (!_var_lookup(vcp)) {
406 if (!sourcing && !var_clear_allow_undefined) {
407 fprintf(stderr, tr(203, "`%s': undefined variable\n"), vcp->vc_name);
408 goto jleave;
410 } else {
411 if (_localopts != NULL)
412 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
414 /* Always listhead after _var_lookup() */
415 _vars[vcp->vc_prime] = _vars[vcp->vc_prime]->v_link;
416 _var_vfree(vcp->vc_var->v_value);
417 free(vcp->vc_var);
418 vcp->vc_var = NULL;
420 if (vcp->vc_vmap != NULL && vcp->vc_vmap->vm_special &&
421 !_var_check_specials(vcp->vc_okey, FAL0, NULL))
422 goto jleave;
424 err = FAL0;
425 jleave:
426 NYD_LEAVE;
427 return err;
430 static int
431 _var_list_all_cmp(void const *s1, void const *s2)
433 int rv;
434 NYD_ENTER;
436 rv = strcmp(*(char**)UNCONST(s1), *(char**)UNCONST(s2));
437 NYD_LEAVE;
438 return rv;
441 static bool_t
442 _var_set_env(char **ap, bool_t issetenv)
444 char *cp, *cp2, *varbuf, c;
445 size_t errs = 0;
446 NYD_ENTER;
448 for (; *ap != NULL; ++ap) {
449 /* Isolate key */
450 cp = *ap;
451 cp2 = varbuf = ac_alloc(strlen(cp) +1);
452 for (; (c = *cp) != '=' && c != '\0'; ++cp)
453 *cp2++ = c;
454 *cp2 = '\0';
455 if (c == '\0')
456 cp = UNCONST("");
457 else
458 ++cp;
459 if (varbuf == cp2) {
460 fprintf(stderr, tr(41, "Non-null variable name required\n"));
461 ++errs;
462 goto jnext;
465 if (!issetenv && varbuf[0] == 'n' && varbuf[1] == 'o')
466 errs += _var_vokclear(varbuf + 2);
467 else {
468 errs += _var_vokset(varbuf, (uintptr_t)cp);
469 if (issetenv) {
470 #ifdef HAVE_SETENV
471 errs += (setenv(varbuf, cp, 1) != 0);
472 #else
473 ++errs;
474 #endif
477 jnext:
478 ac_free(varbuf);
481 NYD_LEAVE;
482 return (errs != 0);
485 static bool_t
486 _is_closing_angle(char const *cp)
488 bool_t rv = FAL0;
489 NYD_ENTER;
491 while (spacechar(*cp))
492 ++cp;
493 if (*cp++ != '}')
494 goto jleave;
495 while (spacechar(*cp))
496 ++cp;
497 rv = (*cp == '\0');
498 jleave:
499 NYD_LEAVE;
500 return rv;
503 static struct macro *
504 _ma_look(char const *name, struct macro *data, enum ma_flags mafl)
506 enum ma_flags save_mafl;
507 ui32_t h;
508 struct macro *lmp, *mp;
509 NYD_ENTER;
511 save_mafl = mafl;
512 mafl &= MA_TYPE_MASK;
513 h = MA_NAME2HASH(name);
514 h = MA_HASH2PRIME(h);
516 for (lmp = NULL, mp = _macros[h]; mp != NULL; lmp = mp, mp = mp->ma_next) {
517 if ((mp->ma_flags & MA_TYPE_MASK) == mafl && !strcmp(mp->ma_name, name)) {
518 if (save_mafl & MA_UNDEF) {
519 if (lmp == NULL)
520 _macros[h] = mp->ma_next;
521 else
522 lmp->ma_next = mp->ma_next;
524 goto jleave;
528 if (data != NULL) {
529 data->ma_next = _macros[h];
530 _macros[h] = data;
531 mp = NULL;
533 jleave:
534 NYD_LEAVE;
535 return mp;
538 static int
539 _ma_exec(struct macro const *mp, struct var **unroller)
541 struct lostack los;
542 int rv = 0;
543 char *buf;
544 struct mline const *lp;
545 NYD_ENTER;
547 los.s_up = _localopts;
548 los.s_mac = UNCONST(mp);
549 los.s_localopts = NULL;
550 los.s_unroll = FAL0;
551 _localopts = &los;
553 buf = ac_alloc(mp->ma_maxlen +1);
554 for (lp = mp->ma_contents; lp; lp = lp->l_next) {
555 var_clear_allow_undefined = TRU1;
556 memcpy(buf, lp->l_line, lp->l_length +1);
558 struct _n2 {
559 struct _n2 *up;
560 struct lostack *lo;
561 } *x = salloc(sizeof *x); /* FIXME intermediate hack (signal man+) */
562 x->up = temporary_localopts_store;
563 x->lo = _localopts;
564 temporary_localopts_store = x;
565 rv |= execute(buf, 0, lp->l_length); /* XXX break if != 0 ? */
566 temporary_localopts_store = x->up; /* FIXME intermediate hack */
568 var_clear_allow_undefined = FAL0;
570 ac_free(buf);
572 _localopts = los.s_up;
573 if (unroller == NULL) {
574 if (los.s_localopts != NULL)
575 _localopts_unroll(&los.s_localopts);
576 } else
577 *unroller = los.s_localopts;
578 NYD_LEAVE;
579 return rv;
582 static int
583 _ma_list(enum ma_flags mafl)
585 FILE *fp;
586 char const *typestr;
587 struct macro *mq;
588 ui32_t ti, mc;
589 struct mline *lp;
590 NYD_ENTER;
592 if ((fp = Ftmp(NULL, "listmacs", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
593 NULL) {
594 perror("tmpfile");
595 mc = 1;
596 goto jleave;
599 mafl &= MA_TYPE_MASK;
600 typestr = (mafl & MA_ACC) ? "account" : "define";
602 for (ti = mc = 0; ti < MA_PRIME; ++ti)
603 for (mq = _macros[ti]; mq; mq = mq->ma_next)
604 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
605 if (++mc > 1)
606 fputc('\n', fp);
607 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
608 for (lp = mq->ma_contents; lp != NULL; lp = lp->l_next)
609 fprintf(fp, " %s\n", lp->l_line);
610 fputs("}\n", fp);
612 if (mc)
613 page_or_print(fp, 0);
615 mc = (ui32_t)ferror(fp);
616 Fclose(fp);
617 jleave:
618 NYD_LEAVE;
619 return (int)mc;
622 static bool_t
623 _ma_define1(char const *name, enum ma_flags mafl)
625 bool_t rv = FAL0;
626 struct macro *mp;
627 struct mline *lp, *lst = NULL, *lnd = NULL;
628 char *linebuf = NULL, *cp;
629 size_t linesize = 0, maxlen = 0;
630 int n, i;
631 NYD_ENTER;
633 mp = scalloc(1, sizeof *mp);
634 mp->ma_name = sstrdup(name);
635 mp->ma_flags = mafl;
637 for (;;) {
638 n = readline_input("", TRU1, &linebuf, &linesize, NULL);
639 if (n == 0)
640 continue;
641 if (n < 0) {
642 fprintf(stderr, tr(75, "Unterminated %s definition: \"%s\".\n"),
643 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
644 if (sourcing && !loading)
645 unstack();
646 goto jerr;
648 if (_is_closing_angle(linebuf))
649 break;
651 /* Trim WS */
652 for (cp = linebuf, i = 0; i < n; ++cp, ++i)
653 if (!whitechar(*cp))
654 break;
655 if (i == n)
656 continue;
657 n -= i;
658 while (whitechar(cp[n - 1]))
659 if (--n == 0)
660 break;
661 if (n == 0)
662 continue;
664 maxlen = MAX(maxlen, (size_t)n);
665 cp[n++] = '\0';
667 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n);
668 memcpy(lp->l_line, cp, n);
669 lp->l_length = (size_t)--n;
670 if (lst != NULL) {
671 lnd->l_next = lp;
672 lnd = lp;
673 } else
674 lst = lnd = lp;
676 mp->ma_contents = lst;
677 mp->ma_maxlen = maxlen;
679 if (_ma_look(mp->ma_name, mp, mafl) != NULL) {
680 fprintf(stderr, tr(76, "A %s named `%s' already exists.\n"),
681 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
682 lst = mp->ma_contents;
683 goto jerr;
686 rv = TRU1;
687 jleave:
688 if (linebuf != NULL)
689 free(linebuf);
690 NYD_LEAVE;
691 return rv;
693 jerr:
694 if (lst != NULL)
695 _ma_freelines(lst);
696 free(mp->ma_name);
697 free(mp);
698 goto jleave;
701 static void
702 _ma_undef1(char const *name, enum ma_flags mafl)
704 struct macro *mp;
705 NYD_ENTER;
707 if ((mp = _ma_look(name, NULL, mafl | MA_UNDEF)) != NULL) {
708 _ma_freelines(mp->ma_contents);
709 free(mp->ma_name);
710 free(mp);
711 } else
712 fprintf(stderr, tr(571, "%s `%s' is not defined\n"),
713 (mafl & MA_ACC ? "Account" : "Macro"), name);
714 NYD_LEAVE;
717 static void
718 _ma_freelines(struct mline *lp)
720 struct mline *lq;
721 NYD_ENTER;
723 for (lq = NULL; lp != NULL; ) {
724 if (lq != NULL)
725 free(lq);
726 lq = lp;
727 lp = lp->l_next;
729 if (lq)
730 free(lq);
731 NYD_LEAVE;
734 static void
735 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
737 struct var *vap;
738 size_t nl, vl;
739 NYD_ENTER;
741 /* Propagate unrolling up the stack, as necessary */
742 while (!losp->s_unroll && (losp = losp->s_up) != NULL)
744 if (losp == NULL)
745 goto jleave;
747 /* We've found a level that wants to unroll; check wether it does it yet */
748 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
749 if (!strcmp(vap->v_name, name))
750 goto jleave;
752 nl = strlen(name) + 1;
753 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
754 vap = smalloc(sizeof(*vap) - VFIELD_SIZEOF(struct var, v_name) + nl + vl);
755 vap->v_link = losp->s_localopts;
756 losp->s_localopts = vap;
757 memcpy(vap->v_name, name, nl);
758 if (vl == 0)
759 vap->v_value = NULL;
760 else {
761 vap->v_value = vap->v_name + nl;
762 memcpy(vap->v_value, ovap->v_value, vl);
764 jleave:
765 NYD_LEAVE;
768 static void
769 _localopts_unroll(struct var **vapp)
771 struct lostack *save_los;
772 struct var *x, *vap;
773 NYD_ENTER;
775 vap = *vapp;
776 *vapp = NULL;
778 save_los = _localopts;
779 _localopts = NULL;
780 while (vap != NULL) {
781 x = vap;
782 vap = vap->v_link;
783 vok_vset(x->v_name, x->v_value);
784 free(x);
786 _localopts = save_los;
787 NYD_LEAVE;
790 FL char *
791 _var_oklook(enum okeys okey)
793 struct var_carrier vc;
794 char *rv;
795 NYD_ENTER;
797 vc.vc_vmap = _var_map + okey;
798 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
799 vc.vc_hash = _var_map[okey].vm_hash;
800 vc.vc_okey = okey;
802 if (!_var_lookup(&vc)) {
803 if ((rv = getenv(vc.vc_name)) != NULL) {
804 _var_set(&vc, rv);
805 assert(vc.vc_var != NULL);
806 goto jvar;
808 } else
809 jvar:
810 rv = vc.vc_var->v_value;
811 NYD_LEAVE;
812 return rv;
815 FL bool_t
816 _var_okset(enum okeys okey, uintptr_t val)
818 struct var_carrier vc;
819 bool_t rv;
820 NYD_ENTER;
822 vc.vc_vmap = _var_map + okey;
823 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
824 vc.vc_hash = _var_map[okey].vm_hash;
825 vc.vc_okey = okey;
827 rv = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
828 NYD_LEAVE;
829 return rv;
832 FL bool_t
833 _var_okclear(enum okeys okey)
835 struct var_carrier vc;
836 bool_t rv;
837 NYD_ENTER;
839 vc.vc_vmap = _var_map + okey;
840 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
841 vc.vc_hash = _var_map[okey].vm_hash;
842 vc.vc_okey = okey;
844 rv = _var_clear(&vc);
845 NYD_LEAVE;
846 return rv;
849 FL char *
850 _var_voklook(char const *vokey)
852 struct var_carrier vc;
853 char *rv;
854 NYD_ENTER;
856 _var_revlookup(&vc, vokey);
858 if (!_var_lookup(&vc)) {
859 if ((rv = getenv(vc.vc_name)) != NULL) {
860 _var_set(&vc, rv);
861 assert(vc.vc_var != NULL);
862 goto jvar;
864 } else
865 jvar:
866 rv = vc.vc_var->v_value;
867 NYD_LEAVE;
868 return rv;
871 FL bool_t
872 _var_vokset(char const *vokey, uintptr_t val)
874 struct var_carrier vc;
875 bool_t err;
876 NYD_ENTER;
878 _var_revlookup(&vc, vokey);
880 err = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
881 NYD_LEAVE;
882 return err;
885 FL bool_t
886 _var_vokclear(char const *vokey)
888 struct var_carrier vc;
889 bool_t err;
890 NYD_ENTER;
892 _var_revlookup(&vc, vokey);
894 err = _var_clear(&vc);
895 NYD_LEAVE;
896 return err;
899 FL void
900 var_list_all(void)
902 FILE *fp;
903 char **vacp, **cap;
904 struct var *vp;
905 size_t no, i;
906 char const *fmt;
907 NYD_ENTER;
909 if ((fp = Ftmp(NULL, "listvars", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
910 NULL) {
911 perror("tmpfile");
912 goto jleave;
915 for (no = i = 0; i < MA_PRIME; ++i)
916 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
917 ++no;
918 vacp = salloc(no * sizeof(*vacp));
919 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
920 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
921 *cap++ = vp->v_name;
923 if (no > 1)
924 qsort(vacp, no, sizeof *vacp, &_var_list_all_cmp);
926 i = (ok_blook(bsdcompat) || ok_blook(bsdset));
927 fmt = (i != 0) ? "%s\t%s\n" : "%s=\"%s\"\n";
929 for (cap = vacp; no != 0; ++cap, --no) {
930 char *cp = vok_vlook(*cap); /* XXX when lookup checks val/bin, change */
931 if (cp == NULL)
932 cp = UNCONST("");
933 if (i || *cp != '\0')
934 fprintf(fp, fmt, *cap, cp);
935 else
936 fprintf(fp, "%s\n", *cap);
939 page_or_print(fp, PTR2SIZE(cap - vacp));
940 Fclose(fp);
941 jleave:
942 NYD_LEAVE;
945 FL int
946 c_varshow(void *v)
948 struct var_carrier vc;
949 char **argv = v, *val;
950 bool_t isenv, isset;
951 NYD_ENTER;
953 if (*argv == NULL) {
954 v = NULL;
955 goto jleave;
958 for (; *argv != NULL; ++argv) {
959 memset(&vc, 0, sizeof vc);
960 _var_revlookup(&vc, *argv);
961 if (_var_lookup(&vc)) {
962 val = vc.vc_var->v_value;
963 isenv = FAL0;
964 } else
965 isenv = ((val = getenv(vc.vc_name)) != NULL);
966 if (val == NULL)
967 val = UNCONST("NULL");
968 isset = (vc.vc_var != NULL);
970 if (vc.vc_vmap != NULL) {
971 if (vc.vc_vmap->vm_binary)
972 printf("%s: binary option (%d): isset=%d/environ=%d\n",
973 vc.vc_name, vc.vc_okey, isset, isenv);
974 else
975 printf("%s: value option (%d): isset=%d/environ=%d value<%s>\n",
976 vc.vc_name, vc.vc_okey, isset, isenv, val);
977 } else
978 printf("%s: isset=%d/environ=%d value<%s>\n",
979 vc.vc_name, isset, isenv, val);
981 jleave:
982 NYD_LEAVE;
983 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
986 FL int
987 c_set(void *v)
989 char **ap = v;
990 int err;
991 NYD_ENTER;
993 if (*ap == NULL) {
994 var_list_all();
995 err = 0;
996 } else
997 err = _var_set_env(ap, FAL0);
998 NYD_LEAVE;
999 return err;
1002 FL int
1003 c_setenv(void *v)
1005 char **ap = v;
1006 int err;
1007 NYD_ENTER;
1009 if (!(err = starting))
1010 err = _var_set_env(ap, TRU1);
1011 NYD_LEAVE;
1012 return err;
1015 FL int
1016 c_unset(void *v)
1018 char **ap = v;
1019 int err = 0;
1020 NYD_ENTER;
1022 while (*ap != NULL)
1023 err |= _var_vokclear(*ap++);
1024 NYD_LEAVE;
1025 return err;
1028 FL int
1029 c_unsetenv(void *v)
1031 int err;
1032 NYD_ENTER;
1034 if (!(err = starting)) {
1035 char **ap;
1036 bool_t tmp = var_clear_allow_undefined;
1037 var_clear_allow_undefined = TRU1;
1038 for (ap = v; *ap != NULL; ++ap) {
1039 bool_t bad = _var_vokclear(*ap);
1040 if (
1041 #ifdef HAVE_SETENV
1042 unsetenv(*ap) != 0 &&
1043 #endif
1046 err = 1;
1048 var_clear_allow_undefined = tmp;
1050 NYD_LEAVE;
1051 return err;
1054 FL int
1055 c_define(void *v)
1057 int rv = 1;
1058 char **args = v;
1059 NYD_ENTER;
1061 if (args[0] == NULL) {
1062 rv = _ma_list(MA_NONE);
1063 goto jleave;
1066 if (args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
1067 args[2] != NULL) {
1068 fprintf(stderr, tr(505, "Syntax is: define <name> {"));
1069 goto jleave;
1072 rv = !_ma_define1(args[0], MA_NONE);
1073 jleave:
1074 NYD_LEAVE;
1075 return rv;
1078 FL int
1079 c_undefine(void *v)
1081 int rv = 1;
1082 char **args = v;
1083 NYD_ENTER;
1085 if (*args == NULL) {
1086 fprintf(stderr, tr(504, "`%s': required arguments are missing\n"),
1087 "undefine");
1088 goto jleave;
1091 _ma_undef1(*args, MA_NONE);
1092 while (*++args != NULL);
1093 rv = 0;
1094 jleave:
1095 NYD_LEAVE;
1096 return rv;
1099 FL int
1100 c_call(void *v)
1102 int rv = 1;
1103 char **args = v;
1104 char const *errs, *name;
1105 struct macro *mp;
1106 NYD_ENTER;
1108 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
1109 errs = tr(507, "Syntax is: call <%s>\n");
1110 name = "name";
1111 goto jerr;
1114 if ((mp = _ma_look(*args, NULL, MA_NONE)) == NULL) {
1115 errs = tr(508, "Undefined macro called: `%s'\n");
1116 name = *args;
1117 goto jerr;
1120 rv = _ma_exec(mp, NULL);
1121 jleave:
1122 NYD_LEAVE;
1123 return rv;
1124 jerr:
1125 fprintf(stderr, errs, name);
1126 goto jleave;
1129 FL int
1130 callhook(char const *name, int nmail)
1132 int len, rv;
1133 struct macro *mp;
1134 char *var, *cp;
1135 NYD_ENTER;
1137 var = ac_alloc(len = strlen(name) + 12 +1);
1138 snprintf(var, len, "folder-hook-%s", name);
1139 if ((cp = vok_vlook(var)) == NULL && (cp = ok_vlook(folder_hook)) == NULL) {
1140 rv = 0;
1141 goto jleave;
1143 if ((mp = _ma_look(cp, NULL, MA_NONE)) == NULL) {
1144 fprintf(stderr, tr(49, "Cannot call hook for folder \"%s\": "
1145 "Macro \"%s\" does not exist.\n"), name, cp);
1146 rv = 1;
1147 goto jleave;
1150 inhook = nmail ? 3 : 1; /* XXX enum state machine */
1151 rv = _ma_exec(mp, NULL);
1152 inhook = 0;
1153 jleave:
1154 ac_free(var);
1155 NYD_LEAVE;
1156 return rv;
1159 FL int
1160 c_account(void *v)
1162 char **args = v;
1163 struct macro *mp;
1164 int rv = 1, i, oqf, nqf;
1165 NYD_ENTER;
1167 if (args[0] == NULL) {
1168 rv = _ma_list(MA_ACC);
1169 goto jleave;
1172 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1173 if (args[2] != NULL) {
1174 fprintf(stderr, tr(517, "Syntax is: account <name> {\n"));
1175 goto jleave;
1177 if (!asccasecmp(args[0], ACCOUNT_NULL)) {
1178 fprintf(stderr, tr(521, "Error: `%s' is a reserved name.\n"),
1179 ACCOUNT_NULL);
1180 goto jleave;
1182 rv = !_ma_define1(args[0], MA_ACC);
1183 goto jleave;
1186 if (inhook) {
1187 fprintf(stderr, tr(518, "Cannot change account from within a hook.\n"));
1188 goto jleave;
1191 save_mbox_for_possible_quitstuff();
1193 mp = NULL;
1194 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
1195 (mp = _ma_look(args[0], NULL, MA_ACC)) == NULL) {
1196 fprintf(stderr, tr(519, "Account `%s' does not exist.\n"), args[0]);
1197 goto jleave;
1200 oqf = savequitflags();
1201 if (_acc_curr != NULL && _acc_curr->ma_localopts != NULL)
1202 _localopts_unroll(&_acc_curr->ma_localopts);
1203 account_name = (mp != NULL) ? mp->ma_name : NULL;
1204 _acc_curr = mp;
1206 if (mp != NULL && _ma_exec(mp, &mp->ma_localopts) == CBAD) {
1207 /* XXX account switch incomplete, unroll? */
1208 fprintf(stderr, tr(520, "Switching to account `%s' failed.\n"), args[0]);
1209 goto jleave;
1212 if (!starting && !inhook) {
1213 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1214 restorequitflags(oqf);
1215 if ((i = setfile("%", 0)) < 0)
1216 goto jleave;
1217 callhook(mailname, 0);
1218 if (i > 0 && !ok_blook(emptystart))
1219 goto jleave;
1220 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1221 restorequitflags(nqf);
1223 rv = 0;
1224 jleave:
1225 NYD_LEAVE;
1226 return rv;
1229 FL int
1230 c_unaccount(void *v)
1232 int rv = 1;
1233 char **args = v;
1234 NYD_ENTER;
1236 if (*args == NULL) {
1237 fprintf(stderr, tr(504, "`%s': required arguments are missing\n"),
1238 "unaccount");
1239 goto jleave;
1242 rv = 0;
1243 do {
1244 if (account_name != NULL && !strcmp(account_name, *args)) {
1245 fprintf(stderr, tr(506,
1246 "Rejecting deletion of currently active account `%s'\n"), *args);
1247 continue;
1249 _ma_undef1(*args, MA_ACC);
1250 } while (*++args != NULL);
1251 jleave:
1252 NYD_LEAVE;
1253 return rv;
1256 FL int
1257 c_localopts(void *v)
1259 int rv = 1;
1260 char **c = v;
1261 NYD_ENTER;
1263 if (_localopts == NULL) {
1264 fprintf(stderr, tr(522,
1265 "Cannot use `localopts' but from within a `define' or `account'\n"));
1266 goto jleave;
1269 _localopts->s_unroll = (**c == '0') ? FAL0 : TRU1;
1270 rv = 0;
1271 jleave:
1272 NYD_LEAVE;
1273 return rv;
1276 FL void
1277 temporary_localopts_free(void) /* XXX intermediate hack */
1279 struct _n2 {
1280 struct _n2 *up;
1281 struct lostack *lo;
1282 } *x;
1283 NYD_ENTER;
1285 var_clear_allow_undefined = FAL0;
1286 x = temporary_localopts_store;
1287 temporary_localopts_store = NULL;
1288 _localopts = NULL;
1290 while (x != NULL) {
1291 struct lostack *losp = x->lo;
1292 x = x->up;
1293 _localopts_unroll(&losp->s_localopts);
1295 NYD_LEAVE;
1298 /* vim:set fenc=utf-8:s-it-mode */