1 /* SPDX-License-Identifier: LGPL-2.1+ */
11 #include "alloc-util.h"
13 #include "extract-word.h"
16 #include "string-util.h"
19 int extract_first_word(const char **p
, char **ret
, const char *separators
, ExtractFlags flags
) {
20 _cleanup_free_
char *s
= NULL
;
21 size_t allocated
= 0, sz
= 0;
25 char quote
= 0; /* 0 or ' or " */
26 bool backslash
= false; /* whether we've just seen a backslash */
31 /* Bail early if called after last value or with no input */
37 separators
= WHITESPACE
;
39 /* Parses the first word of a string, and returns it in
40 * *ret. Removes all quotes in the process. When parsing fails
41 * (because of an uneven number of quotes or similar), leaves
42 * the pointer *p at the first invalid character. */
44 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
)
45 if (!GREEDY_REALLOC(s
, allocated
, sz
+1))
48 for (;; (*p
)++, c
= **p
) {
50 goto finish_force_terminate
;
51 else if (strchr(separators
, c
)) {
52 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
) {
54 goto finish_force_next
;
57 /* We found a non-blank character, so we will always
58 * want to return a string (even if it is empty),
59 * allocate it here. */
60 if (!GREEDY_REALLOC(s
, allocated
, sz
+1))
66 for (;; (*p
)++, c
= **p
) {
68 if (!GREEDY_REALLOC(s
, allocated
, sz
+7))
72 if ((flags
& EXTRACT_CUNESCAPE_RELAX
) &&
73 (!quote
|| flags
& EXTRACT_RELAX
)) {
74 /* If we find an unquoted trailing backslash and we're in
75 * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
78 * Unbalanced quotes will only be allowed in EXTRACT_RELAX
79 * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
82 goto finish_force_terminate
;
84 if (flags
& EXTRACT_RELAX
)
85 goto finish_force_terminate
;
89 if (flags
& EXTRACT_CUNESCAPE
) {
90 bool eight_bit
= false;
93 r
= cunescape_one(*p
, (size_t) -1, &u
, &eight_bit
);
95 if (flags
& EXTRACT_CUNESCAPE_RELAX
) {
106 sz
+= utf8_encode_unichar(s
+ sz
, u
);
113 } else if (quote
) { /* inside either single or double quotes */
114 for (;; (*p
)++, c
= **p
) {
116 if (flags
& EXTRACT_RELAX
)
117 goto finish_force_terminate
;
119 } else if (c
== quote
) { /* found the end quote */
122 } else if (c
== '\\' && !(flags
& EXTRACT_RETAIN_ESCAPE
)) {
126 if (!GREEDY_REALLOC(s
, allocated
, sz
+2))
134 for (;; (*p
)++, c
= **p
) {
136 goto finish_force_terminate
;
137 else if (IN_SET(c
, '\'', '"') && (flags
& EXTRACT_UNQUOTE
)) {
140 } else if (c
== '\\' && !(flags
& EXTRACT_RETAIN_ESCAPE
)) {
143 } else if (strchr(separators
, c
)) {
144 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
) {
146 goto finish_force_next
;
148 /* Skip additional coalesced separators. */
149 for (;; (*p
)++, c
= **p
) {
151 goto finish_force_terminate
;
152 if (!strchr(separators
, c
))
158 if (!GREEDY_REALLOC(s
, allocated
, sz
+2))
167 finish_force_terminate
:
183 int extract_first_word_and_warn(
186 const char *separators
,
189 const char *filename
,
191 const char *rvalue
) {
193 /* Try to unquote it, if it fails, warn about it and try again
194 * but this time using EXTRACT_CUNESCAPE_RELAX to keep the
195 * backslashes verbatim in invalid escape sequences. */
201 r
= extract_first_word(p
, ret
, separators
, flags
);
205 if (r
== -EINVAL
&& !(flags
& EXTRACT_CUNESCAPE_RELAX
)) {
207 /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
209 r
= extract_first_word(p
, ret
, separators
, flags
|EXTRACT_CUNESCAPE_RELAX
);
211 /* It worked this time, hence it must have been an invalid escape sequence. */
212 log_syntax(unit
, LOG_WARNING
, filename
, line
, EINVAL
, "Ignoring unknown escape sequences: \"%s\"", *ret
);
216 /* If it's still EINVAL; then it must be unbalanced quoting, report this. */
218 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unbalanced quoting, ignoring: \"%s\"", rvalue
);
221 /* Can be any error, report it */
222 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to decode word \"%s\", ignoring: %m", rvalue
);
225 /* We pass ExtractFlags as unsigned int (to avoid undefined behaviour when passing
226 * an object that undergoes default argument promotion as an argument to va_start).
227 * Let's make sure that ExtractFlags fits into an unsigned int. */
228 assert_cc(sizeof(enum ExtractFlags
) <= sizeof(unsigned));
230 int extract_many_words(const char **p
, const char *separators
, unsigned flags
, ...) {
235 /* Parses a number of words from a string, stripping any
236 * quotes if necessary. */
240 /* Count how many words are expected */
243 if (!va_arg(ap
, char **))
252 /* Read all words into a temporary array */
254 for (c
= 0; c
< n
; c
++) {
256 r
= extract_first_word(p
, &l
[c
], separators
, flags
);
260 for (j
= 0; j
< c
; j
++)
270 /* If we managed to parse all words, return them in the passed
273 for (i
= 0; i
< n
; i
++) {
276 v
= va_arg(ap
, char **);