zpmod  b19981f
High-performance Zsh module for script optimization and filesystem helpers
bundle_build.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
6 // NOLINTBEGIN(misc-include-cleaner)
7 /* Canonical module header ordering: include gateway first
8  * (aggregates vendor .epro via zpmod_imports.h as needed).
9  */
10 #include "zpmod.mdh"
11 #include "zpmod.pro"
12 /* System headers after gateway */
13 #include "zpmod_bundle.h"
14 #include <dirent.h>
15 #include <errno.h>
16 #include <limits.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <time.h>
23 
24 /* Portable fallback for platforms that don't define PATH_MAX in <limits.h>. */
25 #ifndef PATH_MAX
26 #define PATH_MAX 4096
27 #endif
28 
29 /* Accept these file suffixes (exact) */
30 static int has_ext(const char *name) {
31  size_t len = strlen(name);
32  if (len >= 4 && strcmp(name + len - 4, ".zsh") == 0) {
33  return 1;
34  }
35  if (len >= 12 && strcmp(name + len - 12, ".plugin.zsh") == 0) {
36  return 1;
37  }
38  return 0;
39 }
40 
41 struct bb_entry {
42  char *rel;
43  char *abs;
44  off_t size;
45  time_t mtime;
46 };
47 struct bb_vec {
48  struct bb_entry *items;
49  size_t size;
50  size_t cap;
51 };
52 static void bb_vec_init(struct bb_vec *v) {
53  v->items = NULL;
54  v->size = 0;
55  v->cap = 0;
56 }
57 static int bb_vec_push(struct bb_vec *v, struct bb_entry *e) {
58  if (v->size == v->cap) {
59  size_t nc = v->cap ? v->cap * 2 : 32;
60  void *nb = zrealloc(v->items, nc * sizeof(struct bb_entry));
61  if (!nb) {
62  return 1;
63  }
64  v->items = (struct bb_entry *)nb;
65  v->cap = nc;
66  }
67  v->items[v->size++] = *e;
68  return 0;
69 }
70 static void bb_vec_free(struct bb_vec *v) {
71  if (!v) {
72  return;
73  }
74  for (size_t i = 0; i < v->size; i++) {
75  if (v->items[i].rel) {
76  zsfree(v->items[i].rel);
77  }
78  if (v->items[i].abs) {
79  zsfree(v->items[i].abs);
80  }
81  }
82  if (v->items) {
83  zfree(v->items, v->cap * sizeof(struct bb_entry));
84  }
85  v->items = NULL;
86  v->size = v->cap = 0;
87 }
88 
89 /* Recursive collection */
90 // NOLINTBEGIN(misc-no-recursion)
91 static int bb_collect(char *nam, const char *root, const char *sub,
92  struct bb_vec *out) {
93  char path[PATH_MAX];
94  if (sub && *sub) {
95  snprintf(path, sizeof(path), "%s/%s", root, sub);
96  } else {
97  snprintf(path, sizeof(path), "%s", root);
98  }
99  DIR *d = opendir(path);
100  if (!d) {
101  return 0;
102  }
103  struct dirent *de;
104  while ((de = readdir(d)) != NULL) {
105  if (de->d_name[0] == '.') {
106  continue;
107  }
108  char rel[PATH_MAX];
109  if (sub && *sub) {
110  snprintf(rel, sizeof(rel), "%s/%s", sub, de->d_name);
111  } else {
112  snprintf(rel, sizeof(rel), "%s", de->d_name);
113  }
114  char abs[PATH_MAX];
115  snprintf(abs, sizeof(abs), "%s/%s", root, rel);
116  struct stat st;
117  if (stat(abs, &st) != 0) {
118  continue;
119  }
120  if (S_ISDIR(st.st_mode)) {
121  bb_collect(nam, root, rel, out);
122  continue;
123  }
124  if (!S_ISREG(st.st_mode)) {
125  continue;
126  }
127  if (!has_ext(de->d_name)) {
128  continue;
129  }
130  size_t rlen = strlen(rel) + 1;
131  size_t alen = strlen(abs) + 1;
132  char *rdup = (char *)zalloc(rlen);
133  char *adup = (char *)zalloc(alen);
134  if (!rdup || !adup) {
135  if (rdup) {
136  zsfree(rdup);
137  }
138  if (adup) {
139  zsfree(adup);
140  }
141  closedir(d);
142  return 1;
143  }
144  memcpy(rdup, rel, rlen);
145  memcpy(adup, abs, alen);
146  struct bb_entry e;
147  e.rel = rdup;
148  e.abs = adup;
149  e.size = st.st_size;
150  e.mtime = st.st_mtime;
151  if (bb_vec_push(out, &e)) {
152  zsfree(rdup);
153  zsfree(adup);
154  closedir(d);
155  return 1;
156  }
157  }
158  closedir(d);
159  return 0;
160 }
161 // NOLINTEND(misc-no-recursion)
162 
163 /* Lexicographic sort (C locale) */
164 // NOLINTBEGIN(bugprone-easily-swappable-parameters)
165 static int bb_cmp(const void *a, const void *b) {
166  const struct bb_entry *ea = a;
167  const struct bb_entry *eb = b;
168  return strcmp(ea->rel, eb->rel);
169 }
170 // NOLINTEND(bugprone-easily-swappable-parameters)
171 
172 // NOLINTEND(misc-include-cleaner)
static void bb_vec_init(struct bb_vec *v)
Definition: bundle_build.c:52
static void bb_vec_free(struct bb_vec *v)
Definition: bundle_build.c:70
static int has_ext(const char *name)
Definition: bundle_build.c:30
static int bb_vec_push(struct bb_vec *v, struct bb_entry *e)
Definition: bundle_build.c:57
static int bb_collect(char *nam, const char *root, const char *sub, struct bb_vec *out)
Definition: bundle_build.c:91
static int bb_cmp(const void *a, const void *b)
Definition: bundle_build.c:165
#define PATH_MAX
Definition: bundle_build.c:26
Definition: bundle_build.c:41
off_t size
Definition: bundle_build.c:44
time_t mtime
Definition: bundle_build.c:45
char * abs
Definition: bundle_build.c:43
char * rel
Definition: bundle_build.c:42
size_t cap
Definition: bundle_build.c:50
struct bb_entry * items
Definition: bundle_build.c:48
size_t size
Definition: bundle_build.c:49
Module declaration header (mdh) for zpmod.
Prototype stub for zpmod when building out-of-tree.
Startup bundle builder interface.
void * zalloc(size_t size)
void zsfree(char *ptr)
void zfree(void *ptr, size_t size)
void * zrealloc(void *ptr, size_t size)
char ** path