1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
17 #include "alloc-util.h"
19 #include "extract-word.h"
22 #include "string-util.h"
25 int extract_first_word(const char **p
, char **ret
, const char *separators
, ExtractFlags flags
) {
26 _cleanup_free_
char *s
= NULL
;
27 size_t allocated
= 0, sz
= 0;
31 char quote
= 0; /* 0 or ' or " */
32 bool backslash
= false; /* whether we've just seen a backslash */
37 /* Bail early if called after last value or with no input */
43 separators
= WHITESPACE
;
45 /* Parses the first word of a string, and returns it in
46 * *ret. Removes all quotes in the process. When parsing fails
47 * (because of an uneven number of quotes or similar), leaves
48 * the pointer *p at the first invalid character. */
50 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
)
51 if (!GREEDY_REALLOC(s
, allocated
, sz
+1))
54 for (;; (*p
)++, c
= **p
) {
56 goto finish_force_terminate
;
57 else if (strchr(separators
, c
)) {
58 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
) {
60 goto finish_force_next
;
63 /* We found a non-blank character, so we will always
64 * want to return a string (even if it is empty),
65 * allocate it here. */
66 if (!GREEDY_REALLOC(s
, allocated
, sz
+1))
72 for (;; (*p
)++, c
= **p
) {
74 if (!GREEDY_REALLOC(s
, allocated
, sz
+7))
78 if ((flags
& EXTRACT_CUNESCAPE_RELAX
) &&
79 (!quote
|| flags
& EXTRACT_RELAX
)) {
80 /* If we find an unquoted trailing backslash and we're in
81 * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
84 * Unbalanced quotes will only be allowed in EXTRACT_RELAX
85 * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
88 goto finish_force_terminate
;
90 if (flags
& EXTRACT_RELAX
)
91 goto finish_force_terminate
;
95 if (flags
& EXTRACT_CUNESCAPE
) {
96 bool eight_bit
= false;
99 r
= cunescape_one(*p
, (size_t) -1, &u
, &eight_bit
);
101 if (flags
& EXTRACT_CUNESCAPE_RELAX
) {
112 sz
+= utf8_encode_unichar(s
+ sz
, u
);
119 } else if (quote
) { /* inside either single or double quotes */
120 for (;; (*p
)++, c
= **p
) {
122 if (flags
& EXTRACT_RELAX
)
123 goto finish_force_terminate
;
125 } else if (c
== quote
) { /* found the end quote */
128 } else if (c
== '\\' && !(flags
& EXTRACT_RETAIN_ESCAPE
)) {
132 if (!GREEDY_REALLOC(s
, allocated
, sz
+2))
140 for (;; (*p
)++, c
= **p
) {
142 goto finish_force_terminate
;
143 else if (IN_SET(c
, '\'', '"') && (flags
& EXTRACT_QUOTES
)) {
146 } else if (c
== '\\' && !(flags
& EXTRACT_RETAIN_ESCAPE
)) {
149 } else if (strchr(separators
, c
)) {
150 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
) {
152 goto finish_force_next
;
154 /* Skip additional coalesced separators. */
155 for (;; (*p
)++, c
= **p
) {
157 goto finish_force_terminate
;
158 if (!strchr(separators
, c
))
164 if (!GREEDY_REALLOC(s
, allocated
, sz
+2))
173 finish_force_terminate
:
189 int extract_first_word_and_warn(
192 const char *separators
,
195 const char *filename
,
197 const char *rvalue
) {
199 /* Try to unquote it, if it fails, warn about it and try again
200 * but this time using EXTRACT_CUNESCAPE_RELAX to keep the
201 * backslashes verbatim in invalid escape sequences. */
207 r
= extract_first_word(p
, ret
, separators
, flags
);
211 if (r
== -EINVAL
&& !(flags
& EXTRACT_CUNESCAPE_RELAX
)) {
213 /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
215 r
= extract_first_word(p
, ret
, separators
, flags
|EXTRACT_CUNESCAPE_RELAX
);
217 /* It worked this time, hence it must have been an invalid escape sequence. */
218 log_syntax(unit
, LOG_WARNING
, filename
, line
, EINVAL
, "Ignoring unknown escape sequences: \"%s\"", *ret
);
222 /* If it's still EINVAL; then it must be unbalanced quoting, report this. */
224 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unbalanced quoting, ignoring: \"%s\"", rvalue
);
227 /* Can be any error, report it */
228 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to decode word \"%s\", ignoring: %m", rvalue
);
231 /* We pass ExtractFlags as unsigned int (to avoid undefined behaviour when passing
232 * an object that undergoes default argument promotion as an argument to va_start).
233 * Let's make sure that ExtractFlags fits into an unsigned int. */
234 assert_cc(sizeof(enum ExtractFlags
) <= sizeof(unsigned));
236 int extract_many_words(const char **p
, const char *separators
, unsigned flags
, ...) {
241 /* Parses a number of words from a string, stripping any
242 * quotes if necessary. */
246 /* Count how many words are expected */
249 if (!va_arg(ap
, char **))
258 /* Read all words into a temporary array */
260 for (c
= 0; c
< n
; c
++) {
262 r
= extract_first_word(p
, &l
[c
], separators
, flags
);
266 for (j
= 0; j
< c
; j
++)
276 /* If we managed to parse all words, return them in the passed
279 for (i
= 0; i
< n
; i
++) {
282 v
= va_arg(ap
, char **);