1 /* $NetBSD: argv.c,v 1.3 2022/10/08 16:12:50 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* argv 3
6 /* SUMMARY
7 /* string array utilities
8 /* SYNOPSIS
9 /* #include <argv.h>
10 /*
11 /* ARGV *argv_alloc(len)
12 /* ssize_t len;
13 /*
14 /* ARGV *argv_sort(argvp)
15 /* ARGV *argvp;
16 /*
17 /* ARGV *argv_free(argvp)
18 /* ARGV *argvp;
19 /*
20 /* void argv_add(argvp, arg, ..., ARGV_END)
21 /* ARGV *argvp;
22 /* char *arg;
23 /*
24 /* void argv_addn(argvp, arg, arg_len, ..., ARGV_END)
25 /* ARGV *argvp;
26 /* char *arg;
27 /* ssize_t arg_len;
28 /*
29 /* void argv_terminate(argvp);
30 /* ARGV *argvp;
31 /*
32 /* void argv_truncate(argvp, len);
33 /* ARGV *argvp;
34 /* ssize_t len;
35 /*
36 /* void argv_insert_one(argvp, pos, arg)
37 /* ARGV *argvp;
38 /* ssize_t pos;
39 /* const char *arg;
40 /*
41 /* void argv_replace_one(argvp, pos, arg)
42 /* ARGV *argvp;
43 /* ssize_t pos;
44 /* const char *arg;
45 /*
46 /* void argv_delete(argvp, pos, how_many)
47 /* ARGV *argvp;
48 /* ssize_t pos;
49 /* ssize_t how_many;
50 /*
51 /* void ARGV_FAKE_BEGIN(argv, arg)
52 /* const char *arg;
53 /*
54 /* void ARGV_FAKE_END
55 /* DESCRIPTION
56 /* The functions in this module manipulate arrays of string
57 /* pointers. An ARGV structure contains the following members:
58 /* .IP len
59 /* The length of the \fIargv\fR array member.
60 /* .IP argc
61 /* The number of \fIargv\fR elements used.
62 /* .IP argv
63 /* An array of pointers to null-terminated strings.
64 /* .PP
65 /* argv_alloc() returns an empty string array of the requested
66 /* length. The result is ready for use by argv_add(). The array
67 /* is null terminated.
68 /*
69 /* argv_sort() sorts the elements of argvp in place returning
70 /* the original array.
71 /*
72 /* argv_add() copies zero or more strings and adds them to the
73 /* specified string array. The array is null terminated.
74 /* Terminate the argument list with a null pointer. The manifest
75 /* constant ARGV_END provides a convenient notation for this.
76 /*
77 /* argv_addn() is like argv_add(), but each string is followed
78 /* by a string length argument.
79 /*
80 /* argv_free() releases storage for a string array, and conveniently
81 /* returns a null pointer.
82 /*
83 /* argv_terminate() null-terminates its string array argument.
84 /*
85 /* argv_truncate() truncates its argument to the specified
86 /* number of entries, but does not reallocate memory. The
87 /* result is null-terminated.
88 /*
89 /* argv_insert_one() inserts one string at the specified array
90 /* position.
91 /*
92 /* argv_replace_one() replaces one string at the specified
93 /* position. The old string is destroyed after the update is
94 /* made.
95 /*
96 /* argv_delete() deletes the specified number of elements
97 /* starting at the specified array position. The result is
98 /* null-terminated.
99 /*
100 /* ARGV_FAKE_BEGIN/END are an optimization for the case where
101 /* a single string needs to be passed into an ARGV-based
102 /* interface. ARGV_FAKE_BEGIN() opens a statement block and
103 /* allocates a stack-based ARGV structure named after the first
104 /* argument, that encapsulates the second argument. This
105 /* implementation allocates no heap memory and creates no copy
106 /* of the second argument. ARGV_FAKE_END closes the statement
107 /* block and thereby releases storage.
108 /* SEE ALSO
109 /* msg(3) diagnostics interface
110 /* DIAGNOSTICS
111 /* Fatal errors: memory allocation problem.
112 /* LICENSE
113 /* .ad
114 /* .fi
115 /* The Secure Mailer license must be distributed with this software.
116 /* AUTHOR(S)
117 /* Wietse Venema
118 /* IBM T.J. Watson Research
119 /* P.O. Box 704
120 /* Yorktown Heights, NY 10598, USA
121 /*--*/
122
123 /* System libraries. */
124
125 #include <sys_defs.h>
126 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */
127 #include <stdarg.h>
128 #include <string.h>
129
130 /* Application-specific. */
131
132 #include "mymalloc.h"
133 #include "msg.h"
134 #include "argv.h"
135
136 /* argv_free - destroy string array */
137
argv_free(ARGV * argvp)138 ARGV *argv_free(ARGV *argvp)
139 {
140 char **cpp;
141
142 for (cpp = argvp->argv; cpp < argvp->argv + argvp->argc; cpp++)
143 myfree(*cpp);
144 myfree((void *) argvp->argv);
145 myfree((void *) argvp);
146 return (0);
147 }
148
149 /* argv_alloc - initialize string array */
150
argv_alloc(ssize_t len)151 ARGV *argv_alloc(ssize_t len)
152 {
153 ARGV *argvp;
154 ssize_t sane_len;
155
156 /*
157 * Make sure that always argvp->argc < argvp->len.
158 */
159 argvp = (ARGV *) mymalloc(sizeof(*argvp));
160 argvp->len = 0;
161 sane_len = (len < 2 ? 2 : len);
162 argvp->argv = (char **) mymalloc((sane_len + 1) * sizeof(char *));
163 argvp->len = sane_len;
164 argvp->argc = 0;
165 argvp->argv[0] = 0;
166 return (argvp);
167 }
168
argv_cmp(const void * e1,const void * e2)169 static int argv_cmp(const void *e1, const void *e2)
170 {
171 const char *s1 = *(const char **) e1;
172 const char *s2 = *(const char **) e2;
173
174 return strcmp(s1, s2);
175 }
176
177 /* argv_sort - sort array in place */
178
argv_sort(ARGV * argvp)179 ARGV *argv_sort(ARGV *argvp)
180 {
181 qsort(argvp->argv, argvp->argc, sizeof(argvp->argv[0]), argv_cmp);
182 return (argvp);
183 }
184
185 /* argv_extend - extend array */
186
argv_extend(ARGV * argvp)187 static void argv_extend(ARGV *argvp)
188 {
189 ssize_t new_len;
190
191 new_len = argvp->len * 2;
192 argvp->argv = (char **)
193 myrealloc((void *) argvp->argv, (new_len + 1) * sizeof(char *));
194 argvp->len = new_len;
195 }
196
197 /* argv_add - add string to vector */
198
argv_add(ARGV * argvp,...)199 void argv_add(ARGV *argvp,...)
200 {
201 char *arg;
202 va_list ap;
203
204 /*
205 * Make sure that always argvp->argc < argvp->len.
206 */
207 #define ARGV_SPACE_LEFT(a) ((a)->len - (a)->argc - 1)
208
209 va_start(ap, argvp);
210 while ((arg = va_arg(ap, char *)) != 0) {
211 if (ARGV_SPACE_LEFT(argvp) <= 0)
212 argv_extend(argvp);
213 argvp->argv[argvp->argc++] = mystrdup(arg);
214 }
215 va_end(ap);
216 argvp->argv[argvp->argc] = 0;
217 }
218
219 /* argv_addn - add string to vector */
220
argv_addn(ARGV * argvp,...)221 void argv_addn(ARGV *argvp,...)
222 {
223 char *arg;
224 ssize_t len;
225 va_list ap;
226
227 /*
228 * Make sure that always argvp->argc < argvp->len.
229 */
230 va_start(ap, argvp);
231 while ((arg = va_arg(ap, char *)) != 0) {
232 if ((len = va_arg(ap, ssize_t)) < 0)
233 msg_panic("argv_addn: bad string length %ld", (long) len);
234 if (ARGV_SPACE_LEFT(argvp) <= 0)
235 argv_extend(argvp);
236 argvp->argv[argvp->argc++] = mystrndup(arg, len);
237 }
238 va_end(ap);
239 argvp->argv[argvp->argc] = 0;
240 }
241
242 /* argv_terminate - terminate string array */
243
argv_terminate(ARGV * argvp)244 void argv_terminate(ARGV *argvp)
245 {
246
247 /*
248 * Trust that argvp->argc < argvp->len.
249 */
250 argvp->argv[argvp->argc] = 0;
251 }
252
253 /* argv_truncate - truncate string array */
254
argv_truncate(ARGV * argvp,ssize_t len)255 void argv_truncate(ARGV *argvp, ssize_t len)
256 {
257 char **cpp;
258
259 /*
260 * Sanity check.
261 */
262 if (len < 0)
263 msg_panic("argv_truncate: bad length %ld", (long) len);
264
265 if (len < argvp->argc) {
266 for (cpp = argvp->argv + len; cpp < argvp->argv + argvp->argc; cpp++)
267 myfree(*cpp);
268 argvp->argc = len;
269 argvp->argv[argvp->argc] = 0;
270 }
271 }
272
273 /* argv_insert_one - insert one string into array */
274
argv_insert_one(ARGV * argvp,ssize_t where,const char * arg)275 void argv_insert_one(ARGV *argvp, ssize_t where, const char *arg)
276 {
277 ssize_t pos;
278
279 /*
280 * Sanity check.
281 */
282 if (where < 0 || where > argvp->argc)
283 msg_panic("argv_insert_one bad position: %ld", (long) where);
284
285 if (ARGV_SPACE_LEFT(argvp) <= 0)
286 argv_extend(argvp);
287 for (pos = argvp->argc; pos >= where; pos--)
288 argvp->argv[pos + 1] = argvp->argv[pos];
289 argvp->argv[where] = mystrdup(arg);
290 argvp->argc += 1;
291 }
292
293 /* argv_replace_one - replace one string in array */
294
argv_replace_one(ARGV * argvp,ssize_t where,const char * arg)295 void argv_replace_one(ARGV *argvp, ssize_t where, const char *arg)
296 {
297 char *temp;
298
299 /*
300 * Sanity check.
301 */
302 if (where < 0 || where >= argvp->argc)
303 msg_panic("argv_replace_one bad position: %ld", (long) where);
304
305 temp = argvp->argv[where];
306 argvp->argv[where] = mystrdup(arg);
307 myfree(temp);
308 }
309
310 /* argv_delete - remove string(s) from array */
311
argv_delete(ARGV * argvp,ssize_t first,ssize_t how_many)312 void argv_delete(ARGV *argvp, ssize_t first, ssize_t how_many)
313 {
314 ssize_t pos;
315
316 /*
317 * Sanity check.
318 */
319 if (first < 0 || how_many < 0 || first + how_many > argvp->argc)
320 msg_panic("argv_delete bad range: (start=%ld count=%ld)",
321 (long) first, (long) how_many);
322
323 for (pos = first; pos < first + how_many; pos++)
324 myfree(argvp->argv[pos]);
325 for (pos = first; pos <= argvp->argc - how_many; pos++)
326 argvp->argv[pos] = argvp->argv[pos + how_many];
327 argvp->argc -= how_many;
328 }
329