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