1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Account, macro and variable handling.
3 *@ HOWTO add a new non-dynamic boolean or value option:
4 *@ - add an entry to nail.h:enum okeys
5 *@ - run mk-okey-map.pl
6 *@ - update the manual!
8 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
9 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
12 * Copyright (c) 1980, 1993
13 * The Regents of the University of California. All rights reserved.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 * 3. 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
40 #define n_FILE accmacvar
42 #ifndef HAVE_AMALGAMATION
43 # define _ACCMACVAR_SOURCE /* For _features[] */
47 /* Note: changing the hash function must be reflected in mk-okey-map.pl */
48 #define MA_PRIME HSHSIZE
49 #define MA_NAME2HASH(N) torek_hash(N)
50 #define MA_HASH2PRIME(H) ((H) % MA_PRIME)
55 MA_TYPE_MASK
= MA_ACC
,
56 MA_UNDEF
= 1<<1, /* Unlink after lookup */
57 MA_DELETED
= 1<<13 /* Only for _acc_curr: deleted while active */
62 VM_BOOLEAN
= 1<<0, /* ok_b_* */
63 VM_RDONLY
= 1<<1, /* May not be set by user */
64 VM_SPECIAL
= 1<<2, /* Wants _var_check_specials() evaluation */
65 VM_VIRTUAL
= 1<<3 /* "Stateless": no var* -- implies VM_RDONLY */
69 struct macro
*ma_next
;
71 struct mline
*ma_contents
;
72 ui32_t ma_maxlen
; /* Maximum line length */
73 enum ma_flags ma_flags
;
74 struct var
*ma_localopts
; /* `account' unroll list, for `localopts' */
80 ui32_t l_leadspaces
; /* Number of leading SPC characters */
81 char l_line
[VFIELD_SIZE(0)];
87 char v_name
[VFIELD_SIZE(0)];
92 struct var
const *vv_var
;
98 ui16_t vm_flags
; /* var_map_flags bits */
106 struct var_map
const *vc_vmap
;
111 struct var_carrier vs_vc
; /* _var_revlookup() */
112 char const *vs_value
; /* Value (from wherever it came) or NULL */
113 bool_t vs_isset
; /* Managed by us and existent */
114 bool_t vs_isenv
; /* Set, but managed by environ */
115 bool_t vs_isasm
; /* Is an assembled variable */
120 struct lostack
*s_up
; /* Outer context */
121 struct macro
*s_mac
; /* Context (`account' or `define') */
122 struct var
*s_localopts
;
123 bool_t s_unroll
; /* Unroll? */
126 /* Include the constant mk-okey-map.pl output */
130 static struct macro
*_acc_curr
; /* Currently active account */
131 static struct lostack
*_localopts
; /* Currently executing macro unroll list */
132 /* TODO We really deserve localopts support for *folder-hook*s, so hack it in
133 * TODO today via a static lostack, it should be a field in mailbox, once that
134 * TODO is a real multi-instance object */
135 static struct var
*_folder_hook_localopts
;
137 /* TODO once we have a dynamically sized hashtable we could unite _macros and
138 * TODO _variables into a single hashtable, stripping down fun interface;
139 * TODO also, setting and clearing a variable can be easily joined */
140 static struct var
*_vars
[MA_PRIME
]; /* TODO dynamically spaced */
141 static struct macro
*_macros
[MA_PRIME
]; /* TODO dynamically spaced */
143 /* Special cased value string allocation */
144 static char * _var_vcopy(char const *str
);
145 static void _var_vfree(char *cp
);
147 /* Check for special housekeeping. */
148 static bool_t
_var_check_specials(enum okeys okey
, bool_t enable
,
151 /* If a variable name begins with a lowercase-character and contains at
152 * least one '@', it is converted to all-lowercase. This is necessary
153 * for lookups of names based on email addresses.
154 * Following the standard, only the part following the last '@' should
155 * be lower-cased, but practice has established otherwise here.
156 * Return value may have been placed in string dope (salloc()) */
157 static char const * _var_canonify(char const *vn
);
159 /* Try to reverse lookup an option name to an enum okeys mapping.
160 * Updates vcp.vc_name and vcp.vc_hash; vcp.vc_vmap is NULL if none found */
161 static bool_t
_var_revlookup(struct var_carrier
*vcp
, char const *name
);
163 /* Lookup a variable from vcp.vc_(vmap|name|hash), return wether it was found.
164 * Sets vcp.vc_prime; vcp.vc_var is NULL if not found */
165 static bool_t
_var_lookup(struct var_carrier
*vcp
);
167 /* Completely fill vsp with data for name, return wether it was set as an
168 * internal variable (it may still have been set as an environment variable) */
169 static bool_t
_var_broadway(struct var_show
*vsp
, char const *name
);
171 /* Set variable from vcp.vc_(vmap|name|hash), return success */
172 static bool_t
_var_set(struct var_carrier
*vcp
, char const *value
);
174 /* Clear variable from vcp.vc_(vmap|name|hash); sets vcp.vc_var to NULL,
176 static bool_t
_var_clear(struct var_carrier
*vcp
);
178 /* List all variables */
179 static void _var_list_all(void);
181 static int __var_list_all_cmp(void const *s1
, void const *s2
);
182 static char * __var_simple_quote(char const *cp
);
184 /* Shared c_set() and c_setenv() impl, return success */
185 static bool_t
_var_set_env(char **ap
, bool_t issetenv
);
187 /* Does cp consist solely of WS and a } */
188 static bool_t
_is_closing_angle(char const *cp
);
190 /* Lookup for macros/accounts */
191 static struct macro
*_ma_look(char const *name
, struct macro
*data
,
194 /* Walk all lines of a macro and execute() them */
195 static int _ma_exec(struct macro
const *mp
, struct var
**unroller
,
198 /* User display helpers */
199 static int _ma_list(enum ma_flags mafl
);
201 /* _ma_define() returns error for faulty definitions and already existing
202 * names, _ma_undefine() returns error if a named thing doesn't exist */
203 static bool_t
_ma_define(char const *name
, enum ma_flags mafl
);
204 static bool_t
_ma_undefine(char const *name
, enum ma_flags mafl
);
205 static void _ma_freelines(struct mline
*lp
);
207 /* Update replay-log */
208 static void _localopts_add(struct lostack
*losp
, char const *name
,
210 static void _localopts_unroll(struct var
**vapp
);
213 _var_vcopy(char const *str
)
222 len
= strlen(str
) +1;
224 memcpy(news
, str
, len
);
240 _var_check_specials(enum okeys okey
, bool_t enable
, char **val
)
258 case ok_b_skipemptybody
:
262 flag
= (enable
&& !(options
& OPT_VERB
))
263 ? OPT_VERB
: OPT_VERB
| OPT_VERBVERB
;
266 ok
= (val
!= NULL
&& var_folder_updated(*val
, &cp
));
267 if (ok
&& cp
!= NULL
) {
269 /* It's smalloc()ed, but ensure we don't leak */
278 case ok_v_line_editor_cursor_right
:
279 if ((ok
= (val
!= NULL
&& *val
!= NULL
))) {
280 /* Set with no value? TODO very guly */
281 if (*(cp
= *val
) != '\0') {
285 c
= n_shell_expand_escape(&x
, FAL0
);
289 } while (*x
!= '\0');
310 _var_canonify(char const *vn
)
313 if (!upperchar(*vn
)) {
316 for (vp
= vn
; *vp
!= '\0' && *vp
!= '@'; ++vp
)
318 vn
= (*vp
== '@') ? i_strdup(vn
) : vn
;
325 _var_revlookup(struct var_carrier
*vcp
, char const *name
)
328 struct var_map
const *vmp
;
331 vcp
->vc_name
= name
= _var_canonify(name
);
332 vcp
->vc_hash
= hash
= MA_NAME2HASH(name
);
334 for (i
= hash
% _VAR_REV_PRIME
, j
= 0; j
<= _VAR_REV_LONGEST
; ++j
) {
335 ui32_t x
= _var_revmap
[i
];
336 if (x
== _VAR_REV_ILL
)
339 if (vmp
->vm_hash
== hash
&& !strcmp(_var_keydat
+ vmp
->vm_keyoff
, name
)) {
341 vcp
->vc_okey
= (enum okeys
)x
;
344 if (++i
== _VAR_REV_PRIME
) {
345 #ifdef _VAR_REV_WRAPAROUND
356 return (vcp
!= NULL
);
360 _var_lookup(struct var_carrier
*vcp
)
362 struct var
**vap
, *lvp
, *vp
;
365 /* XXX _So_ unlikely that it should be checked if normal lookup fails! */
366 if (UNLIKELY(vcp
->vc_vmap
!= NULL
&&
367 (vcp
->vc_vmap
->vm_flags
& VM_VIRTUAL
) != 0)) {
368 struct var_virtual
const *vvp
;
370 for (vvp
= _var_virtuals
;
371 PTRCMP(vvp
, <, _var_virtuals
+ NELEM(_var_virtuals
)); ++vvp
)
372 if (vvp
->vv_okey
== vcp
->vc_okey
) {
373 vp
= UNCONST(vvp
->vv_var
);
379 vap
= _vars
+ (vcp
->vc_prime
= MA_HASH2PRIME(vcp
->vc_hash
));
381 for (lvp
= NULL
, vp
= *vap
; vp
!= NULL
; lvp
= vp
, vp
= vp
->v_link
)
382 if (!strcmp(vp
->v_name
, vcp
->vc_name
)) {
383 /* Relink as head, hope it "sorts on usage" over time.
384 * _var_clear() relies on this behaviour */
386 lvp
->v_link
= vp
->v_link
;
400 _var_broadway(struct var_show
*vsp
, char const *name
)
405 memset(vsp
, 0, sizeof *vsp
);
407 vsp
->vs_isasm
= !_var_revlookup(&vsp
->vs_vc
, name
);
409 if ((vsp
->vs_isset
= rv
= _var_lookup(&vsp
->vs_vc
))) {
410 vsp
->vs_value
= vsp
->vs_vc
.vc_var
->v_value
;
411 vsp
->vs_isenv
= FAL0
;
413 vsp
->vs_isenv
= ((vsp
->vs_value
= getenv(vsp
->vs_vc
.vc_name
)) != NULL
);
420 _var_set(struct var_carrier
*vcp
, char const *value
)
428 ok
= _var_clear(vcp
);
434 if (vcp
->vc_vmap
!= NULL
&& (vcp
->vc_vmap
->vm_flags
& VM_RDONLY
)) {
435 n_err(_("Variable readonly: \"%s\"\n"), vcp
->vc_name
);
440 /* Don't care what happens later on, store this in the unroll list */
441 if (_localopts
!= NULL
)
442 _localopts_add(_localopts
, vcp
->vc_name
, vcp
->vc_var
);
444 if ((vp
= vcp
->vc_var
) == NULL
) {
445 size_t l
= strlen(vcp
->vc_name
) + 1;
448 vp
= smalloc(sizeof(*vp
) - VFIELD_SIZEOF(struct var
, v_name
) + l
);
449 vp
->v_link
= _vars
[vcp
->vc_prime
];
450 _vars
[vcp
->vc_prime
] = vp
;
451 memcpy(vp
->v_name
, vcp
->vc_name
, l
);
456 if (vcp
->vc_vmap
== NULL
)
457 vp
->v_value
= _var_vcopy(value
);
459 /* Via `set' etc. the user may give even boolean options non-boolean
460 * values, ignore that and force boolean xxx error log? */
461 if (vcp
->vc_vmap
->vm_flags
& VM_BOOLEAN
)
463 vp
->v_value
= _var_vcopy(value
);
465 /* Check if update allowed XXX wasteful on error! */
466 if ((vcp
->vc_vmap
->vm_flags
& VM_SPECIAL
) &&
467 !(ok
= _var_check_specials(vcp
->vc_okey
, TRU1
, &vp
->v_value
))) {
468 char *cp
= vp
->v_value
;
482 _var_clear(struct var_carrier
*vcp
)
487 if (!_var_lookup(vcp
)) {
488 if (!(pstate
& PS_IN_LOAD
) && (options
& OPT_D_V
))
489 n_err(_("Variable undefined: \"%s\"\n"), vcp
->vc_name
);
490 } else if (vcp
->vc_vmap
!= NULL
&& (vcp
->vc_vmap
->vm_flags
& VM_RDONLY
)) {
491 n_err(_("Variable readonly: \"%s\"\n"), vcp
->vc_name
);
494 if (_localopts
!= NULL
)
495 _localopts_add(_localopts
, vcp
->vc_name
, vcp
->vc_var
);
497 /* Always listhead after _var_lookup() */
498 _vars
[vcp
->vc_prime
] = _vars
[vcp
->vc_prime
]->v_link
;
499 _var_vfree(vcp
->vc_var
->v_value
);
503 if (vcp
->vc_vmap
!= NULL
&& (vcp
->vc_vmap
->vm_flags
& VM_SPECIAL
))
504 ok
= _var_check_specials(vcp
->vc_okey
, FAL0
, NULL
);
517 char const **vacp
, **cap
;
520 if ((fp
= Ftmp(NULL
, "listvars", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
522 n_perr(_("tmpfile"), 0);
526 for (no
= i
= 0; i
< MA_PRIME
; ++i
)
527 for (vp
= _vars
[i
]; vp
!= NULL
; vp
= vp
->v_link
)
529 no
+= NELEM(_var_virtuals
);
531 vacp
= salloc(no
* sizeof(*vacp
));
533 for (cap
= vacp
, i
= 0; i
< MA_PRIME
; ++i
)
534 for (vp
= _vars
[i
]; vp
!= NULL
; vp
= vp
->v_link
)
536 for (i
= 0; i
< NELEM(_var_virtuals
); ++i
)
537 *cap
++ = _var_virtuals
[i
].vv_var
->v_name
;
540 qsort(vacp
, no
, sizeof *vacp
, &__var_list_all_cmp
);
542 i
= (ok_blook(bsdcompat
) || ok_blook(bsdset
));
543 for (cap
= vacp
; no
!= 0; ++cap
, --no
) {
544 char const *asmis
, *fmt
;
546 if (!_var_broadway(&vs
, *cap
))
548 if (vs
.vs_value
== NULL
)
551 asmis
= !(options
& OPT_D_VV
) ? ""
552 : vs
.vs_isasm
? "*" : " ";
556 if (vs
.vs_vc
.vc_vmap
!= NULL
&&
557 (vs
.vs_vc
.vc_vmap
->vm_flags
& VM_BOOLEAN
))
560 fmt
= "%sset %s=\"%s\"\n";
561 if (*vs
.vs_value
!= '\0')
562 vs
.vs_value
= __var_simple_quote(vs
.vs_value
);
565 /* Shall a code checker complain on that, i'm in holiday */
566 fprintf(fp
, fmt
, asmis
, *cap
, vs
.vs_value
);
569 page_or_print(fp
, PTR2SIZE(cap
- vacp
));
576 __var_list_all_cmp(void const *s1
, void const *s2
)
581 rv
= strcmp(*(char**)UNCONST(s1
), *(char**)UNCONST(s2
));
587 __var_simple_quote(char const *cp
) /* TODO "unite" with string_quote(), etc.. */
595 for (i
= 0, cp_base
= cp
; (c
= *cp
) != '\0'; ++i
, ++cp
)
600 for (esc
= FAL0
, i
= 0, cp
= cp_base
; (c
= *cp
) != '\0'; rv
[i
++] = c
, ++cp
) {
614 _var_set_env(char **ap
, bool_t issetenv
)
616 char *cp
, *cp2
, *varbuf
, c
;
620 for (; *ap
!= NULL
; ++ap
) {
623 cp2
= varbuf
= ac_alloc(strlen(cp
) +1);
624 for (; (c
= *cp
) != '=' && c
!= '\0'; ++cp
)
632 n_err(_("Non-null variable name required\n"));
637 if (varbuf
[0] == 'n' && varbuf
[1] == 'o') {
638 char const *k
= varbuf
+ 2;
640 if (issetenv
&& (!strcmp(k
, "HOME") || /* TODO generic */
641 !strcmp(k
, "LOGNAME") || !strcmp(k
, "USER") || /* TODO .. */
642 !strcmp(k
, "TMPDIR"))) {/* TODO approach */
643 if (options
& OPT_D_V
)
644 n_err(_("Cannot `unsetenv' \"%s\"\n"), k
);
649 errs
+= !_var_vokclear(k
);
652 errs
+= (unsetenv(k
) != 0);
658 errs
+= !_var_vokset(varbuf
, (uintptr_t)cp
);
661 static char const *cp_buf
[3];
662 char const **pl
, **pe
;
664 if (!strcmp(varbuf
, "HOME")) /* TODO generic approach..*/
665 pl
= cp_buf
+ 0, pe
= &homedir
;
666 else if (!strcmp(varbuf
, "LOGNAME") || !strcmp(varbuf
, "USER"))
667 pl
= cp_buf
+ 1, pe
= &myname
;
668 else if (!strcmp(varbuf
, "TMPDIR")) /* TODO ..until here */
669 pl
= cp_buf
+ 2, pe
= &tempdir
;
675 *pe
= *pl
= sstrdup(cp
);
679 errs
+= (setenv(varbuf
, cp
, 1) != 0);
694 _is_closing_angle(char const *cp
)
699 while (spacechar(*cp
))
703 while (spacechar(*cp
))
711 static struct macro
*
712 _ma_look(char const *name
, struct macro
*data
, enum ma_flags mafl
)
714 enum ma_flags save_mafl
;
716 struct macro
*lmp
, *mp
;
720 mafl
&= MA_TYPE_MASK
;
721 h
= MA_NAME2HASH(name
);
722 h
= MA_HASH2PRIME(h
);
724 for (lmp
= NULL
, mp
= _macros
[h
]; mp
!= NULL
; lmp
= mp
, mp
= mp
->ma_next
) {
725 if ((mp
->ma_flags
& MA_TYPE_MASK
) == mafl
&& !strcmp(mp
->ma_name
, name
)) {
726 if (save_mafl
& MA_UNDEF
) {
728 _macros
[h
] = mp
->ma_next
;
730 lmp
->ma_next
= mp
->ma_next
;
732 /* TODO it should also be possible to delete running macros */
733 if ((mafl
& MA_ACC
) &&
734 account_name
!= NULL
&& !strcmp(account_name
, name
)) {
735 mp
->ma_flags
|= MA_DELETED
;
736 n_err(_("Delayed deletion of active account \"%s\"\n"), name
);
738 _ma_freelines(mp
->ma_contents
);
741 mp
= (struct macro
*)-1;
749 data
->ma_next
= _macros
[h
];
759 _ma_exec(struct macro
const *mp
, struct var
**unroller
, bool_t localopts
)
763 struct n2
{struct n2
*up
; struct lostack
*lo
;} *x
; /* FIXME hack (sigman+) */
764 struct mline
const *lp
;
768 los
.s_up
= _localopts
;
769 los
.s_mac
= UNCONST(mp
); /* But not used.. */
770 los
.s_localopts
= (unroller
== NULL
) ? NULL
: *unroller
;
771 los
.s_unroll
= localopts
;
774 x
= salloc(sizeof *x
); /* FIXME intermediate hack (signal man+) */
775 x
->up
= temporary_localopts_store
;
777 temporary_localopts_store
= x
;
779 buf
= ac_alloc(mp
->ma_maxlen
+1);
780 for (lp
= mp
->ma_contents
; lp
; lp
= lp
->l_next
) {
781 memcpy(buf
, lp
->l_line
, lp
->l_length
+1);
782 rv
|= execute(buf
, lp
->l_length
); /* XXX break if != 0 ? */
786 temporary_localopts_store
= x
->up
; /* FIXME intermediate hack */
788 _localopts
= los
.s_up
;
789 if (unroller
== NULL
) {
790 if (los
.s_localopts
!= NULL
)
791 _localopts_unroll(&los
.s_localopts
);
793 *unroller
= los
.s_localopts
;
799 _ma_list(enum ma_flags mafl
)
808 if ((fp
= Ftmp(NULL
, "listmacs", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
810 n_perr(_("tmpfile"), 0);
815 mafl
&= MA_TYPE_MASK
;
816 typestr
= (mafl
& MA_ACC
) ? "account" : "define";
818 for (ti
= mc
= 0; ti
< MA_PRIME
; ++ti
)
819 for (mq
= _macros
[ti
]; mq
; mq
= mq
->ma_next
)
820 if ((mq
->ma_flags
& MA_TYPE_MASK
) == mafl
) {
823 fprintf(fp
, "%s %s {\n", typestr
, mq
->ma_name
);
824 for (lp
= mq
->ma_contents
; lp
!= NULL
; lp
= lp
->l_next
) {
825 for (i
= lp
->l_leadspaces
; i
> 0; --i
)
827 fputs(lp
->l_line
, fp
);
833 page_or_print(fp
, 0);
835 mc
= (ui32_t
)ferror(fp
);
843 _ma_define(char const *name
, enum ma_flags mafl
)
847 struct mline
*lp
, *lst
= NULL
, *lnd
= NULL
;
848 char *linebuf
= NULL
, *cp
;
850 ui32_t maxlen
= 0, leaspc
;
851 union {int i
; ui32_t ui
;} n
;
854 mp
= scalloc(1, sizeof *mp
);
855 mp
->ma_name
= sstrdup(name
);
859 n
.i
= readline_input("", TRU1
, &linebuf
, &linesize
, NULL
);
863 n_err(_("Unterminated %s definition: \"%s\"\n"),
864 (mafl
& MA_ACC
? "account" : "macro"), mp
->ma_name
);
865 if ((pstate
& PS_IN_LOAD
) == PS_SOURCING
)
869 if (_is_closing_angle(linebuf
))
872 /* Trim WS xxx we count tabs as one space here */
873 for (cp
= linebuf
, leaspc
= 0; n
.ui
> 0 && whitechar(*cp
); ++cp
, --n
.ui
)
875 leaspc
= (leaspc
+ 8) & ~7;
880 for (; whitechar(cp
[n
.ui
- 1]); --n
.ui
)
884 maxlen
= MAX(maxlen
, n
.ui
);
886 lp
= scalloc(1, sizeof(*lp
) - VFIELD_SIZEOF(struct mline
, l_line
) + n
.ui
);
887 memcpy(lp
->l_line
, cp
, n
.ui
);
888 lp
->l_length
= --n
.ui
;
889 lp
->l_leadspaces
= leaspc
;
897 mp
->ma_contents
= lst
;
898 mp
->ma_maxlen
= maxlen
;
900 if (_ma_look(mp
->ma_name
, mp
, mafl
) != NULL
) {
901 n_err(_("A %s named \"%s\" already exists\n"),
902 (mafl
& MA_ACC
? "account" : "macro"), mp
->ma_name
);
903 lst
= mp
->ma_contents
;
923 _ma_undefine(char const *name
, enum ma_flags mafl
)
931 if (LIKELY(name
[0] != '*' || name
[1] != '\0')) {
932 if ((mp
= _ma_look(name
, NULL
, mafl
| MA_UNDEF
)) == NULL
) {
933 n_err(_("%s \"%s\" is not defined\n"),
934 (mafl
& MA_ACC
? "Account" : "Macro"), name
);
938 struct macro
**mpp
, *lmp
;
940 for (mpp
= _macros
; PTRCMP(mpp
, <, _macros
+ NELEM(_macros
)); ++mpp
)
941 for (lmp
= NULL
, mp
= *mpp
; mp
!= NULL
;) {
942 if ((mp
->ma_flags
& MA_TYPE_MASK
) == mafl
) {
943 /* xxx Expensive but rare: be simple */
944 _ma_look(mp
->ma_name
, NULL
, mafl
| MA_UNDEF
);
945 mp
= (lmp
== NULL
) ? *mpp
: lmp
->ma_next
;
957 _ma_freelines(struct mline
*lp
)
962 for (lq
= NULL
; lp
!= NULL
; ) {
974 _localopts_add(struct lostack
*losp
, char const *name
, struct var
*ovap
)
980 /* Propagate unrolling up the stack, as necessary */
981 while (!losp
->s_unroll
&& (losp
= losp
->s_up
) != NULL
)
986 /* We've found a level that wants to unroll; check wether it does it yet */
987 for (vap
= losp
->s_localopts
; vap
!= NULL
; vap
= vap
->v_link
)
988 if (!strcmp(vap
->v_name
, name
))
991 nl
= strlen(name
) + 1;
992 vl
= (ovap
!= NULL
) ? strlen(ovap
->v_value
) + 1 : 0;
993 vap
= smalloc(sizeof(*vap
) - VFIELD_SIZEOF(struct var
, v_name
) + nl
+ vl
);
994 vap
->v_link
= losp
->s_localopts
;
995 losp
->s_localopts
= vap
;
996 memcpy(vap
->v_name
, name
, nl
);
1000 vap
->v_value
= vap
->v_name
+ nl
;
1001 memcpy(vap
->v_value
, ovap
->v_value
, vl
);
1008 _localopts_unroll(struct var
**vapp
)
1010 struct lostack
*save_los
;
1011 struct var
*x
, *vap
;
1017 save_los
= _localopts
;
1019 while (vap
!= NULL
) {
1022 vok_vset(x
->v_name
, x
->v_value
);
1025 _localopts
= save_los
;
1030 _var_oklook(enum okeys okey
)
1032 struct var_carrier vc
;
1036 vc
.vc_vmap
= _var_map
+ okey
;
1037 vc
.vc_name
= _var_keydat
+ _var_map
[okey
].vm_keyoff
;
1038 vc
.vc_hash
= _var_map
[okey
].vm_hash
;
1041 if (!_var_lookup(&vc
)) {
1042 if ((rv
= getenv(vc
.vc_name
)) != NULL
) {
1044 assert(vc
.vc_var
!= NULL
);
1049 rv
= vc
.vc_var
->v_value
;
1055 _var_okset(enum okeys okey
, uintptr_t val
)
1057 struct var_carrier vc
;
1061 vc
.vc_vmap
= _var_map
+ okey
;
1062 vc
.vc_name
= _var_keydat
+ _var_map
[okey
].vm_keyoff
;
1063 vc
.vc_hash
= _var_map
[okey
].vm_hash
;
1066 ok
= _var_set(&vc
, (val
== 0x1 ? "" : (char const*)val
));
1072 _var_okclear(enum okeys okey
)
1074 struct var_carrier vc
;
1078 vc
.vc_vmap
= _var_map
+ okey
;
1079 vc
.vc_name
= _var_keydat
+ _var_map
[okey
].vm_keyoff
;
1080 vc
.vc_hash
= _var_map
[okey
].vm_hash
;
1083 rv
= _var_clear(&vc
);
1089 _var_voklook(char const *vokey
)
1091 struct var_carrier vc
;
1095 _var_revlookup(&vc
, vokey
);
1097 if (!_var_lookup(&vc
)) {
1098 if ((rv
= getenv(vc
.vc_name
)) != NULL
) {
1100 assert(vc
.vc_var
!= NULL
);
1105 rv
= vc
.vc_var
->v_value
;
1111 _var_vokset(char const *vokey
, uintptr_t val
)
1113 struct var_carrier vc
;
1117 _var_revlookup(&vc
, vokey
);
1119 ok
= _var_set(&vc
, (val
== 0x1 ? "" : (char const*)val
));
1125 _var_vokclear(char const *vokey
)
1127 struct var_carrier vc
;
1131 _var_revlookup(&vc
, vokey
);
1133 err
= !_var_clear(&vc
);
1139 _env_look(char const *envkey
, bool_t envonly
) /* TODO rather dummy yet!! */
1145 rv
= getenv(envkey
); /* TODO rework vars: cache, output a la mimetypes */
1147 rv
= _var_voklook(envkey
);
1154 _var_xoklook(enum okeys okey
, struct url
const *urlp
, enum okey_xlook_mode oxm
)
1156 struct var_carrier vc
;
1157 struct str
const *us
;
1159 char *nbuf
= NULL
/* CC happiness */, *rv
;
1162 assert(oxm
& (OXM_PLAIN
| OXM_H_P
| OXM_U_H_P
));
1164 /* For simplicity: allow this case too */
1165 if (!(oxm
& (OXM_H_P
| OXM_U_H_P
)))
1168 vc
.vc_vmap
= _var_map
+ okey
;
1169 vc
.vc_name
= _var_keydat
+ _var_map
[okey
].vm_keyoff
;
1172 us
= (oxm
& OXM_U_H_P
) ? &urlp
->url_u_h_p
: &urlp
->url_h_p
;
1173 nlen
= strlen(vc
.vc_name
);
1174 nbuf
= ac_alloc(nlen
+ 1 + us
->l
+1);
1175 memcpy(nbuf
, vc
.vc_name
, nlen
);
1178 /* One of .url_u_h_p and .url_h_p we test in here */
1179 memcpy(nbuf
+ nlen
, us
->s
, us
->l
+1);
1180 vc
.vc_name
= _var_canonify(nbuf
);
1181 vc
.vc_hash
= MA_NAME2HASH(vc
.vc_name
);
1182 if (_var_lookup(&vc
))
1186 if (oxm
& OXM_H_P
) {
1187 us
= &urlp
->url_h_p
;
1188 memcpy(nbuf
+ nlen
, us
->s
, us
->l
+1);
1189 vc
.vc_name
= _var_canonify(nbuf
);
1190 vc
.vc_hash
= MA_NAME2HASH(vc
.vc_name
);
1191 if (_var_lookup(&vc
)) {
1193 rv
= vc
.vc_var
->v_value
;
1199 rv
= (oxm
& OXM_PLAIN
) ? _var_oklook(okey
) : NULL
;
1201 if (oxm
& (OXM_H_P
| OXM_U_H_P
))
1206 #endif /* HAVE_SOCKETS */
1212 char const **argv
= v
;
1217 else for (; *argv
!= NULL
; ++argv
) {
1218 _var_broadway(&vs
, *argv
);
1219 if (vs
.vs_value
== NULL
)
1220 vs
.vs_value
= "NULL";
1222 if (vs
.vs_vc
.vc_vmap
!= NULL
) {
1223 ui16_t f
= vs
.vs_vc
.vc_vmap
->vm_flags
;
1226 printf(_("\"%s\": (%d) boolean%s%s: set=%d (ENVIRON=%d)\n"),
1227 vs
.vs_vc
.vc_name
, vs
.vs_vc
.vc_okey
,
1228 (f
& VM_RDONLY
? ", read-only" : ""),
1229 (f
& VM_VIRTUAL
? ", virtual" : ""), vs
.vs_isset
, vs
.vs_isenv
);
1231 printf(_("\"%s\": (%d) value%s%s: set=%d (ENVIRON=%d) value<%s>\n"),
1232 vs
.vs_vc
.vc_name
, vs
.vs_vc
.vc_okey
,
1233 (f
& VM_RDONLY
? ", read-only" : ""),
1234 (f
& VM_VIRTUAL
? ", virtual" : ""), vs
.vs_isset
, vs
.vs_isenv
,
1237 printf("\"%s\": (assembled): set=%d (ENVIRON=%d) value<%s>\n",
1238 vs
.vs_vc
.vc_name
, vs
.vs_isset
, vs
.vs_isenv
, vs
.vs_value
);
1241 return (v
== NULL
? !STOP
: !OKAY
); /* xxx 1:bad 0:good -- do some */
1255 err
= !_var_set_env(ap
, FAL0
);
1267 if (!(err
= !(pstate
& PS_STARTED
)))
1268 err
= !_var_set_env(ap
, TRU1
);
1281 err
|= !_var_vokclear(*ap
++);
1292 if (!(err
= !(pstate
& PS_STARTED
))) {
1295 for (ap
= v
; *ap
!= NULL
; ++ap
) {
1298 if (!strcmp(*ap
, "HOME") || /* TODO generic */
1299 !strcmp(*ap
, "LOGNAME") || !strcmp(*ap
, "USER") || /* TODO .. */
1300 !strcmp(*ap
, "TMPDIR")) { /* TODO approach */
1301 if (options
& OPT_D_V
)
1302 n_err(_("Cannot `unsetenv' \"%s\"\n"), *ap
);
1307 bad
= !_var_vokclear(*ap
);
1310 unsetenv(*ap
) != 0 ||
1324 struct var_carrier vc
;
1325 sighandler_type sigint
;
1327 char *val
, **argv
= v
;
1331 sigint
= safe_signal(SIGINT
, SIG_IGN
);
1333 while (*argv
!= NULL
) {
1334 memset(&vc
, 0, sizeof vc
);
1336 _var_revlookup(&vc
, *argv
++);
1338 if (vc
.vc_vmap
!= NULL
) {
1339 if (vc
.vc_vmap
->vm_flags
& VM_BOOLEAN
) {
1340 n_err(_("`varedit': can't edit boolean variable \"%s\"\n"),
1344 if (vc
.vc_vmap
->vm_flags
& VM_RDONLY
) {
1345 n_err(_("`varedit': can't edit readonly variable \"%s\"\n"),
1353 if ((of
= Ftmp(NULL
, "vared", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1355 n_perr(_("`varedit': can't create temporary file, bailing out"), 0);
1358 } else if (vc
.vc_var
!= NULL
&& *(val
= vc
.vc_var
->v_value
) != '\0' &&
1359 sizeof *val
!= fwrite(val
, strlen(val
), sizeof *val
, of
)) {
1360 n_perr(_("`varedit' failed to write old value to temporary file"), 0);
1367 nf
= run_editor(of
, (off_t
)-1, 'e', FAL0
, NULL
, NULL
, SEND_MBOX
, sigint
);
1373 off_t l
= fsize(nf
);
1376 base
= smalloc((size_t)l
+ 1);
1378 for (l
= 0, val
= base
; (c
= getc(nf
)) != EOF
; ++val
)
1379 if (c
== '\n' || c
== '\r') {
1383 *val
= (char)(uc_i
)c
;
1389 if (!vok_vset(vc
.vc_name
, base
))
1395 n_err(_("`varedit': can't start $EDITOR, bailing out\n"));
1401 safe_signal(SIGINT
, sigint
);
1413 if (args
[0] == NULL
) {
1414 rv
= _ma_list(MA_NONE
);
1418 if (args
[1] == NULL
|| args
[1][0] != '{' || args
[1][1] != '\0' ||
1420 n_err(_("Syntax is: define <name> {"));
1424 rv
= !_ma_define(args
[0], MA_NONE
);
1440 rv
|= !_ma_undefine(*args
, MA_NONE
);
1441 while (*++args
!= NULL
);
1451 char const *errs
, *name
;
1455 if (args
[0] == NULL
|| (args
[1] != NULL
&& args
[2] != NULL
)) {
1456 errs
= _("Syntax is: call <%s>\n");
1461 if ((mp
= _ma_look(*args
, NULL
, MA_NONE
)) == NULL
) {
1462 errs
= _("Undefined macro called: \"%s\"\n");
1467 rv
= _ma_exec(mp
, NULL
, FAL0
);
1477 check_folder_hook(bool_t nmail
) /* TODO temporary, v15: drop */
1482 struct var
**unroller
;
1486 var
= ac_alloc(len
= strlen(mailname
) + sizeof("folder-hook-") -1 +1);
1488 /* First try the fully resolved path */
1489 snprintf(var
, len
, "folder-hook-%s", mailname
);
1490 if ((cp
= vok_vlook(var
)) != NULL
)
1493 /* If we are under *folder*, try the usual +NAME syntax, too */
1494 if (displayname
[0] == '+') {
1495 char *x
= mailname
+ len
;
1497 for (; x
> mailname
; --x
)
1499 snprintf(var
, len
, "folder-hook-+%s", x
);
1500 if ((cp
= vok_vlook(var
)) != NULL
)
1506 /* Plain *folder-hook* is our last try */
1507 if ((cp
= ok_vlook(folder_hook
)) == NULL
)
1511 if ((mp
= _ma_look(cp
, NULL
, MA_NONE
)) == NULL
) {
1512 n_err(_("Cannot call *folder-hook* for \"%s\": "
1513 "macro \"%s\" does not exist\n"), displayname
, cp
);
1518 pstate
&= ~PS_HOOK_MASK
;
1520 pstate
|= PS_HOOK_NEWMAIL
;
1524 unroller
= &_folder_hook_localopts
;
1526 rv
= (_ma_exec(mp
, unroller
, TRU1
) == 0);
1527 pstate
&= ~PS_HOOK_MASK
;
1540 int rv
= 1, i
, oqf
, nqf
;
1543 if (args
[0] == NULL
) {
1544 rv
= _ma_list(MA_ACC
);
1548 if (args
[1] && args
[1][0] == '{' && args
[1][1] == '\0') {
1549 if (args
[2] != NULL
) {
1550 n_err(_("Syntax is: account <name> {\n"));
1553 if (!asccasecmp(args
[0], ACCOUNT_NULL
)) {
1554 n_err(_("Error: \"%s\" is a reserved name\n"), ACCOUNT_NULL
);
1557 rv
= !_ma_define(args
[0], MA_ACC
);
1561 if (pstate
& PS_HOOK_MASK
) {
1562 n_err(_("Cannot change account from within a hook\n"));
1566 save_mbox_for_possible_quitstuff();
1569 if (asccasecmp(args
[0], ACCOUNT_NULL
) != 0 &&
1570 (mp
= _ma_look(args
[0], NULL
, MA_ACC
)) == NULL
) {
1571 n_err(_("Account \"%s\" does not exist\n"), args
[0]);
1575 oqf
= savequitflags();
1577 if (_acc_curr
!= NULL
) {
1578 if (_acc_curr
->ma_localopts
!= NULL
)
1579 _localopts_unroll(&_acc_curr
->ma_localopts
);
1580 if (_acc_curr
->ma_flags
& MA_DELETED
) { /* xxx can be made generic? */
1581 _ma_freelines(_acc_curr
->ma_contents
);
1582 free(_acc_curr
->ma_name
);
1587 account_name
= (mp
!= NULL
) ? mp
->ma_name
: NULL
;
1590 if (mp
!= NULL
&& _ma_exec(mp
, &mp
->ma_localopts
, TRU1
) == CBAD
) {
1591 /* XXX account switch incomplete, unroll? */
1592 n_err(_("Switching to account \"%s\" failed\n"), args
[0]);
1596 if ((pstate
& (PS_STARTED
| PS_HOOK_MASK
)) == PS_STARTED
) {
1597 nqf
= savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1598 restorequitflags(oqf
);
1599 if ((i
= setfile("%", 0)) < 0)
1601 check_folder_hook(FAL0
);
1602 if (i
> 0 && !ok_blook(emptystart
))
1604 announce(ok_blook(bsdcompat
) || ok_blook(bsdannounce
));
1605 restorequitflags(nqf
);
1614 c_unaccount(void *v
)
1623 rv
|= !_ma_undefine(*args
, MA_ACC
);
1624 while (*++args
!= NULL
);
1630 c_localopts(void *v
)
1636 if (_localopts
== NULL
) {
1637 n_err(_("Cannot use `localopts' but from within a "
1638 "`define' or `account'\n"));
1642 _localopts
->s_unroll
= (boolify(*c
, UIZ_MAX
, FAL0
) > 0);
1650 temporary_localopts_free(void) /* XXX intermediate hack */
1652 struct n2
{struct n2
*up
; struct lostack
*lo
;} *x
;
1655 x
= temporary_localopts_store
;
1656 temporary_localopts_store
= NULL
;
1660 struct lostack
*losp
= x
->lo
;
1662 if (losp
->s_localopts
!= NULL
)
1663 _localopts_unroll(&losp
->s_localopts
);
1669 temporary_localopts_folder_hook_unroll(void) /* XXX intermediate hack */
1672 if (_folder_hook_localopts
!= NULL
) {
1673 void *save
= _localopts
;
1675 _localopts_unroll(&_folder_hook_localopts
);