1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2010 Lennart Poettering
15 #include "alloc-util.h"
17 #include "extract-word.h"
20 #include "string-util.h"
23 int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
24 _cleanup_free_ char *s = NULL;
25 size_t allocated = 0, sz = 0;
29 char quote = 0; /* 0 or ' or " */
30 bool backslash = false; /* whether we've just seen a backslash */
35 /* Bail early if called after last value or with no input */
41 separators = WHITESPACE;
43 /* Parses the first word of a string, and returns it in
44 * *ret. Removes all quotes in the process. When parsing fails
45 * (because of an uneven number of quotes or similar), leaves
46 * the pointer *p at the first invalid character. */
48 if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
49 if (!GREEDY_REALLOC(s, allocated, sz+1))
52 for (;; (*p)++, c = **p) {
54 goto finish_force_terminate;
55 else if (strchr(separators, c)) {
56 if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
58 goto finish_force_next;
61 /* We found a non-blank character, so we will always
62 * want to return a string (even if it is empty),
63 * allocate it here. */
64 if (!GREEDY_REALLOC(s, allocated, sz+1))
70 for (;; (*p)++, c = **p) {
72 if (!GREEDY_REALLOC(s, allocated, sz+7))
76 if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
77 (!quote || flags & EXTRACT_RELAX)) {
78 /* If we find an unquoted trailing backslash and we're in
79 * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
82 * Unbalanced quotes will only be allowed in EXTRACT_RELAX
83 * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
86 goto finish_force_terminate;
88 if (flags & EXTRACT_RELAX)
89 goto finish_force_terminate;
93 if (flags & EXTRACT_CUNESCAPE) {
94 bool eight_bit = false;
97 r = cunescape_one(*p, (size_t) -1, &u, &eight_bit);
99 if (flags & EXTRACT_CUNESCAPE_RELAX) {
110 sz += utf8_encode_unichar(s + sz, u);
117 } else if (quote) { /* inside either single or double quotes */
118 for (;; (*p)++, c = **p) {
120 if (flags & EXTRACT_RELAX)
121 goto finish_force_terminate;
123 } else if (c == quote) { /* found the end quote */
126 } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
130 if (!GREEDY_REALLOC(s, allocated, sz+2))
138 for (;; (*p)++, c = **p) {
140 goto finish_force_terminate;
141 else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_QUOTES)) {
144 } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
147 } else if (strchr(separators, c)) {
148 if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
150 goto finish_force_next;
152 /* Skip additional coalesced separators. */
153 for (;; (*p)++, c = **p) {
155 goto finish_force_terminate;
156 if (!strchr(separators, c))
162 if (!GREEDY_REALLOC(s, allocated, sz+2))
171 finish_force_terminate:
187 #if 0 /// UNNEEDED by elogind
188 int extract_first_word_and_warn(
191 const char *separators,
194 const char *filename,
196 const char *rvalue) {
198 /* Try to unquote it, if it fails, warn about it and try again
199 * but this time using EXTRACT_CUNESCAPE_RELAX to keep the
200 * backslashes verbatim in invalid escape sequences. */
206 r = extract_first_word(p, ret, separators, flags);
210 if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
212 /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
214 r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
216 /* It worked this time, hence it must have been an invalid escape sequence. */
217 log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret);
221 /* If it's still EINVAL; then it must be unbalanced quoting, report this. */
223 return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue);
226 /* Can be any error, report it */
227 return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue);
230 /* We pass ExtractFlags as unsigned int (to avoid undefined behaviour when passing
231 * an object that undergoes default argument promotion as an argument to va_start).
232 * Let's make sure that ExtractFlags fits into an unsigned int. */
233 assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned));
235 int extract_many_words(const char **p, const char *separators, unsigned flags, ...) {
240 /* Parses a number of words from a string, stripping any
241 * quotes if necessary. */
245 /* Count how many words are expected */
248 if (!va_arg(ap, char **))
257 /* Read all words into a temporary array */
259 for (c = 0; c < n; c++) {
261 r = extract_first_word(p, &l[c], separators, flags);
265 for (j = 0; j < c; j++)
275 /* If we managed to parse all words, return them in the passed
278 for (i = 0; i < n; i++) {
281 v = va_arg(ap, char **);