From 137c94144cb236363f7af4ac86801fefde96d495 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Wed, 1 Mar 2000 10:08:02 +0000 Subject: zsh-workers/9947 --- Completion/Core/compdump | 3 +- Completion/Core/compinit | 7 +- Doc/Zsh/builtins.yo | 49 ++++ Doc/Zsh/func.yo | 21 +- Src/builtin.c | 4 +- Src/cond.c | 2 +- Src/exec.c | 96 +++++-- Src/glob.c | 22 +- Src/lex.c | 6 +- Src/loop.c | 4 +- Src/math.c | 7 +- Src/mem.c | 2 +- Src/parse.c | 657 +++++++++++++++++++++++++++++++++++++++++------ Src/text.c | 4 +- Src/utils.c | 2 +- Src/zsh.h | 21 +- 16 files changed, 766 insertions(+), 141 deletions(-) diff --git a/Completion/Core/compdump b/Completion/Core/compdump index c577747a1..0d8666f75 100644 --- a/Completion/Core/compdump +++ b/Completion/Core/compdump @@ -14,13 +14,14 @@ # to see if auto-dump should re-dump the dump-file. emulate -L zsh +setopt extendedglob typeset _d_file _d_f _d_bks _d_line _d_als _d_file=${_comp_dumpfile-${0:h}/compinit.dump}.$HOST.$$ typeset -U _d_files -_d_files=( ${^~fpath:/.}/_(|*[^~])(N:t) ) +_d_files=( ${^~fpath:/.}/^([^_]*|*~|*.zwc)(N:t) ) print "#files: $#_d_files" > $_d_file diff --git a/Completion/Core/compinit b/Completion/Core/compinit index 5aaaea8fe..a421c2d8d 100644 --- a/Completion/Core/compinit +++ b/Completion/Core/compinit @@ -56,6 +56,7 @@ # default dumpfile) is now the default; to turn off dumping use -D. emulate -L zsh +setopt extendedglob typeset _i_dumpfile _i_files _i_line _i_done _i_dir _i_autodump=1 typeset _i_tag _i_file _i_addfiles @@ -419,7 +420,7 @@ zstyle ':completion:*:options' prefix-hidden yes" # Now we automatically make the definition files autoloaded. typeset -U _i_files -_i_files=( ${^~fpath:/.}/_(|*[^~])(N:t) ) +_i_files=( ${^~fpath:/.}/^([^_]*|*~|*.zwc)(N:t) ) if [[ $#_i_files -lt 20 || $_compdir = */Core || -d $_compdir/Core ]]; then # Too few files: we need some more directories, # or we need to check that all directories (not just Core) are present. @@ -438,7 +439,7 @@ if [[ $#_i_files -lt 20 || $_compdir = */Core || -d $_compdir/Core ]]; then _i_addfiles[$_i_line]= done fpath=($fpath $_i_addfiles) - _i_files=( ${^~fpath:/.}/_(|*[^~])(N:t) ) + _i_files=( ${^~fpath:/.}/^([^_]*|*~|*.zwc)(N:t) ) fi fi @@ -468,7 +469,7 @@ fi if [[ -z "$_i_done" ]]; then for _i_dir in $fpath; do [[ $_i_dir = . ]] && continue - for _i_file in $_i_dir/_(|*[^~])(N); do + for _i_file in $_i_dir/^([^_]*|*~|*.zwc)(N); do read -rA _i_line < $_i_file _i_tag=$_i_line[1] shift _i_line diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index a46ec0f92..3491c8591 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1283,6 +1283,55 @@ findex(which) item(tt(which) [ tt(-wpams) ] var(name) ...)( Equivalent to tt(whence -c). ) +findex(zcompile) +cindex(wordcode, creation) +cindex(compilation) +xitem(tt(zcompile) [ tt(-U) ] [ tt(-r) | tt(-m) ] var(file) [ var(function) ... ]) +item(tt(zcompile -t) var(file) [ var(name) ... ])( +This builtin command can be used to create and display files +containing the wordcode for functions. In the first form, a wordcode +file is created. If called with only the var(file) argument, the +wordcode file has the name `var(file)tt(.zwc)' and will be placed in +the same directory as the var(file). This will make the wordcode file +be loaded instead of the normal function file when the function is +autoloaded (see +ifzman(\ +the section `Autoloading Functions' in zmanref(zshfunc) +)\ +ifnzman(\ +noderef(Functions) +) +for a description of how autoloaded functions are searched). + +If there is at least one var(function) argument, the wordcode for all +these functions will be put in the created wordcode var(file). Such +files containing the code for multiple functions are intended to be +used as elements of the tt(FPATH)/tt(fpath) special array. + +If the tt(-U) option is given, aliases in the var(function)s will not +be expanded. If the tt(-r) option is given, the function(s) in the +file will be read and copied into the shell's memory when they are +autoloaded. If the tt(-m) option is given instead, the wordcode file +will be mapped into the shell's memory. This is done in such a way +that multiple instances of the shell running on the same host will +share this mapped function. If neither tt(-r) nor tt(-m) are given, +the tt(zcompile) builtin decides which style is used based on the size +of the resulting wordcode file. + +In every case, the created file contains two versions of the wordcode, +one for big-endian machines and one for small-endian machines. The +upshot of this is that the wordcode file is machine independent and if +it is read or mapped, only one half of the file will really be used +(and mapped). + +In the second form, with the tt(-t) option, an existing wordcode file is +tested. Without further arguments, the names of the function files +used for it are listed. The first line tells the version of the shell +the file was created with and how the file will be used (mapping or +reading the file). With arguments, only the return value is set +to zero if all var(name)s name functiones defined in the file and +non-zero if at least one var(name) is not contained in the wordcode file. +) findex(zmodload) cindex(modules, loading) cindex(loading modules) diff --git a/Doc/Zsh/func.yo b/Doc/Zsh/func.yo index c2462b7dd..43c063f8c 100644 --- a/Doc/Zsh/func.yo +++ b/Doc/Zsh/func.yo @@ -33,10 +33,17 @@ cindex(autoloading functions) cindex(functions, autoloading) A function can be marked as em(undefined) using the tt(autoload) builtin (or `tt(functions -u)' or `tt(typeset -fu)'). Such a function has no -body. When the function is first executed, the tt(fpath) -variable will be searched for a file with the same name as the -function. The usual alias expansion during reading will be suppressed if -the tt(autoload) builtin or its equivalent is given the option tt(-U); +body. When the function is first executed, each element of the tt(fpath) +variable will first be searched for a file with the same name as the +function plus the extension tt(.zwc) and then with the name of the +function. The first file will only be used if it was created with the +tt(zcompile) builtin command, if it contains the wordcode for the +function and it is either older than the file with the name of the +function in the same directory or if such a file does not exist. The +usual alias expansion during reading will be suppressed +if the tt(autoload) builtin or its equivalent is given the option +tt(-U), for wordcode files this has to be decided when creating the +file with the tt(-U) option of the tt(zcompile) builtin command; this is recommended for the use of functions supplied with the zsh distribution. Thus to define functions for autoloading, a typical sequence is: @@ -44,6 +51,12 @@ is: example(fpath=(~/myfuncs $fpath) autoload myfunc1 myfunc2 ...) +The elements of the tt(fpath) array may also name wordcode files +directly. This is mostly useful for wordcode files containing multiple +functions, in which case the file is treated like a directory +containing files for functions and will be searched for the definition +of the function. + pindex(KSH_AUTOLOAD, use of) If the tt(KSH_AUTOLOAD) option is set, or the file contains only a simple definition of the function, the file's contents will be diff --git a/Src/builtin.c b/Src/builtin.c index bf29e0d33..60595c3bd 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -124,6 +124,7 @@ static struct builtin builtins[] = BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsw", "ca"), BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsw", "c"), BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ILabcfdipue", NULL), + BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUmr", NULL), }; /****************************************/ @@ -2151,7 +2152,8 @@ mkautofn(Shfunc shf) p->shf = shf; p->npats = 0; p->pats = NULL; - p->heap = 0; + p->alloc = EA_REAL; + p->dump = NULL; p->prog[0] = WCB_LIST((Z_SYNC | Z_END), 0); p->prog[1] = WCB_SUBLIST(WC_SUBLIST_END, 0, 3); diff --git a/Src/cond.c b/Src/cond.c index 87c89ea92..654b756a3 100644 --- a/Src/cond.c +++ b/Src/cond.c @@ -206,7 +206,7 @@ evalcond(Estate state) &htok)); if (htok) singsub(&right); - save = (!state->prog->heap && + save = (state->prog->alloc != EA_HEAP && !strcmp(opat, right) && pprog != dummy_patprog2); if (!(pprog = patcompile(right, (save ? PAT_ZDUP : PAT_STATIC), diff --git a/Src/exec.c b/Src/exec.c index f186a1be2..df42bad32 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1265,16 +1265,25 @@ mod_export void untokenize(char *s) { if (*s) { - char *p = s; int c; while ((c = *s++)) if (itok(c)) { + char *p = s - 1; + if (c != Nularg) *p++ = ztokens[c - Pound]; - } else - *p++ = c; - *p = '\0'; + + while ((c = *s++)) { + if (itok(c)) { + if (c != Nularg) + *p++ = ztokens[c - Pound]; + } else + *p++ = c; + } + *p = '\0'; + break; + } } } @@ -3010,7 +3019,7 @@ execfuncdef(Estate state, int do_exec) { Shfunc shf; char *s; - int signum, nprg, npats, len, plen, i, htok = 0; + int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0; Wordcode beg = state->pc, end; Eprog prog; Patprog *pp; @@ -3018,26 +3027,40 @@ execfuncdef(Estate state, int do_exec) end = beg + WC_FUNCDEF_SKIP(state->pc[-1]); names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok); - nprg = *state->pc++ - 4; + nprg = end - beg; + sbeg = *state->pc++; + nstrs = *state->pc++; npats = *state->pc++; - plen = (end - state->pc) * sizeof(wordcode); - len = plen + (npats * sizeof(Patprog)); + nprg = (end - state->pc); + plen = nprg * sizeof(wordcode); + len = plen + (npats * sizeof(Patprog)) + nstrs; if (htok) execsubst(names); while ((s = (char *) ugetnode(names))) { prog = (Eprog) zalloc(sizeof(*prog)); - prog->heap = 0; - prog->len = len; prog->npats = npats; - prog->pats = pp = (Patprog *) zalloc(len); - prog->prog = (Wordcode) (prog->pats + npats); + prog->len = len; + if (state->prog->dump) { + prog->alloc = EA_MAP; + incrdumpcount(state->prog->dump); + prog->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog)); + prog->prog = state->pc; + prog->strs = state->strs + sbeg; + prog->dump = state->prog->dump; + } else { + prog->alloc = EA_REAL; + prog->pats = pp = (Patprog *) zalloc(len); + prog->prog = (Wordcode) (prog->pats + npats); + prog->strs = (char *) (prog->prog + nprg); + prog->dump = NULL; + memcpy(prog->prog, state->pc, plen); + memcpy(prog->strs, state->strs + sbeg, nstrs); + } for (i = npats; i--; pp++) *pp = dummy_patprog1; - memcpy(prog->prog, state->pc, plen); - prog->strs = (char *) (prog->prog + nprg); prog->shf = NULL; shf = (Shfunc) zalloc(sizeof(*shf)); @@ -3150,7 +3173,10 @@ execautofn(Estate state, int do_exec) } } else { freeeprog(shf->funcdef); - shf->funcdef = zdupeprog(stripkshdef(prog, shf->nam)); + if (prog->alloc == EA_MAP) + shf->funcdef = stripkshdef(prog, shf->nam); + else + shf->funcdef = zdupeprog(stripkshdef(prog, shf->nam)); shf->flags &= ~PM_UNDEFINED; } popheap(); @@ -3180,7 +3206,10 @@ loadautofn(Shfunc shf) } if (!prog) prog = &dummy_eprog; - shf->funcdef = zdupeprog(stripkshdef(prog, shf->nam)); + if (prog->alloc == EA_MAP) + shf->funcdef = stripkshdef(prog, shf->nam); + else + shf->funcdef = zdupeprog(stripkshdef(prog, shf->nam)); shf->flags &= ~PM_UNDEFINED; popheap(); @@ -3339,6 +3368,8 @@ getfpfunc(char *s) sprintf(buf, "%s/%s", *pp, s); else strcpy(buf, s); + if ((r = try_dump_file(*pp, s, buf))) + return r; unmetafy(buf, NULL); if (!access(buf, R_OK) && (fd = open(buf, O_RDONLY | O_NOCTTY)) != -1) { if ((len = lseek(fd, 0, 2)) != -1) { @@ -3372,7 +3403,7 @@ getfpfunc(char *s) * contents of that definition. Otherwise, use the entire file. */ /**/ -static Eprog +Eprog stripkshdef(Eprog prog, char *name) { Wordcode pc = prog->prog; @@ -3399,25 +3430,34 @@ stripkshdef(Eprog prog, char *name) { Eprog ret; Wordcode end = pc + WC_FUNCDEF_SKIP(code); - int nprg = pc[2] - 4; - int npats = pc[3]; - int plen, len, i; + int sbeg = pc[2], nstrs = pc[3], nprg, npats = pc[4], plen, len, i; Patprog *pp; - pc += 4; + pc += 5; - plen = (end - pc) * sizeof(wordcode); - len = plen + (npats * sizeof(Patprog)); + nprg = end - pc; + plen = nprg * sizeof(wordcode); + len = plen + (npats * sizeof(Patprog)) + nstrs; - ret = (Eprog) zhalloc(sizeof(*ret)); - ret->heap = 1; + if (prog->alloc == EA_MAP) { + ret = prog; + free(prog->pats); + ret->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog)); + ret->prog = pc; + ret->strs = prog->strs + sbeg; + } else { + ret = (Eprog) zhalloc(sizeof(*ret)); + ret->alloc = EA_HEAP; + ret->pats = pp = (Patprog *) zhalloc(len); + ret->prog = (Wordcode) (ret->pats + npats); + memcpy(ret->prog, pc, plen); + memcpy(ret->strs, prog->strs + sbeg, nstrs); + ret->dump = NULL; + } ret->len = len; ret->npats = npats; - ret->pats = pp = (Patprog *) zhalloc(len); - ret->prog = (Wordcode) (ret->pats + npats); for (i = npats; i--; pp++) *pp = dummy_patprog1; - memcpy(ret->prog, pc, plen); ret->strs = (char *) (ret->prog + nprg); ret->shf = NULL; diff --git a/Src/glob.c b/Src/glob.c index 05bfbbafb..c457e97ac 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -2327,16 +2327,22 @@ mod_export void remnulargs(char *s) { if (*s) { - char *t = s, *p = s, c; + char *o = s, c; while ((c = *s++)) - if (!INULL(c)) - *p++ = c; - *p = '\0'; - if (!*t) { - t[0] = Nularg; - t[1] = '\0'; - } + if (INULL(c)) { + char *t = s - 1; + + while ((c = *s++)) + if (!INULL(c)) + *t++ = c; + *t = '\0'; + if (!*o) { + o[0] = Nularg; + o[1] = '\0'; + } + break; + } } } diff --git a/Src/lex.c b/Src/lex.c index 1031b57e2..333aa403b 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -194,7 +194,7 @@ struct lexstack { int eclen, ecused, ecfree, ecnpats; Wordcode ecbuf; Eccstr ecstrs; - int ecsoffs; + int ecsoffs, ecssub, ecnfunc; unsigned char *cstack; int csp; @@ -255,6 +255,8 @@ lexsave(void) ls->ecbuf = ecbuf; ls->ecstrs = ecstrs; ls->ecsoffs = ecsoffs; + ls->ecssub = ecssub; + ls->ecnfunc = ecnfunc; cmdsp = 0; inredir = 0; hdocs = NULL; @@ -314,6 +316,8 @@ lexrestore(void) ecbuf = lstack->ecbuf; ecstrs = lstack->ecstrs; ecsoffs = lstack->ecsoffs; + ecssub = lstack->ecssub; + ecnfunc = lstack->ecnfunc; hlinesz = lstack->hlinesz; errflag = 0; diff --git a/Src/loop.c b/Src/loop.c index 2459d65b8..b1edb22c4 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -524,7 +524,7 @@ execcase(Estate state, int do_exec) opat = pat = ecgetstr(state, EC_DUP, NULL); singsub(&pat); - save = (!state->prog->heap && + save = (state->prog->alloc != EA_HEAP && !strcmp(pat, opat) && *spprog != dummy_patprog2); pat2 = dupstring(pat); @@ -548,7 +548,7 @@ execcase(Estate state, int do_exec) state->pc - 2, &htok)); if (htok) singsub(&pat); - save = (!state->prog->heap && + save = (state->prog->alloc != EA_HEAP && !strcmp(pat, opat) && *spprog != dummy_patprog2); } if (!(pprog = patcompile(pat, (save ? PAT_ZDUP : PAT_STATIC), diff --git a/Src/math.c b/Src/math.c index a3609d429..ce685e2db 100644 --- a/Src/math.c +++ b/Src/math.c @@ -396,7 +396,7 @@ zzlex(void) } if (iident(*ptr)) { int func = 0; - char *p, q; + char *p; p = ptr; while (iident(*++ptr)); @@ -413,10 +413,7 @@ zzlex(void) ptr++; } } - q = *ptr; - *ptr = '\0'; - yylval = dupstring(p); - *ptr = q; + yylval = dupstrpfx(p, ptr - p); return (func ? FUNC : (cct ? CID : ID)); } else if (cct) { diff --git a/Src/mem.c b/Src/mem.c index 1e627cd99..29bd213f4 100644 --- a/Src/mem.c +++ b/Src/mem.c @@ -1272,7 +1272,7 @@ bin_mem(char *name, char **argv, char *ops, int func) printf("blocks is shown. For otherwise used blocks the first few\n"); printf("bytes are shown as an ASCII dump.\n"); } - printf("\nblock list:\nnum\ttnum\taddr\tlen\tstate\tcum\n"); + printf("\nblock list:\nnum\ttnum\taddr\t\tlen\tstate\tcum\n"); for (m = m_l, mf = m_free, ii = fi = ui = 1; ((char *)m) < m_high; m = (struct m_hdr *)(((char *)m) + M_ISIZE + m->len), ii++) { for (j = 0, ms = NULL; j < M_NSMALL && !ms; j++) diff --git a/Src/parse.c b/Src/parse.c index df7671c5d..0b11668c2 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -131,10 +131,11 @@ struct heredocs *hdocs; * - if (type == PIPE), followed by pipe * * WC_FUNCDEF - * - data contains offset to after body-strings + * - data contains offset to after body * - followed by number of names * - followed by names - * - followed by number of codes for body + * - followed by offset to first string + * - followed by length of string table * - followed by number of patterns for body * - follwoed by codes for body * - followed by strings for body @@ -230,28 +231,7 @@ Wordcode ecbuf; /**/ Eccstr ecstrs; /**/ -int ecsoffs; - -/* Make at least n bytes free (aligned to sizeof(wordcode)). */ - -static int -ecspace(int n) -{ - n = (n + sizeof(wordcode) - 1) / sizeof(wordcode); - - if (ecfree < n) { - int a = (n > 256 ? n : 256); - - ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode), - (eclen + a) * sizeof(wordcode)); - eclen += a; - ecfree += a; - } - ecused += n; - ecfree -= n; - - return ecused - 1; -} +int ecsoffs, ecssub, ecnfunc; /* Insert n free code-slots at position p. */ @@ -323,7 +303,7 @@ ecstrcode(char *s) Eccstr p, q = NULL; for (p = ecstrs; p; q = p, p = p->next) - if (!strcmp(s, p->str)) + if (p->nfunc == ecnfunc && !strcmp(s, p->str)) return p->offs; p = (Eccstr) zhalloc(sizeof(*p)); @@ -332,8 +312,9 @@ ecstrcode(char *s) q->next = p; else ecstrs = p; - p->offs = (ecsoffs << 2) | (t ? 1 : 0); + p->offs = ((ecsoffs - ecssub) << 2) | (t ? 1 : 0); p->str = s; + p->nfunc = ecnfunc; ecsoffs += l; return p->offs; @@ -370,6 +351,8 @@ init_parse(void) ecused = 0; ecstrs = NULL; ecsoffs = ecnpats = 0; + ecssub = 0; + ecnfunc = 0; } /* Build eprog. */ @@ -393,7 +376,8 @@ bld_eprog(void) ret->prog = (Wordcode) (ret->pats + ecnpats); ret->strs = (char *) (ret->prog + ecused); ret->shf = NULL; - ret->heap = 1; + ret->alloc = EA_HEAP; + ret->dump = NULL; for (l = 0; l < ecnpats; l++) ret->pats[l] = dummy_patprog1; memcpy(ret->prog, ecbuf, ecused * sizeof(wordcode)); @@ -1288,8 +1272,8 @@ par_subsh(int *complex) static void par_funcdef(void) { - int oecused = ecused, oldlineno = lineno, num = 0, sbeg, onp, p, c = 0; - Eccstr ostrs; + int oecused = ecused, oldlineno = lineno, num = 0, onp, p, c = 0; + int so, oecssub = ecssub; lineno = 0; nocorrect = 1; @@ -1311,6 +1295,7 @@ par_funcdef(void) } ecadd(0); ecadd(0); + ecadd(0); nocorrect = 0; if (tok == INOUTPAR) @@ -1318,10 +1303,8 @@ par_funcdef(void) while (tok == SEPER) yylex(); - sbeg = ecsoffs; - ecsoffs = 0; - ostrs = ecstrs; - ecstrs = NULL; + ecnfunc++; + ecssub = so = ecsoffs; onp = ecnpats; ecnpats = 0; @@ -1330,43 +1313,28 @@ par_funcdef(void) par_list(&c); if (tok != OUTBRACE) { lineno += oldlineno; - ecsoffs = sbeg; - ecstrs = ostrs; ecnpats = onp; + ecssub = oecssub; YYERRORV(oecused); } yylex(); } else if (unset(SHORTLOOPS)) { lineno += oldlineno; - ecsoffs = sbeg; - ecstrs = ostrs; ecnpats = onp; + ecssub = oecssub; YYERRORV(oecused); } else par_list1(&c); ecadd(WCB_END()); - ecbuf[p + num + 2] = ecused - num - p; - ecbuf[p + num + 3] = ecnpats; + ecbuf[p + num + 2] = so - oecssub; + ecbuf[p + num + 3] = ecsoffs - so; + ecbuf[p + num + 4] = ecnpats; ecbuf[p + 1] = num; - if (ecsoffs) { - int beg = ecused, l; - Eccstr sp; - char *sq; - - ecspace(ecsoffs); - - for (sp = ecstrs, sq = (char *) (ecbuf + beg); sp; - sp = sp->next, sq += l) { - l = strlen(sp->str) + 1; - memcpy(sq, sp->str, l); - } - } lineno += oldlineno; - ecsoffs = sbeg; - ecstrs = ostrs; ecnpats = onp; + ecssub = oecssub; ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); } @@ -1481,8 +1449,7 @@ par_simple(int *complex, int nr) p += 3; /* 3 codes per redirection */ sr++; } else if (tok == INOUTPAR) { - int oldlineno = lineno, sbeg, onp; - Eccstr ostrs; + int oldlineno = lineno, onp, so, oecssub = ecssub; *complex = c; lineno = 0; @@ -1496,11 +1463,10 @@ par_simple(int *complex, int nr) ecbuf[p + 1] = argc; ecadd(0); ecadd(0); + ecadd(0); - sbeg = ecsoffs; - ecsoffs = 0; - ostrs = ecstrs; - ecstrs = NULL; + ecnfunc++; + ecssub = so = ecsoffs; onp = ecnpats; ecnpats = 0; @@ -1512,9 +1478,8 @@ par_simple(int *complex, int nr) if (tok != OUTBRACE) { cmdpop(); lineno += oldlineno; - ecsoffs = sbeg; - ecstrs = ostrs; ecnpats = onp; + ecssub = oecssub; YYERROR(oecused); } yylex(); @@ -1532,26 +1497,13 @@ par_simple(int *complex, int nr) cmdpop(); ecadd(WCB_END()); - ecbuf[p + argc + 2] = ecused - argc - p; - ecbuf[p + argc + 3] = ecnpats; - - if (ecsoffs) { - int beg = ecused, l; - Eccstr sp; - char *sq; + ecbuf[p + argc + 2] = so - oecssub; + ecbuf[p + argc + 3] = ecsoffs - so; + ecbuf[p + argc + 4] = ecnpats; - ecspace(ecsoffs); - - for (sp = ecstrs, sq = (char *) (ecbuf + beg); sp; - sp = sp->next, sq += l) { - l = strlen(sp->str) + 1; - memcpy(sq, sp->str, l); - } - } lineno += oldlineno; - ecsoffs = sbeg; - ecstrs = ostrs; ecnpats = onp; + ecssub = oecssub; ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); @@ -2020,7 +1972,8 @@ zdupeprog(Eprog p) return p; r = (Eprog) zalloc(sizeof(*r)); - r->heap = 0; + r->alloc = EA_REAL; + r->dump = NULL; r->len = p->len; r->npats = p->npats; pp = r->pats = (Patprog *) zcalloc(r->len); @@ -2056,7 +2009,11 @@ freeeprogs(void) while ((p = (Eprog) getlinknode(eprog_free))) { for (i = p->npats, pp = p->pats; i--; pp++) freepatprog(*pp); - zfree(p->pats, p->len); + if (p->dump) { + decrdumpcount(p->dump); + zfree(p->pats, p->npats * sizeof(Patprog)); + } else + zfree(p->pats, p->len); zfree(p, sizeof(*p)); } } @@ -2083,6 +2040,17 @@ ecgetstr(Estate s, int dup, int *tok) } if (tok) *tok = (c & 1); + + /*** Since function dump files are mapped read-only, avoiding to + * to duplicate strings when they don't contain tokens may fail + * when one of the many utility functions happens to write to + * one of the strings (without really modifying it). + * If that happens to you and you don't feel like debugging it, + * just change the line below to: + * + * return (dup ? dupstring(r) : r); + */ + return ((dup == EC_DUP || (dup && (c & 1))) ? dupstring(r) : r); } @@ -2193,3 +2161,530 @@ init_eprog(void) eprog_free = znewlinklist(); } + +/* Code for function dump files. + * + * Dump files consist of a header and the function bodies (the wordcode + * plus the string table) and that twice: once for the byte-order of the + * host the file was created on and once for the other byte-order. The + * header describes where the beginning of the `other' version is and it + * is up to the shell reading the file to decide which version it needs. + * This is done by checking if the first word is FD_MAGIC (then the + * shell reading the file has the same byte order as the one that created + * the file) or if it is FD_OMAGIC, then the `other' version has to be + * read. + * The header is the magic number, a word containing the flags (if the + * file should be mapped or read and if this header is the `other' one), + * the version string in a field of 40 characters and the descriptions + * for the functions in the dump file. + * Each description consists of a struct fdhead followed by the name, + * aligned to sizeof(wordcode) (i.e. 4 bytes). + */ + +#include "version.h" + +#define FD_EXT ".zwc" +#define FD_MINMAP 4096 + +#define FD_PRELEN 12 +#define FD_MAGIC 0x01020304 +#define FD_OMAGIC 0x04030201 + +#define FDF_MAP 1 +#define FDF_OTHER 2 + +typedef struct fdhead *FDHead; + +struct fdhead { + wordcode start; /* offset to function definition */ + wordcode len; /* length of wordcode/strings */ + wordcode npats; /* number of patterns needed */ + wordcode strs; /* offset to strings */ + wordcode hlen; /* header length (incl. name) */ + wordcode tail; /* offset to name tail */ +}; + +#define fdheaderlen(f) (((Wordcode) (f))[FD_PRELEN]) + +#define fdmagic(f) (((Wordcode) (f))[0]) +#define fdbyte(f, i) ((wordcode) (((unsigned char *) (((Wordcode) (f)) + 1))[i])) +#define fdflags(f) fdbyte(f, 0) +#define fdother(f) (fdbyte(f, 1) + (fdbyte(f, 2) << 8) + (fdbyte(f, 3) << 16)) +#define fdsetother(f, o) \ + do { \ + fdbyte(f, 1) = (o & 0xff); \ + fdbyte(f, 2) = (o >> 8) & 0xff; \ + fdbyte(f, 3) = (o >> 16) & 0xff; \ + } while (0) +#define fdversion(f) ((char *) ((f) + 2)) + +#define firstfdhead(f) ((FDHead) (((Wordcode) (f)) + FD_PRELEN)) +#define nextfdhead(f) ((FDHead) (((Wordcode) (f)) + (f)->hlen)) + +#define fdname(f) ((char *) (((FDHead) (f)) + 1)) + +/* Try to find the description for the given function name. */ + +static FDHead +dump_find_func(Wordcode h, char *name) +{ + FDHead n, e = (FDHead) (h + fdheaderlen(h)); + + for (n = firstfdhead(h); n < e; n = nextfdhead(n)) + if (!strcmp(name, fdname(n) + n->tail)) + return n; + + return NULL; +} + +/**/ +int +bin_zcompile(char *nam, char **args, char *ops, int func) +{ + int map; + + if (ops['t']) { + Wordcode f; + + if (!*args) { + zerrnam(nam, "too few arguments", NULL, 0); + return 1; + } + if (!(f = load_dump_header(*args))) { + zerrnam(nam, "invalid dump file: %s", *args, 0); + return 1; + } + if (args[1]) { + for (args++; *args; args++) + if (!dump_find_func(f, *args)) + return 1; + return 0; + } else { + FDHead h, e = (FDHead) (f + fdheaderlen(f)); + + printf("function dump file (%s) for zsh-%s\n", + ((fdflags(f) & FDF_MAP) ? "mapped" : "read"), fdversion(f)); + for (h = firstfdhead(f); h < e; h = nextfdhead(h)) + printf("%s\n", fdname(h)); + return 0; + } + } + if (!*args) { + zerrnam(nam, "too few arguments", NULL, 0); + return 1; + } + map = (ops['m'] ? 2 : (ops['r'] ? 0 : 1)); + + if (!args[1]) + return build_dump(nam, dyncat(*args, FD_EXT), args, ops['U'], map); + + return build_dump(nam, *args, args + 1, ops['U'], map); +} + +/* Load the header of a dump file. Returns NULL if the file isn't a + * valid dump file. */ + +/**/ +static Wordcode +load_dump_header(char *name) +{ + int fd; + wordcode buf[FD_PRELEN + 1]; + + if ((fd = open(name, O_RDONLY)) < 0) + return NULL; + + if (read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) != + ((FD_PRELEN + 1) * sizeof(wordcode)) || + strcmp(ZSH_VERSION, fdversion(buf))) { + close(fd); + return NULL; + } else { + int len; + Wordcode head; + + if (fdmagic(buf) == FD_MAGIC) { + len = fdheaderlen(buf) * sizeof(wordcode); + head = (Wordcode) zhalloc(len); + } + else { + int o = fdother(buf); + + if (lseek(fd, o, 0) == -1 || + read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) != + ((FD_PRELEN + 1) * sizeof(wordcode))) { + close(fd); + return NULL; + } + len = fdheaderlen(buf) * sizeof(wordcode); + head = (Wordcode) zhalloc(len); + } + memcpy(head, buf, (FD_PRELEN + 1) * sizeof(wordcode)); + + if (read(fd, head + (FD_PRELEN + 1), + len - ((FD_PRELEN + 1) * sizeof(wordcode))) != + len - ((FD_PRELEN + 1) * sizeof(wordcode))) { + close(fd); + return NULL; + } + close(fd); + return head; + } +} + +/* Swap the bytes in a wordcode. */ + +static void +fdswap(Wordcode p, int n) +{ + wordcode c; + + for (; n--; p++) { + c = *p; + *p = (((c & 0xff) << 24) | + ((c & 0xff00) << 8) | + ((c & 0xff0000) >> 8) | + ((c & 0xff000000) >> 24)); + } +} + +/* Write a dump file. */ + +/**/ +static int +build_dump(char *nam, char *dump, char **files, int ali, int map) +{ + int dfd, fd, hlen, tlen, flen, tmp, ona = noaliases, other = 0, ohlen; + LinkList progs; + LinkNode node; + struct fdhead head; + wordcode pre[FD_PRELEN]; + char *file, **ofiles = files, **oofiles = files, *name, *tail; + Eprog prog; + + if ((dfd = open(dump, O_WRONLY|O_CREAT, 0600)) < 0) { + zerrnam(nam, "can't write dump file: %s", dump, 0); + return 1; + } + progs = newlinklist(); + noaliases = ali; + + for (hlen = FD_PRELEN, tlen = 0; *files; files++) { + if ((fd = open(*files, O_RDONLY)) < 0 || + (flen = lseek(fd, 0, 2)) == -1) { + if (fd >= 0) + close(fd); + close(dfd); + zerrnam(nam, "can't open file: %s", *files, 0); + noaliases = ona; + return 1; + } + file = (char *) zalloc(flen + 1); + file[flen] = '\0'; + lseek(fd, 0, 0); + if (read(fd, file, flen) != flen) { + close(fd); + close(dfd); + zfree(file, flen); + zerrnam(nam, "can't read file: %s", *files, 0); + noaliases = ona; + return 1; + } + close(fd); + file = metafy(file, flen, META_REALLOC); + + if (!(prog = parse_string(file, 1)) || errflag) { + close(dfd); + zfree(file, flen); + zerrnam(nam, "can't read file: %s", *files, 0); + noaliases = ona; + return 1; + } + zfree(file, flen); + + addlinknode(progs, prog); + + flen = (strlen(*files) + sizeof(wordcode)) / sizeof(wordcode); + hlen += (sizeof(head) / sizeof(wordcode)) + flen; + + tlen += (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + } + noaliases = ona; + + tlen = (tlen + hlen) * sizeof(wordcode); + if (map == 1) + map = (tlen >= FD_MINMAP); + + for (ohlen = hlen; ; hlen = ohlen) { + fdmagic(pre) = (other ? FD_OMAGIC : FD_MAGIC); + fdflags(pre) = (map ? FDF_MAP : 0) | other; + fdsetother(pre, tlen); + strcpy(fdversion(pre), ZSH_VERSION); + write(dfd, pre, FD_PRELEN * sizeof(wordcode)); + + for (node = firstnode(progs), ofiles = oofiles; node; + ofiles++, incnode(node)) { + prog = (Eprog) getdata(node); + head.start = hlen; + hlen += (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + head.len = prog->len - (prog->npats * sizeof(Patprog)); + head.npats = prog->npats; + head.strs = prog->strs - ((char *) prog->prog); + head.hlen = (sizeof(struct fdhead) / sizeof(wordcode)) + + (strlen(*ofiles) + sizeof(wordcode)) / sizeof(wordcode); + for (name = tail = *ofiles; *name; name++) + if (*name == '/') + tail = name + 1; + head.tail = tail - *ofiles; + if (other) + fdswap((Wordcode) &head, sizeof(head) / sizeof(wordcode)); + write(dfd, &head, sizeof(head)); + tmp = strlen(*ofiles) + 1; + write(dfd, *ofiles, tmp); + if ((tmp &= (sizeof(wordcode) - 1))) + write(dfd, &head, sizeof(wordcode) - tmp); + } + for (node = firstnode(progs); node; incnode(node)) { + prog = (Eprog) getdata(node); + tmp = (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + if (other) + fdswap(prog->prog, (((Wordcode) prog->strs) - prog->prog)); + write(dfd, prog->prog, tmp * sizeof(wordcode)); + } + if (other) + break; + other = FDF_OTHER; + } + close(dfd); + return 0; +} + +#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MMAP) && defined(HAVE_MUNMAP) + +#include + +#if defined(MAP_SHARED) && defined(PROT_READ) + +#define USE_MMAP 1 + +#endif +#endif + +#ifdef USE_MMAP + +/* List of dump files mapped. */ + +static FuncDump dumps; + +/* Load a dump file (i.e. map it). */ + +static void +load_dump_file(char *dump, int other, int len) +{ + FuncDump d; + Wordcode addr; + int fd, off; + + if (other) { + static size_t pgsz = 0; + + if (!pgsz) { + +#ifdef _SC_PAGESIZE + pgsz = sysconf(_SC_PAGESIZE); /* SVR4 */ +#else +# ifdef _SC_PAGE_SIZE + pgsz = sysconf(_SC_PAGE_SIZE); /* HPUX */ +# else + pgsz = getpagesize(); +# endif +#endif + + pgsz--; + } + off = len & ~pgsz; + } else + off = 0; + + if ((fd = open(dump, O_RDONLY)) < 0) + return; + + fd = movefd(fd); + + if ((addr = (Wordcode) mmap(NULL, len, PROT_READ, MAP_SHARED, fd, off)) == + ((Wordcode) -1)) { + close(fd); + return; + } + d = (FuncDump) zalloc(sizeof(*d)); + d->next = dumps; + dumps = d; + d->name = ztrdup(dump); + d->fd = fd; + d->map = addr + (other ? (len - off) / sizeof(wordcode) : 0); + d->addr = addr; + d->len = len; + d->count = 0; +} + +/* See if `dump' is the name of a dump file and it has the definition + * for the function `name'. If so, return an eprog for it. */ + +/**/ +Eprog +try_dump_file(char *dump, char *name, char *func) +{ + int isrec = 0; + Wordcode d; + FDHead h; + FuncDump f; + + rec: + + d = NULL; + for (f = dumps; f; f = f->next) + if (!strcmp(dump, f->name)) { + d = f->map; + break; + } + if (!f && (isrec || !(d = load_dump_header(dump)))) { + if (!isrec) { + struct stat stc, stn; + char *p = (char *) zhalloc(strlen(dump) + strlen(name) + + strlen(FD_EXT) + 2); + + sprintf(p, "%s/%s%s", dump, name, FD_EXT); + + /* Ignore the dump file if it is older than the normal one. */ + if (stat(p, &stc) || stat(func, &stn) || stn.st_mtime > stc.st_mtime) + return NULL; + + if (!(d = load_dump_header(dump = p))) + return NULL; + + } else + return NULL; + } + if ((h = dump_find_func(d, name))) { + /* Found the name. If the file is already mapped, return the eprog, + * otherwise map it and just go up. */ + if (f) { + Eprog prog = (Eprog) zalloc(sizeof(*prog)); + Patprog *pp; + int np; + + prog->alloc = EA_MAP; + prog->len = h->len; + prog->npats = np = h->npats; + prog->pats = pp = (Patprog *) zalloc(np * sizeof(Patprog)); + prog->prog = f->map + h->start; + prog->strs = ((char *) prog->prog) + h->strs; + prog->shf = NULL; + prog->dump = f; + + incrdumpcount(f); + + while (np--) + *pp++ = dummy_patprog1; + + return prog; + } else if (fdflags(d) & FDF_MAP) { + load_dump_file(dump, (fdflags(d) & FDF_OTHER), fdother(d)); + isrec = 1; + goto rec; + } else { + Eprog prog; + Patprog *pp; + int np, fd, po = h->npats * sizeof(Patprog); + + if ((fd = open(dump, O_RDONLY)) < 0 || + lseek(fd, ((h->start * sizeof(wordcode)) + + ((fdflags(d) & FDF_OTHER) ? fdother(d) : 0)), 0) < 0) { + if (fd >= 0) + close(fd); + return NULL; + } + d = (Wordcode) zalloc(h->len + po); + + if (read(fd, ((char *) d) + po, h->len) != h->len) { + close(fd); + zfree(d, h->len); + + return NULL; + } + close(fd); + + prog = (Eprog) zalloc(sizeof(*prog)); + + prog->alloc = EA_MAP; + prog->len = h->len + po; + prog->npats = np = h->npats; + prog->pats = pp = (Patprog *) d; + prog->prog = (Wordcode) (((char *) d) + po); + prog->strs = ((char *) prog->prog) + h->strs; + prog->shf = NULL; + prog->dump = f; + + while (np--) + *pp++ = dummy_patprog1; + + return prog; + } + } + return NULL; +} + +/* Increment the reference counter for a dump file. */ + +/**/ +void +incrdumpcount(FuncDump f) +{ + f->count++; +} + +/* Decrement the reference counter for a dump file. If zero, unmap the file. */ + +/**/ +void +decrdumpcount(FuncDump f) +{ + f->count--; + if (!f->count) { + FuncDump p, q; + + for (q = NULL, p = dumps; p && p != f; q = p, p = p->next); + if (p) { + if (q) + q->next = p->next; + else + dumps = p->next; + munmap((void *) f->addr, f->len); + zclose(f->fd); + zfree(f, sizeof(*f)); + } + } +} + +#else + +Eprog +try_dump_file(char *dump, char *name, char *func) +{ + return NULL; +} + +void +incrdumpcount(FuncDump f) +{ +} + +void +decrdumpcount(FuncDump f) +{ +} + +#endif diff --git a/Src/text.c b/Src/text.c index 235064efc..ab052b22a 100644 --- a/Src/text.c +++ b/Src/text.c @@ -378,8 +378,8 @@ gettext2(Estate state) n = tpush(code, 1); n->u._funcdef.strs = state->strs; n->u._funcdef.end = end; - state->strs = (char *) (p + (*state->pc)); - state->pc += 2; + state->strs += *state->pc; + state->pc += 3; } } else { state->strs = s->u._funcdef.strs; diff --git a/Src/utils.c b/Src/utils.c index 6d91200a0..db404ec26 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1704,7 +1704,7 @@ spacesplit(char *s, int allownull, int heap) { char *t, **ret, **ptr; int l = sizeof(*ret) * (wordcount(s, NULL, -!allownull) + 1); - char *(*dup)(char *) = (heap ? dupstring : ztrdup); + char *(*dup)(const char *) = (heap ? dupstring : ztrdup); ptr = ret = (heap ? (char **) hcalloc(l) : (char **) zcalloc(l)); diff --git a/Src/zsh.h b/Src/zsh.h index 332bae9fd..77de1ec61 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -476,24 +476,40 @@ struct value { #define MAX_ARRLEN 262144 /********************************************/ -/* Defintions for byte code */ +/* Defintions for word code */ /********************************************/ typedef unsigned int wordcode; typedef wordcode *Wordcode; +typedef struct funcdump *FuncDump; typedef struct eprog *Eprog; +struct funcdump { + FuncDump next; /* next in list */ + char *name; /* path name */ + int fd; /* file descriptor */ + Wordcode map; /* pointer to header */ + Wordcode addr; /* mapped region */ + int len; /* length */ + int count; /* reference count */ +}; + struct eprog { - int heap; /* != 0 if in heap memory */ + int alloc; /* EA_* below */ int len; /* total block length */ int npats; /* Patprog cache size */ Patprog *pats; /* the memory block, the patterns */ Wordcode prog; /* memory block ctd, the code */ char *strs; /* memory block ctd, the strings */ Shfunc shf; /* shell function for autoload */ + FuncDump dump; /* dump file this is in */ }; +#define EA_REAL 0 +#define EA_HEAP 1 +#define EA_MAP 2 + typedef struct estate *Estate; struct estate { @@ -508,6 +524,7 @@ struct eccstr { Eccstr next; char *str; wordcode offs; + int nfunc; }; #define EC_NODUP 0 -- cgit 1.4.1