1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
31 #include "alloc-util.h"
33 #include "extract-word.h"
36 #include "string-util.h"
39 int extract_first_word(const char **p
, char **ret
, const char *separators
, ExtractFlags flags
) {
40 _cleanup_free_
char *s
= NULL
;
41 size_t allocated
= 0, sz
= 0;
45 char quote
= 0; /* 0 or ' or " */
46 bool backslash
= false; /* whether we've just seen a backslash */
51 /* Bail early if called after last value or with no input */
53 goto finish_force_terminate
;
57 separators
= WHITESPACE
;
59 /* Parses the first word of a string, and returns it in
60 * *ret. Removes all quotes in the process. When parsing fails
61 * (because of an uneven number of quotes or similar), leaves
62 * the pointer *p at the first invalid character. */
64 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
)
65 if (!GREEDY_REALLOC(s
, allocated
, sz
+1))
68 for (;; (*p
) ++, c
= **p
) {
70 goto finish_force_terminate
;
71 else if (strchr(separators
, c
)) {
72 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
) {
74 goto finish_force_next
;
77 /* We found a non-blank character, so we will always
78 * want to return a string (even if it is empty),
79 * allocate it here. */
80 if (!GREEDY_REALLOC(s
, allocated
, sz
+1))
86 for (;; (*p
) ++, c
= **p
) {
88 if (!GREEDY_REALLOC(s
, allocated
, sz
+7))
92 if ((flags
& EXTRACT_CUNESCAPE_RELAX
) &&
93 (!quote
|| flags
& EXTRACT_RELAX
)) {
94 /* If we find an unquoted trailing backslash and we're in
95 * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
98 * Unbalanced quotes will only be allowed in EXTRACT_RELAX
99 * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
102 goto finish_force_terminate
;
104 if (flags
& EXTRACT_RELAX
)
105 goto finish_force_terminate
;
109 if (flags
& EXTRACT_CUNESCAPE
) {
110 bool eight_bit
= false;
113 r
= cunescape_one(*p
, (size_t) -1, &u
, &eight_bit
);
115 if (flags
& EXTRACT_CUNESCAPE_RELAX
) {
126 sz
+= utf8_encode_unichar(s
+ sz
, u
);
133 } else if (quote
) { /* inside either single or double quotes */
134 for (;; (*p
) ++, c
= **p
) {
136 if (flags
& EXTRACT_RELAX
)
137 goto finish_force_terminate
;
139 } else if (c
== quote
) { /* found the end quote */
142 } else if (c
== '\\' && !(flags
& EXTRACT_RETAIN_ESCAPE
)) {
146 if (!GREEDY_REALLOC(s
, allocated
, sz
+2))
154 for (;; (*p
) ++, c
= **p
) {
156 goto finish_force_terminate
;
157 else if ((c
== '\'' || c
== '"') && (flags
& EXTRACT_QUOTES
)) {
160 } else if (c
== '\\' && !(flags
& EXTRACT_RETAIN_ESCAPE
)) {
163 } else if (strchr(separators
, c
)) {
164 if (flags
& EXTRACT_DONT_COALESCE_SEPARATORS
) {
166 goto finish_force_next
;
168 /* Skip additional coalesced separators. */
169 for (;; (*p
) ++, c
= **p
) {
171 goto finish_force_terminate
;
172 if (!strchr(separators
, c
))
178 if (!GREEDY_REALLOC(s
, allocated
, sz
+2))
187 finish_force_terminate
:
204 int extract_first_word_and_warn(
207 const char *separators
,
210 const char *filename
,
212 const char *rvalue
) {
214 /* Try to unquote it, if it fails, warn about it and try again
215 * but this time using EXTRACT_CUNESCAPE_RELAX to keep the
216 * backslashes verbatim in invalid escape sequences. */
222 r
= extract_first_word(p
, ret
, separators
, flags
);
226 if (r
== -EINVAL
&& !(flags
& EXTRACT_CUNESCAPE_RELAX
)) {
228 /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
230 r
= extract_first_word(p
, ret
, separators
, flags
|EXTRACT_CUNESCAPE_RELAX
);
232 /* It worked this time, hence it must have been an invalid escape sequence we could correct. */
233 log_syntax(unit
, LOG_WARNING
, filename
, line
, EINVAL
, "Invalid escape sequences in line, correcting: \"%s\"", rvalue
);
237 /* If it's still EINVAL; then it must be unbalanced quoting, report this. */
239 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unbalanced quoting, ignoring: \"%s\"", rvalue
);
242 /* Can be any error, report it */
243 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to decode word \"%s\", ignoring: %m", rvalue
);
246 int extract_many_words(const char **p
, const char *separators
, ExtractFlags flags
, ...) {
251 /* Parses a number of words from a string, stripping any
252 * quotes if necessary. */
256 /* Count how many words are expected */
259 if (!va_arg(ap
, char **))
268 /* Read all words into a temporary array */
270 for (c
= 0; c
< n
; c
++) {
272 r
= extract_first_word(p
, &l
[c
], separators
, flags
);
276 for (j
= 0; j
< c
; j
++)
286 /* If we managed to parse all words, return them in the passed
289 for (i
= 0; i
< n
; i
++) {
292 v
= va_arg(ap
, char **);