Use new vok_ interface
[s-mailx.git] / acmava.c
blob3826058e97e1cd19dfe867a302224b4fe7fd30cd
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 * TODO in general it would be nice if it would be possible to define "macros"
46 * TODO etc. inside of other "macros"
49 #define MA_PRIME HSHSIZE
50 #define MA_HASH(S) (strhash(S) % MA_PRIME)
52 enum ma_flags {
53 MA_NONE = 0,
54 MA_ACC = 1<<0,
55 MA_TYPE_MASK = MA_ACC,
56 MA_UNDEF = 1<<1 /* Unlink after lookup */
59 struct macro {
60 struct macro *ma_next;
61 char *ma_name;
62 struct mline *ma_contents;
63 size_t ma_maxlen; /* Maximum line length */
64 enum ma_flags ma_flags;
65 struct var *ma_localopts; /* `account' unroll list, for `localopts' */
68 struct mline {
69 struct mline *l_next;
70 size_t l_length;
71 char l_line[VFIELD_SIZE(sizeof(size_t))];
74 struct var {
75 struct var *v_link;
76 char *v_name;
77 char *v_value;
80 struct lostack {
81 struct lostack *s_up; /* Outer context */
82 struct macro *s_mac; /* Context (`account' or `define') */
83 struct var *s_localopts;
84 bool_t s_unroll; /* Unroll? */
87 static struct macro *_acc_curr; /* Currently active account */
88 static struct lostack *_localopts; /* Currently executing macro unroll list */
90 /* TODO once we have a dynamically sized hashtable we could unite _macros and
91 * TODO _variables into a single hashtable, stripping down fun interface;
92 * TODO also, setting and clearing a variable can be easily joined */
93 static struct macro *_macros[MA_PRIME]; /* TODO dynamically spaced */
94 static struct var *_vars[MA_PRIME]; /* TODO dynamically spaced */
96 /* Special cased value string allocation */
97 static char * _vcopy(char const *str);
98 static void _vfree(char *cp);
100 /* Check for special housekeeping. */
101 static bool_t _check_special_vars(char const *name, bool_t enable,
102 char **val);
104 /* If a variable name begins with a lowercase-character and contains at
105 * least one '@', it is converted to all-lowercase. This is necessary
106 * for lookups of names based on email addresses.
108 * Following the standard, only the part following the last '@' should
109 * be lower-cased, but practice has established otherwise here */
110 static char const * _canonify(char const *vn);
112 /* Locate a variable and return its variable node */
113 static struct var * _lookup(char const *name, ui_it h, bool_t hisset);
115 /* Line *cp* consists solely of WS and a } */
116 static bool_t _is_closing_angle(char const *cp);
118 /* Lookup for macros/accounts */
119 static struct macro *_malook(char const *name, struct macro *data,
120 enum ma_flags mafl);
122 /* Walk all lines of a macro and execute() them */
123 static int _maexec(struct macro const *mp, struct var **unroll_store);
125 /* User display helpers */
126 static int _list_macros(enum ma_flags mafl);
128 /* */
129 static bool_t _define1(char const *name, enum ma_flags mafl);
130 static void _undef1(char const *name, enum ma_flags mafl);
131 static void _freelines(struct mline *lp);
133 /* qsort(3) helper */
134 static int __var_list_all_cmp(void const *s1, void const *s2);
136 /* Update replay-log */
137 static void _localopts_add(struct lostack *losp, char const *name,
138 struct var *ovap);
139 static void _localopts_unroll(struct var **vapp);
141 static char *
142 _vcopy(char const *str)
144 char *news;
145 size_t len;
147 if (*str == '\0')
148 news = UNCONST("");
149 else {
150 len = strlen(str) + 1;
151 news = smalloc(len);
152 memcpy(news, str, len);
154 return news;
157 static void
158 _vfree(char *cp)
160 if (*cp != '\0')
161 free(cp);
164 static bool_t
165 _check_special_vars(char const *name, bool_t enable, char **val)
167 /* TODO _check_special_vars --> value cache
168 * TODO in general: some may not be unset etc. etc. All this shouldn't
169 * TODO be handled like this, but through a generic value-cache interface,
170 * TODO which may apply constaints *before* use; also see below */
171 char *cp = NULL;
172 bool_t rv = TRU1;
173 int flag = 0;
175 if (strcmp(name, "debug") == 0)
176 flag = OPT_DEBUG;
177 else if (strcmp(name, "header") == 0)
178 flag = OPT_N_FLAG, enable = !enable;
179 else if (strcmp(name, "skipemptybody") == 0)
180 flag = OPT_E_FLAG;
181 else if (strcmp(name, "verbose") == 0)
182 flag = OPT_VERBOSE;
183 else if (strcmp(name, "folder") == 0) {
184 rv = (val == NULL || var_folder_updated(*val, &cp));
185 if (rv && cp != NULL) {
186 _vfree(*val);
187 /* It's smalloc()ed, but ensure we don't leak */
188 if (*cp == '\0') {
189 *val = UNCONST("");
190 free(cp);
191 } else
192 *val = cp;
195 #ifdef HAVE_NCL
196 else if (strcmp(name, "line-editor-cursor-right") == 0 &&
197 (rv = (val != NULL && *val != NULL))) {
198 char const *x = cp = *val;
199 int c;
201 /* Set with no value? *//*TODO invalid,but no way to "re-unset";see above
202 * TODO adjust tty.c when line-editor-cursor-right is properly handled in
203 * TODO here */
204 if (*x != '\0') {
205 do {
206 c = expand_shell_escape(&x, FAL0);
207 if (c < 0)
208 break;
209 *cp++ = (char)c;
210 } while (*x != '\0');
211 *cp++ = '\0';
214 #endif
216 if (flag) {
217 if (enable)
218 options |= flag;
219 else
220 options &= ~flag;
222 return rv;
225 static char const *
226 _canonify(char const *vn)
228 if (! upperchar(*vn)) {
229 char const *vp;
231 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
233 vn = (*vp == '@') ? i_strdup(vn) : vn;
235 return vn;
238 static struct var *
239 _lookup(char const *name, ui_it h, bool_t hisset)
241 struct var **vap, *lvp, *vp;
243 if (! hisset)
244 h = MA_HASH(name);
245 vap = _vars + h;
247 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
248 if (*vp->v_name == *name && strcmp(vp->v_name, name) == 0) {
249 /* Relink as head, hope it "sorts on usage" over time */
250 if (lvp != NULL) {
251 lvp->v_link = vp->v_link;
252 vp->v_link = *vap;
253 *vap = vp;
255 goto jleave;
257 vp = NULL;
258 jleave:
259 return vp;
262 static bool_t
263 _is_closing_angle(char const *cp)
265 bool_t rv = FAL0;
266 while (spacechar(*cp))
267 ++cp;
268 if (*cp++ != '}')
269 goto jleave;
270 while (spacechar(*cp))
271 ++cp;
272 rv = (*cp == '\0');
273 jleave:
274 return rv;
277 static struct macro *
278 _malook(char const *name, struct macro *data, enum ma_flags mafl)
280 enum ma_flags save_mafl;
281 ui_it h;
282 struct macro *lmp, *mp;
284 save_mafl = mafl;
285 mafl &= MA_TYPE_MASK;
286 h = MA_HASH(name);
288 for (lmp = NULL, mp = _macros[h]; mp != NULL; lmp = mp, mp = mp->ma_next) {
289 if ((mp->ma_flags & MA_TYPE_MASK) == mafl &&
290 strcmp(mp->ma_name, name) == 0) {
291 if (save_mafl & MA_UNDEF) {
292 if (lmp == NULL)
293 _macros[h] = mp->ma_next;
294 else
295 lmp->ma_next = mp->ma_next;
297 goto jleave;
301 if (data != NULL) {
302 data->ma_next = _macros[h];
303 _macros[h] = data;
304 mp = NULL;
306 jleave:
307 return mp;
310 static int
311 _maexec(struct macro const *mp, struct var **unroll_store)
313 struct lostack los;
314 int rv = 0;
315 struct mline const *lp;
316 char *buf = ac_alloc(mp->ma_maxlen + 1);
318 los.s_up = _localopts;
319 los.s_mac = UNCONST(mp);
320 los.s_localopts = NULL;
321 los.s_unroll = FAL0;
322 _localopts = &los;
324 for (lp = mp->ma_contents; lp; lp = lp->l_next) {
325 var_unset_allow_undefined = TRU1;
326 memcpy(buf, lp->l_line, lp->l_length + 1);
327 rv |= execute(buf, 0, lp->l_length); /* XXX break if != 0 ? */
328 var_unset_allow_undefined = FAL0;
331 _localopts = los.s_up;
332 if (unroll_store == NULL)
333 _localopts_unroll(&los.s_localopts);
334 else
335 *unroll_store = los.s_localopts;
337 ac_free(buf);
338 return rv;
341 static int
342 _list_macros(enum ma_flags mafl)
344 FILE *fp;
345 char *cp;
346 char const *typestr;
347 struct macro *mq;
348 ui_it ti, mc;
349 struct mline *lp;
351 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
352 perror("tmpfile");
353 return 1;
355 rm(cp);
356 Ftfree(&cp);
358 mafl &= MA_TYPE_MASK;
359 typestr = (mafl & MA_ACC) ? "account" : "define";
361 for (ti = mc = 0; ti < MA_PRIME; ++ti)
362 for (mq = _macros[ti]; mq; mq = mq->ma_next)
363 if ((mq->ma_flags & MA_TYPE_MASK) == mafl) {
364 if (++mc > 1)
365 fputc('\n', fp);
366 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
367 for (lp = mq->ma_contents; lp; lp = lp->l_next)
368 fprintf(fp, " %s\n", lp->l_line);
369 fputs("}\n", fp);
371 if (mc)
372 page_or_print(fp, 0);
374 mc = (ui_it)ferror(fp);
375 Fclose(fp);
376 return (int)mc;
379 static bool_t
380 _define1(char const *name, enum ma_flags mafl)
382 bool_t rv = FAL0;
383 struct macro *mp;
384 struct mline *lp, *lst = NULL, *lnd = NULL;
385 char *linebuf = NULL, *cp;
386 size_t linesize = 0, maxlen = 0;
387 int n, i;
389 mp = scalloc(1, sizeof *mp);
390 mp->ma_name = sstrdup(name);
391 mp->ma_flags = mafl;
393 for (;;) {
394 n = readline_input(LNED_LF_ESC, "", &linebuf, &linesize);
395 if (n <= 0) {
396 fprintf(stderr, tr(75, "Unterminated %s definition: \"%s\".\n"),
397 (mafl & MA_ACC ? "account" : "macro"), mp->ma_name);
398 if (sourcing)
399 unstack();
400 goto jerr;
402 if (_is_closing_angle(linebuf))
403 break;
405 /* Trim WS */
406 for (cp = linebuf, i = 0; i < n; ++cp, ++i)
407 if (! whitechar(*cp))
408 break;
409 if (i == n)
410 continue;
411 n -= i;
412 while (whitechar(cp[n - 1]))
413 if (--n == 0)
414 break;
415 if (n == 0)
416 continue;
418 maxlen = MAX(maxlen, (size_t)n);
419 cp[n++] = '\0';
421 lp = scalloc(1, sizeof(*lp) - VFIELD_SIZEOF(struct mline, l_line) + n);
422 memcpy(lp->l_line, cp, n);
423 lp->l_length = (size_t)--n;
424 if (lst != NULL) {
425 lnd->l_next = lp;
426 lnd = lp;
427 } else
428 lst = lnd = lp;
430 mp->ma_contents = lst;
431 mp->ma_maxlen = maxlen;
433 if (_malook(mp->ma_name, mp, mafl) != NULL) {
434 if (! (mafl & MA_ACC)) {
435 fprintf(stderr, tr(76, "A macro named \"%s\" already exists.\n"),
436 mp->ma_name);
437 lst = mp->ma_contents;
438 goto jerr;
440 _undef1(mp->ma_name, MA_ACC);
441 _malook(mp->ma_name, mp, MA_ACC);
444 rv = TRU1;
445 jleave:
446 if (linebuf != NULL)
447 free(linebuf);
448 return rv;
449 jerr:
450 if (lst != NULL)
451 _freelines(lst);
452 free(mp->ma_name);
453 free(mp);
454 goto jleave;
457 static void
458 _undef1(char const *name, enum ma_flags mafl)
460 struct macro *mp;
462 if ((mp = _malook(name, NULL, mafl | MA_UNDEF)) != NULL) {
463 _freelines(mp->ma_contents);
464 free(mp->ma_name);
465 free(mp);
469 static void
470 _freelines(struct mline *lp)
472 struct mline *lq;
474 for (lq = NULL; lp != NULL; ) {
475 if (lq != NULL)
476 free(lq);
477 lq = lp;
478 lp = lp->l_next;
480 if (lq)
481 free(lq);
484 static int
485 __var_list_all_cmp(void const *s1, void const *s2)
487 return strcmp(*(char**)UNCONST(s1), *(char**)UNCONST(s2));
490 static void
491 _localopts_add(struct lostack *losp, char const *name, struct var *ovap)
493 struct var *vap;
494 size_t nl, vl;
496 /* Propagate unrolling up the stack, as necessary */
497 while (! losp->s_unroll && (losp = losp->s_up) != NULL)
499 if (losp == NULL)
500 goto jleave;
502 /* We have found a level that wants to unroll; check wether it does it yet */
503 for (vap = losp->s_localopts; vap != NULL; vap = vap->v_link)
504 if (strcmp(vap->v_name, name) == 0)
505 goto jleave;
507 nl = strlen(name) + 1;
508 vl = (ovap != NULL) ? strlen(ovap->v_value) + 1 : 0;
509 vap = smalloc(sizeof(*vap) + nl + vl);
510 vap->v_link = losp->s_localopts;
511 losp->s_localopts = vap;
512 vap->v_name = (char*)(vap + 1);
513 memcpy(vap->v_name, name, nl);
514 if (vl == 0)
515 vap->v_value = NULL;
516 else {
517 vap->v_value = (char*)(vap + 1) + nl;
518 memcpy(vap->v_value, ovap->v_value, vl);
520 jleave:
524 static void
525 _localopts_unroll(struct var **vapp)
527 struct lostack *save_los;
528 struct var *x, *vap;
530 vap = *vapp;
531 *vapp = NULL;
533 save_los = _localopts;
534 _localopts = NULL;
535 while (vap != NULL) {
536 x = vap;
537 vap = vap->v_link;
538 var_assign(x->v_name, x->v_value);
539 free(x);
541 _localopts = save_los;
544 FL bool_t
545 var_assign(char const *name, char const *val)
547 bool_t err;
548 struct var *vp;
549 ui_it h;
550 char *oval;
552 if (val == NULL) {
553 bool_t tmp = var_unset_allow_undefined;
554 var_unset_allow_undefined = TRU1;
555 err = var_unset(name);
556 var_unset_allow_undefined = tmp;
557 goto jleave;
560 name = _canonify(name);
561 h = MA_HASH(name);
562 vp = _lookup(name, h, TRU1);
564 /* Don't care what happens later on, store this in the unroll list */
565 if (_localopts != NULL)
566 _localopts_add(_localopts, name, vp);
568 if (vp == NULL) {
569 vp = (struct var*)scalloc(1, sizeof *vp);
570 vp->v_name = _vcopy(name);
571 vp->v_link = _vars[h];
572 _vars[h] = vp;
573 oval = UNCONST("");
574 } else
575 oval = vp->v_value;
576 vp->v_value = _vcopy(val);
578 /* Check if update allowed XXX wasteful on error! */
579 if ((err = !_check_special_vars(name, TRU1, &vp->v_value))) {
580 char *cp = vp->v_value;
581 vp->v_value = oval;
582 oval = cp;
584 if (*oval != '\0')
585 _vfree(oval);
586 jleave:
587 return err;
590 FL bool_t
591 var_unset(char const *name)
593 int err = TRU1;
594 ui_it h;
595 struct var *vp;
597 name = _canonify(name);
598 h = MA_HASH(name);
599 vp = _lookup(name, h, TRU1);
601 if (vp == NULL) {
602 if (!sourcing && !var_unset_allow_undefined) {
603 fprintf(stderr, tr(203, "\"%s\": undefined variable\n"), name);
604 goto jleave;
606 } else {
607 if (_localopts != NULL)
608 _localopts_add(_localopts, name, vp);
610 /* Always listhead after _lookup() */
611 _vars[h] = _vars[h]->v_link;
612 _vfree(vp->v_name);
613 _vfree(vp->v_value);
614 free(vp);
616 if (!_check_special_vars(name, FAL0, NULL))
617 goto jleave;
619 err = FAL0;
620 jleave:
621 return err;
624 FL char *
625 var_lookup(char const *name, bool_t look_environ)
627 struct var *vp;
628 char *rv;
630 name = _canonify(name);
631 if ((vp = _lookup(name, 0, FAL0)) != NULL)
632 rv = vp->v_value;
633 else if (! look_environ)
634 rv = NULL;
635 else if ((rv = getenv(name)) != NULL && *rv != '\0')
636 rv = savestr(rv);
637 return rv;
640 static char const * const _tempo_okmap[] = {
641 "add-file-recipients",
642 "allnet",
643 "append",
644 "ask",
645 "askatend",
646 "askattach",
647 "askbcc",
648 "askcc",
649 "asksign",
650 "asksub",
651 "attachment-ask-content-description",
652 "attachment-ask-content-disposition",
653 "attachment-ask-content-id",
654 "attachment-ask-content-type",
655 "autocollapse",
656 "autoprint",
657 "autothread",
658 "bang",
659 "batch-exit-on-error",
660 "bsdannounce",
661 "bsdcompat",
662 "bsdflags",
663 "bsdheadline",
664 "bsdmsgs",
665 "bsdorder",
666 "bsdset",
667 "debug",
668 "disconnected",
669 "dot",
670 "editalong",
671 "editheaders",
672 "emptybox",
673 "emptystart",
674 "flipr",
675 "forward-as-attachment",
676 "fullnames",
677 "header",
678 "hold",
679 "idna-disable",
680 "ignore",
681 "ignoreeof",
682 "imap-use-starttls",
683 "keep",
684 "keep-content-length",
685 "keepsave",
686 "line-editor-disable",
687 "markanswered",
688 "message-id-disable",
689 "metoo",
690 "mime-allow-text-controls",
691 "mime-counter-evidence",
692 "outfolder",
693 "page",
694 "piperaw",
695 "pop3-bulk-load",
696 "pop3-no-apop",
697 "pop3-use-starttls",
698 "print-all-chars",
699 "print-alternatives",
700 "quiet",
701 "quote-as-attachment",
702 "recipients-in-cc",
703 "record-resent",
704 "reply-in-same-charset",
705 "Replyall",
706 "rfc822-body-from_",
707 "save",
708 "searchheaders",
709 "sendcharsets-else-ttycharset",
710 "sendwait",
711 "showlast",
712 "showname",
713 "showto",
714 "skipemptybody",
715 "smime-force-encryption",
716 "smime-no-default-ca",
717 "smime-sign",
718 "smtp-use-starttls",
719 "ssl-no-default-ca",
720 "ssl-v2-allow",
721 "writebackedited",
722 "verbose",
724 "attrlist",
725 "autobcc",
726 "autocc",
727 "autoinc",
728 "autosort",
729 "charset-7bit",
730 "charset-8bit",
731 "cmd",
732 "crt",
733 "datefield",
734 "datefield-markout-older",
735 "DEAD",
736 "EDITOR",
737 "encoding",
738 "escape",
739 "folder",
740 "folder-hook",
741 "from",
742 "fwdheading",
743 "headline",
744 "hostname",
745 "imap-auth",
746 "imap-cache",
747 "imap-keepalive",
748 "imap-list-depth",
749 "indentprefix",
750 "line-editor-cursor-right",
751 "LISTER",
752 "MAIL",
753 "MBOX",
754 "mimetypes-load-control",
755 "NAIL_EXTRA_RC",
756 "NAIL_HEAD",
757 "NAIL_HISTFILE",
758 "NAIL_HISTSIZE",
759 "NAIL_TAIL",
760 "newfolders",
761 "newmail",
762 "ORGANIZATION",
763 "PAGER",
764 "pop3-keepalive",
765 "prompt",
766 "quote",
767 "quote-fold",
768 "record",
769 "replyto",
770 "screen",
771 "sendcharsets",
772 "sender",
773 "sendmail",
774 "sendmail-progname",
775 "SHELL",
776 "Sign",
777 "sign",
778 "signature",
779 "smime-ca-dir",
780 "smime-ca-file",
781 "smime-crl-dir",
782 "smime-crl-file",
783 "smime-sign-cert",
784 "smime-sign-include-certs",
785 "smtp",
786 "smtp-auth",
787 "smtp-auth-password",
788 "smtp-auth-user",
789 "spam-command",
790 "spam-host",
791 "spam-maxsize",
792 "spam-port",
793 "spam-socket",
794 "spam-user",
795 "ssl-ca-dir",
796 "ssl-ca-file",
797 "ssl-cert",
798 "ssl-cipher-list",
799 "ssl-crl-dir",
800 "ssl-crl-file",
801 "ssl-key",
802 "ssl-method",
803 "ssl-rand-egd",
804 "ssl-rand-file",
805 "ssl-verify",
806 "stealthmua",
807 "toplines",
808 "ttycharset",
809 "VISUAL"
812 FL char *
813 _var_oklook(enum okeys okey)
815 char const *k = _tempo_okmap[okey];
817 return var_lookup(k, TRU1);
820 FL bool_t
821 _var_okset(enum okeys okey, uintptr_t val)
823 char const *k = _tempo_okmap[okey];
825 return var_assign(k, (val == 0x1) ? "" : (char const*)val);
828 FL bool_t
829 _var_okclear(enum okeys okey)
831 char const *k = _tempo_okmap[okey];
833 return var_unset(k);
836 FL char *
837 _var_voklook(char const *vokey)
839 return var_lookup(vokey, TRU1);
842 FL bool_t
843 _var_vokset(char const *vokey, uintptr_t val)
845 return var_assign(vokey, (val == 0x1) ? "" : (char const*)val);
848 FL bool_t
849 _var_vokclear(char const *vokey)
851 return var_unset(vokey);
854 FL void
855 var_list_all(void)
857 FILE *fp;
858 char *cp, **vacp, **cap;
859 struct var *vp;
860 size_t no, i;
861 char const *fmt;
863 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
864 perror("tmpfile");
865 goto jleave;
867 rm(cp);
868 Ftfree(&cp);
870 for (no = i = 0; i < MA_PRIME; ++i)
871 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
872 ++no;
873 vacp = salloc(no * sizeof(*vacp));
874 for (cap = vacp, i = 0; i < MA_PRIME; ++i)
875 for (vp = _vars[i]; vp != NULL; vp = vp->v_link)
876 *cap++ = vp->v_name;
878 if (no > 1)
879 qsort(vacp, no, sizeof *vacp, &__var_list_all_cmp);
881 i = (ok_blook(bsdcompat) || ok_blook(bsdset));
882 fmt = (i != 0) ? "%s\t%s\n" : "%s=\"%s\"\n";
884 for (cap = vacp; no != 0; ++cap, --no) {
885 cp = vok_vlook(*cap); /* XXX when lookup checks value/binary, change */
886 if (cp == NULL)
887 cp = UNCONST("");
888 if (i || *cp != '\0')
889 fprintf(fp, fmt, *cap, cp);
890 else
891 fprintf(fp, "%s\n", *cap);
894 page_or_print(fp, (size_t)(cap - vacp));
895 Fclose(fp);
896 jleave:
900 FL int
901 cdefine(void *v)
903 int rv = 1;
904 char **args = v;
905 char const *errs;
907 if (args[0] == NULL) {
908 errs = tr(504, "Missing macro name to `define'");
909 goto jerr;
911 if (args[1] == NULL || strcmp(args[1], "{") || args[2] != NULL) {
912 errs = tr(505, "Syntax is: define <name> {");
913 goto jerr;
915 rv = ! _define1(args[0], MA_NONE);
916 jleave:
917 return rv;
918 jerr:
919 fprintf(stderr, "%s\n", errs);
920 goto jleave;
923 FL int
924 cundef(void *v)
926 int rv = 1;
927 char **args = v;
929 if (*args == NULL) {
930 fprintf(stderr, tr(506, "Missing macro name to `undef'\n"));
931 goto jleave;
934 _undef1(*args, MA_NONE);
935 while (*++args);
936 rv = 0;
937 jleave:
938 return rv;
941 FL int
942 ccall(void *v)
944 int rv = 1;
945 char **args = v;
946 char const *errs, *name;
947 struct macro *mp;
949 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
950 errs = tr(507, "Syntax is: call <%s>\n");
951 name = "name";
952 goto jerr;
955 if ((mp = _malook(*args, NULL, MA_NONE)) == NULL) {
956 errs = tr(508, "Undefined macro called: \"%s\"\n");
957 name = *args;
958 goto jerr;
961 rv = _maexec(mp, NULL);
962 jleave:
963 return rv;
964 jerr:
965 fprintf(stderr, errs, name);
966 goto jleave;
969 FL int
970 callhook(char const *name, int nmail)
972 int len, rv;
973 struct macro *mp;
974 char *var, *cp;
976 var = ac_alloc(len = strlen(name) + 13);
977 snprintf(var, len, "folder-hook-%s", name);
978 if ((cp = vok_vlook(var)) == NULL && (cp = ok_vlook(folder_hook)) == NULL) {
979 rv = 0;
980 goto jleave;
982 if ((mp = _malook(cp, NULL, MA_NONE)) == NULL) {
983 fprintf(stderr, tr(49, "Cannot call hook for folder \"%s\": "
984 "Macro \"%s\" does not exist.\n"), name, cp);
985 rv = 1;
986 goto jleave;
989 inhook = nmail ? 3 : 1;
990 rv = _maexec(mp, NULL);
991 inhook = 0;
992 jleave:
993 ac_free(var);
994 return rv;
997 FL int
998 cdefines(void *v)
1000 (void)v;
1001 return _list_macros(MA_NONE);
1004 FL int
1005 c_account(void *v)
1007 char **args = v;
1008 struct macro *mp;
1009 int rv = 1, i, oqf, nqf;
1011 if (args[0] == NULL) {
1012 rv = _list_macros(MA_ACC);
1013 goto jleave;
1016 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1017 if (args[2] != NULL) {
1018 fprintf(stderr, tr(517, "Syntax is: account <name> {\n"));
1019 goto jleave;
1021 if (asccasecmp(args[0], ACCOUNT_NULL) == 0) {
1022 fprintf(stderr, tr(521, "Error: `%s' is a reserved name.\n"),
1023 ACCOUNT_NULL);
1024 goto jleave;
1026 rv = ! _define1(args[0], MA_ACC);
1027 goto jleave;
1030 if (inhook) {
1031 fprintf(stderr, tr(518, "Cannot change account from within a hook.\n"));
1032 goto jleave;
1035 save_mbox_for_possible_quitstuff();
1037 mp = NULL;
1038 if (asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
1039 (mp = _malook(args[0], NULL, MA_ACC)) == NULL) {
1040 fprintf(stderr, tr(519, "Account `%s' does not exist.\n"), args[0]);
1041 goto jleave;
1044 oqf = savequitflags();
1045 if (_acc_curr != NULL)
1046 _localopts_unroll(&_acc_curr->ma_localopts);
1047 account_name = (mp != NULL) ? mp->ma_name : NULL;
1048 _acc_curr = mp;
1050 if (mp != NULL && _maexec(mp, &mp->ma_localopts) == CBAD) {
1051 /* XXX account switch incomplete, unroll? */
1052 fprintf(stderr, tr(520, "Switching to account `%s' failed.\n"), args[0]);
1053 goto jleave;
1056 if (! starting && ! inhook) {
1057 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1058 restorequitflags(oqf);
1059 if ((i = setfile("%", 0)) < 0)
1060 goto jleave;
1061 callhook(mailname, 0);
1062 if (i > 0 && !ok_blook(emptystart))
1063 goto jleave;
1064 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1065 restorequitflags(nqf);
1067 rv = 0;
1068 jleave:
1069 return rv;
1072 FL int
1073 c_localopts(void *v)
1075 int rv = 1;
1076 char **c = v;
1078 if (_localopts == NULL) {
1079 fprintf(stderr, tr(522,
1080 "Cannot use `localopts' but from within a `define' or `account'\n"));
1081 goto jleave;
1084 _localopts->s_unroll = (**c == '0') ? FAL0 : TRU1;
1085 rv = 0;
1086 jleave:
1087 return rv;
1090 /* vim:set fenc=utf-8:s-it-mode */