zpmod  b19981f
High-performance Zsh module for script optimization and filesystem helpers
readarray.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
6 /* Canonical module header ordering */
7 #include "zpmod.mdh"
8 #include "zpmod.pro"
9 #include "zpmod_vendor_shims.h"
10 /* System headers after gateway */
11 #include "zpmod_emoji.h"
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 
16 static void readarray_usage(void) {
17  fprintf(stdout,
18  "%sUsage:%s readarray [-d delim] [-n count] [-O origin] [-s count] "
19  "[-t] [-u fd] [-C callback] [-c quantum] array\n"
20  "Read records from standard input (or -u fd) into the named array.\n",
21  zp_icon("📥 "), "");
22  fflush(stdout);
23 }
24 
26 int bin_readarray(char *nam, char **argv, Options ops, int func) {
27  (void)func; /* unused */
28  int srcfd = 0;
29  char *callback = NULL;
30  char *oarr_name = NULL;
31  FILE *stream = NULL;
32  if (OPT_ISSET(ops, 'u')) {
33  srcfd = OPT_ARG(ops, 'u') ? atoi(OPT_ARG(ops, 'u')) : 0;
34  }
35  if (OPT_ISSET(ops, 'C')) {
36  callback = OPT_ARG(ops, 'C') ? ztrdup(OPT_ARG(ops, 'C')) : NULL;
37  }
38  if (!*argv) {
39  zwarnnam(nam, "%d: Name of the output array is required, aborting",
40  __LINE__);
41  if (callback) {
42  zsfree(callback);
43  }
44  return 1;
45  }
46  oarr_name = ztrdup(*argv);
47  ++argv;
48  if (*argv) {
49  zwarnnam(nam,
50  "%d: Extra arguments detected, only one argument is needed, see "
51  "-h, aborting",
52  __LINE__);
53  if (callback) {
54  zsfree(callback);
55  }
56  if (oarr_name) {
57  zsfree(oarr_name);
58  }
59  return 1;
60  }
61  if (!OPT_ISSET(ops, 'O')) {
62  unsetparam(oarr_name);
63  char **emptyarr = (char **)zalloc(sizeof(char *));
64  emptyarr[0] = NULL;
65  setaparam(oarr_name, emptyarr);
66  }
67  stream = fdopen(srcfd, "r");
68  if (!stream) {
69  zwarnnam(nam, "line %d: couldn't open descriptor %d: %e", __LINE__, srcfd,
70  errno);
71  if (callback) {
72  zsfree(callback);
73  }
74  if (oarr_name) {
75  zsfree(oarr_name);
76  }
77  return 1;
78  }
79 #ifdef HAVE_GETLINE
80  int delim = '\n';
81  int to_copy = 0;
82  int start_at = 1;
83  int skip_first = 0;
84  int remdel = 0;
85  int quantum = 5000;
86  if (OPT_ISSET(ops, 'd')) {
87  delim = OPT_ARG(ops, 'd') ? OPT_ARG(ops, 'd')[0] : '\n';
88  }
89  if (OPT_ISSET(ops, 'n')) {
90  to_copy = OPT_ARG(ops, 'n') ? atoi(OPT_ARG(ops, 'n')) : 0;
91  }
92  if (OPT_ISSET(ops, 'O')) {
93  start_at = OPT_ARG(ops, 'O') ? atoi(OPT_ARG(ops, 'O')) : 1;
94  }
95  if (OPT_ISSET(ops, 's')) {
96  skip_first = OPT_ARG(ops, 's') ? atoi(OPT_ARG(ops, 's')) : 0;
97  }
98  if (OPT_ISSET(ops, 't')) {
99  remdel = 1;
100  }
101  if (OPT_ISSET(ops, 'c')) {
102  quantum = OPT_ARG(ops, 'c') ? atoi(OPT_ARG(ops, 'c')) : 5000;
103  }
104  char *line = NULL;
105  size_t len = 0;
106  ssize_t read_len;
107  int index = start_at;
108  if (delim == '\n') {
109  while ((read_len = getline(&line, &len, stream)) != -1) {
110  if (skip_first > 0) {
111  skip_first--;
112  continue;
113  }
114  if (remdel && read_len > 0 && line[read_len - 1] == '\n') {
115  line[--read_len] = '\0';
116  }
117  if (to_copy > 0 && index - start_at >= to_copy)
118  break;
119  char indexed_name[256];
120  snprintf(indexed_name, sizeof(indexed_name), "%s[%d]", oarr_name, index);
121  setsparam(indexed_name, line);
122  if (callback && (index - start_at + 1) % quantum == 0) {
123  char idx_str[20];
124  snprintf(idx_str, sizeof(idx_str), "%d", index);
125  char *args[] = {idx_str, line, NULL};
126  execstring(callback, args, 0, 0);
127  }
128  index++;
129  }
130  free(line);
131  } else {
132  size_t cap = 1024, sz = 0;
133  char *buf = (char *)zalloc(cap);
134  int c;
135  if (!buf) {
136  zwarnnam(nam, "%d: Out of memory", __LINE__);
137  goto done;
138  }
139  while ((c = fgetc(stream)) != EOF) {
140  if (skip_first > 0 && c == delim) {
141  skip_first--;
142  sz = 0;
143  continue;
144  }
145  if (sz + 1 >= cap) {
146  size_t newcap = cap * 2;
147  char *nbuf = (char *)zrealloc(buf, newcap);
148  if (!nbuf) {
149  zfree(buf, cap);
150  zwarnnam(nam, "%d: Out of memory", __LINE__);
151  goto done;
152  }
153  buf = nbuf;
154  cap = newcap;
155  }
156  if (c == delim) {
157  if (!remdel) {
158  buf[sz++] = (char)delim;
159  }
160  buf[sz] = '\0';
161  if (to_copy > 0 && index - start_at >= to_copy)
162  break;
163  char indexed_name[256];
164  snprintf(indexed_name, sizeof(indexed_name), "%s[%d]", oarr_name,
165  index);
166  setsparam(indexed_name, buf);
167  if (callback && (index - start_at + 1) % quantum == 0) {
168  char idx_str[20];
169  snprintf(idx_str, sizeof(idx_str), "%d", index);
170  char *args[] = {idx_str, buf, NULL};
171  execstring(callback, args, 0, 0);
172  }
173  index++;
174  sz = 0;
175  } else {
176  buf[sz++] = (char)c;
177  }
178  }
179  if (sz > 0 && (to_copy == 0 || index - start_at < to_copy)) {
180  buf[sz] = '\0';
181  char indexed_name[256];
182  snprintf(indexed_name, sizeof(indexed_name), "%s[%d]", oarr_name, index);
183  setsparam(indexed_name, buf);
184  }
185  if (buf)
186  zfree(buf, cap);
187  }
188 #else
189  (void)nam;
190  (void)argv; /* no-op when getline isn't available */
191 #endif
192 done:
193  if (stream) {
194  fclose(stream);
195  }
196  if (callback) {
197  zsfree(callback);
198  }
199  if (oarr_name) {
200  zsfree(oarr_name);
201  }
202  return 0;
203 }
const char * zp_icon(const char *s)
Return icon string if enabled, empty string otherwise.
Definition: emoji.c:58
static void readarray_usage(void)
Definition: readarray.c:16
int bin_readarray(char *nam, char **argv, Options ops, int func)
readarray builtin entrypoint
Definition: readarray.c:26
Module declaration header (mdh) for zpmod.
Prototype stub for zpmod when building out-of-tree.
Optional terminal/locale detection for emoji support in messages.
void * zalloc(size_t size)
void zsfree(char *ptr)
void unsetparam(const char *name)
void setaparam(const char *name, char **value)
void zfree(void *ptr, size_t size)
void setsparam(const char *name, char *value)
void * zrealloc(void *ptr, size_t size)
void zwarnnam(const char *, const char *,...)
char * ztrdup(const char *)
Local, non-invasive shims to suppress benign vendor header warnings.