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