From 4047a43208c9247d8791a0f4e1faabd68c10b758 Mon Sep 17 00:00:00 2001 From: "Steffen (Daode) Nurpmeso" Date: Thu, 27 Jul 2017 22:13:59 +0200 Subject: [PATCH] Extend `localopts' with attributes: [scope], call, call-fixate.. Like this we can enforce `call'ed macros to honour some variable change encapsulation property. --- accmacvar.c | 79 ++++++++++++++++++++++++++++++++++------------------- cc-test.sh | 50 ++++++++++++++++++++++++++++++++-- cmd-tab.h | 4 +-- nail.1 | 90 ++++++++++++++++++++++++++++++++++++++++--------------------- nailfuns.h | 6 ++--- 5 files changed, 165 insertions(+), 64 deletions(-) diff --git a/accmacvar.c b/accmacvar.c index 9c3403e3..5e1b5db8 100644 --- a/accmacvar.c +++ b/accmacvar.c @@ -75,6 +75,17 @@ enum a_amv_mac_flags{ a_AMV_MF__MAX = 0xFFu }; +enum a_amv_loflags{ + a_AMV_LF_NONE = 0, + a_AMV_LF_SCOPE = 1u<<0, /* Current scope `localopts' on */ + a_AMV_LF_SCOPE_FIXATE = 1u<<1, /* Ditto, but fixated */ + a_AMV_LF_SCOPE_MASK = a_AMV_LF_SCOPE | a_AMV_LF_SCOPE_FIXATE, + a_AMV_LF_CALL = 1u<<2, /* `localopts' on for `call'ed scopes */ + a_AMV_LF_CALL_FIXATE = 1u<<3, /* Ditto, but fixated */ + a_AMV_LF_CALL_MASK = a_AMV_LF_CALL | a_AMV_LF_CALL_FIXATE, + a_AMV_LF_CALL_TO_SCOPE_SHIFT = 2 +}; + /* make-okey-map.pl ensures that _VIRT implies _RDONLY and _NODEL, and that * _IMPORT implies _ENV; it doesn't verify anything... */ enum a_amv_var_flags{ @@ -164,7 +175,7 @@ struct a_amv_mac_call_args{ struct a_amv_var **amca_unroller; void (*amca_hook_pre)(void *); void *amca_hook_arg; - bool_t amca_lopts_on; + ui8_t amca_loflags; bool_t amca_ps_hook_mask; bool_t amca_no_xcall; /* We want n_GO_INPUT_NO_XCALL for this */ ui8_t amca__pad[5]; @@ -176,7 +187,7 @@ struct a_amv_lostack{ struct a_amv_mac_call_args *as_amcap; struct a_amv_lostack *as_up; /* Outer context */ struct a_amv_var *as_lopts; - bool_t as_unroll; /* Unrolling enabled? */ + ui8_t as_loflags; /* enum a_amv_mac_loflags */ ui8_t avs__pad[7]; }; @@ -438,6 +449,9 @@ a_amv_mac_call(void *v, bool_t silent_nexist){ memset(amcap, 0, sizeof *amcap); amcap->amca_name = name; amcap->amca_amp = amp; + if(a_amv_lopts != NULL) + amcap->amca_loflags = (a_amv_lopts->as_loflags & a_AMV_LF_CALL_MASK + ) >> a_AMV_LF_CALL_TO_SCOPE_SHIFT; if(argc > 0){ amcap->amca_pospar.app_count = (ui16_t)argc; amcap->amca_pospar.app_not_heap = TRU1; @@ -479,7 +493,7 @@ a_amv_mac_exec(struct a_amv_mac_call_args *amcap){ losp->as_up = NULL; losp->as_lopts = *amcap->amca_unroller; } - losp->as_unroll = amcap->amca_lopts_on; + losp->as_loflags = amcap->amca_loflags; a_amv_lopts = losp; if(amcap->amca_hook_pre != NULL) @@ -663,12 +677,10 @@ a_amv_mac_def(char const *name, enum a_amv_mac_flags amf){ /* Create the new macro */ n.s = strlen(name) +1; amp = smalloc(n_VSTRUCT_SIZEOF(struct a_amv_mac, am_name) + n.s); - amp->am_next = NULL; + memset(amp, 0, sizeof *amp); amp->am_maxlen = maxlen; amp->am_line_cnt = line_cnt; - amp->am_refcnt = 0; amp->am_flags = amf; - amp->am_lopts = NULL; memcpy(amp->am_name, name, n.s); /* C99 */{ struct a_amv_mac_line **amlpp; @@ -755,13 +767,13 @@ a_amv_lopts_add(struct a_amv_lostack *alp, char const *name, /* Propagate unrolling up the stack, as necessary */ assert(alp != NULL); for(;;){ - if(alp->as_unroll) + if(alp->as_loflags & a_AMV_LF_SCOPE_MASK) break; if((alp = alp->as_up) == NULL) goto jleave; } - /* Check whether this variable is handled yet */ + /* Check whether this variable is handled yet XXX Boost: ID check etc.!! */ for(avp = alp->as_lopts; avp != NULL; avp = avp->av_link) if(!strcmp(avp->av_name, name)) goto jleave; @@ -2057,7 +2069,7 @@ c_account(void *v){ amcap->amca_name = amp->am_name; amcap->amca_amp = amp; amcap->amca_unroller = &->am_lopts; - amcap->amca_lopts_on = TRU1; + amcap->amca_loflags = a_AMV_LF_SCOPE_FIXATE; amcap->amca_no_xcall = TRU1; ++amp->am_refcnt; /* We may not run 0 to avoid being deleted! */ if(!a_amv_mac_exec(amcap)){ @@ -2103,8 +2115,9 @@ c_unaccount(void *v){ } FL int -c_localopts(void *v){ - char **argv; +c_localopts(void *vp){ + enum a_amv_loflags alf, alm; + char const **argv; int rv; NYD_ENTER; @@ -2116,22 +2129,35 @@ c_localopts(void *v){ goto jleave; } - rv = 0; + if((argv = vp)[1] == NULL || is_asccaseprefix((++argv)[-1], "scope")) + alf = alm = a_AMV_LF_SCOPE; + else if(is_asccaseprefix(argv[-1], "call")) + alf = a_AMV_LF_CALL, alm = a_AMV_LF_CALL_MASK; + else if(is_asccaseprefix(argv[-1], "call-fixate")) + alf = a_AMV_LF_CALL_FIXATE, alm = a_AMV_LF_CALL_MASK; + else{ + n_err(_("Synopsis: localopts: [] \n")); + goto jleave; + } - if(n_pstate & (n_PS_HOOK | n_PS_COMPOSE_MODE)){ + if(alf == a_AMV_LF_SCOPE && + (a_amv_lopts->as_loflags & a_AMV_LF_SCOPE_FIXATE)){ if(n_poption & n_PO_D_V) - n_err(_("Cannot turn off `localopts' for compose-mode hooks\n")); + n_err(_("Cannot turn off `localopts', setting is fixated\n")); goto jleave; } - a_amv_lopts->as_unroll = (boolify(*(argv = v), UIZ_MAX, FAL0) > 0); + a_amv_lopts->as_loflags &= ~alm; + if(boolify(*argv, UIZ_MAX, FAL0) > 0) + a_amv_lopts->as_loflags |= alf; + rv = 0; jleave: NYD_LEAVE; return rv; } FL int -c_shift(void *v){ +c_shift(void *vp){ struct a_amv_pospar *appp; ui16_t i; int rv; @@ -2139,15 +2165,15 @@ c_shift(void *v){ rv = 1; - if((v = *(char**)v) == NULL) + if((vp = *(char**)vp) == NULL) i = 1; else{ si16_t sib; - if((n_idec_si16_cp(&sib, v, 10, NULL + if((n_idec_si16_cp(&sib, vp, 10, NULL ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED) ) != n_IDEC_STATE_CONSUMED || sib < 0){ - n_err(_("`shift': invalid argument: %s\n"), v); + n_err(_("`shift': invalid argument: %s\n"), vp); goto jleave; } i = (ui16_t)sib; @@ -2185,7 +2211,7 @@ jleave: } FL int -c_return(void *v){ /* TODO the exit status should be m_si64! */ +c_return(void *vp){ /* TODO the exit status should be m_si64! */ int rv; NYD_ENTER; @@ -2196,7 +2222,7 @@ c_return(void *v){ /* TODO the exit status should be m_si64! */ n_pstate_err_no = n_ERR_NONE; rv = 0; - if((argv = v)[0] != NULL){ + if((argv = vp)[0] != NULL){ si32_t i; if((n_idec_si32_cp(&i, argv[0], 10, NULL @@ -2287,7 +2313,7 @@ jmac: amcap->amca_unroller = &a_amv_folder_hook_lopts; n_pstate |= n_PS_HOOK; } - amcap->amca_lopts_on = TRU1; + amcap->amca_loflags = a_AMV_LF_SCOPE_FIXATE; amcap->amca_ps_hook_mask = TRU1; amcap->amca_no_xcall = TRU1; rv = a_amv_mac_exec(amcap); @@ -2340,7 +2366,7 @@ temporary_compose_mode_hook_call(char const *macname, amcap->amca_unroller = &a_amv_compose_lopts; amcap->amca_hook_pre = hook_pre; amcap->amca_hook_arg = hook_arg; - amcap->amca_lopts_on = TRU1; + amcap->amca_loflags = a_AMV_LF_SCOPE_FIXATE; amcap->amca_ps_hook_mask = TRU1; amcap->amca_no_xcall = TRU1; n_pstate &= ~n_PS_HOOK_MASK; @@ -2349,10 +2375,10 @@ temporary_compose_mode_hook_call(char const *macname, a_amv_mac_exec(amcap); else{ cmh_losp = n_lofi_alloc(sizeof *cmh_losp); + memset(cmh_losp, 0, sizeof *cmh_losp); cmh_losp->as_global_saved = a_amv_lopts; - cmh_losp->as_up = NULL; cmh_losp->as_lopts = *(cmh_losp->as_amcap = amcap)->amca_unroller; - cmh_losp->as_unroll = TRU1; + cmh_losp->as_loflags = a_AMV_LF_SCOPE_FIXATE; a_amv_lopts = cmh_losp; } } @@ -3171,12 +3197,11 @@ jenum_plusminus: rema[i].rm_eo - rema[i].rm_so); *reargv = NULL; + memset(&los, 0, sizeof los); hold_all_sigs(); /* TODO DISLIKE! */ los.as_global_saved = a_amv_lopts; los.as_amcap = &amca; los.as_up = los.as_global_saved; - los.as_lopts = NULL; - los.as_unroll = FAL0; a_amv_lopts = &los; /* C99 */{ diff --git a/cc-test.sh b/cc-test.sh index d8df41ad..e93dad3e 100755 --- a/cc-test.sh +++ b/cc-test.sh @@ -1316,7 +1316,7 @@ t_behave_localopts() { t_prolog # Nestable conditions test - ${cat} <<- '__EOT' | ${MAILX} ${ARGS} > "${MBOX}" + ${cat} <<- '__EOT' | ${MAILX} ${ARGS} > "${MBOX}" 2>&1 define t2 { echo in: t2 set t2=t2 @@ -1349,9 +1349,55 @@ t_behave_localopts() { echo active trouble: $gv1 $lv1 ${lv2} ${lv3} ${gv2}, $t3 account null echo active null: $gv1 $lv1 ${lv2} ${lv3} ${gv2}, $t3 + + # + define ll2 { + localopts $1 + set x=2 + echo ll2=$x + } + define ll1 { + wysh set y=$1; shift; eval localopts $y; localopts $1; shift + set x=1 + echo ll1.1=$x + call ll2 $1 + echo ll1.2=$x + } + define ll0 { + wysh set y=$1; shift; eval localopts $y; localopts $1; shift + set x=0 + echo ll0.1=$x + call ll1 $y "$@" + echo ll0.2=$x + } + define llx { + echo ----- $1: $2 -> $3 -> $4 + echo ll-1.1=$x + eval localopts $1 + call ll0 "$@" + echo ll-1.2=$x + unset x + } + define lly { + call llx 'call off' on on on + call llx 'call off' off on on + call llx 'call off' on off on + call llx 'call off' on off off + localopts call-fixate on + call llx 'call-fixate on' on on on + call llx 'call-fixate on' off on on + call llx 'call-fixate on' on off on + call llx 'call-fixate on' on off off + unset x;localopts call on + call llx 'call on' on on on + call llx 'call on' off on on + call llx 'call on' on off on + call llx 'call on' on off off + } + call lly __EOT - check behave:localopts 0 "${MBOX}" '1936527193 192' + check behave:localopts 0 "${MBOX}" '4016155249 1246' t_epilog } diff --git a/cmd-tab.h b/cmd-tab.h index 7181c7be..cec55900 100644 --- a/cmd-tab.h +++ b/cmd-tab.h @@ -502,8 +502,8 @@ n_CMD_ARG_DESC_SUBCLASS_DEF(vpospar, 2, a_ctab_cad_vpospar){ { "list", &a_ctab_c_list, (M | TWYSH), 0, 1, NULL DS(N_("List all commands (with argument: in prefix search order)")) }, - { "localopts", &c_localopts, (H | M | X | TWYSH), 1, 1, NULL - DS(N_("Inside `define' / `account': isolate modifications? "))}, + { "localopts", &c_localopts, (H | M | X | TWYSH), 1, 2, NULL + DS(N_("Localize variable modifications? [] "))}, { "netrc", #ifdef HAVE_NETRC diff --git a/nail.1 b/nail.1 index 1270a365..262f75ef 100644 --- a/nail.1 +++ b/nail.1 @@ -4011,6 +4011,13 @@ results in a .Va ^ERR Ns -OVERFLOW ) , and are otherwise controllable via .Ic vpospar . +Changes to other +.Sx ENVIRONMENT +as well as +.Sx "INTERNAL VARIABLES" +can be reverted before the current level regains control by setting +.Ic localopts +for called macro(s) (or in them, of course). Macro execution can be terminated at any time by calling .Ic return . . @@ -5291,53 +5298,76 @@ e.g., from within a macro that is called via .Va on-compose-splice . .El . +. .Mx .It Ic localopts -This command can be used to localize changes to variables, meaning that -their state will be reverted to the former one once the covered scope +This command can be used to localize changes to (linked) +.Sx ENVIRONMENT +as well as +.Sx "INTERNAL VARIABLES" , +meaning that their state will be reverted to the former one once the +.Dq covered scope is left. It can only be used inside of macro definition blocks introduced by .Ic account or -.Ic define , -and is interpreted as a boolean (string, see -.Sx "INTERNAL VARIABLES" ) ; -the -.Dq covered scope -of an account is left once it is switched off again. -.Bd -literal -offset indent -define temporary_settings { - set possibly_global_option1 - localopts on - set local_option1 - set local_option2 - localopts off - set possibly_global_option2 -} -.Ed +.Ic define . +The covered scope of an +.Ic account +is left once a different account is activated, and some macros, notably +.Va folder-hook Ns s , +use their own specific notion of covered scope, here it will be extended +until the folder is left again. +. .Pp -.Sy Note -that this setting -.Dq stacks up : -i.e., if +This setting stacks up: i.e., if .Ql macro1 enables change localization and calls .Ql macro2 , which explicitly resets localization, then any value changes within .Ql macro2 -will still be reverted by -.Ql macro1 ! -\*(ID Once the outermost level, the one which enabled localization -first, is left, but no earlier, all adjustments will be unrolled. -\*(ID Likewise worth knowing, if in this example +will still be reverted when the scope of +.Ql macro1 +is left. +(Caveats: if in this example .Ql macro2 changes to a different .Ic account -which sets some variables that are yet covered by localizations, their -scope will be extended, and in fact leaving the +which sets some variables that are already covered by localizations, +their scope will be extended, and in fact leaving the .Ic account will (thus) restore settings in (likely) global scope which actually -were defined in a local, private context. +were defined in a local, macro private context!) +. +.Pp +This command takes one or two arguments, the optional first one +specifies an attribute that may be one of +.Cm scope , +which refers to the current scope and is thus the default, +.Cm call , +which causes any macro that is being +.Ic call Ns +ed to be started with localization enabled by default, as well as +.Cm call-fixate , +which (if enabled) disallows any called macro to turn off localization: +like this it can be ensured that once the current scope regains control, +any changes made in deeper levels have been reverted. +The latter two are mutually exclusive. +The (second) argument is interpreted as a boolean (string, see +.Sx "INTERNAL VARIABLES" ) +and states whether the given attribute shall be turned on or off. +. +.Bd -literal -offset indent +define temporary_settings { + set possibly_global_option1 + localopts on + set local_option1 + set local_option2 + localopts scope off + set possibly_global_option2 +} +.Ed +. . .Mx .It Ic Lreply diff --git a/nailfuns.h b/nailfuns.h index 0f87e122..9812f9eb 100644 --- a/nailfuns.h +++ b/nailfuns.h @@ -169,9 +169,9 @@ FL int c_account(void *v); FL int c_unaccount(void *v); /* `localopts', `shift', `return' */ -FL int c_localopts(void *v); -FL int c_shift(void *v); -FL int c_return(void *v); +FL int c_localopts(void *vp); +FL int c_shift(void *vp); +FL int c_return(void *vp); /* TODO Check whether a *folder-hook* exists for the currently active mailbox */ FL bool_t temporary_folder_hook_check(bool_t nmail); -- 2.11.4.GIT