zpmod  b19981f
High-performance Zsh module for script optimization and filesystem helpers
source.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
12 #ifndef ZPMOD_ANALYSIS
13 #include "zpmod.mdh"
14 #include "zpmod.pro"
15 #include "zpmod_vendor_shims.h"
16 #else
17 #include "zpmod_analysis_stubs.h"
18 #endif
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #if defined(__has_include)
26 #if __has_include(<sys/mman.h>)
27 #include <sys/mman.h>
28 #define USE_MMAP 1
29 #endif
30 #endif
31 #include "zpmod_compat.h"
32 #include "zpmod_emoji.h"
33 #include "zpmod_source.h"
34 #include "zpmod_utils.h"
35 
36 /* State */
37 static HandlerFunc original_dot = NULL, original_source = NULL;
39 static int zp_sevent_count = 0;
40 
41 /* dot/source replacement */
48 int bin_custom_dot(char *name, char **argv, UNUSED(Options ops),
49  UNUSED(int func)) {
50  char **old;
51  char *old0 = NULL;
52  int diddot = 0;
53  int dotdot = 0;
54  char *s;
55  char **t;
56  char *enam;
57  char *arg0;
58  char *buf;
59  struct stat st;
60  enum source_return ret;
61  if (!*argv) {
62  return 0;
63  }
64  old = pparams;
65  if (argv[1]) {
66  pparams = zarrdup(argv + 1);
67  }
68  enam = arg0 = ztrdup(*argv);
69  if (isset(zp_conv_opt(FUNCTIONARGZERO__))) {
70  old0 = argzero;
71  argzero = ztrdup(arg0);
72  }
73  s = unmeta(enam);
74  errno = ENOENT;
75  ret = SOURCE_NOT_FOUND;
76  if (*name != '.' && access(s, F_OK) == 0 && stat(s, &st) >= 0 &&
77  !S_ISDIR(st.st_mode)) {
78  diddot = 1;
79  ret = custom_source(enam);
80  }
81  if (ret == SOURCE_NOT_FOUND) {
82  for (s = arg0; *s; s++) {
83  if (*s == '/') {
84  if (*arg0 == '.') {
85  if (arg0 + 1 == s) {
86  ++diddot;
87  } else if (arg0[1] == '.' && arg0 + 2 == s) {
88  ++dotdot;
89  }
90  }
91  ret = custom_source(arg0);
92  break;
93  }
94  }
95  if (!*s || (ret == SOURCE_NOT_FOUND && isset(zp_conv_opt(PATHDIRS__)) &&
96  diddot < 2 && dotdot == 0)) {
97  pushheap();
98  for (t = path; *t; t++) {
99  if (!(*t)[0] || ((*t)[0] == '.' && !(*t)[1])) {
100  if (diddot) {
101  continue;
102  }
103  diddot = 1;
104  buf = dupstring(arg0);
105  } else {
106  buf = zhtricat(*t, "/", arg0);
107  }
108  s = unmeta(buf);
109  if (access(s, F_OK) == 0 && stat(s, &st) >= 0 && !S_ISDIR(st.st_mode)) {
110  ret = custom_source(enam = buf);
111  break;
112  }
113  }
114  popheap();
115  }
116  }
117  if (argv[1]) {
119  pparams = old;
120  }
121  if (ret == SOURCE_NOT_FOUND) {
122  if (isset(zp_conv_opt(POSIXBUILTINS__))) {
123  zerrnam(name, "%e: %s", errno, enam);
124  } else {
125  zwarnnam(name, "%e: %s", errno, enam);
126  }
127  }
128  zsfree(arg0);
129  if (old0) {
130  zsfree(argzero);
131  argzero = old0;
132  }
133  return ret == SOURCE_OK ? lastval : 128 - ret;
134 }
135 
136 /* ZWC access (trimmed) */
137 #define FD_EXT ".zwc"
138 #define FD_PRELEN 12
139 #define FD_MAGIC 0x04050607
140 #define FD_OMAGIC 0x07060504
141 #define FDF_MAP 1
142 #define FDF_OTHER 2
143 
144 typedef struct fdhead *FDHead;
145 struct fdhead {
146  wordcode start;
147  wordcode len;
148  wordcode npats;
149  wordcode strs;
150  wordcode hlen;
151  wordcode flags;
152 };
153 #define FDHEADERLEN(f) (((Wordcode)(f))[FD_PRELEN])
154 #define FDMAGIC(f) (((Wordcode)(f))[0])
155 #define FDSETBYTE(f, i, v) \
156  ((((unsigned char *)(((Wordcode)(f)) + 1))[i]) = ((unsigned char)(v)))
157 #define fdbyte(f, i) ((wordcode)(((unsigned char *)(((Wordcode)(f)) + 1))[i]))
158 #define FDFLAGS(f) fdbyte(f, 0)
159 #define FDOTHER(f) (fdbyte(f, 1) + (fdbyte(f, 2) << 8) + (fdbyte(f, 3) << 16))
160 #define FDVERSION(f) ((char *)((f) + 2))
161 #define FIRSTFDHEAD(f) ((FDHead)(((Wordcode)(f)) + FD_PRELEN))
162 #define NEXTFDHEAD(f) ((FDHead)(((Wordcode)(f)) + (f)->hlen))
163 #define FDHFLAGS(f) (((FDHead)(f))->flags)
164 #define FDHTAIL(f) (((FDHead)(f))->flags >> 2)
165 #define FDHF_KSHLOAD 1
166 #define FDHF_ZSHLOAD 2
167 #define FDNAME(f) ((char *)(((FDHead)(f)) + 1))
168 
169 #ifdef USE_MMAP
170 static FuncDump dumps;
171 static int custom_zwcstat(char *filename, struct stat *buf) {
172  if (stat(filename, buf)) {
173 #ifdef HAVE_FSTAT
174  for (FuncDump fdump_iter = dumps; fdump_iter;
175  fdump_iter = fdump_iter->next) {
176  if (!strncmp(filename, fdump_iter->filename,
177  strlen(fdump_iter->filename)) &&
178  !fstat(fdump_iter->fd, buf)) {
179  return 0;
180  }
181  }
182 #endif
183  return 1;
184  }
185  return 0;
186 }
187 #else
188 #define custom_zwcstat(f, b) (!!stat(f, b))
189 #endif
190 
191 static FDHead custom_dump_find_func(Wordcode h, char *name) {
192  FDHead n;
193  FDHead e = (FDHead)(h + FDHEADERLEN(h));
194  for (n = FIRSTFDHEAD(h); n < e; n = NEXTFDHEAD(n)) {
195  if (!strcmp(name, FDNAME(n) + FDHTAIL(n))) {
196  return n;
197  }
198  }
199  return NULL;
200 }
201 
202 static Wordcode custom_load_dump_header(char *nam, char *name, int err) {
203  int fd;
204  int v = 1;
205  wordcode buf[FD_PRELEN + 1];
206  if ((fd = open(name, O_RDONLY)) < 0) {
207  if (err) {
208  zwarnnam(nam, "%d: can't open zwc file: %s", __LINE__, name);
209  }
210  return NULL;
211  }
212  if (read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) !=
213  ((FD_PRELEN + 1) * sizeof(wordcode)) ||
214  (v = (FDMAGIC(buf) != FD_MAGIC && FDMAGIC(buf) != FD_OMAGIC)) ||
215  strcmp(FDVERSION(buf), getsparam("ZSH_VERSION")) != 0) {
216  if (err) {
217  if (!v) {
218  zwarnnam(nam, "%d: zwc file has wrong version (zsh-%s): %s", __LINE__,
219  FDVERSION(buf), name);
220  } else {
221  zwarnnam(nam, "%d: invalid zwc file: %s", __LINE__, name);
222  }
223  }
224  close(fd);
225  return NULL;
226  }
227  int len;
228  Wordcode head;
229  if (FDMAGIC(buf) == FD_MAGIC) {
230  len = FDHEADERLEN(buf) * sizeof(wordcode);
231  head = (Wordcode)zhalloc(len);
232  } else {
233  int o = FDOTHER(buf);
234  if (lseek(fd, o, 0) == -1 ||
235  read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) !=
236  ((FD_PRELEN + 1) * sizeof(wordcode))) {
237  zwarnnam(nam, "%d: invalid zwc file: %s", __LINE__, name);
238  close(fd);
239  return NULL;
240  }
241  len = FDHEADERLEN(buf) * sizeof(wordcode);
242  head = (Wordcode)zhalloc(len);
243  }
244  memcpy(head, buf, (FD_PRELEN + 1) * sizeof(wordcode));
245  len -= (FD_PRELEN + 1) * sizeof(wordcode);
246  if (read(fd, head + (FD_PRELEN + 1), len) != len) {
247  close(fd);
248  zwarnnam(nam, "%d: invalid zwc file: %s", __LINE__, name);
249  return NULL;
250  }
251  close(fd);
252  return head;
253 }
254 
255 #ifdef USE_MMAP
256 static void custom_load_dump_file(char *dump, struct stat *sbuf, int other,
257  int len) {
258  FuncDump d;
259  Wordcode addr;
260  int fd;
261  int off;
262  int mlen;
263  if (other) {
264  static size_t pgsz = 0;
265  if (!pgsz) {
266 #ifdef _SC_PAGESIZE
267  pgsz = sysconf(_SC_PAGESIZE);
268 #else
269 #ifdef _SC_PAGE_SIZE
270  pgsz = sysconf(_SC_PAGE_SIZE);
271 #else
272  pgsz = getpagesize();
273 #endif
274 #endif
275  pgsz--;
276  }
277  off = len & ~pgsz;
278  mlen = len + (len - off);
279  } else {
280  off = 0;
281  mlen = len;
282  }
283  if ((fd = open(dump, O_RDONLY)) < 0) {
284  return;
285  }
286  fd = movefd(fd);
287  if (fd == -1) {
288  return;
289  }
290  if ((addr = (Wordcode)mmap(NULL, mlen, PROT_READ, MAP_SHARED, fd, off)) ==
291  ((Wordcode)-1)) {
292  close(fd);
293  return;
294  }
295  d = (FuncDump)zalloc(sizeof(*d));
296  d->next = dumps;
297  dumps = d;
298  d->dev = sbuf->st_dev;
299  d->ino = sbuf->st_ino;
300  d->fd = fd;
301 #ifdef FD_CLOEXEC
302  fcntl(fd, F_SETFD, FD_CLOEXEC);
303 #endif
304  d->map = addr + (other ? (len - off) / sizeof(wordcode) : 0);
305  d->addr = addr;
306  d->len = len;
307  d->count = 0;
308  d->filename = ztrdup(dump);
309 }
310 #endif
311 
312 /* Forward (file-local) declaration */
313 static Eprog custom_check_dump_file(char *file, struct stat *sbuf, char *name,
314  int *ksh, int test_only);
315 
323  Eprog prog;
324  struct stat stc;
325  struct stat stn;
326  int rc;
327  int rn;
328  int faltered = 0;
329  int flen;
330  char *wc;
331  char *tail;
332  char *file_dup;
333  char *zi_mod_debug = getsparam("ZI_MOD_DEBUG");
334  int debug_enabled = (zi_mod_debug && !strcmp(zi_mod_debug, "1"));
335  if ((tail = strrchr(file, '/'))) {
336  tail++;
337  } else {
338  tail = file;
339  }
340  if (strsfx(FD_EXT, file)) {
341  queue_signals();
342  prog = custom_check_dump_file(file, NULL, tail, NULL, 0);
343  unqueue_signals();
344  return prog;
345  }
346  wc = dyncat(file, FD_EXT);
347  rc = stat(wc, &stc);
348  rn = stat(file, &stn);
349  if (file != tail) {
350  faltered = 1;
351  *--tail = '\0';
352  }
353  file_dup = ztrdup(file);
354  flen = strlen(file);
355  if (faltered) {
356  *tail++ = '/';
357  }
358  if ((!rn && (rc || (stc.st_mtime < stn.st_mtime))) &&
359  (access(file_dup, W_OK) == 0 || debug_enabled)) {
360  char *args[] = {file, NULL};
361  struct options ops;
362  memset(ops.ind, 0, MAX_OPS * sizeof(unsigned char));
363  ops.args = NULL;
364  ops.argscount = ops.argsalloc = 0;
365  ops.ind['U'] = 1;
366  if (access(file, R_OK) == 0 && access(file, F_OK) == 0 &&
367  0 != strcmp(file, "/dev/null") && 0 != strcmp(file, "./")) {
368  bin_zcompile("ZIModule_", args, &ops, 0);
369  } else if (debug_enabled) {
370  zwarnnam("ZIModule",
371  "%d: Couldn't read the script: `%s', compilation skipped",
372  __LINE__, file);
373  }
374  rc = stat(wc, &stc);
375  }
376  zfree(file_dup, flen);
377  queue_signals();
378  if (!rc && (rn || stc.st_mtime >= stn.st_mtime) &&
379  (prog = custom_check_dump_file(wc, &stc, tail, NULL, 0))) {
380  unqueue_signals();
381  return prog;
382  }
383  unqueue_signals();
384  return NULL;
385 }
386 
387 static Eprog custom_check_dump_file(char *file, struct stat *sbuf, char *name,
388  int *ksh, int test_only) {
389  int isrec = 0;
390  Wordcode d;
391  FDHead h;
392  FuncDump f;
393  struct stat lsbuf;
394  if (!sbuf) {
395  if (custom_zwcstat(file, &lsbuf)) {
396  return NULL;
397  }
398  sbuf = &lsbuf;
399  }
400 #ifdef USE_MMAP
401 rec:
402 #endif
403  d = NULL;
404 #ifdef USE_MMAP
405  for (f = dumps; f; f = f->next) {
406  if (f->dev == sbuf->st_dev && f->ino == sbuf->st_ino) {
407  d = f->map;
408  break;
409  }
410  }
411 #else
412  f = NULL;
413 #endif
414  if (!f && (isrec || !(d = custom_load_dump_header(NULL, file, 0)))) {
415  return NULL;
416  }
417  if ((h = custom_dump_find_func(d, name))) {
418  if (test_only) {
419  return &dummy_eprog;
420  }
421 #ifdef USE_MMAP
422  if (f) {
423  Eprog prog = (Eprog)zalloc(sizeof(*prog));
424  Patprog *pp;
425  int np;
426  prog->flags = EF_MAP;
427  prog->len = h->len;
428  prog->npats = np = h->npats;
429  prog->nref = 1;
430  prog->pats = pp = (Patprog *)zalloc(np * sizeof(Patprog));
431  prog->prog = f->map + h->start;
432  prog->strs = ((char *)prog->prog) + h->strs;
433  prog->shf = NULL;
434  prog->dump = NULL;
435  incrdumpcount(f);
436  while (np--) {
437  *pp++ = dummy_patprog1;
438  }
439  if (ksh) {
440  *ksh = ((FDHFLAGS(h) & FDHF_KSHLOAD)
441  ? 2
442  : ((FDHFLAGS(h) & FDHF_ZSHLOAD) ? 0 : 1));
443  }
444  return prog;
445  }
446  if (FDFLAGS(d) & FDF_MAP) {
447  custom_load_dump_file(file, sbuf, (FDFLAGS(d) & FDF_OTHER), FDOTHER(d));
448  isrec = 1;
449  goto rec;
450  }
451 #endif
452  {
453  Eprog prog;
454  Patprog *pp;
455  int np;
456  int fd;
457  int po = h->npats * sizeof(Patprog);
458  if ((fd = open(file, O_RDONLY)) < 0 ||
459  lseek(fd,
460  ((h->start * sizeof(wordcode)) +
461  ((FDFLAGS(d) & FDF_OTHER) ? FDOTHER(d) : 0)),
462  0) < 0) {
463  if (fd >= 0) {
464  close(fd);
465  }
466  return NULL;
467  }
468  d = (Wordcode)zalloc(h->len + po);
469  if (read(fd, ((char *)d) + po, h->len) != (int)h->len) {
470  close(fd);
471  zfree(d, h->len);
472  return NULL;
473  }
474  close(fd);
475  prog = (Eprog)zalloc(sizeof(*prog));
476  prog->flags = EF_REAL;
477  prog->len = h->len + po;
478  prog->npats = np = h->npats;
479  prog->nref = 1;
480  prog->pats = pp = (Patprog *)d;
481  prog->prog = (Wordcode)(((char *)d) + po);
482  prog->strs = ((char *)prog->prog) + h->strs;
483  prog->shf = NULL;
484  prog->dump = f;
485  while (np--) {
486  *pp++ = dummy_patprog1;
487  }
488  if (ksh) {
489  *ksh = ((FDHFLAGS(h) & FDHF_KSHLOAD)
490  ? 2
491  : ((FDHFLAGS(h) & FDHF_ZSHLOAD) ? 0 : 1));
492  }
493  return prog;
494  }
495  }
496  return NULL;
497 }
498 
499 /* custom_source and reporting */
506 mod_export enum source_return custom_source(char *s) {
507  Eprog prog;
508  int tempfd = -1;
509  int fd;
510  int cj;
511  zlong oldlineno;
512  int oldshst;
513  int osubsh;
514  int oloops;
515  char *old_scriptname = scriptname;
516  char *us;
517  char *old_scriptfilename = scriptfilename;
518  unsigned char *ocs;
519  int ocsp;
520  int otrap_return = trap_return;
521  int otrap_state = trap_state;
522  struct funcstack fstack;
523  enum source_return ret = SOURCE_OK;
524  SEventNode zp_node;
525  struct timeval zp_tv;
526  struct timezone zp_dummy_tz;
527  double zp_prev_tv;
528  zp_tv.tv_sec = zp_tv.tv_usec = 0;
529  gettimeofday(&zp_tv, &zp_dummy_tz);
530  zp_prev_tv =
531  ((((double)zp_tv.tv_sec) * 1000.0) + (((double)zp_tv.tv_usec) / 1000.0));
532  if (!s || (!(prog = custom_try_source_file((us = unmeta(s)))) &&
533  (tempfd = movefd(open(us, O_RDONLY | O_NOCTTY))) == -1)) {
534  return SOURCE_NOT_FOUND;
535  }
536  fd = SHIN;
537  osubsh = subsh;
538  cj = thisjob;
539  oldlineno = lineno;
540  oloops = loops;
541  oldshst = opts[zp_conv_opt(SHINSTDIN__)]; /* unchanged; explicit suffix */
542  ocs = cmdstack;
543  ocsp = cmdsp;
544  cmdstack = (unsigned char *)zalloc(CMDSTACKSZ);
545  cmdsp = 0;
546  if (!prog) {
547  SHIN = tempfd;
548  shinbufsave();
549  }
550  subsh = 0;
551  lineno = 1;
552  loops = 0;
553  dosetopt(zp_conv_opt(SHINSTDIN__), 0, 1, opts); /* ensure suffixed enum */
554  scriptname = s;
555  scriptfilename = s;
556  if (isset(zp_conv_opt(SOURCETRACE__))) {
557  printprompt4();
558  fprintf(xtrerr ? xtrerr : stderr, "<sourcetrace>\n");
559  }
560  trap_state = TRAP_STATE_INACTIVE;
561  sourcelevel++;
562  fstack.name = scriptfilename;
563  fstack.caller =
564  funcstack ? funcstack->name
565  : dupstring(old_scriptfilename ? old_scriptfilename : "zsh");
566  fstack.flineno = 0;
567  fstack.lineno = oldlineno;
568  fstack.filename = scriptfilename;
569  fstack.prev = funcstack;
570  fstack.tp = FS_SOURCE;
571  funcstack = &fstack;
572  if (prog) {
573  pushheap();
574  errflag &= ~ERRFLAG_ERROR;
575  execode(prog, 1, 0, "filecode");
576  popheap();
577  if (errflag) {
578  ret = SOURCE_ERROR;
579  }
580  } else {
581  switch (loop(0, 0)) {
582  case LOOP_OK:
583  break;
584  case LOOP_EMPTY:
585  lastval = 0;
586  break;
587  case LOOP_ERROR:
588  ret = SOURCE_ERROR;
589  break;
590  }
591  }
592  funcstack = funcstack->prev;
593  sourcelevel--;
594  trap_state = otrap_state;
595  trap_return = otrap_return;
596  if (prog) {
597  freeeprog(prog);
598  } else {
599  close(SHIN);
600  fdtable[SHIN] = FDT_UNUSED;
601  SHIN = fd;
602  shinbufrestore();
603  }
604  subsh = osubsh;
605  thisjob = cj;
606  lineno = oldlineno;
607  loops = oloops;
608  dosetopt(zp_conv_opt(SHINSTDIN__), oldshst, 1,
609  opts); /* ensure suffixed enum */
610  errflag &= ~ERRFLAG_ERROR;
611  if (!exit_pending) {
612  retflag = 0;
613  }
614  scriptname = old_scriptname;
615  scriptfilename = old_scriptfilename;
616  zfree(cmdstack, CMDSTACKSZ);
617  cmdstack = ocs;
618  cmdsp = ocsp;
619  zp_tv.tv_sec = zp_tv.tv_usec = 0;
620  gettimeofday(&zp_tv, &zp_dummy_tz);
621  zp_node = (SEventNode)zshcalloc(sizeof(struct zp_sevent_node));
622  if (zp_node) {
623  char bkp;
624  char *dir_path;
625  char *file_name;
626  char *full_path;
627  char *slash;
628  int is_dot_slash;
629  if (s[0] == '/') {
630  full_path = ztrdup(s);
631  } else {
632  size_t pwd_len;
633  size_t rel_len;
634  size_t off;
635  is_dot_slash = (s[0] == '.' && s[1] == '/');
636  pwd_len = strlen(pwd);
637  off = is_dot_slash ? 2U : 0U;
638  rel_len = strlen(s) - off;
639  full_path = (char *)zalloc(sizeof(char) * (pwd_len + rel_len + 2U));
640  int n1 = snprintf(full_path, pwd_len + 1, "%s", pwd);
641  (void)n1;
642  snprintf(full_path + pwd_len, rel_len + 2U, "/%s", s + off);
643  }
644  slash = strrchr(full_path, '/');
645  file_name = ztrdup(slash + 1);
646  bkp = slash[1];
647  slash[1] = '\0';
648  dir_path = ztrdup(full_path);
649  slash[1] = bkp;
650  ++zp_sevent_count;
651  zp_node->event.id = zp_sevent_count;
652  zp_node->event.ts = (long)zp_prev_tv;
653  zp_node->event.dir_path = dir_path;
654  zp_node->event.file_name = file_name;
655  zp_node->event.full_path = full_path;
656  zp_node->event.duration = ((((double)zp_tv.tv_sec) * 1000.0) +
657  (((double)zp_tv.tv_usec) / 1000.0)) -
658  zp_prev_tv;
659  zp_node->event.load_error = ret;
660  char zp_tmp[20];
661  snprintf(zp_tmp, sizeof(zp_tmp), "%d", zp_node->event.id);
662  zp_tmp[sizeof(zp_tmp) - 1] = '\0';
663  if (zp_source_events) {
664  addhashnode(zp_source_events, ztrdup(zp_tmp), (void *)zp_node);
665  }
666  }
667  return ret;
668 }
669 
670 char *zp_build_source_report(int no_paths, int *rep_size) {
671  char *report;
672  char zp_tmp[20];
673  int current_size;
674  int space_left;
675  int current_end;
676  int idx;
677  int printed;
678  SEventNode node;
679  current_size = 127;
680  current_end = 0;
681  report = (char *)zalloc(sizeof(char) * (current_size + 1));
682  if (!report) {
683  *rep_size = 0;
684  return ztrdup("ERROR: couldn't allocate initial buffer, aborted\n");
685  }
686  space_left = 127;
687  report[current_end] = '\0';
688  *rep_size = current_size + 1;
689  for (idx = 1; idx <= zp_sevent_count; ++idx) {
690  snprintf(zp_tmp, sizeof(zp_tmp), "%d", idx);
691  zp_tmp[sizeof(zp_tmp) - 1] = '\0';
692  if (!(node = (SEventNode)gethashnode2(zp_source_events, zp_tmp))) {
693  continue;
694  }
695  {
696  const char *pfx = zp_icon("⏱️ ");
697  printed =
698  snprintf(NULL, 0, "%s%4.0lf ms %s\n", pfx, node->event.duration,
699  no_paths ? node->event.file_name : node->event.full_path);
700  }
701  if (space_left < printed) {
702  current_size += printed - space_left + 25;
703  space_left += printed - space_left + 25;
704  char *new_report =
705  (char *)zrealloc(report, sizeof(char) * (current_size + 1));
706  if (!new_report) {
707  zfree(report, *rep_size);
708  *rep_size = 0;
709  return ztrdup("ERROR: Couldn't realloc buffer, aborted\n");
710  }
711  report = new_report;
712  *rep_size = current_size + 1;
713  }
714  {
715  const char *pfx = zp_icon("⏱️ ");
716  printed =
717  snprintf(report + current_end, space_left + 1, "%s%4.0lf ms %s\n",
718  pfx, node->event.duration,
719  no_paths ? node->event.file_name : node->event.full_path);
720  }
721  current_end += printed;
722  space_left -= printed;
723  }
724  return report;
725 }
726 
728  SEventNode s = (SEventNode)hn;
729  zsfree(hn->nam);
730  zsfree(s->event.dir_path);
731  zsfree(s->event.file_name);
732  zsfree(s->event.full_path);
733  zfree(s, sizeof(struct zp_sevent_node));
734 }
735 
736 /* Setup/finish helpers for overriding builtin handlers and hashtable */
738  Builtin bn = (Builtin)builtintab->getnode2(builtintab, ".");
739  if (bn) {
740  original_dot = bn->handlerfunc;
741  bn->handlerfunc = bin_custom_dot;
742  }
743  bn = (Builtin)builtintab->getnode2(builtintab, "source");
744  if (bn) {
745  original_source = bn->handlerfunc;
746  bn->handlerfunc = bin_custom_dot;
747  }
748  if (!(zp_source_events = newhashtable(8, "zp_source_events", NULL))) {
749  zwarn("Cannot create the hash table");
750  return;
751  }
752  zp_source_events->hash = hasher;
753  zp_source_events->emptytable = emptyhashtable;
754  zp_source_events->cmpnodes = strcmp;
755  zp_source_events->addnode = addhashnode;
756  zp_source_events->getnode = gethashnode2;
757  zp_source_events->getnode2 = gethashnode2;
758  zp_source_events->removenode = removehashnode;
760 }
761 
763  Builtin bn = (Builtin)builtintab->getnode2(builtintab, ".");
764  if (bn) {
765  bn->handlerfunc = original_dot;
766  }
767  bn = (Builtin)builtintab->getnode2(builtintab, "source");
768  if (bn) {
769  bn->handlerfunc = original_source;
770  }
771  if (zp_source_events) {
773  zp_source_events = NULL;
774  }
775 }
const char * zp_icon(const char *s)
Return icon string if enabled, empty string otherwise.
Definition: emoji.c:58
int zp_conv_opt(int zp_opt_num)
Convert a stable option enum to a runtime option index (sign-preserving).
Definition: options.c:52
#define FDVERSION(f)
Definition: source.c:160
Eprog custom_try_source_file(char *file)
Try to locate or build a ZWC dump for the script and return Eprog.
Definition: source.c:322
#define FD_EXT
Definition: source.c:137
void zp_source_restore_overrides(void)
Definition: source.c:762
#define FDHEADERLEN(f)
Definition: source.c:153
#define FDF_OTHER
Definition: source.c:142
static int zp_sevent_count
Definition: source.c:39
void zp_source_setup_overrides(void)
Definition: source.c:737
#define FDHF_ZSHLOAD
Definition: source.c:166
#define FDOTHER(f)
Definition: source.c:159
#define FDF_MAP
Definition: source.c:141
static FDHead custom_dump_find_func(Wordcode h, char *name)
Definition: source.c:191
static HandlerFunc original_source
Definition: source.c:37
static HandlerFunc original_dot
Definition: source.c:37
mod_export enum source_return custom_source(char *s)
Execute a sourced script and record a timing event for reporting.
Definition: source.c:506
#define FD_OMAGIC
Definition: source.c:140
#define FDFLAGS(f)
Definition: source.c:158
#define FDHFLAGS(f)
Definition: source.c:163
static Wordcode custom_load_dump_header(char *nam, char *name, int err)
Definition: source.c:202
int bin_custom_dot(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
Replacement handler for '.
Definition: source.c:48
#define FDHF_KSHLOAD
Definition: source.c:165
void zp_free_sevent_node(HashNode hn)
Hash lifecycle.
Definition: source.c:727
#define custom_zwcstat(f, b)
Definition: source.c:188
#define FD_MAGIC
Definition: source.c:139
#define FDNAME(f)
Definition: source.c:167
#define NEXTFDHEAD(f)
Definition: source.c:162
char * zp_build_source_report(int no_paths, int *rep_size)
Definition: source.c:670
#define FDMAGIC(f)
Definition: source.c:154
static HashTable zp_source_events
Definition: source.c:38
#define FD_PRELEN
Definition: source.c:138
struct fdhead * FDHead
Definition: source.c:144
static Eprog custom_check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh, int test_only)
Definition: source.c:387
#define FIRSTFDHEAD(f)
Definition: source.c:161
#define FDHTAIL(f)
Definition: source.c:164
Definition: source.c:145
wordcode hlen
Definition: source.c:150
wordcode flags
Definition: source.c:151
wordcode len
Definition: source.c:147
wordcode npats
Definition: source.c:148
wordcode strs
Definition: source.c:149
wordcode start
Definition: source.c:146
char * file_name
Definition: zpmod_source.h:24
char * full_path
Definition: zpmod_source.h:25
char * dir_path
Definition: zpmod_source.h:23
double duration
Definition: zpmod_source.h:26
struct source_event event
Definition: zpmod_source.h:32
Module declaration header (mdh) for zpmod.
#define mod_export
Prototype stub for zpmod when building out-of-tree.
#define UNUSED(x)
@ SOURCETRACE__
Definition: zpmod_compat.h:20
@ POSIXBUILTINS__
Definition: zpmod_compat.h:18
@ PATHDIRS__
Definition: zpmod_compat.h:17
@ FUNCTIONARGZERO__
Definition: zpmod_compat.h:16
@ SHINSTDIN__
Definition: zpmod_compat.h:19
Optional terminal/locale detection for emoji support in messages.
void * zalloc(size_t size)
void zsfree(char *ptr)
char * dupstring(const char *s)
void zfree(void *ptr, size_t size)
void * zrealloc(void *ptr, size_t size)
char * getsparam(const char *name)
void emptyhashtable(HashTable)
void deletehashtable(HashTable)
void execode(Eprog, int, int, char *)
void zwarnnam(const char *, const char *,...)
volatile int retflag
int sourcelevel
void queue_signals(void)
int SHIN
char ** path
void pushheap(void)
FILE * xtrerr
struct hashtable * HashTable
void * cmdstack
int subsh
void zwarn(const char *,...)
void freeeprog(Eprog)
int thisjob
void unqueue_signals(void)
int loops
char * pwd
struct builtin * Builtin
int cmdsp
short * fdtable
void shinbufrestore(void)
char * zhtricat(const char *, const char *, const char *)
HashNode gethashnode2(HashTable, const char *)
struct eprog dummy_eprog
HashTable newhashtable(int, char *, PrintTableStats)
int movefd(int)
char * argzero
int bin_zcompile(char *, char **, void *, int)
char * scriptfilename
void * zshcalloc(size_t)
struct hashnode * HashNode
char * unmeta(char *)
int strsfx(const char *, const char *)
int trap_return
volatile int errflag
HashNode removehashnode(HashTable, const char *)
void incrdumpcount(void *)
unsigned int hasher(const char *)
void * zhalloc(size_t)
int dosetopt(int, int, int, unsigned char *)
struct patprog * Patprog
void freearray(char **)
char opts[]
void zerrnam(const char *, const char *,...)
void popheap(void)
int loop(int, int)
volatile long lastval
char ** pparams
struct eprog * Eprog
volatile int exit_pending
char * scriptname
void addhashnode(HashTable, char *, void *)
HashTable builtintab
char ** zarrdup(char **)
zlong lineno
Funcstack funcstack
void printprompt4(void)
volatile int trap_state
void shinbufsave(void)
char * dyncat(const char *, const char *)
char * ztrdup(const char *)
Public interfaces for source-study and source overrides.
struct zp_sevent_node * SEventNode
Definition: zpmod_source.h:15
Utility helpers shared across module components.
Local, non-invasive shims to suppress benign vendor header warnings.