1/*
2 * $Id: argv.c,v 1.12 2018/06/12 22:47:23 tom Exp $
3 *
4 *  argv - Reusable functions for argv-parsing.
5 *
6 *  Copyright 2011-2017,2018	Thomas E. Dickey
7 *
8 *  This program is free software; you can redistribute it and/or modify
9 *  it under the terms of the GNU Lesser General Public License, version 2.1
10 *  as published by the Free Software Foundation.
11 *
12 *  This program is distributed in the hope that it will be useful, but
13 *  WITHOUT ANY WARRANTY; without even the implied warranty of
14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 *  Lesser General Public License for more details.
16 *
17 *  You should have received a copy of the GNU Lesser General Public
18 *  License along with this program; if not, write to
19 *	Free Software Foundation, Inc.
20 *	51 Franklin St., Fifth Floor
21 *	Boston, MA 02110, USA.
22 */
23
24#include <dialog.h>
25#include <string.h>
26
27/*
28 * Convert a string to an argv[], returning a char** index (which must be
29 * freed by the caller).  The string is modified (replacing gaps between
30 * tokens with nulls).
31 */
32char **
33dlg_string_to_argv(char *blob)
34{
35    size_t n, k;
36    int pass;
37    size_t length = strlen(blob);
38    char **result = 0;
39
40#ifdef HAVE_DLG_TRACE
41    if (dialog_state.trace_output) {
42	DLG_TRACE(("# dlg_string_to_argv:\n"));
43	DLG_TRACE(("# given:\n"));
44	for (n = k = 0; n < length; ++n) {
45	    if (blob[n] == '\n') {
46		DLG_TRACE(("#%s\t%.*s\\n\n",
47			   k ? "+" : "",
48			   (int) (n - k), blob + k));
49		k = n + 1;
50	    }
51	}
52	if (n > k) {
53	    DLG_TRACE(("#%s\t%.*s\n",
54		       k ? "+" : "",
55		       (int) (n - k), blob + k));
56	}
57	DLG_TRACE(("# result:\n"));
58    }
59#endif
60    for (pass = 0; pass < 2; ++pass) {
61	bool inparm = FALSE;
62	bool quoted = FALSE;
63	bool escape = FALSE;
64	char *param = blob;
65	size_t count = 0;
66
67	for (n = 0; n < length; ++n) {
68	    if (escape) {
69		;
70	    } else if (quoted && blob[n] == '"') {
71		quoted = FALSE;
72	    } else if (blob[n] == '"') {
73		quoted = TRUE;
74		if (!inparm) {
75		    if (pass)
76			result[count] = param;
77		    ++count;
78		    inparm = TRUE;
79		}
80	    } else if (!quoted && isspace(UCH(blob[n]))) {
81		if (inparm) {
82		    if (pass) {
83			*param++ = '\0';
84		    }
85		    inparm = FALSE;
86		}
87	    } else {
88		if (blob[n] == '\\') {
89		    if (n + 1 == length) {
90			break;	/* The string is terminated by a backslash */
91		    } else if ((blob[n + 1] == '\\') ||
92			       (blob[n + 1] == '"') ||
93			       (!quoted && blob[n + 1] == '\n')) {
94			/* eat the backslash */
95			if (pass) {
96			    --length;
97			    for (k = n; k < length; ++k)
98				blob[k] = blob[k + 1];
99			    blob[length] = '\0';
100			} else {
101			    escape = TRUE;
102			    continue;
103			}
104		    }
105		}
106		if (!inparm) {
107		    if (pass)
108			result[count] = param;
109		    ++count;
110		    inparm = TRUE;
111		}
112		if (pass) {
113		    *param++ = blob[n];
114		}
115	    }
116	    escape = FALSE;
117	}
118
119	if (!pass) {
120	    if (count) {
121		result = dlg_calloc(char *, count + 1);
122		assert_ptr(result, "string_to_argv");
123	    } else {
124		break;		/* no tokens found */
125	    }
126	} else {
127	    *param = '\0';
128	}
129    }
130#ifdef HAVE_DLG_TRACE
131    if (result != 0) {
132	for (n = 0; result[n] != 0; ++n) {
133	    DLG_TRACE(("#\targv[%d] = %s\n", (int) n, result[n]));
134	}
135    }
136#endif
137    return result;
138}
139
140/*
141 * Count the entries in an argv list.
142 */
143int
144dlg_count_argv(char **argv)
145{
146    int result = 0;
147
148    if (argv != 0) {
149	while (argv[result] != 0)
150	    ++result;
151    }
152    return result;
153}
154
155int
156dlg_eat_argv(int *argcp, char ***argvp, int start, int count)
157{
158    int k;
159
160    *argcp -= count;
161    for (k = start; k <= *argcp; k++)
162	(*argvp)[k] = (*argvp)[k + count];
163    (*argvp)[*argcp] = 0;
164    return TRUE;
165}
166