Offer a simple coloured display..
[s-mailx.git] / acmava.c
blob0edfd33b6e9a3ad3aeee166cb91af6c2f0570a11
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 - 2013 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.
131 * Following the standard, only the part following the last '@' should
132 * be lower-cased, but practice has established otherwise here */
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 on "error" */
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 on "error" */
141 static bool_t _var_lookup(struct var_carrier *vcp);
143 /* Set variable from vcp.vc_(vmap|name|hash); sets vcp.vc_var if NULL */
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 /* Line *cp* consists 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;
181 if (*str == '\0')
182 news = UNCONST("");
183 else {
184 len = strlen(str) + 1;
185 news = smalloc(len);
186 memcpy(news, str, len);
188 return news;
191 static void
192 _vfree(char *cp)
194 if (*cp != '\0')
195 free(cp);
198 static bool_t
199 _check_special_vars(enum okeys okey, bool_t enable, char **val)
201 char *cp = NULL;
202 bool_t rv = TRU1;
203 int flag = 0;
205 switch (okey) {
206 case ok_b_debug:
207 flag = OPT_DEBUG;
208 break;
209 case ok_b_header:
210 flag = OPT_N_FLAG;
211 enable = !enable;
212 break;
213 case ok_b_skipemptybody:
214 flag = OPT_E_FLAG;
215 break;
216 case ok_b_verbose:
217 flag = OPT_VERBOSE;
218 break;
219 case ok_v_folder:
220 rv = (val == NULL || var_folder_updated(*val, &cp));
221 if (rv && cp != NULL) {
222 _vfree(*val);
223 /* It's smalloc()ed, but ensure we don't leak */
224 if (*cp == '\0') {
225 *val = UNCONST("");
226 free(cp);
227 } else
228 *val = cp;
230 break;
231 #ifdef HAVE_NCL
232 case ok_v_line_editor_cursor_right:
233 if ((rv = (val != NULL && *val != NULL))) {
234 /* Set with no value? TODO very guly */
235 if (*(cp = *val) != '\0') {
236 char const *x = cp;
237 int c;
238 do {
239 c = expand_shell_escape(&x, FAL0);
240 if (c < 0)
241 break;
242 *cp++ = (char)c;
243 } while (*x != '\0');
244 *cp++ = '\0';
247 break;
248 #endif
249 default:
250 break;
253 if (flag) {
254 if (enable)
255 options |= flag;
256 else
257 options &= ~flag;
259 return rv;
262 static char const *
263 _canonify(char const *vn)
265 if (!upperchar(*vn)) {
266 char const *vp;
268 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
270 vn = (*vp == '@') ? i_strdup(vn) : vn;
272 return vn;
275 static bool_t
276 _var_revlookup(struct var_carrier *vcp, char const *name)
278 ui32_t hash, i, j;
279 struct var_map const *vmp;
281 vcp->vc_name = name = _canonify(name);
282 vcp->vc_hash = hash = MA_NAME2HASH(name);
284 for (i = hash % _VAR_REV_PRIME, j = 0; j <= _VAR_REV_LONGEST; ++j) {
285 ui32_t x = _var_revmap[i];
286 if (x == _VAR_REV_ILL)
287 break;
288 vmp = _var_map + x;
289 if (vmp->vm_hash == hash &&
290 !strcmp(_var_keydat + vmp->vm_keyoff, name)) {
291 vcp->vc_vmap = vmp;
292 vcp->vc_okey = (enum okeys)x;
293 goto jleave;
295 if (++i == _VAR_REV_PRIME) {
296 #ifdef _VAR_REV_WRAPAROUND
297 i = 0;
298 #else
299 break;
300 #endif
303 vcp->vc_vmap = NULL;
304 vcp = NULL;
305 jleave:
306 return (vcp != NULL);
309 static bool_t
310 _var_lookup(struct var_carrier *vcp)
312 struct var **vap, *lvp, *vp;
314 vap = _vars + (vcp->vc_prime = MA_HASH2PRIME(vcp->vc_hash));
316 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
317 if (strcmp(vp->v_name, vcp->vc_name) == 0) {
318 /* Relink as head, hope it "sorts on usage" over time */
319 if (lvp != NULL) {
320 lvp->v_link = vp->v_link;
321 vp->v_link = *vap;
322 *vap = vp;
324 goto jleave;
326 vp = NULL;
327 jleave:
328 vcp->vc_var = vp;
329 return (vp != NULL);
332 static bool_t
333 _var_set(struct var_carrier *vcp, char const *value)
335 bool_t err;
336 struct var *vp;
337 char *oval;
339 if (value == NULL) {
340 bool_t tmp = var_clear_allow_undefined;
341 var_clear_allow_undefined = TRU1;
342 err = _var_clear(vcp);
343 var_clear_allow_undefined = tmp;
344 goto jleave;
347 _var_lookup(vcp);
349 /* Don't care what happens later on, store this in the unroll list */
350 if (_localopts != NULL)
351 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
353 if ((vp = vcp->vc_var) == NULL) {
354 size_t l = strlen(vcp->vc_name) + 1;
356 vcp->vc_var =
357 vp = smalloc(sizeof(*vp) - VFIELD_SIZEOF(struct var, v_name) + l);
358 vp->v_link = _vars[vcp->vc_prime];
359 _vars[vcp->vc_prime] = vp;
360 memcpy(vp->v_name, vcp->vc_name, l);
361 oval = UNCONST("");
362 } else
363 oval = vp->v_value;
364 vp->v_value = _vcopy(value);
366 /* Check if update allowed XXX wasteful on error! */
367 if (vcp->vc_vmap != NULL && vcp->vc_vmap->vm_special &&
368 (err = !_check_special_vars(vcp->vc_okey, TRU1, &vp->v_value))) {
369 char *cp = vp->v_value;
370 vp->v_value = oval;
371 oval = cp;
373 if (*oval != '\0')
374 _vfree(oval);
375 err = FAL0;
376 jleave:
377 return err;
380 static bool_t
381 _var_clear(struct var_carrier *vcp)
383 bool_t err = TRU1;
385 if (!_var_lookup(vcp)) {
386 if (!sourcing && !var_clear_allow_undefined) {
387 fprintf(stderr, tr(203, "\"%s\": undefined variable\n"),
388 vcp->vc_name);
389 goto jleave;
391 } else {
392 if (_localopts != NULL)
393 _localopts_add(_localopts, vcp->vc_name, vcp->vc_var);
395 /* Always listhead after _var_lookup() */
396 _vars[vcp->vc_prime] = _vars[vcp->vc_prime]->v_link;
397 _vfree(vcp->vc_var->v_value);
398 free(vcp->vc_var);
399 vcp->vc_var = NULL;
401 if (vcp->vc_vmap != NULL && vcp->vc_vmap->vm_special &&
402 !_check_special_vars(vcp->vc_okey, FAL0, NULL))
403 goto jleave;
405 err = FAL0;
406 jleave:
407 return err;
410 static bool_t
411 _is_closing_angle(char const *cp)
413 bool_t rv = FAL0;
415 while (spacechar(*cp))
416 ++cp;
417 if (*cp++ != '}')
418 goto jleave;
419 while (spacechar(*cp))
420 ++cp;
421 rv = (*cp == '\0');
422 jleave:
423 return rv;
426 static struct macro *
427 _malook(char const *name, struct macro *data, enum ma_flags mafl)
429 enum ma_flags save_mafl;
430 ui32_t h;
431 struct macro *lmp, *mp;
433 save_mafl = mafl;
434 mafl &= MA_TYPE_MASK;
435 h = MA_NAME2HASH(name);
436 h = MA_HASH2PRIME(h);
438 for (lmp = NULL, mp = _macros[h]; mp != NULL; lmp = mp, mp = mp->ma_next) {
439 if ((mp->ma_flags & MA_TYPE_MASK) == mafl &&
440 strcmp(mp->ma_name, name) == 0) {
441 if (save_mafl & MA_UNDEF) {
442 if (lmp == NULL)
443 _macros[h] = mp->ma_next;
444 else
445 lmp->ma_next = mp->ma_next;
447 goto jleave;
451 if (data != NULL) {
452 data->ma_next = _macros[h];
453 _macros[h] = data;
454 mp = NULL;
456 jleave:
457 return mp;
460 static int
461 _maexec(struct macro const *mp, struct var **unroller)
463 struct lostack los;
464 int rv = 0;
465 struct mline const *lp;
466 char *buf = ac_alloc(mp->ma_maxlen + 1);
468 los.s_up = _localopts;
469 los.s_mac = UNCONST(mp);
470 los.s_localopts = NULL;
471 los.s_unroll = FAL0;
472 _localopts = &los;
474 for (lp = mp->ma_contents; lp; lp = lp->l_next) {
475 var_clear_allow_undefined = TRU1;
476 memcpy(buf, lp->l_line, lp->l_length + 1);
477 rv |= execute(buf, 0, lp->l_length); /* XXX break if != 0 ? */
478 var_clear_allow_undefined = FAL0;
481 _localopts = los.s_up;
482 if (unroller == NULL)
483 _localopts_unroll(&los.s_localopts);
484 else
485 *unroller = los.s_localopts;
487 ac_free(buf);
488 return rv;
491 static int
492 _list_macros(enum ma_flags mafl)
494 FILE *fp;
495 char *cp;
496 char const *typestr;
497 struct macro *mq;
498 ui32_t ti, mc;
499 struct mline *lp;
501 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
502 perror("tmpfile");
503 return 1;
505 rm(cp);
506 Ftfree(&cp);
508 mafl &= MA_TYPE_MASK;
509 typestr = (mafl & MA_ACC) ? "account" : "define";
511 for (ti = mc = 0; ti < MA_PRIME; ++ti)
512 for (mq = _macros[ti]; mq; mq = mq->ma_next)
513 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
514 if (++mc > 1)
515 fputc('\n', fp);
516 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
517 for (lp = mq->ma_contents; lp; lp = lp->l_next)
518 fprintf(fp, " %s\n", lp->l_line);
519 fputs("}\n", fp);
521 if (mc)
522 page_or_print(fp, 0);
524 mc = (ui32_t)ferror(fp);
525 Fclose(fp);
526 return (int)mc;
529 static bool_t
530 _define1(char const *name, enum ma_flags mafl)
532 bool_t rv = FAL0;
533 struct macro *mp;
534 struct mline *lp, *lst = NULL, *lnd = NULL;
535 char *linebuf = NULL, *cp;
536 size_t linesize = 0, maxlen = 0;
537 int n, i;
539 mp = scalloc(1, sizeof *mp);
540 mp->ma_name = sstrdup(name);
541 mp->ma_flags = mafl;
543 for (;;) {
544 n = readline_input(LNED_LF_ESC, "", &linebuf, &linesize);
545 if (n <= 0) {
546 fprintf(stderr, tr(75, "Unterminated %s definition: \"%s\".\n"),
547 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
548 if (sourcing)
549 unstack();
550 goto jerr;
552 if (_is_closing_angle(linebuf))
553 break;
555 /* Trim WS */
556 for (cp = linebuf, i = 0; i < n; ++cp, ++i)
557 if (!whitechar(*cp))
558 break;
559 if (i == n)
560 continue;
561 n -= i;
562 while (whitechar(cp[n - 1]))
563 if (--n == 0)
564 break;
565 if (n == 0)
566 continue;
568 maxlen = MAX(maxlen, (size_t)n);
569 cp[n++] = '\0';
571 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n);
572 memcpy(lp->l_line, cp, n);
573 lp->l_length = (size_t)--n;
574 if (lst != NULL) {
575 lnd->l_next = lp;
576 lnd = lp;
577 } else
578 lst = lnd = lp;
580 mp->ma_contents = lst;
581 mp->ma_maxlen = maxlen;
583 if (_malook(mp->ma_name, mp, mafl) != NULL) {
584 if (!(mafl & MA_ACC)) {
585 fprintf(stderr, tr(76, "A macro named \"%s\" already exists.\n"),
586 mp->ma_name);
587 lst = mp->ma_contents;
588 goto jerr;
590 _undef1(mp->ma_name, MA_ACC);
591 _malook(mp->ma_name, mp, MA_ACC);
594 rv = TRU1;
595 jleave:
596 if (linebuf != NULL)
597 free(linebuf);
598 return rv;
599 jerr:
600 if (lst != NULL)
601 _freelines(lst);
602 free(mp->ma_name);
603 free(mp);
604 goto jleave;
607 static void
608 _undef1(char const *name, enum ma_flags mafl)
610 struct macro *mp;
612 if ((mp = _malook(name, NULL, mafl | MA_UNDEF)) != NULL) {
613 _freelines(mp->ma_contents);
614 free(mp->ma_name);
615 free(mp);
619 static void
620 _freelines(struct mline *lp)
622 struct mline *lq;
624 for (lq = NULL; lp != NULL; ) {
625 if (lq != NULL)
626 free(lq);
627 lq = lp;
628 lp = lp->l_next;
630 if (lq)
631 free(lq);
634 static int
635 _var_list_all_cmp(void const *s1, void const *s2)
637 return strcmp(*(char**)UNCONST(s1), *(char**)UNCONST(s2));
640 static void
641 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
643 struct var *vap;
644 size_t nl, vl;
646 /* Propagate unrolling up the stack, as necessary */
647 while (!losp->s_unroll && (losp = losp->s_up) != NULL)
649 if (losp == NULL)
650 goto jleave;
652 /* We've found a level that wants to unroll; check wether it does it yet */
653 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
654 if (strcmp(vap->v_name, name) == 0)
655 goto jleave;
657 nl = strlen(name) + 1;
658 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
659 vap = smalloc(sizeof(*vap) - VFIELD_SIZEOF(struct var, v_name) + nl + vl);
660 vap->v_link = losp->s_localopts;
661 losp->s_localopts = vap;
662 memcpy(vap->v_name, name, nl);
663 if (vl == 0)
664 vap->v_value = NULL;
665 else {
666 vap->v_value = vap->v_name + nl;
667 memcpy(vap->v_value, ovap->v_value, vl);
669 jleave:
673 static void
674 _localopts_unroll(struct var **vapp)
676 struct lostack *save_los;
677 struct var *x, *vap;
679 vap = *vapp;
680 *vapp = NULL;
682 save_los = _localopts;
683 _localopts = NULL;
684 while (vap != NULL) {
685 x = vap;
686 vap = vap->v_link;
687 vok_vset(x->v_name, x->v_value);
688 free(x);
690 _localopts = save_los;
693 FL char *
694 _var_oklook(enum okeys okey)
696 struct var_carrier vc;
697 char *rv;
699 vc.vc_vmap = _var_map + okey;
700 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
701 vc.vc_hash = _var_map[okey].vm_hash;
702 vc.vc_okey = okey;
704 if (!_var_lookup(&vc)) {
705 if ((rv = getenv(vc.vc_name)) != NULL) {
706 _var_set(&vc, rv);
707 assert(vc.vc_var != NULL);
708 goto jvar;
710 } else
711 jvar:
712 rv = vc.vc_var->v_value;
713 return rv;
716 FL bool_t
717 _var_okset(enum okeys okey, uintptr_t val)
719 struct var_carrier vc;
721 vc.vc_vmap = _var_map + okey;
722 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
723 vc.vc_hash = _var_map[okey].vm_hash;
724 vc.vc_okey = okey;
726 return _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
729 FL bool_t
730 _var_okclear(enum okeys okey)
732 struct var_carrier vc;
734 vc.vc_vmap = _var_map + okey;
735 vc.vc_name = _var_keydat + _var_map[okey].vm_keyoff;
736 vc.vc_hash = _var_map[okey].vm_hash;
737 vc.vc_okey = okey;
739 return _var_clear(&vc);
742 FL char *
743 _var_voklook(char const *vokey)
745 struct var_carrier vc;
746 char *rv;
748 _var_revlookup(&vc, vokey);
750 if (!_var_lookup(&vc)) {
751 if ((rv = getenv(vc.vc_name)) != NULL) {
752 _var_set(&vc, rv);
753 assert(vc.vc_var != NULL);
754 goto jvar;
756 } else
757 jvar:
758 rv = vc.vc_var->v_value;
759 return rv;
762 FL bool_t
763 _var_vokset(char const *vokey, uintptr_t val)
765 struct var_carrier vc;
767 _var_revlookup(&vc, vokey);
769 return _var_set(&vc, (val == 0x1 ? "" : (char const*)val));
772 FL bool_t
773 _var_vokclear(char const *vokey)
775 struct var_carrier vc;
777 _var_revlookup(&vc, vokey);
779 return _var_clear(&vc);
782 FL void
783 var_list_all(void)
785 FILE *fp;
786 char *cp, **vacp, **cap;
787 struct var *vp;
788 size_t no, i;
789 char const *fmt;
791 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
792 perror("tmpfile");
793 goto jleave;
795 rm(cp);
796 Ftfree(&cp);
798 for (no = i = 0; i < MA_PRIME; ++i)
799 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
800 ++no;
801 vacp = salloc(no * sizeof(*vacp));
802 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
803 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
804 *cap++ = vp->v_name;
806 if (no > 1)
807 qsort(vacp, no, sizeof *vacp, &_var_list_all_cmp);
809 i = (ok_blook(bsdcompat) || ok_blook(bsdset));
810 fmt = (i != 0) ? "%s\t%s\n" : "%s=\"%s\"\n";
812 for (cap = vacp; no != 0; ++cap, --no) {
813 cp = vok_vlook(*cap); /* XXX when lookup checks value/binary, change */
814 if (cp == NULL)
815 cp = UNCONST("");
816 if (i || *cp != '\0')
817 fprintf(fp, fmt, *cap, cp);
818 else
819 fprintf(fp, "%s\n", *cap);
822 page_or_print(fp, PTR2SIZE(cap - vacp));
823 Fclose(fp);
824 jleave:
828 FL int
829 c_var_inspect(void *v)
831 struct var_carrier vc;
832 char **argv = v, *val;
833 bool_t isenv, isset;
835 if (*argv == NULL) {
836 v = NULL;
837 goto jleave;
840 for (; *argv != NULL; ++argv) {
841 memset(&vc, 0, sizeof vc);
842 _var_revlookup(&vc, *argv);
843 if (_var_lookup(&vc)) {
844 val = vc.vc_var->v_value;
845 isenv = FAL0;
846 } else
847 isenv = ((val = getenv(vc.vc_name)) != NULL);
848 if (val == NULL)
849 val = UNCONST("NULL");
850 isset = (vc.vc_var != NULL);
852 if (vc.vc_vmap != NULL) {
853 if (vc.vc_vmap->vm_binary)
854 printf("%s: binary option (%d): isset=%d/environ=%d\n",
855 vc.vc_name, vc.vc_okey, isset, isenv);
856 else
857 printf("%s: value option (%d): isset=%d/environ=%d value<%s>\n",
858 vc.vc_name, vc.vc_okey, isset, isenv, val);
859 } else
860 printf("%s: isset=%d/environ=%d value<%s>\n",
861 vc.vc_name, isset, isenv, val);
863 jleave:
864 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
867 FL int
868 c_define(void *v)
870 int rv = 1;
871 char **args = v;
872 char const *errs;
874 if (args[0] == NULL) {
875 errs = tr(504, "Missing macro name to `define'");
876 goto jerr;
878 if (args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
879 args[2] != NULL) {
880 errs = tr(505, "Syntax is: define <name> {");
881 goto jerr;
883 rv = !_define1(args[0], MA_NONE);
884 jleave:
885 return rv;
886 jerr:
887 fprintf(stderr, "%s\n", errs);
888 goto jleave;
891 FL int
892 c_undef(void *v)
894 int rv = 1;
895 char **args = v;
897 if (*args == NULL) {
898 fprintf(stderr, tr(506, "Missing macro name to `undef'\n"));
899 goto jleave;
902 _undef1(*args, MA_NONE);
903 while (*++args != NULL);
904 rv = 0;
905 jleave:
906 return rv;
909 FL int
910 c_call(void *v)
912 int rv = 1;
913 char **args = v;
914 char const *errs, *name;
915 struct macro *mp;
917 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
918 errs = tr(507, "Syntax is: call <%s>\n");
919 name = "name";
920 goto jerr;
923 if ((mp = _malook(*args, NULL, MA_NONE)) == NULL) {
924 errs = tr(508, "Undefined macro called: \"%s\"\n");
925 name = *args;
926 goto jerr;
929 rv = _maexec(mp, NULL);
930 jleave:
931 return rv;
932 jerr:
933 fprintf(stderr, errs, name);
934 goto jleave;
937 FL int
938 callhook(char const *name, int nmail)
940 int len, rv;
941 struct macro *mp;
942 char *var, *cp;
944 var = ac_alloc(len = strlen(name) + 13);
945 snprintf(var, len, "folder-hook-%s", name);
946 if ((cp = vok_vlook(var)) == NULL && (cp = ok_vlook(folder_hook)) == NULL) {
947 rv = 0;
948 goto jleave;
950 if ((mp = _malook(cp, NULL, MA_NONE)) == NULL) {
951 fprintf(stderr, tr(49, "Cannot call hook for folder \"%s\": "
952 "Macro \"%s\" does not exist.\n"), name, cp);
953 rv = 1;
954 goto jleave;
957 inhook = nmail ? 3 : 1; /* XXX enum state machine */
958 rv = _maexec(mp, NULL);
959 inhook = 0;
960 jleave:
961 ac_free(var);
962 return rv;
965 FL int
966 c_defines(void *v)
968 UNUSED(v);
969 return _list_macros(MA_NONE);
972 FL int
973 c_account(void *v)
975 char **args = v;
976 struct macro *mp;
977 int rv = 1, i, oqf, nqf;
979 if (args[0] == NULL) {
980 rv = _list_macros(MA_ACC);
981 goto jleave;
984 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
985 if (args[2] != NULL) {
986 fprintf(stderr, tr(517, "Syntax is: account <name> {\n"));
987 goto jleave;
989 if (asccasecmp(args[0], ACCOUNT_NULL) == 0) {
990 fprintf(stderr, tr(521, "Error: `%s' is a reserved name.\n"),
991 ACCOUNT_NULL);
992 goto jleave;
994 rv = !_define1(args[0], MA_ACC);
995 goto jleave;
998 if (inhook) {
999 fprintf(stderr, tr(518, "Cannot change account from within a hook.\n"));
1000 goto jleave;
1003 save_mbox_for_possible_quitstuff();
1005 mp = NULL;
1006 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
1007 (mp = _malook(args[0], NULL, MA_ACC)) == NULL) {
1008 fprintf(stderr, tr(519, "Account `%s' does not exist.\n"), args[0]);
1009 goto jleave;
1012 oqf = savequitflags();
1013 if (_acc_curr != NULL)
1014 _localopts_unroll(&_acc_curr->ma_localopts);
1015 account_name = (mp != NULL) ? mp->ma_name : NULL;
1016 _acc_curr = mp;
1018 if (mp != NULL && _maexec(mp, &mp->ma_localopts) == CBAD) {
1019 /* XXX account switch incomplete, unroll? */
1020 fprintf(stderr, tr(520, "Switching to account `%s' failed.\n"), args[0]);
1021 goto jleave;
1024 if (!starting && !inhook) {
1025 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1026 restorequitflags(oqf);
1027 if ((i = setfile("%", 0)) < 0)
1028 goto jleave;
1029 callhook(mailname, 0);
1030 if (i > 0 && !ok_blook(emptystart))
1031 goto jleave;
1032 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1033 restorequitflags(nqf);
1035 rv = 0;
1036 jleave:
1037 return rv;
1040 FL int
1041 c_localopts(void *v)
1043 int rv = 1;
1044 char **c = v;
1046 if (_localopts == NULL) {
1047 fprintf(stderr, tr(522,
1048 "Cannot use `localopts' but from within a `define' or `account'\n"));
1049 goto jleave;
1052 _localopts->s_unroll = (**c == '0') ? FAL0 : TRU1;
1053 rv = 0;
1054 jleave:
1055 return rv;
1058 /* vim:set fenc=utf-8:s-it-mode */