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>.
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
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
40 #ifndef HAVE_AMALGAMATION
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
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)
59 MA_TYPE_MASK
= MA_ACC
,
60 MA_UNDEF
= 1<<1 /* Unlink after lookup */
64 struct macro
*ma_next
;
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' */
75 char l_line
[VFIELD_SIZE(sizeof(size_t))];
81 char v_name
[VFIELD_SIZE(sizeof(size_t))];
96 struct var_map
const *vc_vmap
;
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 */
110 static struct macro
*_acc_curr
; /* Currently active account */
111 static struct lostack
*_localopts
; /* Currently executing macro unroll list */
113 /* TODO once we have a dynamically sized hashtable we could unite _macros and
114 * TODO _variables into a single hashtable, stripping down fun interface;
115 * TODO also, setting and clearing a variable can be easily joined */
116 static struct var
*_vars
[MA_PRIME
]; /* TODO dynamically spaced */
117 static struct macro
*_macros
[MA_PRIME
]; /* TODO dynamically spaced */
119 /* Special cased value string allocation */
120 static char * _var_vcopy(char const *str
);
121 static void _var_vfree(char *cp
);
123 /* Check for special housekeeping. */
124 static bool_t
_var_check_specials(enum okeys okey
, bool_t enable
,
127 /* If a variable name begins with a lowercase-character and contains at
128 * least one '@', it is converted to all-lowercase. This is necessary
129 * for lookups of names based on email addresses.
130 * Following the standard, only the part following the last '@' should
131 * be lower-cased, but practice has established otherwise here.
132 * Return value may have been placed in string dope (salloc()) */
133 static char const * _var_canonify(char const *vn
);
135 /* Try to reverse lookup an option name to an enum okeys mapping.
136 * Updates vcp.vc_name and vcp.vc_hash; vcp.vc_vmap is NULL if none found */
137 static bool_t
_var_revlookup(struct var_carrier
*vcp
, char const *name
);
139 /* Lookup a variable from vcp.vc_(vmap|name|hash), return wether it was found.
140 * Sets vcp.vc_prime; vcp.vc_var is NULL if not found */
141 static bool_t
_var_lookup(struct var_carrier
*vcp
);
143 /* Set variable from vcp.vc_(vmap|name|hash), return wether error occurred */
144 static bool_t
_var_set(struct var_carrier
*vcp
, char const *value
);
146 /* Clear variable from vcp.vc_(vmap|name|hash); sets vcp.vc_var to NULL,
147 * return wether error occurred */
148 static bool_t
_var_clear(struct var_carrier
*vcp
);
150 /* var_list_all(): qsort(3) helper */
151 static int _var_list_all_cmp(void const *s1
, void const *s2
);
153 /* Shared c_set() and c_setenv() impl, return wether error(s) occurred */
154 static bool_t
_var_set_env(char **ap
, bool_t issetenv
);
156 /* Does cp consist solely of WS and a } */
157 static bool_t
_is_closing_angle(char const *cp
);
159 /* Lookup for macros/accounts */
160 static struct macro
*_ma_look(char const *name
, struct macro
*data
,
163 /* Walk all lines of a macro and execute() them */
164 static int _ma_exec(struct macro
const *mp
, struct var
**unroller
);
166 /* User display helpers */
167 static int _ma_list(enum ma_flags mafl
);
170 static bool_t
_ma_define1(char const *name
, enum ma_flags mafl
);
171 static void _ma_undef1(char const *name
, enum ma_flags mafl
);
172 static void _ma_freelines(struct mline
*lp
);
174 /* Update replay-log */
175 static void _localopts_add(struct lostack
*losp
, char const *name
,
177 static void _localopts_unroll(struct var
**vapp
);
180 _var_vcopy(char const *str
)
189 len
= strlen(str
) +1;
191 memcpy(news
, str
, len
);
207 _var_check_specials(enum okeys okey
, bool_t enable
, char **val
)
222 case ok_b_skipemptybody
:
226 flag
= (enable
&& !(options
& OPT_VERB
))
227 ? OPT_VERB
: OPT_VERB
| OPT_VERBVERB
;
230 rv
= (val
== NULL
|| var_folder_updated(*val
, &cp
));
231 if (rv
&& cp
!= NULL
) {
233 /* It's smalloc()ed, but ensure we don't leak */
242 case ok_v_line_editor_cursor_right
:
243 if ((rv
= (val
!= NULL
&& *val
!= NULL
))) {
244 /* Set with no value? TODO very guly */
245 if (*(cp
= *val
) != '\0') {
249 c
= expand_shell_escape(&x
, FAL0
);
253 } while (*x
!= '\0');
274 _var_canonify(char const *vn
)
277 if (!upperchar(*vn
)) {
280 for (vp
= vn
; *vp
!= '\0' && *vp
!= '@'; ++vp
)
282 vn
= (*vp
== '@') ? i_strdup(vn
) : vn
;
289 _var_revlookup(struct var_carrier
*vcp
, char const *name
)
292 struct var_map
const *vmp
;
295 vcp
->vc_name
= name
= _var_canonify(name
);
296 vcp
->vc_hash
= hash
= MA_NAME2HASH(name
);
298 for (i
= hash
% _VAR_REV_PRIME
, j
= 0; j
<= _VAR_REV_LONGEST
; ++j
) {
299 ui32_t x
= _var_revmap
[i
];
300 if (x
== _VAR_REV_ILL
)
303 if (vmp
->vm_hash
== hash
&& !strcmp(_var_keydat
+ vmp
->vm_keyoff
, name
)) {
305 vcp
->vc_okey
= (enum okeys
)x
;
308 if (++i
== _VAR_REV_PRIME
) {
309 #ifdef _VAR_REV_WRAPAROUND
320 return (vcp
!= NULL
);
324 _var_lookup(struct var_carrier
*vcp
)
326 struct var
**vap
, *lvp
, *vp
;
329 vap
= _vars
+ (vcp
->vc_prime
= MA_HASH2PRIME(vcp
->vc_hash
));
331 for (lvp
= NULL
, vp
= *vap
; vp
!= NULL
; lvp
= vp
, vp
= vp
->v_link
)
332 if (!strcmp(vp
->v_name
, vcp
->vc_name
)) {
333 /* Relink as head, hope it "sorts on usage" over time.
334 * _var_clear() relies on this behaviour */
336 lvp
->v_link
= vp
->v_link
;
350 _var_set(struct var_carrier
*vcp
, char const *value
)
358 bool_t vcau_save
= var_clear_allow_undefined
;
359 var_clear_allow_undefined
= TRU1
;
360 err
= _var_clear(vcp
);
361 var_clear_allow_undefined
= vcau_save
;
367 /* Don't care what happens later on, store this in the unroll list */
368 if (_localopts
!= NULL
)
369 _localopts_add(_localopts
, vcp
->vc_name
, vcp
->vc_var
);
371 if ((vp
= vcp
->vc_var
) == NULL
) {
372 size_t l
= strlen(vcp
->vc_name
) + 1;
375 vp
= smalloc(sizeof(*vp
) - VFIELD_SIZEOF(struct var
, v_name
) + l
);
376 vp
->v_link
= _vars
[vcp
->vc_prime
];
377 _vars
[vcp
->vc_prime
] = vp
;
378 memcpy(vp
->v_name
, vcp
->vc_name
, l
);
383 if (vcp
->vc_vmap
== NULL
)
384 vp
->v_value
= _var_vcopy(value
);
386 /* Via `set' etc. the user may give even binary options non-binary
387 * values, ignore that and force binary xxx error log? */
388 if (vcp
->vc_vmap
->vm_binary
)
390 vp
->v_value
= _var_vcopy(value
);
392 /* Check if update allowed XXX wasteful on error! */
393 if (vcp
->vc_vmap
->vm_special
&&
394 (err
= !_var_check_specials(vcp
->vc_okey
, TRU1
, &vp
->v_value
))) {
395 char *cp
= vp
->v_value
;
410 _var_clear(struct var_carrier
*vcp
)
415 if (!_var_lookup(vcp
)) {
416 if (!sourcing
&& !var_clear_allow_undefined
) {
417 fprintf(stderr
, _("`%s': undefined variable\n"), vcp
->vc_name
);
421 if (_localopts
!= NULL
)
422 _localopts_add(_localopts
, vcp
->vc_name
, vcp
->vc_var
);
424 /* Always listhead after _var_lookup() */
425 _vars
[vcp
->vc_prime
] = _vars
[vcp
->vc_prime
]->v_link
;
426 _var_vfree(vcp
->vc_var
->v_value
);
430 if (vcp
->vc_vmap
!= NULL
&& vcp
->vc_vmap
->vm_special
&&
431 !_var_check_specials(vcp
->vc_okey
, FAL0
, NULL
))
441 _var_list_all_cmp(void const *s1
, void const *s2
)
446 rv
= strcmp(*(char**)UNCONST(s1
), *(char**)UNCONST(s2
));
452 _var_set_env(char **ap
, bool_t issetenv
)
454 char *cp
, *cp2
, *varbuf
, c
;
458 for (; *ap
!= NULL
; ++ap
) {
461 cp2
= varbuf
= ac_alloc(strlen(cp
) +1);
462 for (; (c
= *cp
) != '=' && c
!= '\0'; ++cp
)
470 fprintf(stderr
, _("Non-null variable name required\n"));
475 if (!issetenv
&& varbuf
[0] == 'n' && varbuf
[1] == 'o')
476 errs
+= _var_vokclear(varbuf
+ 2);
478 errs
+= _var_vokset(varbuf
, (uintptr_t)cp
);
481 errs
+= (setenv(varbuf
, cp
, 1) != 0);
496 _is_closing_angle(char const *cp
)
501 while (spacechar(*cp
))
505 while (spacechar(*cp
))
513 static struct macro
*
514 _ma_look(char const *name
, struct macro
*data
, enum ma_flags mafl
)
516 enum ma_flags save_mafl
;
518 struct macro
*lmp
, *mp
;
522 mafl
&= MA_TYPE_MASK
;
523 h
= MA_NAME2HASH(name
);
524 h
= MA_HASH2PRIME(h
);
526 for (lmp
= NULL
, mp
= _macros
[h
]; mp
!= NULL
; lmp
= mp
, mp
= mp
->ma_next
) {
527 if ((mp
->ma_flags
& MA_TYPE_MASK
) == mafl
&& !strcmp(mp
->ma_name
, name
)) {
528 if (save_mafl
& MA_UNDEF
) {
530 _macros
[h
] = mp
->ma_next
;
532 lmp
->ma_next
= mp
->ma_next
;
539 data
->ma_next
= _macros
[h
];
549 _ma_exec(struct macro
const *mp
, struct var
**unroller
)
554 struct n2
{struct n2
*up
; struct lostack
*lo
;} *x
; /* FIXME hack (sigman+) */
555 struct mline
const *lp
;
559 los
.s_up
= _localopts
;
560 los
.s_mac
= UNCONST(mp
);
561 los
.s_localopts
= NULL
;
565 vcau_save
= var_clear_allow_undefined
;
566 var_clear_allow_undefined
= TRU1
;
568 x
= salloc(sizeof *x
); /* FIXME intermediate hack (signal man+) */
569 x
->up
= temporary_localopts_store
;
571 temporary_localopts_store
= x
;
573 buf
= ac_alloc(mp
->ma_maxlen
+1);
574 for (lp
= mp
->ma_contents
; lp
; lp
= lp
->l_next
) {
575 memcpy(buf
, lp
->l_line
, lp
->l_length
+1);
576 rv
|= execute(buf
, 0, lp
->l_length
); /* XXX break if != 0 ? */
580 temporary_localopts_store
= x
->up
; /* FIXME intermediate hack */
581 var_clear_allow_undefined
= vcau_save
;
583 _localopts
= los
.s_up
;
584 if (unroller
== NULL
) {
585 if (los
.s_localopts
!= NULL
)
586 _localopts_unroll(&los
.s_localopts
);
588 *unroller
= los
.s_localopts
;
594 _ma_list(enum ma_flags mafl
)
603 if ((fp
= Ftmp(NULL
, "listmacs", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
610 mafl
&= MA_TYPE_MASK
;
611 typestr
= (mafl
& MA_ACC
) ? "account" : "define";
613 for (ti
= mc
= 0; ti
< MA_PRIME
; ++ti
)
614 for (mq
= _macros
[ti
]; mq
; mq
= mq
->ma_next
)
615 if ((mq
->ma_flags
& MA_TYPE_MASK
) == mafl
) {
618 fprintf(fp
, "%s %s {\n", typestr
, mq
->ma_name
);
619 for (lp
= mq
->ma_contents
; lp
!= NULL
; lp
= lp
->l_next
)
620 fprintf(fp
, " %s\n", lp
->l_line
);
624 page_or_print(fp
, 0);
626 mc
= (ui32_t
)ferror(fp
);
634 _ma_define1(char const *name
, enum ma_flags mafl
)
638 struct mline
*lp
, *lst
= NULL
, *lnd
= NULL
;
639 char *linebuf
= NULL
, *cp
;
640 size_t linesize
= 0, maxlen
= 0;
644 mp
= scalloc(1, sizeof *mp
);
645 mp
->ma_name
= sstrdup(name
);
649 n
= readline_input("", TRU1
, &linebuf
, &linesize
, NULL
);
653 fprintf(stderr
, _("Unterminated %s definition: \"%s\".\n"),
654 (mafl
& MA_ACC
? "account" : "macro"), mp
->ma_name
);
655 if (sourcing
&& !loading
)
659 if (_is_closing_angle(linebuf
))
663 for (cp
= linebuf
, i
= 0; i
< n
; ++cp
, ++i
)
669 while (whitechar(cp
[n
- 1]))
675 maxlen
= MAX(maxlen
, (size_t)n
);
678 lp
= scalloc(1, sizeof(*lp
) - VFIELD_SIZEOF(struct mline
, l_line
) + n
);
679 memcpy(lp
->l_line
, cp
, n
);
680 lp
->l_length
= (size_t)--n
;
687 mp
->ma_contents
= lst
;
688 mp
->ma_maxlen
= maxlen
;
690 if (_ma_look(mp
->ma_name
, mp
, mafl
) != NULL
) {
691 fprintf(stderr
, _("A %s named `%s' already exists.\n"),
692 (mafl
& MA_ACC
? "account" : "macro"), mp
->ma_name
);
693 lst
= mp
->ma_contents
;
713 _ma_undef1(char const *name
, enum ma_flags mafl
)
718 if ((mp
= _ma_look(name
, NULL
, mafl
| MA_UNDEF
)) != NULL
) {
719 _ma_freelines(mp
->ma_contents
);
723 fprintf(stderr
, _("%s `%s' is not defined\n"),
724 (mafl
& MA_ACC
? "Account" : "Macro"), name
);
729 _ma_freelines(struct mline
*lp
)
734 for (lq
= NULL
; lp
!= NULL
; ) {
746 _localopts_add(struct lostack
*losp
, char const *name
, struct var
*ovap
)
752 /* Propagate unrolling up the stack, as necessary */
753 while (!losp
->s_unroll
&& (losp
= losp
->s_up
) != NULL
)
758 /* We've found a level that wants to unroll; check wether it does it yet */
759 for (vap
= losp
->s_localopts
; vap
!= NULL
; vap
= vap
->v_link
)
760 if (!strcmp(vap
->v_name
, name
))
763 nl
= strlen(name
) + 1;
764 vl
= (ovap
!= NULL
) ? strlen(ovap
->v_value
) + 1 : 0;
765 vap
= smalloc(sizeof(*vap
) - VFIELD_SIZEOF(struct var
, v_name
) + nl
+ vl
);
766 vap
->v_link
= losp
->s_localopts
;
767 losp
->s_localopts
= vap
;
768 memcpy(vap
->v_name
, name
, nl
);
772 vap
->v_value
= vap
->v_name
+ nl
;
773 memcpy(vap
->v_value
, ovap
->v_value
, vl
);
780 _localopts_unroll(struct var
**vapp
)
782 struct lostack
*save_los
;
789 save_los
= _localopts
;
791 while (vap
!= NULL
) {
794 vok_vset(x
->v_name
, x
->v_value
);
797 _localopts
= save_los
;
802 _var_oklook(enum okeys okey
)
804 struct var_carrier vc
;
808 vc
.vc_vmap
= _var_map
+ okey
;
809 vc
.vc_name
= _var_keydat
+ _var_map
[okey
].vm_keyoff
;
810 vc
.vc_hash
= _var_map
[okey
].vm_hash
;
813 if (!_var_lookup(&vc
)) {
814 if ((rv
= getenv(vc
.vc_name
)) != NULL
) {
816 assert(vc
.vc_var
!= NULL
);
821 rv
= vc
.vc_var
->v_value
;
827 _var_okset(enum okeys okey
, uintptr_t val
)
829 struct var_carrier vc
;
833 vc
.vc_vmap
= _var_map
+ okey
;
834 vc
.vc_name
= _var_keydat
+ _var_map
[okey
].vm_keyoff
;
835 vc
.vc_hash
= _var_map
[okey
].vm_hash
;
838 rv
= _var_set(&vc
, (val
== 0x1 ? "" : (char const*)val
));
844 _var_okclear(enum okeys okey
)
846 struct var_carrier vc
;
850 vc
.vc_vmap
= _var_map
+ okey
;
851 vc
.vc_name
= _var_keydat
+ _var_map
[okey
].vm_keyoff
;
852 vc
.vc_hash
= _var_map
[okey
].vm_hash
;
855 rv
= _var_clear(&vc
);
861 _var_voklook(char const *vokey
)
863 struct var_carrier vc
;
867 _var_revlookup(&vc
, vokey
);
869 if (!_var_lookup(&vc
)) {
870 if ((rv
= getenv(vc
.vc_name
)) != NULL
) {
872 assert(vc
.vc_var
!= NULL
);
877 rv
= vc
.vc_var
->v_value
;
883 _var_vokset(char const *vokey
, uintptr_t val
)
885 struct var_carrier vc
;
889 _var_revlookup(&vc
, vokey
);
891 err
= _var_set(&vc
, (val
== 0x1 ? "" : (char const*)val
));
897 _var_vokclear(char const *vokey
)
899 struct var_carrier vc
;
903 _var_revlookup(&vc
, vokey
);
905 err
= _var_clear(&vc
);
912 _var_xoklook(enum okeys okey
, struct url
const *urlp
, enum okey_xlook_mode oxm
)
914 struct var_carrier vc
;
915 struct str
const *us
;
917 char *nbuf
= NULL
/* CC happiness */, *rv
;
920 assert(oxm
& (OXM_PLAIN
| OXM_H_P
| OXM_U_H_P
));
922 /* For simplicity: allow this case too */
923 if (!(oxm
& (OXM_H_P
| OXM_U_H_P
)))
926 vc
.vc_vmap
= _var_map
+ okey
;
927 vc
.vc_name
= _var_keydat
+ _var_map
[okey
].vm_keyoff
;
930 us
= (oxm
& OXM_U_H_P
) ? &urlp
->url_u_h_p
: &urlp
->url_h_p
;
931 nlen
= strlen(vc
.vc_name
);
932 nbuf
= ac_alloc(nlen
+ 1 + us
->l
+1);
933 memcpy(nbuf
, vc
.vc_name
, nlen
);
936 /* One of .url_u_h_p and .url_h_p we test in here */
937 memcpy(nbuf
+ nlen
, us
->s
, us
->l
+1);
938 vc
.vc_name
= _var_canonify(nbuf
);
939 vc
.vc_hash
= MA_NAME2HASH(vc
.vc_name
);
940 if (_var_lookup(&vc
))
946 memcpy(nbuf
+ nlen
, us
->s
, us
->l
+1);
947 vc
.vc_name
= _var_canonify(nbuf
);
948 vc
.vc_hash
= MA_NAME2HASH(vc
.vc_name
);
949 if (_var_lookup(&vc
)) {
951 rv
= vc
.vc_var
->v_value
;
957 rv
= (oxm
& OXM_PLAIN
) ? _var_oklook(okey
) : NULL
;
959 if (oxm
& (OXM_H_P
| OXM_U_H_P
))
964 #endif /* HAVE_SMTP */
976 if ((fp
= Ftmp(NULL
, "listvars", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
982 for (no
= i
= 0; i
< MA_PRIME
; ++i
)
983 for (vp
= _vars
[i
]; vp
!= NULL
; vp
= vp
->v_link
)
985 vacp
= salloc(no
* sizeof(*vacp
));
986 for (cap
= vacp
, i
= 0; i
< MA_PRIME
; ++i
)
987 for (vp
= _vars
[i
]; vp
!= NULL
; vp
= vp
->v_link
)
991 qsort(vacp
, no
, sizeof *vacp
, &_var_list_all_cmp
);
993 i
= (ok_blook(bsdcompat
) || ok_blook(bsdset
));
994 fmt
= (i
!= 0) ? "%s\t%s\n" : "%s=\"%s\"\n";
996 for (cap
= vacp
; no
!= 0; ++cap
, --no
) {
997 char *cp
= vok_vlook(*cap
); /* XXX when lookup checks val/bin, change */
1000 if (i
|| *cp
!= '\0')
1001 fprintf(fp
, fmt
, *cap
, cp
);
1003 fprintf(fp
, "%s\n", *cap
);
1006 page_or_print(fp
, PTR2SIZE(cap
- vacp
));
1015 struct var_carrier vc
;
1016 char **argv
= v
, *val
;
1017 bool_t isenv
, isset
;
1020 if (*argv
== NULL
) {
1025 for (; *argv
!= NULL
; ++argv
) {
1026 memset(&vc
, 0, sizeof vc
);
1027 _var_revlookup(&vc
, *argv
);
1028 if (_var_lookup(&vc
)) {
1029 val
= vc
.vc_var
->v_value
;
1032 isenv
= ((val
= getenv(vc
.vc_name
)) != NULL
);
1034 val
= UNCONST("NULL");
1035 isset
= (vc
.vc_var
!= NULL
);
1037 if (vc
.vc_vmap
!= NULL
) {
1038 if (vc
.vc_vmap
->vm_binary
)
1039 printf("%s: binary option (%d): isset=%d/environ=%d\n",
1040 vc
.vc_name
, vc
.vc_okey
, isset
, isenv
);
1042 printf("%s: value option (%d): isset=%d/environ=%d value<%s>\n",
1043 vc
.vc_name
, vc
.vc_okey
, isset
, isenv
, val
);
1045 printf("%s: isset=%d/environ=%d value<%s>\n",
1046 vc
.vc_name
, isset
, isenv
, val
);
1050 return (v
== NULL
? !STOP
: !OKAY
); /* xxx 1:bad 0:good -- do some */
1064 err
= _var_set_env(ap
, FAL0
);
1076 if (!(err
= starting
))
1077 err
= _var_set_env(ap
, TRU1
);
1090 err
|= _var_vokclear(*ap
++);
1101 if (!(err
= starting
)) {
1103 bool_t vcau_save
= var_clear_allow_undefined
;
1104 var_clear_allow_undefined
= TRU1
;
1106 for (ap
= v
; *ap
!= NULL
; ++ap
) {
1107 bool_t bad
= _var_vokclear(*ap
);
1110 unsetenv(*ap
) != 0 &&
1117 var_clear_allow_undefined
= vcau_save
;
1126 struct var_carrier vc
;
1127 sighandler_type sigint
;
1129 char *val
, **argv
= v
;
1133 sigint
= safe_signal(SIGINT
, SIG_IGN
);
1135 while (*argv
!= NULL
) {
1136 memset(&vc
, 0, sizeof vc
);
1137 _var_revlookup(&vc
, *argv
++);
1139 if (!_var_lookup(&vc
)) {
1140 fprintf(stderr
, _("`varedit': variable `%s' is not set\n"),
1144 } else if (vc
.vc_vmap
!= NULL
&& vc
.vc_vmap
->vm_binary
) {
1145 fprintf(stderr
, _("`varedit' cannot edit binary value `%s'\n"),
1150 if ((of
= Ftmp(NULL
, "vared", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
1152 perror(_("`varedit': cannot create temporary file"));
1155 } else if (*(val
= vc
.vc_var
->v_value
) != '\0' &&
1156 sizeof *val
!= fwrite(val
, strlen(val
), sizeof *val
, of
)) {
1157 perror(_("`varedit' failed to write an old value to temporary file"));
1164 nf
= run_editor(of
, (off_t
)-1, 'e', FAL0
, NULL
, NULL
, SEND_MBOX
, sigint
);
1170 off_t l
= fsize(nf
);
1173 base
= smalloc((size_t)l
+ 1);
1175 for (l
= 0, val
= base
; (c
= getc(nf
)) != EOF
; ++val
)
1176 if (c
== '\n' || c
== '\r') {
1180 *val
= (char)(uc_it
)c
;
1186 if (!vok_vset(vc
.vc_name
, base
))
1194 safe_signal(SIGINT
, sigint
);
1206 if (args
[0] == NULL
) {
1207 rv
= _ma_list(MA_NONE
);
1211 if (args
[1] == NULL
|| args
[1][0] != '{' || args
[1][1] != '\0' ||
1213 fprintf(stderr
, _("Syntax is: define <name> {"));
1217 rv
= !_ma_define1(args
[0], MA_NONE
);
1230 if (*args
== NULL
) {
1231 fprintf(stderr
, _("`%s': required arguments are missing\n"),
1236 _ma_undef1(*args
, MA_NONE
);
1237 while (*++args
!= NULL
);
1249 char const *errs
, *name
;
1253 if (args
[0] == NULL
|| (args
[1] != NULL
&& args
[2] != NULL
)) {
1254 errs
= _("Syntax is: call <%s>\n");
1259 if ((mp
= _ma_look(*args
, NULL
, MA_NONE
)) == NULL
) {
1260 errs
= _("Undefined macro called: `%s'\n");
1265 rv
= _ma_exec(mp
, NULL
);
1270 fprintf(stderr
, errs
, name
);
1275 callhook(char const *name
, int nmail
)
1282 var
= ac_alloc(len
= strlen(name
) + 12 +1);
1283 snprintf(var
, len
, "folder-hook-%s", name
);
1284 if ((cp
= vok_vlook(var
)) == NULL
&& (cp
= ok_vlook(folder_hook
)) == NULL
) {
1288 if ((mp
= _ma_look(cp
, NULL
, MA_NONE
)) == NULL
) {
1289 fprintf(stderr
, _("Cannot call hook for folder \"%s\": "
1290 "Macro \"%s\" does not exist.\n"), name
, cp
);
1295 inhook
= nmail
? 3 : 1; /* XXX enum state machine */
1296 rv
= _ma_exec(mp
, NULL
);
1309 int rv
= 1, i
, oqf
, nqf
;
1312 if (args
[0] == NULL
) {
1313 rv
= _ma_list(MA_ACC
);
1317 if (args
[1] && args
[1][0] == '{' && args
[1][1] == '\0') {
1318 if (args
[2] != NULL
) {
1319 fprintf(stderr
, _("Syntax is: account <name> {\n"));
1322 if (!asccasecmp(args
[0], ACCOUNT_NULL
)) {
1323 fprintf(stderr
, _("Error: `%s' is a reserved name.\n"),
1327 rv
= !_ma_define1(args
[0], MA_ACC
);
1332 fprintf(stderr
, _("Cannot change account from within a hook.\n"));
1336 save_mbox_for_possible_quitstuff();
1339 if (asccasecmp(args
[0], ACCOUNT_NULL
) != 0 &&
1340 (mp
= _ma_look(args
[0], NULL
, MA_ACC
)) == NULL
) {
1341 fprintf(stderr
, _("Account `%s' does not exist.\n"), args
[0]);
1345 oqf
= savequitflags();
1346 if (_acc_curr
!= NULL
&& _acc_curr
->ma_localopts
!= NULL
)
1347 _localopts_unroll(&_acc_curr
->ma_localopts
);
1348 account_name
= (mp
!= NULL
) ? mp
->ma_name
: NULL
;
1351 if (mp
!= NULL
&& _ma_exec(mp
, &mp
->ma_localopts
) == CBAD
) {
1352 /* XXX account switch incomplete, unroll? */
1353 fprintf(stderr
, _("Switching to account `%s' failed.\n"), args
[0]);
1357 if (!starting
&& !inhook
) {
1358 nqf
= savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1359 restorequitflags(oqf
);
1360 if ((i
= setfile("%", 0)) < 0)
1362 callhook(mailname
, 0);
1363 if (i
> 0 && !ok_blook(emptystart
))
1365 announce(ok_blook(bsdcompat
) || ok_blook(bsdannounce
));
1366 restorequitflags(nqf
);
1375 c_unaccount(void *v
)
1381 if (*args
== NULL
) {
1382 fprintf(stderr
, _("`%s': required arguments are missing\n"),
1389 if (account_name
!= NULL
&& !strcmp(account_name
, *args
)) {
1391 _("Rejecting deletion of currently active account `%s'\n"), *args
);
1394 _ma_undef1(*args
, MA_ACC
);
1395 } while (*++args
!= NULL
);
1402 c_localopts(void *v
)
1408 if (_localopts
== NULL
) {
1410 _("Cannot use `localopts' but from within a `define' or `account'\n"));
1414 _localopts
->s_unroll
= (**c
== '0') ? FAL0
: TRU1
;
1422 temporary_localopts_free(void) /* XXX intermediate hack */
1424 struct n2
{struct n2
*up
; struct lostack
*lo
;} *x
;
1427 var_clear_allow_undefined
= FAL0
;
1428 x
= temporary_localopts_store
;
1429 temporary_localopts_store
= NULL
;
1433 struct lostack
*losp
= x
->lo
;
1435 _localopts_unroll(&losp
->s_localopts
);
1440 /* vim:set fenc=utf-8:s-it-mode */