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 #if 0 /// UNNEEDED by elogind
203 int extract_first_word_and_warn(
206 const char *separators,
209 const char *filename,
211 const char *rvalue) {
213 /* Try to unquote it, if it fails, warn about it and try again
214 * but this time using EXTRACT_CUNESCAPE_RELAX to keep the
215 * backslashes verbatim in invalid escape sequences. */
221 r = extract_first_word(p, ret, separators, flags);
225 if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
227 /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
229 r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
231 /* It worked this time, hence it must have been an invalid escape sequence. */
232 log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret);
236 /* If it's still EINVAL; then it must be unbalanced quoting, report this. */
238 return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue);
241 /* Can be any error, report it */
242 return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue);
245 int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) {
250 /* Parses a number of words from a string, stripping any
251 * quotes if necessary. */
255 /* Count how many words are expected */
258 if (!va_arg(ap, char **))
267 /* Read all words into a temporary array */
269 for (c = 0; c < n; c++) {
271 r = extract_first_word(p, &l[c], separators, flags);
275 for (j = 0; j < c; j++)
285 /* If we managed to parse all words, return them in the passed
288 for (i = 0; i < n; i++) {
291 v = va_arg(ap, char **);