xref: /freebsd/contrib/pkgconf/libpkgconf/argvsplit.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
1 /*
2  * argvsplit.c
3  * argv_split() routine
4  *
5  * SPDX-License-Identifier: pkgconf
6  *
7  * Copyright (c) 2012, 2017 pkgconf authors (see AUTHORS).
8  *
9  * Permission to use, copy, modify, and/or distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * This software is provided 'as is' and without any warranty, express or
14  * implied.  In no event shall the authors be liable for any damages arising
15  * from the use of this software.
16  */
17 
18 #include <libpkgconf/stdinc.h>
19 #include <libpkgconf/libpkgconf.h>
20 
21 /*
22  * !doc
23  *
24  * libpkgconf `argvsplit` module
25  * =============================
26  *
27  * This is a lowlevel module which provides parsing of strings into argument vectors,
28  * similar to what a shell would do.
29  */
30 
31 /*
32  * !doc
33  *
34  * .. c:function:: void pkgconf_argv_free(char **argv)
35  *
36  *    Frees an argument vector.
37  *
38  *    :param char** argv: The argument vector to free.
39  *    :return: nothing
40  */
41 void
pkgconf_argv_free(char ** argv)42 pkgconf_argv_free(char **argv)
43 {
44 	if (argv == NULL)
45 		return;
46 
47 	free(argv[0]);
48 	free(argv);
49 }
50 
51 /*
52  * !doc
53  *
54  * .. c:function:: int pkgconf_argv_split(const char *src, int *argc, char ***argv)
55  *
56  *    Splits a string into an argument vector.
57  *
58  *    :param char*   src: The string to split.
59  *    :param int*    argc: A pointer to an integer to store the argument count.
60  *    :param char*** argv: A pointer to a pointer for an argument vector.
61  *    :return: 0 on success, -1 on error.
62  *    :rtype: int
63  */
64 int
pkgconf_argv_split(const char * src,int * argc,char *** argv)65 pkgconf_argv_split(const char *src, int *argc, char ***argv)
66 {
67 	char *buf = calloc(1, strlen(src) + 1);
68 	if (buf == NULL)
69 		return -1;
70 
71 	const char *src_iter;
72 	char *dst_iter;
73 	int argc_count = 0;
74 	int argv_size = 5;
75 	char quote = 0;
76 	bool escaped = false;
77 
78 	src_iter = src;
79 	dst_iter = buf;
80 
81 	*argv = calloc(argv_size, sizeof (void *));
82 	if (*argv == NULL)
83 	{
84 		free(buf);
85 		return -1;
86 	}
87 
88 	(*argv)[argc_count] = dst_iter;
89 
90 	while (*src_iter)
91 	{
92 		if (escaped)
93 		{
94 			/* POSIX: only \CHAR is special inside a double quote if CHAR is {$, `, ", \, newline}. */
95 			if (quote == '"')
96 			{
97 				if (!(*src_iter == '$' || *src_iter == '`' || *src_iter == '"' || *src_iter == '\\'))
98 					*dst_iter++ = '\\';
99 
100 				*dst_iter++ = *src_iter;
101 			}
102 			else
103 			{
104 				*dst_iter++ = *src_iter;
105 			}
106 
107 			escaped = false;
108 		}
109 		else if (quote)
110 		{
111 			if (*src_iter == quote)
112 				quote = 0;
113 			else if (*src_iter == '\\' && quote != '\'')
114 				escaped = true;
115 			else
116 				*dst_iter++ = *src_iter;
117 		}
118 		else if (isspace((unsigned char)*src_iter))
119 		{
120 			if ((*argv)[argc_count] != NULL)
121 			{
122 				argc_count++, dst_iter++;
123 
124 				if (argc_count == argv_size)
125 				{
126 					char **new_argv;
127 
128 					argv_size += 5;
129 					new_argv = pkgconf_reallocarray(*argv, argv_size, sizeof(void *));
130 					if (new_argv == NULL)
131 					{
132 						free(*argv);
133 						free(buf);
134 						*argv = NULL;
135 						return -1;
136 					}
137 
138 					*argv = new_argv;
139 				}
140 
141 				(*argv)[argc_count] = dst_iter;
142 			}
143 		}
144 		else switch(*src_iter)
145 		{
146 			case '\\':
147 				escaped = true;
148 				break;
149 
150 			case '\"':
151 			case '\'':
152 				quote = *src_iter;
153 				break;
154 
155 			default:
156 				*dst_iter++ = *src_iter;
157 				break;
158 		}
159 
160 		src_iter++;
161 	}
162 
163 	if (escaped || quote)
164 	{
165 		free(*argv);
166 		free(buf);
167 		*argv = NULL;
168 		return -1;
169 	}
170 
171 	if (strlen((*argv)[argc_count]))
172 	{
173 		argc_count++;
174 	}
175 
176 	*argc = argc_count;
177 	return 0;
178 }
179