Drop `defines', add `unaccount', rename `undefine'..
[s-mailx.git] / acmava.c
blobe6bcba95d965cd0d898c9b7aa03e56a9106d67e8
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 macro *_macros[MA_PRIME]; /* TODO dynamically spaced */
117 static struct var *_vars[MA_PRIME]; /* TODO dynamically spaced */
119 /* Special cased value string allocation */
120 static char * _vcopy(char const *str);
121 static void _vfree(char *cp);
123 /* Check for special housekeeping. */
124 static bool_t _check_special_vars(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 * _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).
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) */
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 static bool_t _var_clear(struct var_carrier *vcp);
149 /* Does cp consist solely of WS and a } */
150 static bool_t _is_closing_angle(char const *cp);
152 /* Lookup for macros/accounts */
153 static struct macro *_malook(char const *name, struct macro *data,
154 enum ma_flags mafl);
156 /* Walk all lines of a macro and execute() them */
157 static int _maexec(struct macro const *mp, struct var **unroller);
159 /* User display helpers */
160 static int _list_macros(enum ma_flags mafl);
162 /* */
163 static bool_t _define1(char const *name, enum ma_flags mafl);
164 static void _undef1(char const *name, enum ma_flags mafl);
165 static void _freelines(struct mline *lp);
167 /* var_list_all(): qsort(3) helper */
168 static int _var_list_all_cmp(void const *s1, void const *s2);
170 /* Update replay-log */
171 static void _localopts_add(struct lostack *losp, char const *name,
172 struct var *ovap);
173 static void _localopts_unroll(struct var **vapp);
175 static char *
176 _vcopy(char const *str)
178 char *news;
179 size_t len;
180 NYD_ENTER;
182 if (*str == '\0')
183 news = UNCONST("");
184 else {
185 len = strlen(str) +1;
186 news = smalloc(len);
187 memcpy(news, str, len);
189 NYD_LEAVE;
190 return news;
193 static void
194 _vfree(char *cp)
196 NYD_ENTER;
197 if (*cp != '\0')
198 free(cp);
199 NYD_LEAVE;
202 static bool_t
203 _check_special_vars(enum okeys okey, bool_t enable, char **val)
205 char *cp = NULL;
206 bool_t rv = TRU1;
207 int flag = 0;
208 NYD_ENTER;
210 switch (okey) {
211 case ok_b_debug:
212 flag = OPT_DEBUG;
213 break;
214 case ok_b_header:
215 flag = OPT_N_FLAG;
216 enable = !enable;
217 break;
218 case ok_b_skipemptybody:
219 flag = OPT_E_FLAG;
220 break;
221 case ok_b_verbose:
222 flag = OPT_VERBOSE;
223 break;
224 case ok_v_folder:
225 rv = (val == NULL || var_folder_updated(*val, &cp));
226 if (rv && cp != NULL) {
227 _vfree(*val);
228 /* It's smalloc()ed, but ensure we don't leak */
229 if (*cp == '\0') {
230 *val = UNCONST("");
231 free(cp);
232 } else
233 *val = cp;
235 break;
236 #ifdef HAVE_NCL
237 case ok_v_line_editor_cursor_right:
238 if ((rv = (val != NULL && *val != NULL))) {
239 /* Set with no value? TODO very guly */
240 if (*(cp = *val) != '\0') {
241 char const *x = cp;
242 int c;
243 do {
244 c = expand_shell_escape(&x, FAL0);
245 if (c < 0)
246 break;
247 *cp++ = (char)c;
248 } while (*x != '\0');
249 *cp = '\0';
252 break;
253 #endif
254 default:
255 break;
258 if (flag) {
259 if (enable)
260 options |= flag;
261 else
262 options &= ~flag;
264 NYD_LEAVE;
265 return rv;
268 static char const *
269 _canonify(char const *vn)
271 NYD_ENTER;
272 if (!upperchar(*vn)) {
273 char const *vp;
275 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
277 vn = (*vp == '@') ? i_strdup(vn) : vn;
279 NYD_LEAVE;
280 return vn;
283 static bool_t
284 _var_revlookup(struct var_carrier *vcp, char const *name)
286 ui32_t hash, i, j;
287 struct var_map const *vmp;
288 NYD_ENTER;
290 vcp->vc_name = name = _canonify(name);
291 vcp->vc_hash = hash = MA_NAME2HASH(name);
293 for (i = hash % _VAR_REV_PRIME, j = 0; j <= _VAR_REV_LONGEST; ++j) {
294 ui32_t x = _var_revmap[i];
295 if (x == _VAR_REV_ILL)
296 break;
297 vmp = _var_map + x;
298 if (vmp->vm_hash == hash && !strcmp(_var_keydat + vmp->vm_keyoff, name)) {
299 vcp->vc_vmap = vmp;
300 vcp->vc_okey = (enum okeys)x;
301 goto jleave;
303 if (++i == _VAR_REV_PRIME) {
304 #ifdef _VAR_REV_WRAPAROUND
305 i = 0;
306 #else
307 break;
308 #endif
311 vcp->vc_vmap = NULL;
312 vcp = NULL;
313 jleave:
314 NYD_LEAVE;
315 return (vcp != NULL);
318 static bool_t
319 _var_lookup(struct var_carrier *vcp)
321 struct var **vap, *lvp, *vp;
322 NYD_ENTER;
324 vap = _vars + (vcp->vc_prime = MA_HASH2PRIME(vcp->vc_hash));
326 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
327 if (!strcmp(vp->v_name, vcp->vc_name)) {
328 /* Relink as head, hope it "sorts on usage" over time.
329 * _var_clear() relies on this behaviour */
330 if (lvp != NULL) {
331 lvp->v_link = vp->v_link;
332 vp->v_link = *vap;
333 *vap = vp;
335 goto jleave;
337 vp = NULL;
338 jleave:
339 vcp->vc_var = vp;
340 NYD_LEAVE;
341 return (vp != NULL);
344 static bool_t
345 _var_set(struct var_carrier *vcp, char const *value)
347 bool_t err;
348 struct var *vp;
349 char *oval;
350 NYD_ENTER;
352 if (value == NULL) {
353 bool_t tmp = var_clear_allow_undefined;
354 var_clear_allow_undefined = TRU1;
355 err = _var_clear(vcp);
356 var_clear_allow_undefined = tmp;
357 goto jleave;
360 _var_lookup(vcp);
362 /* Don't care what happens later on, store this in the unroll list */
363 if (_localopts != NULL)
364 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
366 if ((vp = vcp->vc_var) == NULL) {
367 size_t l = strlen(vcp->vc_name) + 1;
369 vcp->vc_var =
370 vp = smalloc(sizeof(*vp) - VFIELD_SIZEOF(struct var, v_name) + l);
371 vp->v_link = _vars[vcp->vc_prime];
372 _vars[vcp->vc_prime] = vp;
373 memcpy(vp->v_name, vcp->vc_name, l);
374 oval = UNCONST("");
375 } else
376 oval = vp->v_value;
377 vp->v_value = _vcopy(value);
379 /* Check if update allowed XXX wasteful on error! */
380 if (vcp->vc_vmap != NULL && vcp->vc_vmap->vm_special &&
381 (err = !_check_special_vars(vcp->vc_okey, TRU1, &vp->v_value))) {
382 char *cp = vp->v_value;
383 vp->v_value = oval;
384 oval = cp;
386 if (*oval != '\0')
387 _vfree(oval);
388 err = FAL0;
389 jleave:
390 NYD_LEAVE;
391 return err;
394 static bool_t
395 _var_clear(struct var_carrier *vcp)
397 bool_t err = TRU1;
398 NYD_ENTER;
400 if (!_var_lookup(vcp)) {
401 if (!sourcing && !var_clear_allow_undefined) {
402 fprintf(stderr, tr(203, "`%s': undefined variable\n"), vcp->vc_name);
403 goto jleave;
405 } else {
406 if (_localopts != NULL)
407 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
409 /* Always listhead after _var_lookup() */
410 _vars[vcp->vc_prime] = _vars[vcp->vc_prime]->v_link;
411 _vfree(vcp->vc_var->v_value);
412 free(vcp->vc_var);
413 vcp->vc_var = NULL;
415 if (vcp->vc_vmap != NULL && vcp->vc_vmap->vm_special &&
416 !_check_special_vars(vcp->vc_okey, FAL0, NULL))
417 goto jleave;
419 err = FAL0;
420 jleave:
421 NYD_LEAVE;
422 return err;
425 static bool_t
426 _is_closing_angle(char const *cp)
428 bool_t rv = FAL0;
429 NYD_ENTER;
431 while (spacechar(*cp))
432 ++cp;
433 if (*cp++ != '}')
434 goto jleave;
435 while (spacechar(*cp))
436 ++cp;
437 rv = (*cp == '\0');
438 jleave:
439 NYD_LEAVE;
440 return rv;
443 static struct macro *
444 _malook(char const *name, struct macro *data, enum ma_flags mafl)
446 enum ma_flags save_mafl;
447 ui32_t h;
448 struct macro *lmp, *mp;
449 NYD_ENTER;
451 save_mafl = mafl;
452 mafl &= MA_TYPE_MASK;
453 h = MA_NAME2HASH(name);
454 h = MA_HASH2PRIME(h);
456 for (lmp = NULL, mp = _macros[h]; mp != NULL; lmp = mp, mp = mp->ma_next) {
457 if ((mp->ma_flags & MA_TYPE_MASK) == mafl && !strcmp(mp->ma_name, name)) {
458 if (save_mafl & MA_UNDEF) {
459 if (lmp == NULL)
460 _macros[h] = mp->ma_next;
461 else
462 lmp->ma_next = mp->ma_next;
464 goto jleave;
468 if (data != NULL) {
469 data->ma_next = _macros[h];
470 _macros[h] = data;
471 mp = NULL;
473 jleave:
474 NYD_LEAVE;
475 return mp;
478 static int
479 _maexec(struct macro const *mp, struct var **unroller)
481 struct lostack los;
482 int rv = 0;
483 char *buf;
484 struct mline const *lp;
485 NYD_ENTER;
487 los.s_up = _localopts;
488 los.s_mac = UNCONST(mp);
489 los.s_localopts = NULL;
490 los.s_unroll = FAL0;
491 _localopts = &los;
493 buf = ac_alloc(mp->ma_maxlen +1);
494 for (lp = mp->ma_contents; lp; lp = lp->l_next) {
495 var_clear_allow_undefined = TRU1;
496 memcpy(buf, lp->l_line, lp->l_length +1);
497 temporary_localopts_store = _localopts; /* XXX intermediate hack */
498 /* FIXME if the execute() jumps away (via INT) then our internal state
499 * FIXME is messed up, even though temporary_localopts_free() is likely
500 * FIXME to correct some of that */
501 rv |= execute(buf, 0, lp->l_length); /* XXX break if != 0 ? */
502 temporary_localopts_store = NULL; /* XXX intermediate hack */
503 var_clear_allow_undefined = FAL0;
505 ac_free(buf);
507 _localopts = los.s_up;
508 if (unroller == NULL) {
509 if (los.s_localopts != NULL)
510 _localopts_unroll(&los.s_localopts);
511 } else
512 *unroller = los.s_localopts;
513 NYD_LEAVE;
514 return rv;
517 static int
518 _list_macros(enum ma_flags mafl)
520 FILE *fp;
521 char const *typestr;
522 struct macro *mq;
523 ui32_t ti, mc;
524 struct mline *lp;
525 NYD_ENTER;
527 if ((fp = Ftmp(NULL, "listmacs", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
528 NULL) {
529 perror("tmpfile");
530 mc = 1;
531 goto jleave;
534 mafl &= MA_TYPE_MASK;
535 typestr = (mafl & MA_ACC) ? "account" : "define";
537 for (ti = mc = 0; ti < MA_PRIME; ++ti)
538 for (mq = _macros[ti]; mq; mq = mq->ma_next)
539 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
540 if (++mc > 1)
541 fputc('\n', fp);
542 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
543 for (lp = mq->ma_contents; lp != NULL; lp = lp->l_next)
544 fprintf(fp, " %s\n", lp->l_line);
545 fputs("}\n", fp);
547 if (mc)
548 page_or_print(fp, 0);
550 mc = (ui32_t)ferror(fp);
551 Fclose(fp);
552 jleave:
553 NYD_LEAVE;
554 return (int)mc;
557 static bool_t
558 _define1(char const *name, enum ma_flags mafl)
560 bool_t rv = FAL0;
561 struct macro *mp;
562 struct mline *lp, *lst = NULL, *lnd = NULL;
563 char *linebuf = NULL, *cp;
564 size_t linesize = 0, maxlen = 0;
565 int n, i;
566 NYD_ENTER;
568 mp = scalloc(1, sizeof *mp);
569 mp->ma_name = sstrdup(name);
570 mp->ma_flags = mafl;
572 for (;;) {
573 n = readline_input("", TRU1, &linebuf, &linesize, NULL);
574 if (n <= 0) {
575 fprintf(stderr, tr(75, "Unterminated %s definition: \"%s\".\n"),
576 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
577 if (sourcing)
578 unstack();
579 goto jerr;
581 if (_is_closing_angle(linebuf))
582 break;
584 /* Trim WS */
585 for (cp = linebuf, i = 0; i < n; ++cp, ++i)
586 if (!whitechar(*cp))
587 break;
588 if (i == n)
589 continue;
590 n -= i;
591 while (whitechar(cp[n - 1]))
592 if (--n == 0)
593 break;
594 if (n == 0)
595 continue;
597 maxlen = MAX(maxlen, (size_t)n);
598 cp[n++] = '\0';
600 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n);
601 memcpy(lp->l_line, cp, n);
602 lp->l_length = (size_t)--n;
603 if (lst != NULL) {
604 lnd->l_next = lp;
605 lnd = lp;
606 } else
607 lst = lnd = lp;
609 mp->ma_contents = lst;
610 mp->ma_maxlen = maxlen;
612 if (_malook(mp->ma_name, mp, mafl) != NULL) {
613 fprintf(stderr, tr(76, "A %s named `%s' already exists.\n"),
614 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
615 lst = mp->ma_contents;
616 goto jerr;
619 rv = TRU1;
620 jleave:
621 if (linebuf != NULL)
622 free(linebuf);
623 NYD_LEAVE;
624 return rv;
626 jerr:
627 if (lst != NULL)
628 _freelines(lst);
629 free(mp->ma_name);
630 free(mp);
631 goto jleave;
634 static void
635 _undef1(char const *name, enum ma_flags mafl)
637 struct macro *mp;
638 NYD_ENTER;
640 if ((mp = _malook(name, NULL, mafl | MA_UNDEF)) != NULL) {
641 _freelines(mp->ma_contents);
642 free(mp->ma_name);
643 free(mp);
644 } else
645 fprintf(stderr, tr(571, "%s `%s' is not defined\n"),
646 (mafl & MA_ACC ? "Account" : "Macro"), name);
647 NYD_LEAVE;
650 static void
651 _freelines(struct mline *lp)
653 struct mline *lq;
654 NYD_ENTER;
656 for (lq = NULL; lp != NULL; ) {
657 if (lq != NULL)
658 free(lq);
659 lq = lp;
660 lp = lp->l_next;
662 if (lq)
663 free(lq);
664 NYD_LEAVE;
667 static int
668 _var_list_all_cmp(void const *s1, void const *s2)
670 int rv;
671 NYD_ENTER;
673 rv = strcmp(*(char**)UNCONST(s1), *(char**)UNCONST(s2));
674 NYD_LEAVE;
675 return rv;
678 static void
679 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
681 struct var *vap;
682 size_t nl, vl;
683 NYD_ENTER;
685 /* Propagate unrolling up the stack, as necessary */
686 while (!losp->s_unroll && (losp = losp->s_up) != NULL)
688 if (losp == NULL)
689 goto jleave;
691 /* We've found a level that wants to unroll; check wether it does it yet */
692 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
693 if (!strcmp(vap->v_name, name))
694 goto jleave;
696 nl = strlen(name) + 1;
697 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
698 vap = smalloc(sizeof(*vap) - VFIELD_SIZEOF(struct var, v_name) + nl + vl);
699 vap->v_link = losp->s_localopts;
700 losp->s_localopts = vap;
701 memcpy(vap->v_name, name, nl);
702 if (vl == 0)
703 vap->v_value = NULL;
704 else {
705 vap->v_value = vap->v_name + nl;
706 memcpy(vap->v_value, ovap->v_value, vl);
708 jleave:
709 NYD_LEAVE;
712 static void
713 _localopts_unroll(struct var **vapp)
715 struct lostack *save_los;
716 struct var *x, *vap;
717 NYD_ENTER;
719 vap = *vapp;
720 *vapp = NULL;
722 save_los = _localopts;
723 _localopts = NULL;
724 while (vap != NULL) {
725 x = vap;
726 vap = vap->v_link;
727 vok_vset(x->v_name, x->v_value);
728 free(x);
730 _localopts = save_los;
731 NYD_LEAVE;
734 FL char *
735 _var_oklook(enum okeys okey)
737 struct var_carrier vc;
738 char *rv;
739 NYD_ENTER;
741 vc.vc_vmap = _var_map + okey;
742 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
743 vc.vc_hash = _var_map[okey].vm_hash;
744 vc.vc_okey = okey;
746 if (!_var_lookup(&vc)) {
747 if ((rv = getenv(vc.vc_name)) != NULL) {
748 _var_set(&vc, rv);
749 assert(vc.vc_var != NULL);
750 goto jvar;
752 } else
753 jvar:
754 rv = vc.vc_var->v_value;
755 NYD_LEAVE;
756 return rv;
759 FL bool_t
760 _var_okset(enum okeys okey, uintptr_t val)
762 struct var_carrier vc;
763 bool_t rv;
764 NYD_ENTER;
766 vc.vc_vmap = _var_map + okey;
767 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
768 vc.vc_hash = _var_map[okey].vm_hash;
769 vc.vc_okey = okey;
771 rv = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
772 NYD_LEAVE;
773 return rv;
776 FL bool_t
777 _var_okclear(enum okeys okey)
779 struct var_carrier vc;
780 bool_t rv;
781 NYD_ENTER;
783 vc.vc_vmap = _var_map + okey;
784 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
785 vc.vc_hash = _var_map[okey].vm_hash;
786 vc.vc_okey = okey;
788 rv = _var_clear(&vc);
789 NYD_LEAVE;
790 return rv;
793 FL char *
794 _var_voklook(char const *vokey)
796 struct var_carrier vc;
797 char *rv;
798 NYD_ENTER;
800 _var_revlookup(&vc, vokey);
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_vokset(char const *vokey, uintptr_t val)
818 struct var_carrier vc;
819 bool_t rv;
820 NYD_ENTER;
822 _var_revlookup(&vc, vokey);
824 rv = _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
825 NYD_LEAVE;
826 return rv;
829 FL bool_t
830 _var_vokclear(char const *vokey)
832 struct var_carrier vc;
833 bool_t rv;
834 NYD_ENTER;
836 _var_revlookup(&vc, vokey);
838 rv = _var_clear(&vc);
839 NYD_LEAVE;
840 return rv;
843 FL void
844 var_list_all(void)
846 FILE *fp;
847 char **vacp, **cap;
848 struct var *vp;
849 size_t no, i;
850 char const *fmt;
851 NYD_ENTER;
853 if ((fp = Ftmp(NULL, "listvars", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
854 NULL) {
855 perror("tmpfile");
856 goto jleave;
859 for (no = i = 0; i < MA_PRIME; ++i)
860 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
861 ++no;
862 vacp = salloc(no * sizeof(*vacp));
863 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
864 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
865 *cap++ = vp->v_name;
867 if (no > 1)
868 qsort(vacp, no, sizeof *vacp, &_var_list_all_cmp);
870 i = (ok_blook(bsdcompat) || ok_blook(bsdset));
871 fmt = (i != 0) ? "%s\t%s\n" : "%s=\"%s\"\n";
873 for (cap = vacp; no != 0; ++cap, --no) {
874 char *cp = vok_vlook(*cap); /* XXX when lookup checks val/bin, change */
875 if (cp == NULL)
876 cp = UNCONST("");
877 if (i || *cp != '\0')
878 fprintf(fp, fmt, *cap, cp);
879 else
880 fprintf(fp, "%s\n", *cap);
883 page_or_print(fp, PTR2SIZE(cap - vacp));
884 Fclose(fp);
885 jleave:
886 NYD_LEAVE;
889 FL int
890 c_var_inspect(void *v)
892 struct var_carrier vc;
893 char **argv = v, *val;
894 bool_t isenv, isset;
895 NYD_ENTER;
897 if (*argv == NULL) {
898 v = NULL;
899 goto jleave;
902 for (; *argv != NULL; ++argv) {
903 memset(&vc, 0, sizeof vc);
904 _var_revlookup(&vc, *argv);
905 if (_var_lookup(&vc)) {
906 val = vc.vc_var->v_value;
907 isenv = FAL0;
908 } else
909 isenv = ((val = getenv(vc.vc_name)) != NULL);
910 if (val == NULL)
911 val = UNCONST("NULL");
912 isset = (vc.vc_var != NULL);
914 if (vc.vc_vmap != NULL) {
915 if (vc.vc_vmap->vm_binary)
916 printf("%s: binary option (%d): isset=%d/environ=%d\n",
917 vc.vc_name, vc.vc_okey, isset, isenv);
918 else
919 printf("%s: value option (%d): isset=%d/environ=%d value<%s>\n",
920 vc.vc_name, vc.vc_okey, isset, isenv, val);
921 } else
922 printf("%s: isset=%d/environ=%d value<%s>\n",
923 vc.vc_name, isset, isenv, val);
925 jleave:
926 NYD_LEAVE;
927 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
930 FL int
931 c_define(void *v)
933 int rv = 1;
934 char **args = v;
935 NYD_ENTER;
937 if (args[0] == NULL) {
938 rv = _list_macros(MA_NONE);
939 goto jleave;
942 if (args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
943 args[2] != NULL) {
944 fprintf(stderr, tr(505, "Syntax is: define <name> {"));
945 goto jleave;
948 rv = !_define1(args[0], MA_NONE);
949 jleave:
950 NYD_LEAVE;
951 return rv;
954 FL int
955 c_undefine(void *v)
957 int rv = 1;
958 char **args = v;
959 NYD_ENTER;
961 if (*args == NULL) {
962 fprintf(stderr, tr(504, "`%s': required arguments are missing\n"),
963 "undefine");
964 goto jleave;
967 _undef1(*args, MA_NONE);
968 while (*++args != NULL);
969 rv = 0;
970 jleave:
971 NYD_LEAVE;
972 return rv;
975 FL int
976 c_call(void *v)
978 int rv = 1;
979 char **args = v;
980 char const *errs, *name;
981 struct macro *mp;
982 NYD_ENTER;
984 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
985 errs = tr(507, "Syntax is: call <%s>\n");
986 name = "name";
987 goto jerr;
990 if ((mp = _malook(*args, NULL, MA_NONE)) == NULL) {
991 errs = tr(508, "Undefined macro called: `%s'\n");
992 name = *args;
993 goto jerr;
996 rv = _maexec(mp, NULL);
997 jleave:
998 NYD_LEAVE;
999 return rv;
1000 jerr:
1001 fprintf(stderr, errs, name);
1002 goto jleave;
1005 FL int
1006 callhook(char const *name, int nmail)
1008 int len, rv;
1009 struct macro *mp;
1010 char *var, *cp;
1011 NYD_ENTER;
1013 var = ac_alloc(len = strlen(name) + 12 +1);
1014 snprintf(var, len, "folder-hook-%s", name);
1015 if ((cp = vok_vlook(var)) == NULL && (cp = ok_vlook(folder_hook)) == NULL) {
1016 rv = 0;
1017 goto jleave;
1019 if ((mp = _malook(cp, NULL, MA_NONE)) == NULL) {
1020 fprintf(stderr, tr(49, "Cannot call hook for folder \"%s\": "
1021 "Macro \"%s\" does not exist.\n"), name, cp);
1022 rv = 1;
1023 goto jleave;
1026 inhook = nmail ? 3 : 1; /* XXX enum state machine */
1027 rv = _maexec(mp, NULL);
1028 inhook = 0;
1029 jleave:
1030 ac_free(var);
1031 NYD_LEAVE;
1032 return rv;
1035 FL int
1036 c_account(void *v)
1038 char **args = v;
1039 struct macro *mp;
1040 int rv = 1, i, oqf, nqf;
1041 NYD_ENTER;
1043 if (args[0] == NULL) {
1044 rv = _list_macros(MA_ACC);
1045 goto jleave;
1048 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1049 if (args[2] != NULL) {
1050 fprintf(stderr, tr(517, "Syntax is: account <name> {\n"));
1051 goto jleave;
1053 if (!asccasecmp(args[0], ACCOUNT_NULL)) {
1054 fprintf(stderr, tr(521, "Error: `%s' is a reserved name.\n"),
1055 ACCOUNT_NULL);
1056 goto jleave;
1058 rv = !_define1(args[0], MA_ACC);
1059 goto jleave;
1062 if (inhook) {
1063 fprintf(stderr, tr(518, "Cannot change account from within a hook.\n"));
1064 goto jleave;
1067 save_mbox_for_possible_quitstuff();
1069 mp = NULL;
1070 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
1071 (mp = _malook(args[0], NULL, MA_ACC)) == NULL) {
1072 fprintf(stderr, tr(519, "Account `%s' does not exist.\n"), args[0]);
1073 goto jleave;
1076 oqf = savequitflags();
1077 if (_acc_curr != NULL && _acc_curr->ma_localopts != NULL)
1078 _localopts_unroll(&_acc_curr->ma_localopts);
1079 account_name = (mp != NULL) ? mp->ma_name : NULL;
1080 _acc_curr = mp;
1082 if (mp != NULL && _maexec(mp, &mp->ma_localopts) == CBAD) {
1083 /* XXX account switch incomplete, unroll? */
1084 fprintf(stderr, tr(520, "Switching to account `%s' failed.\n"), args[0]);
1085 goto jleave;
1088 if (!starting && !inhook) {
1089 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1090 restorequitflags(oqf);
1091 if ((i = setfile("%", 0)) < 0)
1092 goto jleave;
1093 callhook(mailname, 0);
1094 if (i > 0 && !ok_blook(emptystart))
1095 goto jleave;
1096 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1097 restorequitflags(nqf);
1099 rv = 0;
1100 jleave:
1101 NYD_LEAVE;
1102 return rv;
1105 FL int
1106 c_unaccount(void *v)
1108 int rv = 1;
1109 char **args = v;
1110 NYD_ENTER;
1112 if (*args == NULL) {
1113 fprintf(stderr, tr(504, "`%s': required arguments are missing\n"),
1114 "unaccount");
1115 goto jleave;
1118 rv = 0;
1119 do {
1120 if (account_name != NULL && !strcmp(account_name, *args)) {
1121 fprintf(stderr, tr(506,
1122 "Rejecting deletion of currently active account `%s'\n"), *args);
1123 continue;
1125 _undef1(*args, MA_ACC);
1126 } while (*++args != NULL);
1127 jleave:
1128 NYD_LEAVE;
1129 return rv;
1132 FL int
1133 c_localopts(void *v)
1135 int rv = 1;
1136 char **c = v;
1137 NYD_ENTER;
1139 if (_localopts == NULL) {
1140 fprintf(stderr, tr(522,
1141 "Cannot use `localopts' but from within a `define' or `account'\n"));
1142 goto jleave;
1145 _localopts->s_unroll = (**c == '0') ? FAL0 : TRU1;
1146 rv = 0;
1147 jleave:
1148 NYD_LEAVE;
1149 return rv;
1152 FL void
1153 temporary_localopts_free(void) /* XXX intermediate hack */
1155 struct lostack *losp;
1156 NYD_ENTER;
1158 var_clear_allow_undefined = FAL0;
1159 losp = temporary_localopts_store;
1160 temporary_localopts_store = NULL;
1161 _localopts_unroll(&losp->s_localopts);
1162 NYD_LEAVE;
1165 /* vim:set fenc=utf-8:s-it-mode */