The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
token.c
Go to the documentation of this file.
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 */
25RCSID("$Id: 9fb1b39f02230880e8eee89f9f800e0e0407a391 $")
26
27#include <stdio.h>
28
29#include <freeradius-devel/util/skip.h>
30#include <freeradius-devel/util/token.h>
31
32
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};
67
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 */
116
118{
120}
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 */
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};
136
138{
139 if (!s) return dflt;
141}
142
143/*
144 * String versions for all of the tokens.
145 */
146char 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 */
225 [ 0 ... T_HASH ] = '?', /* GCC extension for range initialization, also allowed by clang */
226
227 [T_BARE_WORD] = '\0',
229 [T_SINGLE_QUOTED_STRING] = '\'',
230 [T_BACK_QUOTED_STRING] = '`',
232};
233
234#define T(_x) [T_OP_ ## _x] = true
235
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
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
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
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
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 ( (tptr)[0] == (bptr)[0] && \
312 ((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 */
321static 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{
324 char *s;
325 char const *p;
326 char quote;
327 bool triple = false;
328 unsigned int x;
329 size_t i;
330 fr_token_t token;
331
332 buf[0] = '\0';
333
334 /* Skip whitespace */
335 p = *ptr;
336
338
339 if (!*p) {
340 *ptr = p;
341 return T_EOL;
342 }
343
344 /*
345 * Might be a 1 or 2 character token.
346 */
347 if (tok) {
348 for (i = 0; i < tokenlist_len; i++) {
349 if (TOKEN_MATCH(p, tokenlist[i].name.str)) {
350 strcpy(buf, tokenlist[i].name.str);
351 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 if ((tokenlist[i].value < 0) || (tokenlist[i].value >= T_TOKEN_LAST)) return T_INVALID;
358
359 token = tokenlist[i].value;
360 goto done;
361 }
362 }
363 }
364
365 /* Read word. */
366 quote = '\0';
367 switch (*p) {
368 default:
369 token = T_BARE_WORD;
370 break;
371
372 case '\'':
374 break;
375
376 case '"':
378 break;
379
380 case '`':
381 token = T_BACK_QUOTED_STRING;
382 break;
383 }
384
385 if (token != T_BARE_WORD) {
386 quote = *p;
387
388 /*
389 * Triple-quoted strings are copied over verbatim, without escapes.
390 */
391 if ((buflen >= 3) && (p[1] == quote) && (p[2] == quote)) {
392 p += 3;
393 triple = true;
394 }
395
396 p++;
397 }
398 s = buf;
399
400 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 if (!quote) {
407 if (isspace((uint8_t) *p)) break;
408
409
410 if (tok) {
411 for (i = 0; i < tokenlist_len; i++) {
412 if (TOKEN_MATCH(p, tokenlist[i].name.str)) {
413 *s++ = 0;
414 goto done;
415 }
416 }
417 }
418 if (*p == ',') break;
419
420 /*
421 * Copy the character over.
422 */
423 *s++ = *p++;
424 continue;
425 } /* else there was a quotation character */
426
427 /*
428 * Un-escaped quote character. We're done.
429 */
430 if (*p == quote) {
431 if (!triple) {
432 p++;
433 *s++ = 0;
434 goto done;
435 }
436
437 if ((buflen >= 3) && (p[1] == quote) && (p[2] == quote)) {
438 p += 3;
439 *s++ = 0;
440 goto done;
441 }
442
443 *s++ = *p++;
444 continue;
445 }
446
447 /*
448 * Everything but backslash gets copied over.
449 */
450 if (*p != '\\') {
451 *s++ = *p++;
452 continue;
453 }
454
455 /*
456 * There's nothing after the backslash, it's an error.
457 */
458 if (!p[1]) {
459 fr_strerror_const("Unterminated string");
460 return T_INVALID;
461 }
462
463 if (unescape) {
464 p++;
465
466 switch (*p) {
467 case 'r':
468 *s++ = '\r';
469 break;
470 case 'n':
471 *s++ = '\n';
472 break;
473 case 't':
474 *s++ = '\t';
475 break;
476
477 default:
478 if (*p >= '0' && *p <= '9' &&
479 sscanf(p, "%3o", &x) == 1) {
480 *s++ = x;
481 p += 2;
482 } else
483 *s++ = *p;
484 break;
485 }
486 p++;
487
488 } else {
489 /*
490 * Convert backslash-quote to quote, but
491 * leave everything else alone.
492 */
493 if (p[1] == quote) { /* convert '\'' --> ' */
494 p++;
495 } else {
496 if (buflen < 2) {
497 fr_strerror_const("Truncated input");
498 return T_INVALID;
499 }
500
501 *(s++) = *(p++);
502 }
503 *(s++) = *(p++);
504 }
505 }
506
507 *s++ = 0;
508
509 if (quote) {
510 fr_strerror_const("Unterminated string");
511 return T_INVALID;
512 }
513
514done:
515 /* Skip whitespace again. */
517
518 *ptr = p;
519
520 return token;
521}
522
523/*
524 * Read a "word" - this means we don't honor
525 * tokens as delimiters.
526 */
527int getword(char const **ptr, char *buf, int buflen, bool unescape)
528{
529 return getthing(ptr, buf, buflen, false, fr_tokens_table, fr_tokens_table_len, unescape) == T_EOL ? 0 : 1;
530}
531
532
533/*
534 * Read the next word, use tokens as delimiters.
535 */
536fr_token_t gettoken(char const **ptr, char *buf, int buflen, bool unescape)
537{
538 return getthing(ptr, buf, buflen, true, fr_tokens_table, fr_tokens_table_len, unescape);
539}
540
541/*
542 * Expect an operator.
543 */
544fr_token_t getop(char const **ptr)
545{
546 char op[3];
547 fr_token_t token;
548
549 token = getthing(ptr, op, sizeof(op), true, fr_tokens_table, fr_tokens_table_len, false);
550 if (!fr_assignment_op[token] && !fr_comparison_op[token]) {
551 fr_strerror_const("Expected operator");
552 return T_INVALID;
553 }
554 return token;
555}
556
557/*
558 * Expect a string.
559 */
560fr_token_t getstring(char const **ptr, char *buf, int buflen, bool unescape)
561{
562 char const *p;
563
564 if (!ptr || !*ptr || !buf) return T_INVALID;
565
566 p = *ptr;
567
569
570 *ptr = p;
571
572 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
573 return gettoken(ptr, buf, buflen, unescape);
574 }
575
576 return getthing(ptr, buf, buflen, false, fr_tokens_table, fr_tokens_table_len, unescape);
577}
578
579char const *fr_token_name(int token)
580{
581 return fr_table_str_by_value(fr_tokens_table, token, "<INVALID>");
582}
strcpy(log_entry->msg, buffer)
#define RCSID(id)
Definition build.h:512
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define NUM_ELEMENTS(_t)
Definition build.h:358
Test enumeration values.
Definition dict_test.h:92
unsigned char uint8_t
static bool done
Definition radclient.c:80
static char const * name
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
Definition skip.h:36
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:685
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
#define FR_TABLE_INDEXED_ENTRY(_v)
Build a single fr_table_num_indexed_t entry from an enum identifier.
Definition table.h:110
fr_table_elem_name_t name
Definition table.h:58
size_t len
Literal string length.
Definition table.h:43
An element in a table indexed by numeric value.
Definition table.h:92
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
#define MOD(a, b)
const bool fr_assignment_op[T_TOKEN_LAST]
Definition token.c:236
const bool fr_list_assignment_op[T_TOKEN_LAST]
Definition token.c:253
static size_t fr_token_to_enum_str_table_len
Definition token.c:115
#define TOKEN_MATCH(bptr, tptr)
Definition token.c:310
char const * fr_token_name(int token)
Definition token.c:579
fr_token_t gettoken(char const **ptr, char *buf, int buflen, bool unescape)
Definition token.c:536
fr_table_num_ordered_t const fr_tokens_table[]
Definition token.c:33
static fr_token_t getthing(char const **ptr, char *buf, int buflen, bool tok, fr_table_num_ordered_t const *tokenlist, size_t tokenlist_len, bool unescape)
Definition token.c:321
static fr_table_num_sorted_t const fr_token_from_quote_enum_str_table[]
Quote-token names that radconf2json's JSON output uses.
Definition token.c:128
static size_t fr_token_from_quote_enum_str_table_len
Definition token.c:135
const bool fr_str_tok[T_TOKEN_LAST]
Definition token.c:299
fr_table_num_sorted_t const fr_token_quotes_table[]
Definition token.c:68
char const * fr_token_to_enum_str(fr_token_t t)
Return the source-identifier name for a token (e.g.
Definition token.c:117
size_t fr_token_quotes_table_len
Definition token.c:75
size_t fr_tokens_table_len
Definition token.c:66
fr_token_t getop(char const **ptr)
Definition token.c:544
const char fr_token_quote[T_TOKEN_LAST]
Convert tokens back to a quoting character.
Definition token.c:224
static fr_table_num_indexed_t const fr_token_to_enum_str_table[]
Map each fr_token_t to the C identifier of its enum constant.
Definition token.c:84
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:146
fr_token_t getstring(char const **ptr, char *buf, int buflen, bool unescape)
Definition token.c:560
int getword(char const **ptr, char *buf, int buflen, bool unescape)
Definition token.c:527
fr_token_t fr_token_from_quote_enum_str(char const *s, fr_token_t dflt)
Look up a quote token by its source-identifier name.
Definition token.c:137
const bool fr_comparison_op[T_TOKEN_LAST]
Definition token.c:266
const bool fr_binary_op[T_TOKEN_LAST]
Definition token.c:284
#define T(_x)
Definition token.c:234
enum fr_token fr_token_t
@ T_AND
Definition token.h:53
@ T_OP_SUB_EQ
Definition token.h:68
@ T_INVALID
Definition token.h:37
@ T_SUB
Definition token.h:50
@ T_RSHIFT
Definition token.h:60
@ T_RCBRACE
Definition token.h:40
@ T_NOT
Definition token.h:55
@ T_OP_DIV_EQ
Definition token.h:70
@ T_XOR
Definition token.h:56
@ T_RBRACE
Definition token.h:42
@ T_EOL
Definition token.h:38
@ T_SEMICOLON
Definition token.h:44
@ T_DIV
Definition token.h:52
@ T_SINGLE_QUOTED_STRING
Definition token.h:120
@ T_MOD
Definition token.h:58
@ T_OP_AND_EQ
Definition token.h:72
@ T_OP_CMP_TRUE
Definition token.h:102
@ T_BARE_WORD
Definition token.h:118
@ T_OP_EQ
Definition token.h:81
@ T_OP_MUL_EQ
Definition token.h:69
@ T_LAND
Definition token.h:89
@ T_COMPLEMENT
Definition token.h:57
@ T_ADD
Definition token.h:49
@ T_BACK_QUOTED_STRING
Definition token.h:121
@ T_HASH
Definition token.h:117
@ T_OP_SET
Definition token.h:82
@ T_OP_NE
Definition token.h:95
@ T_OP_ADD_EQ
Definition token.h:67
@ T_OP_CMP_FALSE
Definition token.h:103
@ T_OP_LSHIFT_EQ
Definition token.h:75
@ T_LOR
Definition token.h:90
@ T_LCBRACE
Definition token.h:39
@ T_LSHIFT
Definition token.h:61
@ T_OP_RSHIFT_EQ
Definition token.h:74
@ T_OP_REG_EQ
Definition token.h:100
@ T_OP_CMP_EQ_TYPE
Definition token.h:105
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
@ T_OP_CMP_EQ
Definition token.h:104
@ T_OP_INCRM
Definition token.h:111
@ T_LBRACE
Definition token.h:41
@ T_MUL
Definition token.h:51
@ T_OP_LE
Definition token.h:98
@ T_OP_CMP_NE_TYPE
Definition token.h:106
@ T_OP_GE
Definition token.h:96
@ T_OP_GT
Definition token.h:97
@ T_OP_OR_EQ
Definition token.h:71
@ T_SOLIDUS_QUOTED_STRING
Definition token.h:122
@ T_OP_LT
Definition token.h:99
@ T_OP_REG_NE
Definition token.h:101
@ T_COMMA
Definition token.h:43
@ T_OR
Definition token.h:54
@ T_OP_PREPEND
Definition token.h:83
#define T_TOKEN_LAST
Definition token.h:127
#define fr_strerror_const(_msg)
Definition strerror.h:223