2020-05-29 19:02:58 +02:00

1815 lines
49 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- Mode: C; c-default-style: "linux"; c-basic-offset: 4; indent-tabs-mode: nil -*-
* vim:sw=4:sts=4:et
*
* zplugin.c  module for Zplugin plugin manager
*
* Copyright (c) 2017 Sebastian Gniazdowski
* All rights reserved.
*
* The file contains code copied from Zshell source (e.g. code of builtins that are
* then customized by me) and this code is under license:
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and to distribute modified versions of this software for any
* purpose, provided that the above copyright notice and the following
* two paragraphs appear in all copies of this software.
*
* In no event shall Paul Falstad or the Zsh Development Group be liable
* to any party for direct, indirect, special, incidental, or consequential
* damages arising out of the use of this software and its documentation,
* even if Paul Falstad and the Zsh Development Group have been advised of
* the possibility of such damage.
*
* Paul Falstad and the Zsh Development Group specifically disclaim any
* warranties, including, but not limited to, the implied warranties of
* merchantability and fitness for a particular purpose. The software
* provided hereunder is on an "as is" basis, and Paul Falstad and the
* Zsh Development Group have no obligation to provide maintenance,
* support, updates, enhancements, or modifications.
*/
#include "zplugin.mdh"
#include "zplugin.pro"
/* Source/bin_dot related data structures {{{ */
static HandlerFunc originalDot = NULL, originalSource = NULL;
static HashTable zp_source_events = NULL;
static int zp_sevent_count = 0;
struct source_event {
int id;
long ts;
char *dir_path;
char *file_name;
char *full_path;
double duration;
int load_error;
};
struct zp_sevent_node {
struct hashnode node;
struct source_event event;
};
typedef struct zp_sevent_node *SEventNode;
/* }}} */
/* Option support {{{ */
static int zp_opt_for_zsh_version[256] = { 0 };
enum {
OPT_INVALID__,
ALIASESOPT__,
ALIASFUNCDEF__,
ALLEXPORT__,
ALWAYSLASTPROMPT__,
ALWAYSTOEND__,
APPENDHISTORY__,
AUTOCD__,
AUTOCONTINUE__,
AUTOLIST__,
AUTOMENU__,
AUTONAMEDIRS__,
AUTOPARAMKEYS__,
AUTOPARAMSLASH__,
AUTOPUSHD__,
AUTOREMOVESLASH__,
AUTORESUME__,
BADPATTERN__,
BANGHIST__,
BAREGLOBQUAL__,
BASHAUTOLIST__,
BASHREMATCH__,
BEEP__,
BGNICE__,
BRACECCL__,
BSDECHO__,
CASEGLOB__,
CASEMATCH__,
CBASES__,
CDABLEVARS__,
CHASEDOTS__,
CHASELINKS__,
CHECKJOBS__,
CHECKRUNNINGJOBS__,
CLOBBER__,
APPENDCREATE__,
COMBININGCHARS__,
COMPLETEALIASES__,
COMPLETEINWORD__,
CORRECT__,
CORRECTALL__,
CONTINUEONERROR__,
CPRECEDENCES__,
CSHJUNKIEHISTORY__,
CSHJUNKIELOOPS__,
CSHJUNKIEQUOTES__,
CSHNULLCMD__,
CSHNULLGLOB__,
DEBUGBEFORECMD__,
EMACSMODE__,
EQUALS__,
ERREXIT__,
ERRRETURN__,
EXECOPT__,
EXTENDEDGLOB__,
EXTENDEDHISTORY__,
EVALLINENO__,
FLOWCONTROL__,
FORCEFLOAT__,
FUNCTIONARGZERO__,
GLOBOPT__,
GLOBALEXPORT__,
GLOBALRCS__,
GLOBASSIGN__,
GLOBCOMPLETE__,
GLOBDOTS__,
GLOBSTARSHORT__,
GLOBSUBST__,
HASHCMDS__,
HASHDIRS__,
HASHEXECUTABLESONLY__,
HASHLISTALL__,
HISTALLOWCLOBBER__,
HISTBEEP__,
HISTEXPIREDUPSFIRST__,
HISTFCNTLLOCK__,
HISTFINDNODUPS__,
HISTIGNOREALLDUPS__,
HISTIGNOREDUPS__,
HISTIGNORESPACE__,
HISTLEXWORDS__,
HISTNOFUNCTIONS__,
HISTNOSTORE__,
HISTREDUCEBLANKS__,
HISTSAVEBYCOPY__,
HISTSAVENODUPS__,
HISTSUBSTPATTERN__,
HISTVERIFY__,
HUP__,
IGNOREBRACES__,
IGNORECLOSEBRACES__,
IGNOREEOF__,
INCAPPENDHISTORY__,
INCAPPENDHISTORYTIME__,
INTERACTIVE__,
INTERACTIVECOMMENTS__,
KSHARRAYS__,
KSHAUTOLOAD__,
KSHGLOB__,
KSHOPTIONPRINT__,
KSHTYPESET__,
KSHZEROSUBSCRIPT__,
LISTAMBIGUOUS__,
LISTBEEP__,
LISTPACKED__,
LISTROWSFIRST__,
LISTTYPES__,
LOCALLOOPS__,
LOCALOPTIONS__,
LOCALPATTERNS__,
LOCALTRAPS__,
LOGINSHELL__,
LONGLISTJOBS__,
MAGICEQUALSUBST__,
MAILWARNING__,
MARKDIRS__,
MENUCOMPLETE__,
MONITOR__,
MULTIBYTE__,
MULTIFUNCDEF__,
MULTIOS__,
NOMATCH__,
NOTIFY__,
NULLGLOB__,
NUMERICGLOBSORT__,
OCTALZEROES__,
OVERSTRIKE__,
PATHDIRS__,
PATHSCRIPT__,
PIPEFAIL__,
POSIXALIASES__,
POSIXARGZERO__,
POSIXBUILTINS__,
POSIXCD__,
POSIXIDENTIFIERS__,
POSIXJOBS__,
POSIXSTRINGS__,
POSIXTRAPS__,
PRINTEIGHTBIT__,
PRINTEXITVALUE__,
PRIVILEGED__,
PROMPTBANG__,
PROMPTCR__,
PROMPTPERCENT__,
PROMPTSP__,
PROMPTSUBST__,
PUSHDIGNOREDUPS__,
PUSHDMINUS__,
PUSHDSILENT__,
PUSHDTOHOME__,
RCEXPANDPARAM__,
RCQUOTES__,
RCS__,
RECEXACT__,
REMATCHPCRE__,
RESTRICTED__,
RMSTARSILENT__,
RMSTARWAIT__,
SHAREHISTORY__,
SHFILEEXPANSION__,
SHGLOB__,
SHINSTDIN__,
SHNULLCMD__,
SHOPTIONLETTERS__,
SHORTLOOPS__,
SHWORDSPLIT__,
SINGLECOMMAND__,
SINGLELINEZLE__,
SOURCETRACE__,
SUNKEYBOARDHACK__,
TRANSIENTRPROMPT__,
TRAPSASYNC__,
TYPESETSILENT__,
UNSET__,
VERBOSE__,
VIMODE__,
WARNCREATEGLOBAL__,
WARNNESTEDVAR__,
XTRACE__,
USEZLE__,
DVORAK__,
OPT_SIZE__
};
struct zp_option_name {
const char *name;
int enum_val;
};
static struct zp_option_name zp_options[] = {
{"aliases", ALIASESOPT__},
{"aliasfuncdef", ALIASFUNCDEF__},
{"allexport", ALLEXPORT__},
{"alwayslastprompt", ALWAYSLASTPROMPT__},
{"alwaystoend", ALWAYSTOEND__},
{"appendcreate", APPENDCREATE__},
{"appendhistory", APPENDHISTORY__},
{"autocd", AUTOCD__},
{"autocontinue", AUTOCONTINUE__},
{"autolist", AUTOLIST__},
{"automenu", AUTOMENU__},
{"autonamedirs", AUTONAMEDIRS__},
{"autoparamkeys", AUTOPARAMKEYS__},
{"autoparamslash", AUTOPARAMSLASH__},
{"autopushd", AUTOPUSHD__},
{"autoremoveslash", AUTOREMOVESLASH__},
{"autoresume", AUTORESUME__},
{"badpattern", BADPATTERN__},
{"banghist", BANGHIST__},
{"bareglobqual", BAREGLOBQUAL__},
{"bashautolist", BASHAUTOLIST__},
{"bashrematch", BASHREMATCH__},
{"beep", BEEP__},
{"bgnice", BGNICE__},
{"braceccl", BRACECCL__},
{"bsdecho", BSDECHO__},
{"caseglob", CASEGLOB__},
{"casematch", CASEMATCH__},
{"cbases", CBASES__},
{"cprecedences", CPRECEDENCES__},
{"cdablevars", CDABLEVARS__},
{"chasedots", CHASEDOTS__},
{"chaselinks", CHASELINKS__},
{"checkjobs", CHECKJOBS__},
{"checkrunningjobs", CHECKRUNNINGJOBS__},
{"clobber", CLOBBER__},
{"combiningchars", COMBININGCHARS__},
{"completealiases", COMPLETEALIASES__},
{"completeinword", COMPLETEINWORD__},
{"continueonerror", CONTINUEONERROR__},
{"correct", CORRECT__},
{"correctall", CORRECTALL__},
{"cshjunkiehistory", CSHJUNKIEHISTORY__},
{"cshjunkieloops", CSHJUNKIELOOPS__},
{"cshjunkiequotes", CSHJUNKIEQUOTES__},
{"cshnullcmd", CSHNULLCMD__},
{"cshnullglob", CSHNULLGLOB__},
{"debugbeforecmd", DEBUGBEFORECMD__},
{"emacs", EMACSMODE__},
{"equals", EQUALS__},
{"errexit", ERREXIT__},
{"errreturn", ERRRETURN__},
{"exec", EXECOPT__},
{"extendedglob", EXTENDEDGLOB__},
{"extendedhistory", EXTENDEDHISTORY__},
{"evallineno", EVALLINENO__},
{"flowcontrol", FLOWCONTROL__},
{"forcefloat", FORCEFLOAT__},
{"functionargzero", FUNCTIONARGZERO__},
{"glob", GLOBOPT__},
{"globalexport", GLOBALEXPORT__},
{"globalrcs", GLOBALRCS__},
{"globassign", GLOBASSIGN__},
{"globcomplete", GLOBCOMPLETE__},
{"globdots", GLOBDOTS__},
{"globstarshort", GLOBSTARSHORT__},
{"globsubst", GLOBSUBST__},
{"hashcmds", HASHCMDS__},
{"hashdirs", HASHDIRS__},
{"hashexecutablesonly", HASHEXECUTABLESONLY__},
{"hashlistall", HASHLISTALL__},
{"histallowclobber", HISTALLOWCLOBBER__},
{"histbeep", HISTBEEP__},
{"histexpiredupsfirst", HISTEXPIREDUPSFIRST__},
{"histfcntllock", HISTFCNTLLOCK__},
{"histfindnodups", HISTFINDNODUPS__},
{"histignorealldups", HISTIGNOREALLDUPS__},
{"histignoredups", HISTIGNOREDUPS__},
{"histignorespace", HISTIGNORESPACE__},
{"histlexwords", HISTLEXWORDS__},
{"histnofunctions", HISTNOFUNCTIONS__},
{"histnostore", HISTNOSTORE__},
{"histsubstpattern", HISTSUBSTPATTERN__},
{"histreduceblanks", HISTREDUCEBLANKS__},
{"histsavebycopy", HISTSAVEBYCOPY__},
{"histsavenodups", HISTSAVENODUPS__},
{"histverify", HISTVERIFY__},
{"hup", HUP__},
{"ignorebraces", IGNOREBRACES__},
{"ignoreclosebraces", IGNORECLOSEBRACES__},
{"ignoreeof", IGNOREEOF__},
{"incappendhistory", INCAPPENDHISTORY__},
{"incappendhistorytime",INCAPPENDHISTORYTIME__},
{"interactive", INTERACTIVE__},
{"interactivecomments", INTERACTIVECOMMENTS__},
{"ksharrays", KSHARRAYS__},
{"kshautoload", KSHAUTOLOAD__},
{"kshglob", KSHGLOB__},
{"kshoptionprint", KSHOPTIONPRINT__},
{"kshtypeset", KSHTYPESET__},
{"kshzerosubscript", KSHZEROSUBSCRIPT__},
{"listambiguous", LISTAMBIGUOUS__},
{"listbeep", LISTBEEP__},
{"listpacked", LISTPACKED__},
{"listrowsfirst", LISTROWSFIRST__},
{"listtypes", LISTTYPES__},
{"localoptions", LOCALOPTIONS__},
{"localloops", LOCALLOOPS__},
{"localpatterns", LOCALPATTERNS__},
{"localtraps", LOCALTRAPS__},
{"login", LOGINSHELL__},
{"longlistjobs", LONGLISTJOBS__},
{"magicequalsubst", MAGICEQUALSUBST__},
{"mailwarning", MAILWARNING__},
{"markdirs", MARKDIRS__},
{"menucomplete", MENUCOMPLETE__},
{"monitor", MONITOR__},
{"multibyte", MULTIBYTE__},
{"multifuncdef", MULTIFUNCDEF__},
{"multios", MULTIOS__},
{"nomatch", NOMATCH__},
{"notify", NOTIFY__},
{"nullglob", NULLGLOB__},
{"numericglobsort", NUMERICGLOBSORT__},
{"octalzeroes", OCTALZEROES__},
{"overstrike", OVERSTRIKE__},
{"pathdirs", PATHDIRS__},
{"pathscript", PATHSCRIPT__},
{"pipefail", PIPEFAIL__},
{"posixaliases", POSIXALIASES__},
{"posixargzero", POSIXARGZERO__},
{"posixbuiltins", POSIXBUILTINS__},
{"posixcd", POSIXCD__},
{"posixidentifiers", POSIXIDENTIFIERS__},
{"posixjobs", POSIXJOBS__},
{"posixstrings", POSIXSTRINGS__},
{"posixtraps", POSIXTRAPS__},
{"printeightbit", PRINTEIGHTBIT__},
{"printexitvalue", PRINTEXITVALUE__},
{"privileged", PRIVILEGED__},
{"promptbang", PROMPTBANG__},
{"promptcr", PROMPTCR__},
{"promptpercent", PROMPTPERCENT__},
{"promptsp", PROMPTSP__},
{"promptsubst", PROMPTSUBST__},
{"pushdignoredups", PUSHDIGNOREDUPS__},
{"pushdminus", PUSHDMINUS__},
{"pushdsilent", PUSHDSILENT__},
{"pushdtohome", PUSHDTOHOME__},
{"rcexpandparam", RCEXPANDPARAM__},
{"rcquotes", RCQUOTES__},
{"rcs", RCS__},
{"recexact", RECEXACT__},
{"rematchpcre", REMATCHPCRE__},
{"restricted", RESTRICTED__},
{"rmstarsilent", RMSTARSILENT__},
{"rmstarwait", RMSTARWAIT__},
{"sharehistory", SHAREHISTORY__},
{"shfileexpansion", SHFILEEXPANSION__},
{"shglob", SHGLOB__},
{"shinstdin", SHINSTDIN__},
{"shnullcmd", SHNULLCMD__},
{"shoptionletters", SHOPTIONLETTERS__},
{"shortloops", SHORTLOOPS__},
{"shwordsplit", SHWORDSPLIT__},
{"singlecommand", SINGLECOMMAND__},
{"singlelinezle", SINGLELINEZLE__},
{"sourcetrace", SOURCETRACE__},
{"sunkeyboardhack", SUNKEYBOARDHACK__},
{"transientrprompt", TRANSIENTRPROMPT__},
{"trapsasync", TRAPSASYNC__},
{"typesetsilent", TYPESETSILENT__},
{"unset", UNSET__},
{"verbose", VERBOSE__},
{"vi", VIMODE__},
{"warncreateglobal", WARNCREATEGLOBAL__},
{"warnnestedvar", WARNNESTEDVAR__},
{"xtrace", XTRACE__},
{"zle", USEZLE__},
{"dvorak", DVORAK__},
/* Below follow *aliases*, i.e. not-main, alternate option names */
/* There are 10 uncommented entries */
/* {"braceexpand", -IGNOREBRACES__}, */
{"dotglob", GLOBDOTS__},
{"hashall", HASHCMDS__},
{"histappend", APPENDHISTORY__},
{"histexpand", BANGHIST__},
/* {"log", -HISTNOFUNCTIONS__}, */
{"mailwarn", MAILWARNING__},
{"onecmd", SINGLECOMMAND__},
{"physical", CHASELINKS__},
{"promptvars", PROMPTSUBST__},
{"stdin", SHINSTDIN__},
{"trackall", HASHCMDS__},
{NULL, 0}
};
/* }}} */
/* Copied, repeated Zsh macros, data structures, etc. {{{ */
#define FD_EXT ".zwc"
#define FD_MINMAP 4096
#define FD_PRELEN 12
#define FD_MAGIC 0x04050607
#define FD_OMAGIC 0x07060504
#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 flags; /* flags and offset to name tail */
};
#define fdheaderlen(f) (((Wordcode) (f))[FD_PRELEN])
#define fdmagic(f) (((Wordcode) (f))[0])
#define fdsetbyte(f,i,v) \
((((unsigned char *) (((Wordcode) (f)) + 1))[i]) = ((unsigned char) (v)))
#define fdbyte(f,i) ((wordcode) (((unsigned char *) (((Wordcode) (f)) + 1))[i]))
#define fdflags(f) fdbyte(f, 0)
#define fdsetflags(f,v) fdsetbyte(f, 0, v)
#define fdother(f) (fdbyte(f, 1) + (fdbyte(f, 2) << 8) + (fdbyte(f, 3) << 16))
#define fdsetother(f, o) \
do { \
fdsetbyte(f, 1, ((o) & 0xff)); \
fdsetbyte(f, 2, (((o) >> 8) & 0xff)); \
fdsetbyte(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 fdhflags(f) (((FDHead) (f))->flags)
#define fdhtail(f) (((FDHead) (f))->flags >> 2)
#define fdhbldflags(f,t) ((f) | ((t) << 2))
#define FDHF_KSHLOAD 1
#define FDHF_ZSHLOAD 2
#define fdname(f) ((char *) (((FDHead) (f)) + 1))
/* }}} */
/*
* Compatibility functions (i.e. support for multiple Zsh versions)
*/
/* STATIC FUNCTION: zp_setup_options_table {{{ */
/**/
static
void zp_setup_options_table() {
int i, optno;
for ( i = 0; i < sizeof( zp_options ) / sizeof( struct zp_option_name ) - 10 - 1; ++ i ) {
optno = optlookup( zp_options[ i ].name );
zp_opt_for_zsh_version[ zp_options[ i ].enum_val ] = optno;
}
}
/* }}} */
/* STATIC FUNCTION: zp_conv_opt {{{ */
/**/
static
int zp_conv_opt( int zp_opt_num ) {
int sign;
sign = zp_opt_num >= 0 ? 1 : -1;
return sign*zp_opt_for_zsh_version[ sign*zp_opt_num ];
}
/* }}} */
/*
* `.' and `source' overload (profiling loading times)
*/
/* FUNCTION: bin_custom_dot {{{ */
/**/
int
bin_custom_dot(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
{
char **old, *old0 = NULL;
int diddot = 0, dotdot = 0;
char *s, **t, *enam, *arg0, *buf;
struct stat st;
enum source_return ret;
if (!*argv)
return 0;
old = pparams;
/* get arguments for the script */
if (argv[1])
pparams = zarrdup(argv + 1);
enam = arg0 = ztrdup(*argv);
if (isset(zp_conv_opt(FUNCTIONARGZERO__))) {
old0 = argzero;
argzero = ztrdup(arg0);
}
s = unmeta(enam);
errno = ENOENT;
ret = SOURCE_NOT_FOUND;
/* for source only, check in current directory first */
if (*name != '.' && access(s, F_OK) == 0
&& stat(s, &st) >= 0 && !S_ISDIR(st.st_mode)) {
diddot = 1;
ret = custom_source(enam);
}
if (ret == SOURCE_NOT_FOUND) {
/* use a path with / in it */
for (s = arg0; *s; s++)
if (*s == '/') {
if (*arg0 == '.') {
if (arg0 + 1 == s)
++diddot;
else if (arg0[1] == '.' && arg0 + 2 == s)
++dotdot;
}
ret = custom_source(arg0);
break;
}
if (!*s || (ret == SOURCE_NOT_FOUND &&
isset(zp_conv_opt(PATHDIRS__)) && diddot < 2 && dotdot == 0)) {
pushheap();
/* search path for script */
for (t = path; *t; t++) {
if (!(*t)[0] || ((*t)[0] == '.' && !(*t)[1])) {
if (diddot)
continue;
diddot = 1;
buf = dupstring(arg0);
} else
buf = zhtricat(*t, "/", arg0);
s = unmeta(buf);
if (access(s, F_OK) == 0 && stat(s, &st) >= 0
&& !S_ISDIR(st.st_mode)) {
ret = custom_source(enam = buf);
break;
}
}
popheap();
}
}
/* clean up and return */
if (argv[1]) {
freearray(pparams);
pparams = old;
}
if (ret == SOURCE_NOT_FOUND) {
if (isset(zp_conv_opt(POSIXBUILTINS__))) {
/* hard error in POSIX (we'll exit later) */
zerrnam(name, "%d: %e: %s", __LINE__, errno, enam);
} else {
zwarnnam(name, "%d: %e: %s", __LINE__, errno, enam);
}
}
zsfree(arg0);
if (old0) {
zsfree(argzero);
argzero = old0;
}
return ret == SOURCE_OK ? lastval : 128 - ret;
}
/* }}} */
/* FUNCTION: custom_source {{{ */
/**/
mod_export enum source_return
custom_source(char *s)
{
Eprog prog;
int tempfd = -1, fd, cj;
zlong oldlineno;
int oldshst, osubsh, oloops;
FILE *obshin;
char *old_scriptname = scriptname, *us;
char *old_scriptfilename = scriptfilename;
unsigned char *ocs;
int ocsp;
int otrap_return = trap_return, otrap_state = trap_state;
struct funcstack fstack;
enum source_return ret = SOURCE_OK;
/* ZP-CODE */
SEventNode zp_node;
struct timeval zp_tv;
struct timezone zp_dummy_tz;
double zp_prev_tv;
zp_tv.tv_sec = zp_tv.tv_usec = 0;
gettimeofday(&zp_tv, &zp_dummy_tz);
zp_prev_tv = ((((double) zp_tv.tv_sec) * 1000.0) + (((double) zp_tv.tv_usec) / 1000.0));
if (!s ||
(!(prog = custom_try_source_file((us = unmeta(s)))) &&
(tempfd = movefd(open(us, O_RDONLY | O_NOCTTY))) == -1)) {
return SOURCE_NOT_FOUND;
}
/* save the current shell state */
fd = SHIN; /* store the shell input fd */
obshin = bshin; /* store file handle for buffered shell input */
osubsh = subsh; /* store whether we are in a subshell */
cj = thisjob; /* store our current job number */
oldlineno = lineno; /* store our current lineno */
oloops = loops; /* stored the # of nested loops we are in */
oldshst = opts[zp_conv_opt(SHINSTDIN__)]; /* store current value of this option */
ocs = cmdstack;
ocsp = cmdsp;
cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
cmdsp = 0;
if (!prog) {
SHIN = tempfd;
bshin = fdopen(SHIN, "r");
}
subsh = 0;
lineno = 1;
loops = 0;
dosetopt(zp_conv_opt(SHINSTDIN__), 0, 1, opts);
scriptname = s;
scriptfilename = s;
if (isset(zp_conv_opt(SOURCETRACE__))) {
printprompt4();
fprintf(xtrerr ? xtrerr : stderr, "<sourcetrace>\n");
}
/*
* The special return behaviour of traps shouldn't
* trigger in files sourced from traps; the return
* is just a return from the file.
*/
trap_state = TRAP_STATE_INACTIVE;
sourcelevel++;
fstack.name = scriptfilename;
fstack.caller = funcstack ? funcstack->name :
dupstring(old_scriptfilename ? old_scriptfilename : "zsh");
fstack.flineno = 0;
fstack.lineno = oldlineno;
fstack.filename = scriptfilename;
fstack.prev = funcstack;
fstack.tp = FS_SOURCE;
funcstack = &fstack;
if (prog) {
pushheap();
errflag &= ~ERRFLAG_ERROR;
execode(prog, 1, 0, "filecode");
popheap();
if (errflag)
ret = SOURCE_ERROR;
} else {
int value;
/* loop through the file to be sourced */
switch (value=loop(0, 0))
{
case LOOP_OK:
/* nothing to do but compilers like a complete enum */
break;
case LOOP_EMPTY:
/* Empty code resets status */
lastval = 0;
break;
case LOOP_ERROR:
ret = SOURCE_ERROR;
break;
}
}
funcstack = funcstack->prev;
sourcelevel--;
trap_state = otrap_state;
trap_return = otrap_return;
/* restore the current shell state */
if (prog)
freeeprog(prog);
else {
fclose(bshin);
fdtable[SHIN] = FDT_UNUSED;
SHIN = fd; /* the shell input fd */
bshin = obshin; /* file handle for buffered shell input */
}
subsh = osubsh; /* whether we are in a subshell */
thisjob = cj; /* current job number */
lineno = oldlineno; /* our current lineno */
loops = oloops; /* the # of nested loops we are in */
dosetopt(zp_conv_opt(SHINSTDIN__), oldshst, 1, opts); /* SHINSTDIN option */
errflag &= ~ERRFLAG_ERROR;
if (!exit_pending)
retflag = 0;
scriptname = old_scriptname;
scriptfilename = old_scriptfilename;
zfree(cmdstack, CMDSTACKSZ);
cmdstack = ocs;
cmdsp = ocsp;
/* ZP-CODE */
zp_tv.tv_sec = zp_tv.tv_usec = 0;
gettimeofday(&zp_tv, &zp_dummy_tz);
zp_node = (SEventNode) zshcalloc( sizeof( struct zp_sevent_node ) );
if ( zp_node ) {
char zp_tmp[20], bkp;
char *dir_path, *file_name, *full_path, *slash;
int is_dot_slash;
/* Prepare paths */
if ( s[0] == '/' ) {
/* event.full_path */
full_path = ztrdup( s );
} else {
int pwd_len, rel_len;
is_dot_slash = ( s[0] == '.' && s[1] == '/' );
/* event.full_path */
pwd_len = strlen( pwd );
rel_len = strlen( s ) - is_dot_slash * 2;
full_path = (char *) zalloc( sizeof( char ) * ( pwd_len + rel_len + 2 ) );
strcpy( full_path, pwd );
strcat( full_path, "/" );
strcat( full_path, s + is_dot_slash * 2 );
}
/* event.file_name */
slash = strrchr( full_path, '/' );
file_name = ztrdup( slash + 1 );
/* event.dir_path */
bkp = slash[1];
slash[1] = '\0';
dir_path = ztrdup( full_path );
slash[1] = bkp;
/* Fill and add zp_node */
++ zp_sevent_count;
zp_node->event.id = zp_sevent_count;
zp_node->event.ts = (long) zp_prev_tv;
zp_node->event.dir_path = dir_path;
zp_node->event.file_name = file_name;
zp_node->event.full_path = full_path;
zp_node->event.duration = ((((double) zp_tv.tv_sec) * 1000.0) + (((double) zp_tv.tv_usec) / 1000.0)) - zp_prev_tv;
zp_node->event.load_error = ret;
sprintf( zp_tmp, "%d", zp_node->event.id );
zp_tmp[ 19 ] = '\0';
addhashnode( zp_source_events, ztrdup( zp_tmp ), ( void * ) zp_node );
}
return ret;
}
/* }}} */
/* FUNCTION: custom_try_source_file {{{ */
/**/
Eprog
custom_try_source_file(char *file)
{
Eprog prog;
struct stat stc, stn;
int rc, rn, faltered = 0, flen;
char *wc, *tail, *file_dup;
if ((tail = strrchr(file, '/')))
tail++;
else
tail = file;
if (strsfx(FD_EXT, file)) {
queue_signals();
prog = custom_check_dump_file(file, NULL, tail, NULL, 0);
unqueue_signals();
return prog;
}
wc = dyncat(file, FD_EXT);
rc = stat(wc, &stc);
rn = stat(file, &stn);
/* ZP-CODE */
if ( file != tail ) {
faltered = 1;
*--tail = '\0';
}
file_dup = ztrdup( file );
flen = strlen( file );
if ( faltered ) {
*tail++ = '/';
}
/* If there is no zwc file, or if it is less recent than script file */
if ( ( !rn && ( rc || ( stc.st_mtime < stn.st_mtime ) ) ) &&
( access( file_dup, W_OK ) == 0 || 0 == strcmp(
getsparam( "ZINIT_MOD_DEBUG" ) ?
getsparam( "ZINIT_MOD_DEBUG" ) : "0",
"1" ) )
) {
char *args[] = { file, NULL };
struct options ops;
/* Initialise options structure */
memset(ops.ind, 0, MAX_OPS*sizeof(unsigned char));
ops.args = NULL;
ops.argscount = ops.argsalloc = 0;
ops.ind['U'] = 1;
/* Invoke compilation */
if ( access( file, R_OK ) == 0 && access( file, F_OK ) == 0 &&
0 != strcmp( file, "/dev/null" ) && 0 != strcmp( file, "./" ) ) {
bin_zcompile("ZpluginModule_", args, &ops, 0);
} else {
if ( 0 == strcmp(
getsparam( "ZINIT_MOD_DEBUG" ) ?
getsparam( "ZINIT_MOD_DEBUG" ) : "0",
"1" )
) {
zwarnnam( "ZpluginModule",
"%d: Couldn't read the script: `%s', compilation skipped",
__LINE__, file );
}
}
/* Repeat stat for newly created zwc */
rc = stat(wc, &stc);
}
zfree(file_dup, flen);
queue_signals();
if (!rc && (rn || stc.st_mtime >= stn.st_mtime) &&
(prog = custom_check_dump_file(wc, &stc, tail, NULL, 0))) {
unqueue_signals();
return prog;
}
unqueue_signals();
return NULL;
}
/* }}} */
/* Code copied from Zshell's parse.c {{{ */
/**/
#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MMAP) && defined(HAVE_MUNMAP)
#include <sys/mman.h>
/**/
#if defined(MAP_SHARED) && defined(PROT_READ)
/**/
#define USE_MMAP 1
/**/
#endif
/**/
#endif
/**/
#ifdef USE_MMAP
/* List of dump files mapped. */
static FuncDump dumps;
/* }}} */
/* STATIC FUNCTION: custom_zwcstat {{{ */
/**/
static int
custom_zwcstat(char *filename, struct stat *buf)
{
if (stat(filename, buf)) {
#ifdef HAVE_FSTAT
FuncDump f;
for (f = dumps; f; f = f->next) {
if (!strncmp(filename, f->filename, strlen(f->filename)) &&
!fstat(f->fd, buf))
return 0;
}
#endif
return 1;
} else return 0;
}
/* }}} */
/* STATIC FUNCTION: custom_load_dump_file {{{ */
/* Load a dump file (i.e. map it). */
static void
custom_load_dump_file(char *dump, struct stat *sbuf, int other, int len)
{
FuncDump d;
Wordcode addr;
int fd, off, mlen;
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;
mlen = len + (len - off);
} else {
off = 0;
mlen = len;
}
if ((fd = open(dump, O_RDONLY)) < 0)
return;
fd = movefd(fd);
if (fd == -1)
return;
if ((addr = (Wordcode) mmap(NULL, mlen, PROT_READ, MAP_SHARED, fd, off)) ==
((Wordcode) -1)) {
close(fd);
return;
}
d = (FuncDump) zalloc(sizeof(*d));
d->next = dumps;
dumps = d;
d->dev = sbuf->st_dev;
d->ino = sbuf->st_ino;
d->fd = fd;
#ifdef FD_CLOEXEC
fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif
d->map = addr + (other ? (len - off) / sizeof(wordcode) : 0);
d->addr = addr;
d->len = len;
d->count = 0;
d->filename = ztrdup(dump);
}
/* }}} */
/* Code copied from Zshell's parse.c {{{ */
#else
#define custom_zwcstat(f, b) (!!stat(f, b))
/**/
#endif
/* }}} */
/* STATIC FUNCTION: custom_dump_find_func {{{ */
static FDHead
custom_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) + fdhtail(n)))
return n;
return NULL;
}
/* }}} */
/* STATIC FUNCTION: custom_check_dump_file {{{ */
/**/
static Eprog
custom_check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh,
int test_only)
{
int isrec = 0;
Wordcode d;
FDHead h;
FuncDump f;
struct stat lsbuf;
if (!sbuf) {
if (custom_zwcstat(file, &lsbuf))
return NULL;
sbuf = &lsbuf;
}
#ifdef USE_MMAP
rec:
#endif
d = NULL;
#ifdef USE_MMAP
for (f = dumps; f; f = f->next)
if (f->dev == sbuf->st_dev && f->ino == sbuf->st_ino) {
d = f->map;
break;
}
#else
f = NULL;
#endif
if (!f && (isrec || !(d = custom_load_dump_header(NULL, file, 0))))
return NULL;
if ((h = custom_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 (test_only)
{
/* This is all we need. Just return dummy. */
return &dummy_eprog;
}
#ifdef USE_MMAP
if (f) {
Eprog prog = (Eprog) zalloc(sizeof(*prog));
Patprog *pp;
int np;
prog->flags = EF_MAP;
prog->len = h->len;
prog->npats = np = h->npats;
prog->nref = 1; /* allocated from permanent storage */
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;
if (ksh)
*ksh = ((fdhflags(h) & FDHF_KSHLOAD) ? 2 :
((fdhflags(h) & FDHF_ZSHLOAD) ? 0 : 1));
return prog;
} else if (fdflags(d) & FDF_MAP) {
custom_load_dump_file(file, sbuf, (fdflags(d) & FDF_OTHER), fdother(d));
isrec = 1;
goto rec;
} else
#endif
{
Eprog prog;
Patprog *pp;
int np, fd, po = h->npats * sizeof(Patprog);
if ((fd = open(file, 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) != (int)h->len) {
close(fd);
zfree(d, h->len);
return NULL;
}
close(fd);
prog = (Eprog) zalloc(sizeof(*prog));
prog->flags = EF_REAL;
prog->len = h->len + po;
prog->npats = np = h->npats;
prog->nref = 1; /* allocated from permanent storage */
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;
if (ksh)
*ksh = ((fdhflags(h) & FDHF_KSHLOAD) ? 2 :
((fdhflags(h) & FDHF_ZSHLOAD) ? 0 : 1));
return prog;
}
}
return NULL;
}
/* }}} */
/* STATIC FUNCTION: custom_load_dump_header {{{ */
/**/
static Wordcode
custom_load_dump_header(char *nam, char *name, int err)
{
int fd, v = 1;
wordcode buf[FD_PRELEN + 1];
if ((fd = open(name, O_RDONLY)) < 0) {
if (err)
zwarnnam(nam, "%d: can't open zwc file: %s", __LINE__, name);
return NULL;
}
if (read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) !=
((FD_PRELEN + 1) * sizeof(wordcode)) ||
(v = (fdmagic(buf) != FD_MAGIC && fdmagic(buf) != FD_OMAGIC)) ||
strcmp(fdversion(buf), getsparam("ZSH_VERSION"))) {
if (err) {
if (!v) {
zwarnnam(nam, "%d: zwc file has wrong version (zsh-%s): %s",
__LINE__, fdversion(buf), name);
} else
zwarnnam(nam, "%d: invalid zwc file: %s" , __LINE__, name);
}
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))) {
zwarnnam(nam, "%d: invalid zwc file: %s", __LINE__, name);
close(fd);
return NULL;
}
len = fdheaderlen(buf) * sizeof(wordcode);
head = (Wordcode) zhalloc(len);
}
memcpy(head, buf, (FD_PRELEN + 1) * sizeof(wordcode));
len -= (FD_PRELEN + 1) * sizeof(wordcode);
if (read(fd, head + (FD_PRELEN + 1), len) != len) {
close(fd);
zwarnnam(nam, "%d: invalid zwc file: %s", __LINE__, name);
return NULL;
}
close(fd);
return head;
}
}
/* }}} */
/*
* readarray {{{
*
* readarray [-d delim] [-n count] [-O origin] [-s count] [-t] [-u fd]
* [-C callback] [-c quantum] [array]
*
* Reads from stdin or from {fd} (-u option).
* -d {delim} - terminator for each record read (default: newline)
* -n {count} - copy at most {count} records
* -O {origin} - begin storing in {array} at index {origin}
* -s {count} - discard first {count} lines read
* -t - remove trailing {delim} from result
* -u {fd} - read from file descriptor {fd}
* -C {callback} - eval {callback} each time {quantum} records are read
* -c {quantum} - the # of records for the above -C option
*
* Default {quantum} is 5000. Callback obtains 2 arguments, <assign-index> <content-to-assign>,
* i.e. where the record will be assigned in the {array}, and body of the record.
*
* Without -O, readarray clears the array at start.
*
* readarray returns successfully unless a bad option or option argument is
* supplied, {array} is unassignable, or if {array} is not an indexed array.
*/
int bin_readarray( char *nam, char **argv, UNUSED( Options ops ), UNUSED( int func ) ) {
int delim='\n', to_copy = 0, start_at = 1, skip_first = 0, remdel = 0, srcfd = 0, quantum = 5000;
char *callback = NULL, *oarr_name = NULL; // unused: **oarr = NULL;
FILE *stream = NULL;
/* Usage message */
if ( OPT_ISSET( ops, 'h' ) ) {
readarray_usage();
return 0;
}
/* -d {delim} - terminator for each record read (default: newline) */
if ( OPT_ISSET( ops, 'd' ) ) {
delim = OPT_ARG( ops, 'd' ) ? OPT_ARG( ops, 'd' )[0] : '\n';
}
/* -n {count} - copy at most {count} records */
if ( OPT_ISSET( ops, 'n' ) ) {
to_copy = OPT_ARG( ops, 'n' ) ? atoi( OPT_ARG( ops, 'n' ) ) : 0;
}
/* -O {origin} - begin storing in {array} at index {origin} */
if ( OPT_ISSET( ops, 'O' ) ) {
start_at = OPT_ARG( ops, 'O' ) ? atoi( OPT_ARG( ops, 'O' ) ) : 1;
}
/* -s {count} - discard first {count} lines read */
if ( OPT_ISSET( ops, 's' ) ) {
skip_first = OPT_ARG( ops, 's' ) ? atoi( OPT_ARG( ops, 's' ) ) : 0;
}
/* -t - remove trailing {delim} from result */
if ( OPT_ISSET( ops, 't' ) ) {
remdel = 1;
}
/* -u {fd} - read from file descriptor {fd} */
if ( OPT_ISSET( ops, 'u' ) ) {
srcfd = OPT_ARG( ops, 'u' ) ? atoi( OPT_ARG( ops, 'u' ) ) : 0;
}
/* -C {callback} - eval {callback} each time {quantum} records are read */
if ( OPT_ISSET( ops, 'C' ) ) {
callback = OPT_ARG( ops, 'C' ) ? ztrdup( OPT_ARG( ops, 'C' ) ) : NULL;
}
/* -c {quantum} - the # of records for the above -C option */
if ( OPT_ISSET( ops, 'c' ) ) {
quantum = OPT_ARG( ops, 'c' ) ? atoi( OPT_ARG( ops, 'c' ) ) : 5000;
}
/* The name of output array */
if ( !*argv ) {
zwarnnam( nam, "%d: Name of the output array is required, aborting", __LINE__ );
return 1;
} else {
oarr_name = ztrdup( *argv );
++ argv;
}
/* Extra arguments -> error */
if ( *argv ) {
zwarnnam( nam, "%d: Extra arguments detected, only one argument is needed, see -h, aborting", __LINE__ );
return 1;
}
stream = fdopen( srcfd, "r" );
if ( !stream ) {
zwarnnam( nam, "%d: Couldn't read descriptor: %d", __LINE__, nam, srcfd );
return 1;
}
#ifdef HAVE_GETLINE
#endif
return 0;
}
/**/
static void
readarray_usage() {
fprintf( stdout, "Usage: readarray\n" );
fflush( stdout );
}
/* }}} */
/*
* Main builtin `zpmod' and its subcommands
*/
/* FUNCTION: bin_zpmod {{{ */
static int
bin_zpmod( char *nam, char **argv, UNUSED( Options ops ), UNUSED( int func ) ) {
char *subcmd = NULL;
int ret = 0;
if ( OPT_ISSET( ops, 'h' ) ) {
zpmod_usage();
return 0;
}
if ( !*argv ) {
zwarnnam( nam, "%d: `zpmod' takes a sub-command as first argument, see -h", __LINE__ );
return 1;
}
subcmd = *argv ++;
if ( 0 == strcmp( subcmd, "report-append" ) ) {
char *target = NULL, *body = NULL;
int target_len = 0, body_len = 0;
target = *argv ++;
if ( !target ) {
zwarnnam( nam, "%d: `report-append' is missing the target plugin ID (like \"zdharma/zbrowse\", see -h", __LINE__ );
return 1;
}
target = zp_unmetafy_zalloc( target, &target_len );
if ( !target ) {
zwarnnam( nam, "%d: Couldn't allocate new memory (1), operation aborted", __LINE__ );
return 1;
}
body = *argv ++;
if ( !body ) {
zwarnnam( nam, "%d: `report-append' is missing the report-body to append, see -h", __LINE__ );
return 1;
}
body_len = strlen( body );
ret = zp_append_report( nam, target, target_len, body, body_len );
zfree( target, target_len );
} else if ( 0 == strcmp( subcmd, "source-study" ) ) {
char *report;
int rep_size;
report = zp_build_source_report( ! zp_has_option( argv, 'l' ), &rep_size );
fprintf( stdout, "%s", report ? report : "Unknown error, aborted" );
fflush( stdout );
if ( rep_size ) {
zfree( report, rep_size );
} else if ( report ) {
zsfree( report );
}
} else {
zwarnnam( nam, "%d: Unknown zplugin-module command: `%s', see `-h'", __LINE__, subcmd );
}
return ret;
}
/* }}} */
/* FUNCTION: zpmod_usage {{{ */
/**/
void zpmod_usage() {
fprintf( stdout, "Usage: zpmod {subcommand} {subcommand-arguments}\n"
" zpmod report-append {plugin-ID} {new-report-body}\n"
" zpmod source-study [-l]\n"
"\n"
"Command <report-append>:\n"
"\n"
"Used by Zplugin internally to speed up loading plugins with tracking (reporting).\n"
"It extends the given field {plugin-ID} in $ZINIT_REPORTS hash, with the given string\n"
"{new-report-body}.\n"
"\n"
"Command <source-study>:\n"
"\n"
"Displays list of files loaded via `source' or `.' builtins, with duration that each\n"
"loading lasted, in milliseconds. The module tracks all calls to those builtins and\n"
"measures the time each call took. This can be used to e.g. profile loading of plugins,\n"
"regardless of the plugin manager used.\n"
"\n"
"Option -l shows full paths to the files.\n"
);
fflush( stdout );
}
/* }}} */
/* FUNCTION: zp_append_report {{{ */
/**/
static int
zp_append_report( const char *nam, const char *target, int target_len, const char *body, int body_len ) {
Param pm = NULL, val_pm = NULL;
HashTable ht = NULL;
HashNode hn = NULL;
char *target_string = NULL;
int target_string_len = 0, new_extended_len = 0;
/* Get ZINIT_REPORTS associative array */
pm = ( Param ) paramtab->getnode( paramtab, "ZINIT_REPORTS" );
if ( !pm ) {
zwarnnam( nam, "%d: Parameter $ZINIT_REPORTS isn't declared. Zplugin is not loaded? I.e. not sourced.", __LINE__ );
return 1;
}
/* Get ZINIT_REPORTS[{target}] hashed Param */
ht = pm->u.hash;
hn = gethashnode2( ht, target );
val_pm = ( Param ) hn;
if ( !val_pm ) {
zwarnnam( nam, "%d: Plugin %s isn't registered, cannot append to its report.", __LINE__, target );
return 1;
}
/* Nothing to append? */
if ( body_len == 0 ) {
return 0;
}
/* Get string that the hashed Param holds */
target_string = val_pm->u.str;
if( !target_string ) {
target_string_len = 0;
} else {
target_string_len = strlen( target_string );
}
/* Extend the string with additional body_len-bytes */
new_extended_len = target_string_len + body_len;
target_string = realloc( target_string, ( new_extended_len + 1 ) * sizeof( char ) );
if ( NULL == target_string ) {
zwarnnam( nam, "%d: Couldn't allocate new memory (2), operation aborted", __LINE__ );
return 1;
}
/* Copy contents of body, null terminate */
memcpy( target_string + target_string_len, body, sizeof( char ) * body_len );
target_string[ new_extended_len ] = '\0';
/* Store the pointer in case realloc() allocated a new buffer */
val_pm->u.str = target_string;
return 0;
}
/* }}} */
/* FUNCTION: zp_build_source_report {{{ */
/**/
char *zp_build_source_report( int no_paths, int *rep_size ) {
char *report, zp_tmp[ 20 ];
int current_size, space_left, current_end, idx, printed;
SEventNode node;
FILE *null_fle;
current_size = 127;
current_end = 0;
report = ( char * ) zalloc( sizeof( char ) * ( current_size + 1 ) );
space_left = 127;
report[ current_end ] = '\0';
*rep_size = current_size + 1;
if ( ! report ) {
*rep_size = 0;
return ztrdup( "ERROR: couldn't allocate initial buffer, aborted\n" );
}
null_fle = fopen( "/dev/null", "w" );
if ( ! null_fle ) {
zfree( report, *rep_size );
*rep_size = 0;
return ztrdup( "ERROR: couldn't open /dev/null, aborted\n" );
}
for ( idx = 1; idx <= zp_sevent_count; ++ idx ) {
sprintf( zp_tmp, "%d", idx );
zp_tmp[ 19 ] = '\0';
if ( ! ( node = ( SEventNode ) gethashnode2( zp_source_events, zp_tmp ) ) ) {
continue;
}
printed = fprintf( null_fle, "%4.0lf ms %s\n", node->event.duration,
no_paths ? node->event.file_name : node->event.full_path );
if ( space_left < printed ) {
char *report_;
current_size += printed - space_left + 25;
space_left += printed - space_left + 25;
report_ = zrealloc( report, sizeof( char ) * ( current_size + 1 ) );
if ( ! report_ ) {
zfree( report, *rep_size );
*rep_size = 0;
fclose( null_fle );
return ztrdup( "ERROR: Couldn't realloc buffer, aborted\n" );
}
report = report_;
*rep_size = current_size + 1;
}
printed = sprintf( report + current_end, "%4.0lf ms %s\n", node->event.duration,
no_paths ? node->event.file_name : node->event.full_path );
current_end += printed;
space_left -= printed;
}
fclose( null_fle );
return report;
}
/* }}} */
/*
* Needed tool-functions, like function creating a hash parameter
*/
/* FUNCTION: zp_createhashtable {{{ */
/**/
static HashTable
zp_createhashtable( char *name )
{
HashTable ht;
ht = newhashtable( 8, name, NULL );
ht->hash = hasher;
ht->emptytable = emptyhashtable;
ht->filltable = NULL;
ht->cmpnodes = strcmp;
ht->addnode = addhashnode;
ht->getnode = gethashnode2;
ht->getnode2 = gethashnode2;
ht->removenode = removehashnode;
ht->disablenode = NULL;
ht->enablenode = NULL;
ht->freenode = zp_free_sevent_node;
ht->printnode = NULL;
return ht;
}
/* }}} */
/* FUNCTION: zp_createhashparam {{{ */
/**/
static Param
zp_createhashparam( char *name, int flags )
{
Param pm;
HashTable ht;
pm = createparam( name, flags | PM_SPECIAL | PM_HASHED );
if ( !pm ) {
return NULL;
}
if ( pm->old )
pm->level = locallevel;
/* This creates standard hash. */
ht = pm->u.hash = newparamtable( 7, name );
if ( !pm->u.hash ) {
paramtab->removenode( paramtab, name );
paramtab->freenode( &pm->node );
zwarnnam( name, "%d: Out of memory when allocating user-visible hash parameter", __LINE__ );
return NULL;
}
/* Does free Param (unsetfn is called) */
ht->freenode = zp_freeparamnode;
return pm;
}
/* }}} */
/* FUNCTION: zp_free_sevent_node {{{ */
/**/
static void
zp_free_sevent_node( HashNode hn )
{
zsfree( hn->nam );
zfree( hn, sizeof( struct zp_sevent_node ) );
}
/* }}} */
/* FUNCTION: zp_freeparamnode {{{ */
/**/
void
zp_freeparamnode( HashNode hn )
{
Param pm = ( Param ) hn;
/* Upstream: The second argument of unsetfn() is used by modules to
* differentiate "exp"licit unset from implicit unset, as when
* a parameter is going out of scope. It's not clear which
* of these applies here, but passing 1 has always worked.
*/
/* if (delunset) */
pm->gsu.s->unsetfn( pm, 1 );
zsfree( pm->node.nam );
/* If this variable was tied by the user, ename was ztrdup'd */
if ( pm->node.flags & PM_TIED && pm->ename ) {
zsfree( pm->ename );
pm->ename = NULL;
}
zfree( pm, sizeof( struct param ) );
}
/* }}} */
/*
* Tool-functions that are more hacky or problem-solving
*/
/* FUNCTION: zp_has_option {{{ */
/**/
static int
zp_has_option( char **argv, char opt ) {
char *string;
while ( ( string = *argv ) ) {
if ( string[0] == '-' ) {
while ( *++string ) {
if ( string[0] == opt ) {
return 1;
}
}
}
++ argv;
}
return 0;
}
/* }}} */
/* FUNCTION: my_ztrdup_glen {{{ */
/**/
char *
my_ztrdup_glen( const char *s, unsigned *len_ret )
{
char *t;
if ( !s )
return NULL;
t = ( char * )zalloc( ( *len_ret = strlen( ( char * )s ) ) + 1 );
strcpy( t, s );
return t;
}
/* }}} */
/* FUNCTION: zp_unmetafy_zalloc {{{ */
/*
* Unmetafy that:
* - duplicates buffer to work on it - original buffer is unchanged, can be zsfree'd,
* - does zalloc of exact size for the new unmeta-string - this string can be zfree'd,
* - restores work-buffer to original meta-content, to restore strlen - thus work-buffer can be zsfree'd,
* - returns actual length of the output unmeta-string, which should be passed to zfree.
*
* This function can be avoided if there's no need for new buffer, user should first strlen
* the metafied string, store the length into a variable (e.g. meta_length), then unmetafy,
* use the unmeta-content, then zfree( buf, meta_length ).
*/
/**/
char *
zp_unmetafy_zalloc( const char *to_copy, int *new_len )
{
char *work, *to_return;
int my_new_len = 0;
unsigned meta_length = 0;
work = my_ztrdup_glen( to_copy, &meta_length );
if ( !work ) {
return NULL;
}
work = unmetafy( work, &my_new_len );
if ( new_len )
*new_len = my_new_len;
to_return = ( char * )zalloc( ( my_new_len + 1 ) * sizeof( char ) );
if ( !to_return ) {
zfree( work, meta_length );
return NULL;
}
memcpy( to_return, work, sizeof( char ) * my_new_len ); /* memcpy handles $'\0' */
to_return[ my_new_len ] = '\0';
/* Restore original content and correctly zsfree(). */
/* UPDATE: instead of zsfree() here now it is
* zfree() that's used and the length it needs
* is taken above from my_ztrdup_glen */
zfree( work, meta_length );
return to_return;
}
/* }}} */
/*
* Zshell module architecture data structures
*/
/* ARRAY: struct builtin bintab[] {{{ */
static struct builtin bintab[] =
{
BUILTIN( "custom_dot", 0, bin_custom_dot, 1, -1, 0, NULL, NULL ),
BUILTIN( "zpmod", 0, bin_zpmod, 0, -1, 0, "h", NULL ),
};
/* }}} */
/* STRUCT: struct features module_features {{{ */
static struct features module_features =
{
bintab, sizeof( bintab )/sizeof( *bintab ),
NULL, 0,
NULL, 0,
NULL, 0,
0
};
/* }}} */
/*
* Zshell module architecture functions
*/
/* FUNCTION: setup_ {{{ */
/**/
int
setup_( UNUSED( Module m ) )
{
zp_setup_options_table();
Builtin bn = ( Builtin ) builtintab->getnode2( builtintab, "." );
originalDot = bn->handlerfunc;
bn->handlerfunc = bin_custom_dot;
bn = ( Builtin ) builtintab->getnode2( builtintab, "source" );
originalSource = bn->handlerfunc;
bn->handlerfunc = bin_custom_dot;
/* Create private hash with source_prepare requests */
if ( !( zp_source_events = zp_createhashtable( "zp_source_events" ) ) ) {
zwarn( "Cannot create the hash table" );
return 1;
}
return 0;
}
/* }}} */
/* FUNCTION: features_ {{{ */
/**/
int
features_( Module m, char ***features )
{
*features = featuresarray( m, &module_features );
return 0;
}
/* }}} */
/* FUNCTION: enables_ {{{ */
/**/
int
enables_( Module m, int **enables )
{
return handlefeatures( m, &module_features, enables );
}
/* }}} */
/* FUNCTION: boot_ {{{ */
/**/
int
boot_( Module m )
{
return 0;
}
/* }}} */
/* FUNCTION: cleanup_ {{{ */
/**/
int
cleanup_( Module m )
{
return setfeatureenables( m, &module_features, NULL );
}
/* }}} */
/* FUNCTION: finish_ {{{ */
/**/
int
finish_( UNUSED( Module m ) )
{
Builtin bn = ( Builtin ) builtintab->getnode2( builtintab, "." );
bn->handlerfunc = originalDot;
bn = ( Builtin ) builtintab->getnode2( builtintab, "source" );
bn->handlerfunc = originalSource;
printf( "zdharma/zplugin module unloaded\n" );
fflush( stdout );
return 0;
}
/* }}} */