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