run_editor(): also compare size when deciding "has changed"
[s-mailx.git] / accmacvar.c
blobd45ad008c90b37d1f203afc920f45909542bc079
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 vcau_save = var_clear_allow_undefined;
359 var_clear_allow_undefined = TRU1;
360 err = _var_clear(vcp);
361 var_clear_allow_undefined = vcau_save;
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;
383 if (vcp->vc_vmap == NULL)
384 vp->v_value = _var_vcopy(value);
385 else {
386 /* Via `set' etc. the user may give even binary options non-binary
387 * values, ignore that and force binary xxx error log? */
388 if (vcp->vc_vmap->vm_binary)
389 value = UNCONST("");
390 vp->v_value = _var_vcopy(value);
392 /* Check if update allowed XXX wasteful on error! */
393 if (vcp->vc_vmap->vm_special &&
394 (err = !_var_check_specials(vcp->vc_okey, TRU1, &vp->v_value))) {
395 char *cp = vp->v_value;
396 vp->v_value = oval;
397 oval = cp;
401 if (*oval != '\0')
402 _var_vfree(oval);
403 err = FAL0;
404 jleave:
405 NYD_LEAVE;
406 return err;
409 static bool_t
410 _var_clear(struct var_carrier *vcp)
412 bool_t err = TRU1;
413 NYD_ENTER;
415 if (!_var_lookup(vcp)) {
416 if (!sourcing && !var_clear_allow_undefined) {
417 fprintf(stderr, _("`%s': undefined variable\n"), vcp->vc_name);
418 goto jleave;
420 } else {
421 if (_localopts != NULL)
422 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
424 /* Always listhead after _var_lookup() */
425 _vars[vcp->vc_prime] = _vars[vcp->vc_prime]->v_link;
426 _var_vfree(vcp->vc_var->v_value);
427 free(vcp->vc_var);
428 vcp->vc_var = NULL;
430 if (vcp->vc_vmap != NULL && vcp->vc_vmap->vm_special &&
431 !_var_check_specials(vcp->vc_okey, FAL0, NULL))
432 goto jleave;
434 err = FAL0;
435 jleave:
436 NYD_LEAVE;
437 return err;
440 static int
441 _var_list_all_cmp(void const *s1, void const *s2)
443 int rv;
444 NYD_ENTER;
446 rv = strcmp(*(char**)UNCONST(s1), *(char**)UNCONST(s2));
447 NYD_LEAVE;
448 return rv;
451 static bool_t
452 _var_set_env(char **ap, bool_t issetenv)
454 char *cp, *cp2, *varbuf, c;
455 size_t errs = 0;
456 NYD_ENTER;
458 for (; *ap != NULL; ++ap) {
459 /* Isolate key */
460 cp = *ap;
461 cp2 = varbuf = ac_alloc(strlen(cp) +1);
462 for (; (c = *cp) != '=' && c != '\0'; ++cp)
463 *cp2++ = c;
464 *cp2 = '\0';
465 if (c == '\0')
466 cp = UNCONST("");
467 else
468 ++cp;
469 if (varbuf == cp2) {
470 fprintf(stderr, _("Non-null variable name required\n"));
471 ++errs;
472 goto jnext;
475 if (!issetenv && varbuf[0] == 'n' && varbuf[1] == 'o')
476 errs += _var_vokclear(varbuf + 2);
477 else {
478 errs += _var_vokset(varbuf, (uintptr_t)cp);
479 if (issetenv) {
480 #ifdef HAVE_SETENV
481 errs += (setenv(varbuf, cp, 1) != 0);
482 #else
483 ++errs;
484 #endif
487 jnext:
488 ac_free(varbuf);
491 NYD_LEAVE;
492 return (errs != 0);
495 static bool_t
496 _is_closing_angle(char const *cp)
498 bool_t rv = FAL0;
499 NYD_ENTER;
501 while (spacechar(*cp))
502 ++cp;
503 if (*cp++ != '}')
504 goto jleave;
505 while (spacechar(*cp))
506 ++cp;
507 rv = (*cp == '\0');
508 jleave:
509 NYD_LEAVE;
510 return rv;
513 static struct macro *
514 _ma_look(char const *name, struct macro *data, enum ma_flags mafl)
516 enum ma_flags save_mafl;
517 ui32_t h;
518 struct macro *lmp, *mp;
519 NYD_ENTER;
521 save_mafl = mafl;
522 mafl &= MA_TYPE_MASK;
523 h = MA_NAME2HASH(name);
524 h = MA_HASH2PRIME(h);
526 for (lmp = NULL, mp = _macros[h]; mp != NULL; lmp = mp, mp = mp->ma_next) {
527 if ((mp->ma_flags & MA_TYPE_MASK) == mafl && !strcmp(mp->ma_name, name)) {
528 if (save_mafl & MA_UNDEF) {
529 if (lmp == NULL)
530 _macros[h] = mp->ma_next;
531 else
532 lmp->ma_next = mp->ma_next;
534 goto jleave;
538 if (data != NULL) {
539 data->ma_next = _macros[h];
540 _macros[h] = data;
541 mp = NULL;
543 jleave:
544 NYD_LEAVE;
545 return mp;
548 static int
549 _ma_exec(struct macro const *mp, struct var **unroller)
551 struct lostack los;
552 bool_t vcau_save;
553 char *buf;
554 struct n2 {struct n2 *up; struct lostack *lo;} *x; /* FIXME hack (sigman+) */
555 struct mline const *lp;
556 int rv = 0;
557 NYD_ENTER;
559 los.s_up = _localopts;
560 los.s_mac = UNCONST(mp);
561 los.s_localopts = NULL;
562 los.s_unroll = FAL0;
563 _localopts = &los;
565 vcau_save = var_clear_allow_undefined;
566 var_clear_allow_undefined = TRU1;
568 x = salloc(sizeof *x); /* FIXME intermediate hack (signal man+) */
569 x->up = temporary_localopts_store;
570 x->lo = _localopts;
571 temporary_localopts_store = x;
573 buf = ac_alloc(mp->ma_maxlen +1);
574 for (lp = mp->ma_contents; lp; lp = lp->l_next) {
575 memcpy(buf, lp->l_line, lp->l_length +1);
576 rv |= execute(buf, 0, lp->l_length); /* XXX break if != 0 ? */
578 ac_free(buf);
580 temporary_localopts_store = x->up; /* FIXME intermediate hack */
581 var_clear_allow_undefined = vcau_save;
583 _localopts = los.s_up;
584 if (unroller == NULL) {
585 if (los.s_localopts != NULL)
586 _localopts_unroll(&los.s_localopts);
587 } else
588 *unroller = los.s_localopts;
589 NYD_LEAVE;
590 return rv;
593 static int
594 _ma_list(enum ma_flags mafl)
596 FILE *fp;
597 char const *typestr;
598 struct macro *mq;
599 ui32_t ti, mc;
600 struct mline *lp;
601 NYD_ENTER;
603 if ((fp = Ftmp(NULL, "listmacs", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
604 NULL) {
605 perror("tmpfile");
606 mc = 1;
607 goto jleave;
610 mafl &= MA_TYPE_MASK;
611 typestr = (mafl & MA_ACC) ? "account" : "define";
613 for (ti = mc = 0; ti < MA_PRIME; ++ti)
614 for (mq = _macros[ti]; mq; mq = mq->ma_next)
615 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
616 if (++mc > 1)
617 fputc('\n', fp);
618 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
619 for (lp = mq->ma_contents; lp != NULL; lp = lp->l_next)
620 fprintf(fp, " %s\n", lp->l_line);
621 fputs("}\n", fp);
623 if (mc)
624 page_or_print(fp, 0);
626 mc = (ui32_t)ferror(fp);
627 Fclose(fp);
628 jleave:
629 NYD_LEAVE;
630 return (int)mc;
633 static bool_t
634 _ma_define1(char const *name, enum ma_flags mafl)
636 bool_t rv = FAL0;
637 struct macro *mp;
638 struct mline *lp, *lst = NULL, *lnd = NULL;
639 char *linebuf = NULL, *cp;
640 size_t linesize = 0, maxlen = 0;
641 int n, i;
642 NYD_ENTER;
644 mp = scalloc(1, sizeof *mp);
645 mp->ma_name = sstrdup(name);
646 mp->ma_flags = mafl;
648 for (;;) {
649 n = readline_input("", TRU1, &linebuf, &linesize, NULL);
650 if (n == 0)
651 continue;
652 if (n < 0) {
653 fprintf(stderr, _("Unterminated %s definition: \"%s\".\n"),
654 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
655 if (sourcing && !loading)
656 unstack();
657 goto jerr;
659 if (_is_closing_angle(linebuf))
660 break;
662 /* Trim WS */
663 for (cp = linebuf, i = 0; i < n; ++cp, ++i)
664 if (!whitechar(*cp))
665 break;
666 if (i == n)
667 continue;
668 n -= i;
669 while (whitechar(cp[n - 1]))
670 if (--n == 0)
671 break;
672 if (n == 0)
673 continue;
675 maxlen = MAX(maxlen, (size_t)n);
676 cp[n++] = '\0';
678 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n);
679 memcpy(lp->l_line, cp, n);
680 lp->l_length = (size_t)--n;
681 if (lst != NULL) {
682 lnd->l_next = lp;
683 lnd = lp;
684 } else
685 lst = lnd = lp;
687 mp->ma_contents = lst;
688 mp->ma_maxlen = maxlen;
690 if (_ma_look(mp->ma_name, mp, mafl) != NULL) {
691 fprintf(stderr, _("A %s named `%s' already exists.\n"),
692 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
693 lst = mp->ma_contents;
694 goto jerr;
697 rv = TRU1;
698 jleave:
699 if (linebuf != NULL)
700 free(linebuf);
701 NYD_LEAVE;
702 return rv;
704 jerr:
705 if (lst != NULL)
706 _ma_freelines(lst);
707 free(mp->ma_name);
708 free(mp);
709 goto jleave;
712 static void
713 _ma_undef1(char const *name, enum ma_flags mafl)
715 struct macro *mp;
716 NYD_ENTER;
718 if ((mp = _ma_look(name, NULL, mafl | MA_UNDEF)) != NULL) {
719 _ma_freelines(mp->ma_contents);
720 free(mp->ma_name);
721 free(mp);
722 } else
723 fprintf(stderr, _("%s `%s' is not defined\n"),
724 (mafl & MA_ACC ? "Account" : "Macro"), name);
725 NYD_LEAVE;
728 static void
729 _ma_freelines(struct mline *lp)
731 struct mline *lq;
732 NYD_ENTER;
734 for (lq = NULL; lp != NULL; ) {
735 if (lq != NULL)
736 free(lq);
737 lq = lp;
738 lp = lp->l_next;
740 if (lq)
741 free(lq);
742 NYD_LEAVE;
745 static void
746 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
748 struct var *vap;
749 size_t nl, vl;
750 NYD_ENTER;
752 /* Propagate unrolling up the stack, as necessary */
753 while (!losp->s_unroll && (losp = losp->s_up) != NULL)
755 if (losp == NULL)
756 goto jleave;
758 /* We've found a level that wants to unroll; check wether it does it yet */
759 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
760 if (!strcmp(vap->v_name, name))
761 goto jleave;
763 nl = strlen(name) + 1;
764 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
765 vap = smalloc(sizeof(*vap) - VFIELD_SIZEOF(struct var, v_name) + nl + vl);
766 vap->v_link = losp->s_localopts;
767 losp->s_localopts = vap;
768 memcpy(vap->v_name, name, nl);
769 if (vl == 0)
770 vap->v_value = NULL;
771 else {
772 vap->v_value = vap->v_name + nl;
773 memcpy(vap->v_value, ovap->v_value, vl);
775 jleave:
776 NYD_LEAVE;
779 static void
780 _localopts_unroll(struct var **vapp)
782 struct lostack *save_los;
783 struct var *x, *vap;
784 NYD_ENTER;
786 vap = *vapp;
787 *vapp = NULL;
789 save_los = _localopts;
790 _localopts = NULL;
791 while (vap != NULL) {
792 x = vap;
793 vap = vap->v_link;
794 vok_vset(x->v_name, x->v_value);
795 free(x);
797 _localopts = save_los;
798 NYD_LEAVE;
801 FL char *
802 _var_oklook(enum okeys okey)
804 struct var_carrier vc;
805 char *rv;
806 NYD_ENTER;
808 vc.vc_vmap = _var_map + okey;
809 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
810 vc.vc_hash = _var_map[okey].vm_hash;
811 vc.vc_okey = okey;
813 if (!_var_lookup(&vc)) {
814 if ((rv = getenv(vc.vc_name)) != NULL) {
815 _var_set(&vc, rv);
816 assert(vc.vc_var != NULL);
817 goto jvar;
819 } else
820 jvar:
821 rv = vc.vc_var->v_value;
822 NYD_LEAVE;
823 return rv;
826 FL bool_t
827 _var_okset(enum okeys okey, uintptr_t val)
829 struct var_carrier vc;
830 bool_t rv;
831 NYD_ENTER;
833 vc.vc_vmap = _var_map + okey;
834 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
835 vc.vc_hash = _var_map[okey].vm_hash;
836 vc.vc_okey = okey;
838 rv = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
839 NYD_LEAVE;
840 return rv;
843 FL bool_t
844 _var_okclear(enum okeys okey)
846 struct var_carrier vc;
847 bool_t rv;
848 NYD_ENTER;
850 vc.vc_vmap = _var_map + okey;
851 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
852 vc.vc_hash = _var_map[okey].vm_hash;
853 vc.vc_okey = okey;
855 rv = _var_clear(&vc);
856 NYD_LEAVE;
857 return rv;
860 FL char *
861 _var_voklook(char const *vokey)
863 struct var_carrier vc;
864 char *rv;
865 NYD_ENTER;
867 _var_revlookup(&vc, vokey);
869 if (!_var_lookup(&vc)) {
870 if ((rv = getenv(vc.vc_name)) != NULL) {
871 _var_set(&vc, rv);
872 assert(vc.vc_var != NULL);
873 goto jvar;
875 } else
876 jvar:
877 rv = vc.vc_var->v_value;
878 NYD_LEAVE;
879 return rv;
882 FL bool_t
883 _var_vokset(char const *vokey, uintptr_t val)
885 struct var_carrier vc;
886 bool_t err;
887 NYD_ENTER;
889 _var_revlookup(&vc, vokey);
891 err = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
892 NYD_LEAVE;
893 return err;
896 FL bool_t
897 _var_vokclear(char const *vokey)
899 struct var_carrier vc;
900 bool_t err;
901 NYD_ENTER;
903 _var_revlookup(&vc, vokey);
905 err = _var_clear(&vc);
906 NYD_LEAVE;
907 return err;
910 FL char *
911 _var_xoklook(enum okeys okey, struct url const *urlp, enum okey_xlook_mode oxm)
913 struct var_carrier vc;
914 struct str const *us;
915 size_t nlen;
916 char *nbuf = NULL /* CC happiness */, *rv;
917 NYD_ENTER;
919 assert(oxm & (OXM_PLAIN | OXM_H_P | OXM_U_H_P));
921 /* For simplicity: allow this case too */
922 if (!(oxm & (OXM_H_P | OXM_U_H_P)))
923 goto jplain;
925 vc.vc_vmap = _var_map + okey;
926 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
927 vc.vc_okey = okey;
929 us = (oxm & OXM_U_H_P) ? &urlp->url_u_h_p : &urlp->url_h_p;
930 nlen = strlen(vc.vc_name);
931 nbuf = ac_alloc(nlen + 1 + us->l +1);
932 memcpy(nbuf, vc.vc_name, nlen);
933 nbuf[nlen++] = '-';
935 /* One of .url_u_h_p and .url_h_p we test in here */
936 memcpy(nbuf + nlen, us->s, us->l +1);
937 vc.vc_name = _var_canonify(nbuf);
938 vc.vc_hash = MA_NAME2HASH(vc.vc_name);
939 if (_var_lookup(&vc))
940 goto jvar;
942 /* The second */
943 if (oxm & OXM_H_P) {
944 us = &urlp->url_h_p;
945 memcpy(nbuf + nlen, us->s, us->l +1);
946 vc.vc_name = _var_canonify(nbuf);
947 vc.vc_hash = MA_NAME2HASH(vc.vc_name);
948 if (_var_lookup(&vc)) {
949 jvar:
950 rv = vc.vc_var->v_value;
951 goto jleave;
955 jplain:
956 rv = (oxm & OXM_PLAIN) ? _var_oklook(okey) : NULL;
957 jleave:
958 if (oxm & (OXM_H_P | OXM_U_H_P))
959 ac_free(nbuf);
960 NYD_LEAVE;
961 return rv;
964 FL void
965 var_list_all(void)
967 FILE *fp;
968 char **vacp, **cap;
969 struct var *vp;
970 size_t no, i;
971 char const *fmt;
972 NYD_ENTER;
974 if ((fp = Ftmp(NULL, "listvars", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
975 NULL) {
976 perror("tmpfile");
977 goto jleave;
980 for (no = i = 0; i < MA_PRIME; ++i)
981 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
982 ++no;
983 vacp = salloc(no * sizeof(*vacp));
984 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
985 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
986 *cap++ = vp->v_name;
988 if (no > 1)
989 qsort(vacp, no, sizeof *vacp, &_var_list_all_cmp);
991 i = (ok_blook(bsdcompat) || ok_blook(bsdset));
992 fmt = (i != 0) ? "%s\t%s\n" : "%s=\"%s\"\n";
994 for (cap = vacp; no != 0; ++cap, --no) {
995 char *cp = vok_vlook(*cap); /* XXX when lookup checks val/bin, change */
996 if (cp == NULL)
997 cp = UNCONST("");
998 if (i || *cp != '\0')
999 fprintf(fp, fmt, *cap, cp);
1000 else
1001 fprintf(fp, "%s\n", *cap);
1004 page_or_print(fp, PTR2SIZE(cap - vacp));
1005 Fclose(fp);
1006 jleave:
1007 NYD_LEAVE;
1010 FL int
1011 c_varshow(void *v)
1013 struct var_carrier vc;
1014 char **argv = v, *val;
1015 bool_t isenv, isset;
1016 NYD_ENTER;
1018 if (*argv == NULL) {
1019 v = NULL;
1020 goto jleave;
1023 for (; *argv != NULL; ++argv) {
1024 memset(&vc, 0, sizeof vc);
1025 _var_revlookup(&vc, *argv);
1026 if (_var_lookup(&vc)) {
1027 val = vc.vc_var->v_value;
1028 isenv = FAL0;
1029 } else
1030 isenv = ((val = getenv(vc.vc_name)) != NULL);
1031 if (val == NULL)
1032 val = UNCONST("NULL");
1033 isset = (vc.vc_var != NULL);
1035 if (vc.vc_vmap != NULL) {
1036 if (vc.vc_vmap->vm_binary)
1037 printf("%s: binary option (%d): isset=%d/environ=%d\n",
1038 vc.vc_name, vc.vc_okey, isset, isenv);
1039 else
1040 printf("%s: value option (%d): isset=%d/environ=%d value<%s>\n",
1041 vc.vc_name, vc.vc_okey, isset, isenv, val);
1042 } else
1043 printf("%s: isset=%d/environ=%d value<%s>\n",
1044 vc.vc_name, isset, isenv, val);
1046 jleave:
1047 NYD_LEAVE;
1048 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1051 FL int
1052 c_set(void *v)
1054 char **ap = v;
1055 int err;
1056 NYD_ENTER;
1058 if (*ap == NULL) {
1059 var_list_all();
1060 err = 0;
1061 } else
1062 err = _var_set_env(ap, FAL0);
1063 NYD_LEAVE;
1064 return err;
1067 FL int
1068 c_setenv(void *v)
1070 char **ap = v;
1071 int err;
1072 NYD_ENTER;
1074 if (!(err = starting))
1075 err = _var_set_env(ap, TRU1);
1076 NYD_LEAVE;
1077 return err;
1080 FL int
1081 c_unset(void *v)
1083 char **ap = v;
1084 int err = 0;
1085 NYD_ENTER;
1087 while (*ap != NULL)
1088 err |= _var_vokclear(*ap++);
1089 NYD_LEAVE;
1090 return err;
1093 FL int
1094 c_unsetenv(void *v)
1096 int err;
1097 NYD_ENTER;
1099 if (!(err = starting)) {
1100 char **ap;
1101 bool_t vcau_save = var_clear_allow_undefined;
1102 var_clear_allow_undefined = TRU1;
1104 for (ap = v; *ap != NULL; ++ap) {
1105 bool_t bad = _var_vokclear(*ap);
1106 if (
1107 #ifdef HAVE_SETENV
1108 unsetenv(*ap) != 0 &&
1109 #endif
1112 err = 1;
1115 var_clear_allow_undefined = vcau_save;
1117 NYD_LEAVE;
1118 return err;
1121 FL int
1122 c_varedit(void *v)
1124 struct var_carrier vc;
1125 sighandler_type sigint;
1126 FILE *of, *nf;
1127 char *val, **argv = v;
1128 int rv = 0;
1129 NYD_ENTER;
1131 sigint = safe_signal(SIGINT, SIG_IGN);
1133 while (*argv != NULL) {
1134 memset(&vc, 0, sizeof vc);
1135 _var_revlookup(&vc, *argv++);
1137 if (!_var_lookup(&vc)) {
1138 fprintf(stderr, _("`varedit': variable `%s' is not set\n"),
1139 vc.vc_name);
1140 rv = 1;
1141 continue;
1142 } else if (vc.vc_vmap != NULL && vc.vc_vmap->vm_binary) {
1143 fprintf(stderr, _("`varedit' cannot edit binary value `%s'\n"),
1144 vc.vc_name);
1145 continue;
1148 if ((of = Ftmp(NULL, "vared", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1149 NULL) {
1150 perror(_("`varedit': cannot create temporary file"));
1151 rv = 1;
1152 break;
1153 } else if (*(val = vc.vc_var->v_value) != '\0' &&
1154 sizeof *val != fwrite(val, strlen(val), sizeof *val, of)) {
1155 perror(_("`varedit' failed to write an old value to temporary file"));
1156 Fclose(of);
1157 rv = 1;
1158 continue;
1161 fflush_rewind(of);
1162 nf = run_editor(of, (off_t)-1, 'e', FAL0, NULL, NULL, SEND_MBOX, sigint);
1163 Fclose(of);
1165 if (nf != NULL) {
1166 int c;
1167 char *base;
1168 off_t l = fsize(nf);
1170 assert(l >= 0);
1171 base = smalloc((size_t)l + 1);
1173 for (l = 0, val = base; (c = getc(nf)) != EOF; ++val)
1174 if (c == '\n' || c == '\r') {
1175 *val = ' ';
1176 ++l;
1177 } else {
1178 *val = (char)(uc_it)c;
1179 l = 0;
1181 val -= l;
1182 *val = '\0';
1184 if (!vok_vset(vc.vc_name, base))
1185 rv = 1;
1187 free(base);
1189 Fclose(nf);
1192 safe_signal(SIGINT, sigint);
1193 NYD_LEAVE;
1194 return rv;
1197 FL int
1198 c_define(void *v)
1200 int rv = 1;
1201 char **args = v;
1202 NYD_ENTER;
1204 if (args[0] == NULL) {
1205 rv = _ma_list(MA_NONE);
1206 goto jleave;
1209 if (args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
1210 args[2] != NULL) {
1211 fprintf(stderr, _("Syntax is: define <name> {"));
1212 goto jleave;
1215 rv = !_ma_define1(args[0], MA_NONE);
1216 jleave:
1217 NYD_LEAVE;
1218 return rv;
1221 FL int
1222 c_undefine(void *v)
1224 int rv = 1;
1225 char **args = v;
1226 NYD_ENTER;
1228 if (*args == NULL) {
1229 fprintf(stderr, _("`%s': required arguments are missing\n"),
1230 "undefine");
1231 goto jleave;
1234 _ma_undef1(*args, MA_NONE);
1235 while (*++args != NULL);
1236 rv = 0;
1237 jleave:
1238 NYD_LEAVE;
1239 return rv;
1242 FL int
1243 c_call(void *v)
1245 int rv = 1;
1246 char **args = v;
1247 char const *errs, *name;
1248 struct macro *mp;
1249 NYD_ENTER;
1251 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
1252 errs = _("Syntax is: call <%s>\n");
1253 name = "name";
1254 goto jerr;
1257 if ((mp = _ma_look(*args, NULL, MA_NONE)) == NULL) {
1258 errs = _("Undefined macro called: `%s'\n");
1259 name = *args;
1260 goto jerr;
1263 rv = _ma_exec(mp, NULL);
1264 jleave:
1265 NYD_LEAVE;
1266 return rv;
1267 jerr:
1268 fprintf(stderr, errs, name);
1269 goto jleave;
1272 FL int
1273 callhook(char const *name, int nmail)
1275 int len, rv;
1276 struct macro *mp;
1277 char *var, *cp;
1278 NYD_ENTER;
1280 var = ac_alloc(len = strlen(name) + 12 +1);
1281 snprintf(var, len, "folder-hook-%s", name);
1282 if ((cp = vok_vlook(var)) == NULL && (cp = ok_vlook(folder_hook)) == NULL) {
1283 rv = 0;
1284 goto jleave;
1286 if ((mp = _ma_look(cp, NULL, MA_NONE)) == NULL) {
1287 fprintf(stderr, _("Cannot call hook for folder \"%s\": "
1288 "Macro \"%s\" does not exist.\n"), name, cp);
1289 rv = 1;
1290 goto jleave;
1293 inhook = nmail ? 3 : 1; /* XXX enum state machine */
1294 rv = _ma_exec(mp, NULL);
1295 inhook = 0;
1296 jleave:
1297 ac_free(var);
1298 NYD_LEAVE;
1299 return rv;
1302 FL int
1303 c_account(void *v)
1305 char **args = v;
1306 struct macro *mp;
1307 int rv = 1, i, oqf, nqf;
1308 NYD_ENTER;
1310 if (args[0] == NULL) {
1311 rv = _ma_list(MA_ACC);
1312 goto jleave;
1315 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1316 if (args[2] != NULL) {
1317 fprintf(stderr, _("Syntax is: account <name> {\n"));
1318 goto jleave;
1320 if (!asccasecmp(args[0], ACCOUNT_NULL)) {
1321 fprintf(stderr, _("Error: `%s' is a reserved name.\n"),
1322 ACCOUNT_NULL);
1323 goto jleave;
1325 rv = !_ma_define1(args[0], MA_ACC);
1326 goto jleave;
1329 if (inhook) {
1330 fprintf(stderr, _("Cannot change account from within a hook.\n"));
1331 goto jleave;
1334 save_mbox_for_possible_quitstuff();
1336 mp = NULL;
1337 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
1338 (mp = _ma_look(args[0], NULL, MA_ACC)) == NULL) {
1339 fprintf(stderr, _("Account `%s' does not exist.\n"), args[0]);
1340 goto jleave;
1343 oqf = savequitflags();
1344 if (_acc_curr != NULL && _acc_curr->ma_localopts != NULL)
1345 _localopts_unroll(&_acc_curr->ma_localopts);
1346 account_name = (mp != NULL) ? mp->ma_name : NULL;
1347 _acc_curr = mp;
1349 if (mp != NULL && _ma_exec(mp, &mp->ma_localopts) == CBAD) {
1350 /* XXX account switch incomplete, unroll? */
1351 fprintf(stderr, _("Switching to account `%s' failed.\n"), args[0]);
1352 goto jleave;
1355 if (!starting && !inhook) {
1356 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1357 restorequitflags(oqf);
1358 if ((i = setfile("%", 0)) < 0)
1359 goto jleave;
1360 callhook(mailname, 0);
1361 if (i > 0 && !ok_blook(emptystart))
1362 goto jleave;
1363 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1364 restorequitflags(nqf);
1366 rv = 0;
1367 jleave:
1368 NYD_LEAVE;
1369 return rv;
1372 FL int
1373 c_unaccount(void *v)
1375 int rv = 1;
1376 char **args = v;
1377 NYD_ENTER;
1379 if (*args == NULL) {
1380 fprintf(stderr, _("`%s': required arguments are missing\n"),
1381 "unaccount");
1382 goto jleave;
1385 rv = 0;
1386 do {
1387 if (account_name != NULL && !strcmp(account_name, *args)) {
1388 fprintf(stderr,
1389 _("Rejecting deletion of currently active account `%s'\n"), *args);
1390 continue;
1392 _ma_undef1(*args, MA_ACC);
1393 } while (*++args != NULL);
1394 jleave:
1395 NYD_LEAVE;
1396 return rv;
1399 FL int
1400 c_localopts(void *v)
1402 int rv = 1;
1403 char **c = v;
1404 NYD_ENTER;
1406 if (_localopts == NULL) {
1407 fprintf(stderr,
1408 _("Cannot use `localopts' but from within a `define' or `account'\n"));
1409 goto jleave;
1412 _localopts->s_unroll = (**c == '0') ? FAL0 : TRU1;
1413 rv = 0;
1414 jleave:
1415 NYD_LEAVE;
1416 return rv;
1419 FL void
1420 temporary_localopts_free(void) /* XXX intermediate hack */
1422 struct n2 {struct n2 *up; struct lostack *lo;} *x;
1423 NYD_ENTER;
1425 var_clear_allow_undefined = FAL0;
1426 x = temporary_localopts_store;
1427 temporary_localopts_store = NULL;
1428 _localopts = NULL;
1430 while (x != NULL) {
1431 struct lostack *losp = x->lo;
1432 x = x->up;
1433 _localopts_unroll(&losp->s_localopts);
1435 NYD_LEAVE;
1438 /* vim:set fenc=utf-8:s-it-mode */