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