mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-06-14 20:18:06 +02:00
zsh-workers:6113
This commit is contained in:
parent
8ef649b31e
commit
ca7ec1bf02
6 changed files with 178 additions and 133 deletions
|
@ -83,8 +83,7 @@ while true; do
|
|||
else
|
||||
# No exact match, see how many strings match what's on the line.
|
||||
|
||||
tmp2=( "${(@)matches%%${sep}*}" )
|
||||
compadd -O tmp1 - "$tmp2[@]"
|
||||
compadd -O tmp1 - "${(@)matches%%${sep}*}"
|
||||
|
||||
if [[ $#tmp1 -eq 1 ]]; then
|
||||
|
||||
|
|
|
@ -230,14 +230,15 @@ for prepath in "$prepaths[@]"; do
|
|||
if [[ -n "$PREFIX$SUFFIX" ]]; then
|
||||
# See which of them match what's on the line.
|
||||
|
||||
compadd -O tmp2 "$ignore[@]" - "${(@)tmp1##*/}"
|
||||
tmp2=("$tmp1[@]")
|
||||
compadd -D tmp1 "$ignore[@]" - "${(@)tmp1##*/}"
|
||||
|
||||
# If no file matches, save the expanded path and continue with
|
||||
# the outer loop.
|
||||
|
||||
if [[ $#tmp2 -eq 0 ]]; then
|
||||
if [[ "$tmp1[1]" = */* ]]; then
|
||||
tmp2=( "${(@)tmp1#${prepath}${realpath}}" )
|
||||
if [[ $#tmp1 -eq 0 ]]; then
|
||||
if [[ "$tmp2[1]" = */* ]]; then
|
||||
tmp2=( "${(@)tmp2#${prepath}${realpath}}" )
|
||||
if [[ "$tmp2[1]" = */* ]]; then
|
||||
exppaths=( "$exppaths[@]" ${^tmp2%/*}/${tpre}${tsuf} )
|
||||
else
|
||||
|
@ -246,14 +247,6 @@ for prepath in "$prepaths[@]"; do
|
|||
fi
|
||||
continue 2
|
||||
fi
|
||||
|
||||
# Remove all files that weren't matched.
|
||||
|
||||
if [[ "$tmp1[1]" = */* ]]; then
|
||||
tmp1=( "${(@M)tmp1:#*/(${(j:|:)~${(@)tmp2:q}})}" )
|
||||
else
|
||||
tmp1=( "${(@M)tmp1:#(${(j:|:)~${(@)tmp2:q}})}" )
|
||||
fi
|
||||
elif (( ! $#tmp1 )); then
|
||||
continue 2
|
||||
fi
|
||||
|
|
|
@ -340,7 +340,7 @@ xitem([ tt(-W) var(file-prefix) ])
|
|||
xitem([ tt(-J) var(name) ] [ tt(-V) var(name) ] [ tt(-X) var(explanation) ])
|
||||
xitem([ tt(-r) var(remove-chars) ] [ tt(-R) var(remove-func) ])
|
||||
xitem([ tt(-M) var(match-spec) ] [ tt(-O) var(array) ] [ tt(-A) var(array) ])
|
||||
item([ tt(--) ] [ var(words) ... ])(
|
||||
item([ tt(-D) var(array) ] [ tt(--) ] [ var(words) ... ])(
|
||||
|
||||
This builtin command can be used to add matches directly and control
|
||||
all the information the completion code stores with each possible
|
||||
|
@ -518,6 +518,13 @@ on the command line and the string `tt(foo)' as one of the var(words), this
|
|||
option stores the string `tt(nofoo)' in the array, whereas the tt(-O)
|
||||
option stores the `tt(foo)' originally given.
|
||||
)
|
||||
item(tt(-D) var(array))(
|
||||
As with tt(-O), the var(words) are not added to the set of possible
|
||||
completions. Instead, the completion code tests every var(word) if
|
||||
it matches what is on the line. If the var(n)'th var(word) does not
|
||||
match, the var(n)'th element of the var(array) is removed. Elements
|
||||
for which the corresponding var(word) is matched are retained.
|
||||
)
|
||||
item(tt(-), tt(--))(
|
||||
This flag ends the list of flags and options. All arguments after it
|
||||
will be taken as the words to use as matches even if they begin with
|
||||
|
|
|
@ -270,6 +270,31 @@ struct cpattern {
|
|||
#define CAF_ALT 4
|
||||
#define CAF_MATCH 8
|
||||
|
||||
/* Data for compadd and addmatches() */
|
||||
|
||||
typedef struct cadata *Cadata;
|
||||
|
||||
struct cadata {
|
||||
char *ipre;
|
||||
char *isuf;
|
||||
char *ppre;
|
||||
char *psuf;
|
||||
char *prpre;
|
||||
char *pre;
|
||||
char *suf;
|
||||
char *group;
|
||||
char *rems;
|
||||
char *remf;
|
||||
char *ign;
|
||||
int flags;
|
||||
int aflags;
|
||||
Cmatcher match;
|
||||
char *exp;
|
||||
char *apar;
|
||||
char *opar;
|
||||
char *dpar;
|
||||
};
|
||||
|
||||
/* Flags for special parameters. */
|
||||
|
||||
#define CP_WORDS (1 << 0)
|
||||
|
|
|
@ -1691,17 +1691,22 @@ bin_compgen(char *name, char **argv, char *ops, int func)
|
|||
static int
|
||||
bin_compadd(char *name, char **argv, char *ops, int func)
|
||||
{
|
||||
char *p, **sp, *e;
|
||||
char *ipre = NULL, *isuf = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL;
|
||||
char *pre = NULL, *suf = NULL, *group = NULL, *m = NULL, *rs = NULL;
|
||||
char *ign = NULL, *rf = NULL, *expl = NULL, *apar = NULL, *opar = NULL;
|
||||
int f = 0, a = CAF_MATCH, dm;
|
||||
struct cadata dat;
|
||||
char *p, **sp, *e, *m;
|
||||
int dm;
|
||||
Cmatcher match = NULL;
|
||||
|
||||
if (incompfunc != 1) {
|
||||
zerrnam(name, "can only be called from completion function", NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre =
|
||||
dat.pre = dat.suf = dat.group = dat.rems = dat.remf =
|
||||
dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL;
|
||||
dat.match = NULL;
|
||||
dat.flags = 0;
|
||||
dat.aflags = CAF_MATCH;
|
||||
|
||||
for (; *argv && **argv == '-'; argv++) {
|
||||
if (!(*argv)[1]) {
|
||||
argv++;
|
||||
|
@ -1713,64 +1718,64 @@ bin_compadd(char *name, char **argv, char *ops, int func)
|
|||
dm = 0;
|
||||
switch (*p) {
|
||||
case 'q':
|
||||
f |= CMF_REMOVE;
|
||||
dat.flags |= CMF_REMOVE;
|
||||
break;
|
||||
case 'Q':
|
||||
a |= CAF_QUOTE;
|
||||
dat.aflags |= CAF_QUOTE;
|
||||
break;
|
||||
case 'f':
|
||||
f |= CMF_FILE;
|
||||
dat.flags |= CMF_FILE;
|
||||
break;
|
||||
case 'F':
|
||||
sp = &ign;
|
||||
sp = &(dat.ign);
|
||||
e = "string expected after -%c";
|
||||
break;
|
||||
case 'n':
|
||||
f |= CMF_NOLIST;
|
||||
dat.flags |= CMF_NOLIST;
|
||||
break;
|
||||
case 'U':
|
||||
a &= ~CAF_MATCH;
|
||||
dat.aflags &= ~CAF_MATCH;
|
||||
break;
|
||||
case 'P':
|
||||
sp = ⪯
|
||||
sp = &(dat.pre);
|
||||
e = "string expected after -%c";
|
||||
break;
|
||||
case 'S':
|
||||
sp = &suf;
|
||||
sp = &(dat.suf);
|
||||
e = "string expected after -%c";
|
||||
break;
|
||||
case 'J':
|
||||
sp = &group;
|
||||
sp = &(dat.group);
|
||||
e = "group name expected after -%c";
|
||||
break;
|
||||
case 'V':
|
||||
if (!group)
|
||||
a |= CAF_NOSORT;
|
||||
sp = &group;
|
||||
if (!dat.group)
|
||||
dat.aflags |= CAF_NOSORT;
|
||||
sp = &(dat.group);
|
||||
e = "group name expected after -%c";
|
||||
break;
|
||||
case 'i':
|
||||
sp = &ipre;
|
||||
sp = &(dat.ipre);
|
||||
e = "string expected after -%c";
|
||||
break;
|
||||
case 'I':
|
||||
sp = &isuf;
|
||||
sp = &(dat.isuf);
|
||||
e = "string expected after -%c";
|
||||
break;
|
||||
case 'p':
|
||||
sp = &ppre;
|
||||
sp = &(dat.ppre);
|
||||
e = "string expected after -%c";
|
||||
break;
|
||||
case 's':
|
||||
sp = &psuf;
|
||||
sp = &(dat.psuf);
|
||||
e = "string expected after -%c";
|
||||
break;
|
||||
case 'W':
|
||||
sp = &prpre;
|
||||
sp = &(dat.prpre);
|
||||
e = "string expected after -%c";
|
||||
break;
|
||||
case 'a':
|
||||
a |= CAF_ALT;
|
||||
dat.aflags |= CAF_ALT;
|
||||
break;
|
||||
case 'M':
|
||||
sp = &m;
|
||||
|
@ -1778,25 +1783,29 @@ bin_compadd(char *name, char **argv, char *ops, int func)
|
|||
dm = 1;
|
||||
break;
|
||||
case 'X':
|
||||
sp = &expl;
|
||||
sp = &(dat.exp);
|
||||
e = "string expected after -%c";
|
||||
break;
|
||||
case 'r':
|
||||
f |= CMF_REMOVE;
|
||||
sp = &rs;
|
||||
dat.flags |= CMF_REMOVE;
|
||||
sp = &(dat.rems);
|
||||
e = "string expected after -%c";
|
||||
break;
|
||||
case 'R':
|
||||
f |= CMF_REMOVE;
|
||||
sp = &rf;
|
||||
dat.flags |= CMF_REMOVE;
|
||||
sp = &(dat.remf);
|
||||
e = "function name expected after -%c";
|
||||
break;
|
||||
case 'A':
|
||||
sp = &apar;
|
||||
sp = &(dat.apar);
|
||||
e = "parameter name expected after -%c";
|
||||
break;
|
||||
case 'O':
|
||||
sp = ⦷
|
||||
sp = &(dat.opar);
|
||||
e = "parameter name expected after -%c";
|
||||
break;
|
||||
case 'D':
|
||||
sp = &(dat.dpar);
|
||||
e = "parameter name expected after -%c";
|
||||
break;
|
||||
case '-':
|
||||
|
@ -1831,11 +1840,10 @@ bin_compadd(char *name, char **argv, char *ops, int func)
|
|||
return 1;
|
||||
|
||||
match = cpcmatcher(match);
|
||||
a = addmatchesptr(ipre, isuf, ppre, psuf, prpre, pre, suf, group,
|
||||
rs, rf, ign, f, a, match, expl, apar, opar, argv);
|
||||
dm = addmatchesptr(&dat, argv);
|
||||
freecmatcher(match);
|
||||
|
||||
return a;
|
||||
return dm;
|
||||
}
|
||||
|
||||
#define CVT_RANGENUM 0
|
||||
|
|
|
@ -3472,14 +3472,10 @@ set_param(char *name, LinkList l)
|
|||
|
||||
/**/
|
||||
int
|
||||
addmatches(char *ipre, char *isuf,
|
||||
char *ppre, char *psuf, char *prpre, char *pre,
|
||||
char *suf, char *group, char *rems, char *remf, char *ign,
|
||||
int flags, int aflags, Cmatcher match, char *exp,
|
||||
char *apar, char *opar, char **argv)
|
||||
addmatches(Cadata dat, char **argv)
|
||||
{
|
||||
char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
|
||||
char **aign = NULL;
|
||||
char **aign = NULL, **dparr;
|
||||
int lpl, lsl, pl, sl, bpl, bsl, llpl = 0, llsl = 0, nm = mnum;
|
||||
int oisalt = 0, isalt, isexact, doadd;
|
||||
Cline lc = NULL;
|
||||
|
@ -3487,45 +3483,52 @@ addmatches(char *ipre, char *isuf,
|
|||
struct cmlist mst;
|
||||
Cmlist oms = mstack;
|
||||
Comp cp = NULL;
|
||||
LinkList aparl = NULL, oparl = NULL;
|
||||
LinkList aparl = NULL, oparl = NULL, dparl = NULL;
|
||||
|
||||
/* Switch back to the heap that was used when the completion widget
|
||||
* was invoked. */
|
||||
SWITCHHEAPS(compheap) {
|
||||
HEAPALLOC {
|
||||
doadd = (!apar && !opar);
|
||||
if (apar)
|
||||
doadd = (!dat->apar && !dat->opar && !dat->dpar);
|
||||
if (dat->apar)
|
||||
aparl = newlinklist();
|
||||
if (opar)
|
||||
if (dat->opar)
|
||||
oparl = newlinklist();
|
||||
if (exp) {
|
||||
if (dat->dpar) {
|
||||
if (*(dat->dpar) == '(')
|
||||
dparr = NULL;
|
||||
else if ((dparr = get_user_var(dat->dpar)) && !*dparr)
|
||||
dparr = NULL;
|
||||
dparl = newlinklist();
|
||||
}
|
||||
if (dat->exp) {
|
||||
expl = (Cexpl) zhalloc(sizeof(struct cexpl));
|
||||
expl->count = expl->fcount = 0;
|
||||
expl->str = dupstring(exp);
|
||||
expl->str = dupstring(dat->exp);
|
||||
} else
|
||||
expl = NULL;
|
||||
|
||||
/* Store the matcher in our stack of matchers. */
|
||||
if (match) {
|
||||
if (dat->match) {
|
||||
mst.next = mstack;
|
||||
mst.matcher = match;
|
||||
mst.matcher = dat->match;
|
||||
mstack = &mst;
|
||||
|
||||
if (!mnum)
|
||||
add_bmatchers(match);
|
||||
add_bmatchers(dat->match);
|
||||
|
||||
addlinknode(matchers, match);
|
||||
match->refc++;
|
||||
addlinknode(matchers, dat->match);
|
||||
dat->match->refc++;
|
||||
}
|
||||
if (mnum && (mstack || bmatchers))
|
||||
update_bmatchers();
|
||||
|
||||
/* Get the suffixes to ignore. */
|
||||
if (ign)
|
||||
aign = get_user_var(ign);
|
||||
if (dat->ign)
|
||||
aign = get_user_var(dat->ign);
|
||||
/* Get the contents of the completion variables if we have
|
||||
* to perform matching. */
|
||||
if (aflags & CAF_MATCH) {
|
||||
if (dat->aflags & CAF_MATCH) {
|
||||
lipre = dupstring(compiprefix);
|
||||
lisuf = dupstring(compisuffix);
|
||||
lpre = dupstring(compprefix);
|
||||
|
@ -3533,8 +3536,8 @@ addmatches(char *ipre, char *isuf,
|
|||
llpl = strlen(lpre);
|
||||
llsl = strlen(lsuf);
|
||||
/* Test if there is an existing -P prefix. */
|
||||
if (pre && *pre) {
|
||||
pl = pfxlen(pre, lpre);
|
||||
if (dat->pre && *dat->pre) {
|
||||
pl = pfxlen(dat->pre, lpre);
|
||||
llpl -= pl;
|
||||
lpre += pl;
|
||||
}
|
||||
|
@ -3557,79 +3560,76 @@ addmatches(char *ipre, char *isuf,
|
|||
}
|
||||
}
|
||||
/* Now duplicate the strings we have from the command line. */
|
||||
if (ipre)
|
||||
ipre = (lipre ? dyncat(lipre, ipre) : dupstring(ipre));
|
||||
if (dat->ipre)
|
||||
dat->ipre = (lipre ? dyncat(lipre, dat->ipre) :
|
||||
dupstring(dat->ipre));
|
||||
else if (lipre)
|
||||
ipre = lipre;
|
||||
if (isuf)
|
||||
isuf = (lisuf ? dyncat(lisuf, isuf) : dupstring(isuf));
|
||||
dat->ipre = lipre;
|
||||
if (dat->isuf)
|
||||
dat->isuf = (lisuf ? dyncat(lisuf, dat->isuf) :
|
||||
dupstring(dat->isuf));
|
||||
else if (lisuf)
|
||||
isuf = lisuf;
|
||||
if (ppre) {
|
||||
ppre = dupstring(ppre);
|
||||
lpl = strlen(ppre);
|
||||
dat->isuf = lisuf;
|
||||
if (dat->ppre) {
|
||||
dat->ppre = dupstring(dat->ppre);
|
||||
lpl = strlen(dat->ppre);
|
||||
} else
|
||||
lpl = 0;
|
||||
if (psuf) {
|
||||
psuf = dupstring(psuf);
|
||||
lsl = strlen(psuf);
|
||||
if (dat->psuf) {
|
||||
dat->psuf = dupstring(dat->psuf);
|
||||
lsl = strlen(dat->psuf);
|
||||
} else
|
||||
lsl = 0;
|
||||
if (aflags & CAF_MATCH) {
|
||||
s = ppre ? ppre : "";
|
||||
if (llpl <= lpl && strpfx(lpre, s)) {
|
||||
llpl = 0;
|
||||
if (dat->aflags & CAF_MATCH) {
|
||||
s = dat->ppre ? dat->ppre : "";
|
||||
if (llpl <= lpl && strpfx(lpre, s))
|
||||
lpre = "";
|
||||
} else if (llpl > lpl && strpfx(s, lpre)) {
|
||||
llpl -= lpl;
|
||||
else if (llpl > lpl && strpfx(s, lpre))
|
||||
lpre += lpl;
|
||||
} else
|
||||
else
|
||||
*argv = NULL;
|
||||
s = psuf ? psuf : "";
|
||||
if (llsl <= lsl && strsfx(lsuf, s)) {
|
||||
llsl = 0;
|
||||
s = dat->psuf ? dat->psuf : "";
|
||||
if (llsl <= lsl && strsfx(lsuf, s))
|
||||
lsuf = "";
|
||||
} else if (llsl > lsl && strsfx(s, lsuf)) {
|
||||
else if (llsl > lsl && strsfx(s, lsuf))
|
||||
lsuf[llsl - lsl] = '\0';
|
||||
llsl -= lsl;
|
||||
} else
|
||||
else
|
||||
*argv = NULL;
|
||||
}
|
||||
if (*argv) {
|
||||
if (pre)
|
||||
pre = dupstring(pre);
|
||||
if (suf)
|
||||
suf = dupstring(suf);
|
||||
if (!prpre && (prpre = ppre)) {
|
||||
singsub(&prpre);
|
||||
untokenize(prpre);
|
||||
if (dat->pre)
|
||||
dat->pre = dupstring(dat->pre);
|
||||
if (dat->suf)
|
||||
dat->suf = dupstring(dat->suf);
|
||||
if (!dat->prpre && (dat->prpre = dat->ppre)) {
|
||||
singsub(&(dat->prpre));
|
||||
untokenize(dat->prpre);
|
||||
} else
|
||||
prpre = dupstring(prpre);
|
||||
dat->prpre = dupstring(dat->prpre);
|
||||
/* Select the group in which to store the matches. */
|
||||
if (group) {
|
||||
if (dat->group) {
|
||||
endcmgroup(NULL);
|
||||
begcmgroup(group, (aflags & CAF_NOSORT));
|
||||
if (aflags & CAF_NOSORT)
|
||||
begcmgroup(dat->group, (dat->aflags & CAF_NOSORT));
|
||||
if (dat->aflags & CAF_NOSORT)
|
||||
mgroup->flags |= CGF_NOSORT;
|
||||
} else {
|
||||
endcmgroup(NULL);
|
||||
begcmgroup("default", 0);
|
||||
}
|
||||
/* Select the set of matches. */
|
||||
oisalt = (aflags & CAF_ALT);
|
||||
oisalt = (dat->aflags & CAF_ALT);
|
||||
|
||||
if (remf) {
|
||||
remf = dupstring(remf);
|
||||
rems = NULL;
|
||||
} else if (rems)
|
||||
rems = dupstring(rems);
|
||||
if (dat->remf) {
|
||||
dat->remf = dupstring(dat->remf);
|
||||
dat->rems = NULL;
|
||||
} else if (dat->rems)
|
||||
dat->rems = dupstring(dat->rems);
|
||||
|
||||
/* Probably quote the prefix and suffix for testing. */
|
||||
if (!cp && (aflags & CAF_MATCH) && !(aflags & CAF_QUOTE)) {
|
||||
if (!cp && (dat->aflags & CAF_MATCH) &&
|
||||
!(dat->aflags & CAF_QUOTE)) {
|
||||
lpre = quotename(lpre, NULL);
|
||||
lsuf = quotename(lsuf, NULL);
|
||||
llpl = strlen(lpre);
|
||||
llsl = strlen(lsuf);
|
||||
}
|
||||
}
|
||||
/* Walk through the matches given. */
|
||||
|
@ -3638,7 +3638,7 @@ addmatches(char *ipre, char *isuf,
|
|||
bpl = brpl;
|
||||
bsl = brsl;
|
||||
isalt = oisalt;
|
||||
if ((!psuf || !*psuf) && aign) {
|
||||
if ((!dat->psuf || !*(dat->psuf)) && aign) {
|
||||
/* Do the suffix-test. If the match has one of the
|
||||
* suffixes from ign, we put it in the alternate set. */
|
||||
char **pt = aign;
|
||||
|
@ -3649,39 +3649,52 @@ addmatches(char *ipre, char *isuf,
|
|||
&& !strcmp(*pt, s + sl - filell))
|
||||
isalt = 1;
|
||||
|
||||
if (isalt && !doadd)
|
||||
if (isalt && !doadd) {
|
||||
if (dparr && !*++dparr)
|
||||
dparr = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!(aflags & CAF_MATCH)) {
|
||||
if (!(dat->aflags & CAF_MATCH)) {
|
||||
ms = dupstring(s);
|
||||
lc = bld_parts(ms, sl, -1, NULL);
|
||||
isexact = 0;
|
||||
} else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc,
|
||||
!(aflags & CAF_QUOTE),
|
||||
&bpl, &bsl, &isexact)))
|
||||
!(dat->aflags & CAF_QUOTE),
|
||||
&bpl, &bsl, &isexact))) {
|
||||
if (dparr && !*++dparr)
|
||||
dparr = NULL;
|
||||
continue;
|
||||
|
||||
}
|
||||
if (doadd) {
|
||||
cm = add_match_data(isalt, ms, lc, ipre, ipre, isuf, pre,
|
||||
prpre, ppre, psuf, suf, bpl, bsl,
|
||||
flags, isexact);
|
||||
cm->rems = rems;
|
||||
cm->remf = remf;
|
||||
cm = add_match_data(isalt, ms, lc, dat->ipre, dat->ipre,
|
||||
dat->isuf, dat->pre, dat->prpre,
|
||||
dat->ppre, dat->psuf, dat->suf,
|
||||
bpl, bsl, dat->flags, isexact);
|
||||
cm->rems = dat->rems;
|
||||
cm->remf = dat->remf;
|
||||
} else {
|
||||
if (apar)
|
||||
if (dat->apar)
|
||||
addlinknode(aparl, ms);
|
||||
if (opar)
|
||||
if (dat->opar)
|
||||
addlinknode(oparl, s);
|
||||
if (dat->dpar && dparr) {
|
||||
addlinknode(dparl, *dparr);
|
||||
if (!*++dparr)
|
||||
dparr = NULL;
|
||||
}
|
||||
free_cline(lc);
|
||||
}
|
||||
}
|
||||
compnmatches = mnum;
|
||||
if (exp)
|
||||
if (dat->exp)
|
||||
addexpl();
|
||||
if (apar)
|
||||
set_param(apar, aparl);
|
||||
if (opar)
|
||||
set_param(opar, oparl);
|
||||
if (dat->apar)
|
||||
set_param(dat->apar, aparl);
|
||||
if (dat->opar)
|
||||
set_param(dat->opar, oparl);
|
||||
if (dat->dpar)
|
||||
set_param(dat->dpar, dparl);
|
||||
} LASTALLOC;
|
||||
} SWITCHBACKHEAPS;
|
||||
|
||||
|
|
Loading…
Reference in a new issue