/src/freeradius-server/src/lib/util/token.c
Line | Count | Source |
1 | | /* |
2 | | * This library is free software; you can redistribute it and/or |
3 | | * modify it under the terms of the GNU Lesser General Public |
4 | | * License as published by the Free Software Foundation; either |
5 | | * version 2.1 of the License, or (at your option) any later version. |
6 | | * |
7 | | * This library is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
10 | | * Lesser General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU Lesser General Public |
13 | | * License along with this library; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** Tokenisation code and constants |
18 | | * |
19 | | * This is mostly for the attribute filter and user files. |
20 | | * |
21 | | * @file src/lib/util/token.c |
22 | | * |
23 | | * @copyright 2001,2006 The FreeRADIUS server project |
24 | | */ |
25 | | RCSID("$Id: 9fb1b39f02230880e8eee89f9f800e0e0407a391 $") |
26 | | |
27 | | #include <stdio.h> |
28 | | |
29 | | #include <freeradius-devel/util/skip.h> |
30 | | #include <freeradius-devel/util/token.h> |
31 | | |
32 | | |
33 | | fr_table_num_ordered_t const fr_tokens_table[] = { |
34 | | { L("=~"), T_OP_REG_EQ }, /* order is important! */ |
35 | | { L("!~"), T_OP_REG_NE }, |
36 | | { L("{"), T_LCBRACE }, |
37 | | { L("}"), T_RCBRACE }, |
38 | | { L("("), T_LBRACE }, |
39 | | { L(")"), T_RBRACE }, |
40 | | { L(","), T_COMMA }, |
41 | | { L("++"), T_OP_INCRM }, |
42 | | { L("+="), T_OP_ADD_EQ }, |
43 | | { L("-="), T_OP_SUB_EQ }, |
44 | | { L("*="), T_OP_MUL_EQ }, |
45 | | { L("/="), T_OP_DIV_EQ }, |
46 | | { L(":="), T_OP_SET }, |
47 | | { L("=*"), T_OP_CMP_TRUE }, |
48 | | { L("!*"), T_OP_CMP_FALSE }, |
49 | | { L("=="), T_OP_CMP_EQ }, |
50 | | { L("==="), T_OP_CMP_EQ_TYPE }, |
51 | | { L("^="), T_OP_PREPEND }, |
52 | | { L("|="), T_OP_OR_EQ }, |
53 | | { L("&="), T_OP_AND_EQ }, |
54 | | { L("="), T_OP_EQ }, |
55 | | { L("!="), T_OP_NE }, |
56 | | { L("!=="), T_OP_CMP_NE_TYPE }, |
57 | | { L(">>="), T_OP_RSHIFT_EQ }, |
58 | | { L(">="), T_OP_GE }, |
59 | | { L(">"), T_OP_GT }, |
60 | | { L("<<="), T_OP_LSHIFT_EQ }, |
61 | | { L("<="), T_OP_LE }, |
62 | | { L("<"), T_OP_LT }, |
63 | | { L("#"), T_HASH }, |
64 | | { L(";"), T_SEMICOLON } |
65 | | }; |
66 | | size_t fr_tokens_table_len = NUM_ELEMENTS(fr_tokens_table); |
67 | | |
68 | | fr_table_num_sorted_t const fr_token_quotes_table[] = { |
69 | | { L(""), T_BARE_WORD }, |
70 | | { L("'"), T_SINGLE_QUOTED_STRING }, |
71 | | { L("/"), T_SOLIDUS_QUOTED_STRING }, |
72 | | { L("\""), T_DOUBLE_QUOTED_STRING }, |
73 | | { L("`"), T_BACK_QUOTED_STRING } |
74 | | }; |
75 | | size_t fr_token_quotes_table_len = NUM_ELEMENTS(fr_token_quotes_table); |
76 | | |
77 | | /** Map each `fr_token_t` to the C identifier of its enum constant. |
78 | | * |
79 | | * Complement of `fr_tokens[]` (the operator-character / human-display |
80 | | * form like ":=" / "<BARE-WORD>") - this one returns the source-identical |
81 | | * `T_OP_SET` / `T_BARE_WORD` form so tooling (radconf2json, etc.) can |
82 | | * round-trip enum names through a JSON intermediate. |
83 | | */ |
84 | | static fr_table_num_indexed_t const fr_token_to_enum_str_table[] = { |
85 | | FR_TABLE_INDEXED_ENTRY(T_BARE_WORD), |
86 | | FR_TABLE_INDEXED_ENTRY(T_DOUBLE_QUOTED_STRING), |
87 | | FR_TABLE_INDEXED_ENTRY(T_SINGLE_QUOTED_STRING), |
88 | | FR_TABLE_INDEXED_ENTRY(T_BACK_QUOTED_STRING), |
89 | | FR_TABLE_INDEXED_ENTRY(T_SOLIDUS_QUOTED_STRING), |
90 | | FR_TABLE_INDEXED_ENTRY(T_OP_EQ), |
91 | | FR_TABLE_INDEXED_ENTRY(T_OP_NE), |
92 | | FR_TABLE_INDEXED_ENTRY(T_OP_GE), |
93 | | FR_TABLE_INDEXED_ENTRY(T_OP_GT), |
94 | | FR_TABLE_INDEXED_ENTRY(T_OP_LE), |
95 | | FR_TABLE_INDEXED_ENTRY(T_OP_LT), |
96 | | FR_TABLE_INDEXED_ENTRY(T_OP_CMP_EQ), |
97 | | FR_TABLE_INDEXED_ENTRY(T_OP_CMP_EQ_TYPE), |
98 | | FR_TABLE_INDEXED_ENTRY(T_OP_CMP_NE_TYPE), |
99 | | FR_TABLE_INDEXED_ENTRY(T_OP_ADD_EQ), |
100 | | FR_TABLE_INDEXED_ENTRY(T_OP_SUB_EQ), |
101 | | FR_TABLE_INDEXED_ENTRY(T_OP_MUL_EQ), |
102 | | FR_TABLE_INDEXED_ENTRY(T_OP_DIV_EQ), |
103 | | FR_TABLE_INDEXED_ENTRY(T_OP_AND_EQ), |
104 | | FR_TABLE_INDEXED_ENTRY(T_OP_OR_EQ), |
105 | | FR_TABLE_INDEXED_ENTRY(T_OP_RSHIFT_EQ), |
106 | | FR_TABLE_INDEXED_ENTRY(T_OP_LSHIFT_EQ), |
107 | | FR_TABLE_INDEXED_ENTRY(T_OP_SET), |
108 | | FR_TABLE_INDEXED_ENTRY(T_OP_PREPEND), |
109 | | FR_TABLE_INDEXED_ENTRY(T_OP_REG_EQ), |
110 | | FR_TABLE_INDEXED_ENTRY(T_OP_REG_NE), |
111 | | FR_TABLE_INDEXED_ENTRY(T_OP_CMP_TRUE), |
112 | | FR_TABLE_INDEXED_ENTRY(T_OP_CMP_FALSE), |
113 | | FR_TABLE_INDEXED_ENTRY(T_OP_INCRM), |
114 | | }; |
115 | | static size_t fr_token_to_enum_str_table_len = NUM_ELEMENTS(fr_token_to_enum_str_table); |
116 | | |
117 | | char const *fr_token_to_enum_str(fr_token_t t) |
118 | 0 | { |
119 | 0 | return fr_table_str_by_value(fr_token_to_enum_str_table, t, "T_INVALID"); |
120 | 0 | } |
121 | | |
122 | | /** Quote-token names that radconf2json's JSON output uses. |
123 | | * |
124 | | * Only the five quote-typed tokens, kept sorted by name so the |
125 | | * `fr_table_value_by_str` binary search can find them. Used by |
126 | | * radjson2conf when rebuilding a CF tree from JSON. |
127 | | */ |
128 | | static fr_table_num_sorted_t const fr_token_from_quote_enum_str_table[] = { |
129 | | { L("T_BACK_QUOTED_STRING"), T_BACK_QUOTED_STRING }, |
130 | | { L("T_BARE_WORD"), T_BARE_WORD }, |
131 | | { L("T_DOUBLE_QUOTED_STRING"), T_DOUBLE_QUOTED_STRING }, |
132 | | { L("T_SINGLE_QUOTED_STRING"), T_SINGLE_QUOTED_STRING }, |
133 | | { L("T_SOLIDUS_QUOTED_STRING"), T_SOLIDUS_QUOTED_STRING }, |
134 | | }; |
135 | | static size_t fr_token_from_quote_enum_str_table_len = NUM_ELEMENTS(fr_token_from_quote_enum_str_table); |
136 | | |
137 | | fr_token_t fr_token_from_quote_enum_str(char const *s, fr_token_t dflt) |
138 | 0 | { |
139 | 0 | if (!s) return dflt; |
140 | 0 | return fr_table_value_by_str(fr_token_from_quote_enum_str_table, s, dflt); |
141 | 0 | } |
142 | | |
143 | | /* |
144 | | * String versions for all of the tokens. |
145 | | */ |
146 | | char const *fr_tokens[T_TOKEN_LAST] = { |
147 | | [T_INVALID] = "?", |
148 | | [T_EOL] = "EOL", |
149 | | |
150 | | [T_LCBRACE] = "{", |
151 | | [T_RCBRACE] = "}", |
152 | | [T_LBRACE] = "(", |
153 | | [T_RBRACE] = ")", |
154 | | [T_COMMA] = ",", |
155 | | [T_SEMICOLON] = ";", |
156 | | |
157 | | [T_ADD] = "+", |
158 | | [T_SUB] = "-", |
159 | | [T_MUL] = "*", |
160 | | [T_DIV] = "/", |
161 | | [T_AND] = "&", |
162 | | [T_OR] = "|", |
163 | | [T_NOT] = "!", |
164 | | [T_XOR] = "^", |
165 | | [T_COMPLEMENT] = "~", |
166 | | [T_MOD] = "%", |
167 | | |
168 | | [T_RSHIFT] = ">>", |
169 | | [T_LSHIFT] = "<<", |
170 | | |
171 | | [T_LAND] = "&&", |
172 | | [T_LOR] = "||", |
173 | | |
174 | | [T_OP_INCRM] = "++", |
175 | | |
176 | | [T_OP_ADD_EQ] = "+=", |
177 | | [T_OP_SUB_EQ] = "-=", |
178 | | [T_OP_SET] = ":=", |
179 | | [T_OP_EQ] = "=", |
180 | | [T_OP_OR_EQ] = "|=", |
181 | | [T_OP_AND_EQ] = "&=", |
182 | | |
183 | | [T_OP_RSHIFT_EQ] = ">>=", |
184 | | [T_OP_LSHIFT_EQ] = "<<=", |
185 | | |
186 | | [T_OP_NE] = "!=", |
187 | | [T_OP_GE] = ">=", |
188 | | [T_OP_GT] = ">", |
189 | | [T_OP_LE] = "<=", |
190 | | [T_OP_LT] = "<", |
191 | | [T_OP_REG_EQ] = "=~", |
192 | | [T_OP_REG_NE] = "!~", |
193 | | |
194 | | [T_OP_CMP_TRUE] = "=*", |
195 | | [T_OP_CMP_FALSE] = "!*", |
196 | | |
197 | | [T_OP_CMP_EQ] = "==", |
198 | | |
199 | | [T_OP_CMP_EQ_TYPE] = "===", |
200 | | [T_OP_CMP_NE_TYPE] = "!==", |
201 | | |
202 | | [T_OP_PREPEND] = "^=", |
203 | | |
204 | | [T_HASH] = "#", |
205 | | [T_BARE_WORD] = "<BARE-WORD>", |
206 | | [T_DOUBLE_QUOTED_STRING] = "<\"STRING\">", |
207 | | [T_SINGLE_QUOTED_STRING] = "<'STRING'>", |
208 | | [T_BACK_QUOTED_STRING] = "<`STRING`>", |
209 | | [T_SOLIDUS_QUOTED_STRING] = "</STRING/>", |
210 | | }; |
211 | | |
212 | | |
213 | | /* |
214 | | * This is fine. Don't complain. |
215 | | */ |
216 | | #ifdef __clang__ |
217 | | #pragma clang diagnostic ignored "-Wgnu-designator" |
218 | | #endif |
219 | | |
220 | | /** Convert tokens back to a quoting character |
221 | | * |
222 | | * Non-string types convert to '?' to screw ups can be identified easily |
223 | | */ |
224 | | const char fr_token_quote[T_TOKEN_LAST] = { |
225 | | [ 0 ... T_HASH ] = '?', /* GCC extension for range initialization, also allowed by clang */ |
226 | | |
227 | | [T_BARE_WORD] = '\0', |
228 | | [T_DOUBLE_QUOTED_STRING] = '"', |
229 | | [T_SINGLE_QUOTED_STRING] = '\'', |
230 | | [T_BACK_QUOTED_STRING] = '`', |
231 | | [T_SOLIDUS_QUOTED_STRING] = '/', |
232 | | }; |
233 | | |
234 | | #define T(_x) [T_OP_ ## _x] = true |
235 | | |
236 | | const bool fr_assignment_op[T_TOKEN_LAST] = { |
237 | | T(INCRM), /* only used by LDAP :( */ |
238 | | |
239 | | T(ADD_EQ), |
240 | | T(SUB_EQ), |
241 | | T(MUL_EQ), |
242 | | T(DIV_EQ), |
243 | | T(AND_EQ), |
244 | | T(OR_EQ), |
245 | | T(RSHIFT_EQ), |
246 | | T(LSHIFT_EQ), |
247 | | |
248 | | T(SET), |
249 | | T(EQ), |
250 | | T(PREPEND), |
251 | | }; |
252 | | |
253 | | const bool fr_list_assignment_op[T_TOKEN_LAST] = { |
254 | | T(ADD_EQ), /* append */ |
255 | | T(SUB_EQ), /* remove */ |
256 | | T(AND_EQ), /* intersection */ |
257 | | T(OR_EQ), /* union */ |
258 | | T(LE), /* merge RHS */ |
259 | | T(GE), /* merge LHS */ |
260 | | |
261 | | T(SET), |
262 | | T(EQ), |
263 | | T(PREPEND), /* prepend */ |
264 | | }; |
265 | | |
266 | | const bool fr_comparison_op[T_TOKEN_LAST] = { |
267 | | T(NE), |
268 | | T(GE), |
269 | | T(GT), |
270 | | T(LE), |
271 | | T(LT), |
272 | | T(REG_EQ), |
273 | | T(REG_NE), |
274 | | T(CMP_TRUE), |
275 | | T(CMP_FALSE), |
276 | | T(CMP_EQ), |
277 | | T(CMP_EQ_TYPE), |
278 | | T(CMP_NE_TYPE), |
279 | | }; |
280 | | |
281 | | #undef T |
282 | | #define T(_x) [T_ ## _x] = true |
283 | | |
284 | | const bool fr_binary_op[T_TOKEN_LAST] = { |
285 | | T(ADD), |
286 | | T(SUB), |
287 | | T(MUL), |
288 | | T(DIV), |
289 | | T(AND), |
290 | | T(OR), |
291 | | T(MOD), |
292 | | T(RSHIFT), |
293 | | T(LSHIFT), |
294 | | }; |
295 | | |
296 | | |
297 | | #undef T |
298 | | #define T(_x) [T_## _x] = true |
299 | | const bool fr_str_tok[T_TOKEN_LAST] = { |
300 | | T(BARE_WORD), |
301 | | T(DOUBLE_QUOTED_STRING), |
302 | | T(SINGLE_QUOTED_STRING), |
303 | | T(BACK_QUOTED_STRING), |
304 | | }; |
305 | | |
306 | | /* |
307 | | * This works only as long as special tokens |
308 | | * are max. 2 characters, but it's fast. |
309 | | */ |
310 | | #define TOKEN_MATCH(bptr, tptr) \ |
311 | 41.4k | ( (tptr)[0] == (bptr)[0] && \ |
312 | 41.4k | ((tptr)[1] == (bptr)[1] || (tptr)[1] == 0)) |
313 | | |
314 | | /* |
315 | | * Read a word from a buffer and advance pointer. |
316 | | * This function knows about escapes and quotes. |
317 | | * |
318 | | * At end-of-line, buf[0] is set to '\0'. |
319 | | * Returns 0 or special token value. |
320 | | */ |
321 | | static fr_token_t getthing(char const **ptr, char *buf, int buflen, bool tok, |
322 | | fr_table_num_ordered_t const *tokenlist, size_t tokenlist_len, bool unescape) |
323 | 1.13k | { |
324 | 1.13k | char *s; |
325 | 1.13k | char const *p; |
326 | 1.13k | char quote; |
327 | 1.13k | bool triple = false; |
328 | 1.13k | unsigned int x; |
329 | 1.13k | size_t i; |
330 | 1.13k | fr_token_t token; |
331 | | |
332 | 1.13k | buf[0] = '\0'; |
333 | | |
334 | | /* Skip whitespace */ |
335 | 1.13k | p = *ptr; |
336 | | |
337 | 1.13k | fr_skip_whitespace(p); |
338 | | |
339 | 1.13k | if (!*p) { |
340 | 0 | *ptr = p; |
341 | 0 | return T_EOL; |
342 | 0 | } |
343 | | |
344 | | /* |
345 | | * Might be a 1 or 2 character token. |
346 | | */ |
347 | 1.13k | if (tok) { |
348 | 25.3k | for (i = 0; i < tokenlist_len; i++) { |
349 | 25.0k | if (TOKEN_MATCH(p, tokenlist[i].name.str)) { |
350 | 856 | strcpy(buf, tokenlist[i].name.str); |
351 | 856 | p += tokenlist[i].name.len; |
352 | | |
353 | | /* |
354 | | * Try to shut up Coverity, which claims fr_token_t can be between 0..63, not |
355 | | * 0..48??? |
356 | | */ |
357 | 856 | if ((tokenlist[i].value < 0) || (tokenlist[i].value >= T_TOKEN_LAST)) return T_INVALID; |
358 | | |
359 | 856 | token = tokenlist[i].value; |
360 | 856 | goto done; |
361 | 856 | } |
362 | 25.0k | } |
363 | 1.11k | } |
364 | | |
365 | | /* Read word. */ |
366 | 283 | quote = '\0'; |
367 | 283 | switch (*p) { |
368 | 278 | default: |
369 | 278 | token = T_BARE_WORD; |
370 | 278 | break; |
371 | | |
372 | 4 | case '\'': |
373 | 4 | token = T_SINGLE_QUOTED_STRING; |
374 | 4 | break; |
375 | | |
376 | 1 | case '"': |
377 | 1 | token = T_DOUBLE_QUOTED_STRING; |
378 | 1 | break; |
379 | | |
380 | 0 | case '`': |
381 | 0 | token = T_BACK_QUOTED_STRING; |
382 | 0 | break; |
383 | 283 | } |
384 | | |
385 | 283 | if (token != T_BARE_WORD) { |
386 | 5 | quote = *p; |
387 | | |
388 | | /* |
389 | | * Triple-quoted strings are copied over verbatim, without escapes. |
390 | | */ |
391 | 5 | if ((buflen >= 3) && (p[1] == quote) && (p[2] == quote)) { |
392 | 0 | p += 3; |
393 | 0 | triple = true; |
394 | 0 | } |
395 | | |
396 | 5 | p++; |
397 | 5 | } |
398 | 283 | s = buf; |
399 | | |
400 | 14.4k | while (*p && buflen-- > 1) { |
401 | | /* |
402 | | * We're looking for strings. Stop on spaces, or |
403 | | * (if given a token list), on a token, or on a |
404 | | * comma. |
405 | | */ |
406 | 14.3k | if (!quote) { |
407 | 10.2k | if (isspace((uint8_t) *p)) break; |
408 | | |
409 | | |
410 | 9.97k | if (tok) { |
411 | 16.8k | for (i = 0; i < tokenlist_len; i++) { |
412 | 16.3k | if (TOKEN_MATCH(p, tokenlist[i].name.str)) { |
413 | 5 | *s++ = 0; |
414 | 5 | goto done; |
415 | 5 | } |
416 | 16.3k | } |
417 | 532 | } |
418 | 9.96k | if (*p == ',') break; |
419 | | |
420 | | /* |
421 | | * Copy the character over. |
422 | | */ |
423 | 9.96k | *s++ = *p++; |
424 | 9.96k | continue; |
425 | 9.96k | } /* else there was a quotation character */ |
426 | | |
427 | | /* |
428 | | * Un-escaped quote character. We're done. |
429 | | */ |
430 | 4.15k | if (*p == quote) { |
431 | 0 | if (!triple) { |
432 | 0 | p++; |
433 | 0 | *s++ = 0; |
434 | 0 | goto done; |
435 | 0 | } |
436 | | |
437 | 0 | if ((buflen >= 3) && (p[1] == quote) && (p[2] == quote)) { |
438 | 0 | p += 3; |
439 | 0 | *s++ = 0; |
440 | 0 | goto done; |
441 | 0 | } |
442 | | |
443 | 0 | *s++ = *p++; |
444 | 0 | continue; |
445 | 0 | } |
446 | | |
447 | | /* |
448 | | * Everything but backslash gets copied over. |
449 | | */ |
450 | 4.15k | if (*p != '\\') { |
451 | 4.15k | *s++ = *p++; |
452 | 4.15k | continue; |
453 | 4.15k | } |
454 | | |
455 | | /* |
456 | | * There's nothing after the backslash, it's an error. |
457 | | */ |
458 | 0 | if (!p[1]) { |
459 | 0 | fr_strerror_const("Unterminated string"); |
460 | 0 | return T_INVALID; |
461 | 0 | } |
462 | | |
463 | 0 | if (unescape) { |
464 | 0 | p++; |
465 | |
|
466 | 0 | switch (*p) { |
467 | 0 | case 'r': |
468 | 0 | *s++ = '\r'; |
469 | 0 | break; |
470 | 0 | case 'n': |
471 | 0 | *s++ = '\n'; |
472 | 0 | break; |
473 | 0 | case 't': |
474 | 0 | *s++ = '\t'; |
475 | 0 | break; |
476 | | |
477 | 0 | default: |
478 | 0 | if (*p >= '0' && *p <= '9' && |
479 | 0 | sscanf(p, "%3o", &x) == 1) { |
480 | 0 | *s++ = x; |
481 | 0 | p += 2; |
482 | 0 | } else |
483 | 0 | *s++ = *p; |
484 | 0 | break; |
485 | 0 | } |
486 | 0 | p++; |
487 | |
|
488 | 0 | } else { |
489 | | /* |
490 | | * Convert backslash-quote to quote, but |
491 | | * leave everything else alone. |
492 | | */ |
493 | 0 | if (p[1] == quote) { /* convert '\'' --> ' */ |
494 | 0 | p++; |
495 | 0 | } else { |
496 | 0 | if (buflen < 2) { |
497 | 0 | fr_strerror_const("Truncated input"); |
498 | 0 | return T_INVALID; |
499 | 0 | } |
500 | | |
501 | 0 | *(s++) = *(p++); |
502 | 0 | } |
503 | 0 | *(s++) = *(p++); |
504 | 0 | } |
505 | 0 | } |
506 | | |
507 | 278 | *s++ = 0; |
508 | | |
509 | 278 | if (quote) { |
510 | 5 | fr_strerror_const("Unterminated string"); |
511 | 5 | return T_INVALID; |
512 | 5 | } |
513 | | |
514 | 1.13k | done: |
515 | | /* Skip whitespace again. */ |
516 | 1.13k | fr_skip_whitespace(p); |
517 | | |
518 | 1.13k | *ptr = p; |
519 | | |
520 | 1.13k | return token; |
521 | 278 | } |
522 | | |
523 | | /* |
524 | | * Read a "word" - this means we don't honor |
525 | | * tokens as delimiters. |
526 | | */ |
527 | | int getword(char const **ptr, char *buf, int buflen, bool unescape) |
528 | 22 | { |
529 | 22 | return getthing(ptr, buf, buflen, false, fr_tokens_table, fr_tokens_table_len, unescape) == T_EOL ? 0 : 1; |
530 | 22 | } |
531 | | |
532 | | |
533 | | /* |
534 | | * Read the next word, use tokens as delimiters. |
535 | | */ |
536 | | fr_token_t gettoken(char const **ptr, char *buf, int buflen, bool unescape) |
537 | 1.11k | { |
538 | 1.11k | return getthing(ptr, buf, buflen, true, fr_tokens_table, fr_tokens_table_len, unescape); |
539 | 1.11k | } |
540 | | |
541 | | /* |
542 | | * Expect an operator. |
543 | | */ |
544 | | fr_token_t getop(char const **ptr) |
545 | 0 | { |
546 | 0 | char op[3]; |
547 | 0 | fr_token_t token; |
548 | |
|
549 | 0 | token = getthing(ptr, op, sizeof(op), true, fr_tokens_table, fr_tokens_table_len, false); |
550 | 0 | if (!fr_assignment_op[token] && !fr_comparison_op[token]) { |
551 | 0 | fr_strerror_const("Expected operator"); |
552 | 0 | return T_INVALID; |
553 | 0 | } |
554 | 0 | return token; |
555 | 0 | } |
556 | | |
557 | | /* |
558 | | * Expect a string. |
559 | | */ |
560 | | fr_token_t getstring(char const **ptr, char *buf, int buflen, bool unescape) |
561 | 0 | { |
562 | 0 | char const *p; |
563 | |
|
564 | 0 | if (!ptr || !*ptr || !buf) return T_INVALID; |
565 | | |
566 | 0 | p = *ptr; |
567 | |
|
568 | 0 | fr_skip_whitespace(p); |
569 | |
|
570 | 0 | *ptr = p; |
571 | |
|
572 | 0 | if ((*p == '"') || (*p == '\'') || (*p == '`')) { |
573 | 0 | return gettoken(ptr, buf, buflen, unescape); |
574 | 0 | } |
575 | | |
576 | 0 | return getthing(ptr, buf, buflen, false, fr_tokens_table, fr_tokens_table_len, unescape); |
577 | 0 | } |
578 | | |
579 | | char const *fr_token_name(int token) |
580 | 0 | { |
581 | 0 | return fr_table_str_by_value(fr_tokens_table, token, "<INVALID>"); |
582 | 0 | } |