2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include "alloc-util.h"
31 #include "extract-word.h"
34 #include "string-util.h"
37 int extract_first_word(const char **p
, char **ret
, const char *separators
, ExtractFlags flags
) {
38 _cleanup_free_
char *s
= NULL
;
39 size_t allocated
= 0, sz
= 0;
43 char quote
= 0; /* 0 or ' or " */
44 bool backslash
= false; /* whether we've just seen a backslash */
49 /* Bail early if called after last value or with no input */
55 separators
= WHITESPACE
;
57 /* Parses the first word of a string, and returns it in
58 * *ret. Removes all quotes in the process. When parsing fails
59 * (because of an uneven number of quotes or similar), leaves
60 * the pointer *p at the first invalid character. */
62 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
)
63 if (!GREEDY_REALLOC(s
, allocated
, sz
+1))
66 for (;; (*p
)++, c
= **p
) {
68 goto finish_force_terminate
;
69 else if (strchr(separators
, c
)) {
70 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
) {
72 goto finish_force_next
;
75 /* We found a non-blank character, so we will always
76 * want to return a string (even if it is empty),
77 * allocate it here. */
78 if (!GREEDY_REALLOC(s
, allocated
, sz
+1))
84 for (;; (*p
)++, c
= **p
) {
86 if (!GREEDY_REALLOC(s
, allocated
, sz
+7))
90 if ((flags
& EXTRACT_CUNESCAPE_RELAX
) &&
91 (!quote
|| flags
& EXTRACT_RELAX
)) {
92 /* If we find an unquoted trailing backslash and we're in
93 * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
96 * Unbalanced quotes will only be allowed in EXTRACT_RELAX
97 * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
100 goto finish_force_terminate
;
102 if (flags
& EXTRACT_RELAX
)
103 goto finish_force_terminate
;
107 if (flags
& EXTRACT_CUNESCAPE
) {
108 bool eight_bit
= false;
111 r
= cunescape_one(*p
, (size_t) -1, &u
, &eight_bit
);
113 if (flags
& EXTRACT_CUNESCAPE_RELAX
) {
124 sz
+= utf8_encode_unichar(s
+ sz
, u
);
131 } else if (quote
) { /* inside either single or double quotes */
132 for (;; (*p
)++, c
= **p
) {
134 if (flags
& EXTRACT_RELAX
)
135 goto finish_force_terminate
;
137 } else if (c
== quote
) { /* found the end quote */
140 } else if (c
== '\\' && !(flags
& EXTRACT_RETAIN_ESCAPE
)) {
144 if (!GREEDY_REALLOC(s
, allocated
, sz
+2))
152 for (;; (*p
)++, c
= **p
) {
154 goto finish_force_terminate
;
155 else if ((c
== '\'' || c
== '"') && (flags
& EXTRACT_QUOTES
)) {
158 } else if (c
== '\\' && !(flags
& EXTRACT_RETAIN_ESCAPE
)) {
161 } else if (strchr(separators
, c
)) {
162 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
) {
164 goto finish_force_next
;
166 /* Skip additional coalesced separators. */
167 for (;; (*p
)++, c
= **p
) {
169 goto finish_force_terminate
;
170 if (!strchr(separators
, c
))
176 if (!GREEDY_REALLOC(s
, allocated
, sz
+2))
185 finish_force_terminate
:
202 int extract_first_word_and_warn(
205 const char *separators
,
208 const char *filename
,
210 const char *rvalue
) {
212 /* Try to unquote it, if it fails, warn about it and try again
213 * but this time using EXTRACT_CUNESCAPE_RELAX to keep the
214 * backslashes verbatim in invalid escape sequences. */
220 r
= extract_first_word(p
, ret
, separators
, flags
);
224 if (r
== -EINVAL
&& !(flags
& EXTRACT_CUNESCAPE_RELAX
)) {
226 /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
228 r
= extract_first_word(p
, ret
, separators
, flags
|EXTRACT_CUNESCAPE_RELAX
);
230 /* It worked this time, hence it must have been an invalid escape sequence. */
231 log_syntax(unit
, LOG_WARNING
, filename
, line
, EINVAL
, "Ignoring unknown escape sequences: \"%s\"", *ret
);
235 /* If it's still EINVAL; then it must be unbalanced quoting, report this. */
237 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unbalanced quoting, ignoring: \"%s\"", rvalue
);
240 /* Can be any error, report it */
241 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to decode word \"%s\", ignoring: %m", rvalue
);
244 /* We pass ExtractFlags as unsigned int (to avoid undefined behaviour when passing
245 * an object that undergoes default argument promotion as an argument to va_start).
246 * Let's make sure that ExtractFlags fits into an unsigned int. */
247 assert_cc(sizeof(enum ExtractFlags
) <= sizeof(unsigned));
249 int extract_many_words(const char **p
, const char *separators
, unsigned flags
, ...) {
254 /* Parses a number of words from a string, stripping any
255 * quotes if necessary. */
259 /* Count how many words are expected */
262 if (!va_arg(ap
, char **))
271 /* Read all words into a temporary array */
273 for (c
= 0; c
< n
; c
++) {
275 r
= extract_first_word(p
, &l
[c
], separators
, flags
);
279 for (j
= 0; j
< c
; j
++)
289 /* If we managed to parse all words, return them in the passed
292 for (i
= 0; i
< n
; i
++) {
295 v
= va_arg(ap
, char **);