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