The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
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: 74ec6b3425c41bf7c40c7c360065d2fa1aa1f256 $")
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 */
216DIAG_OFF_OPTIONAL(gnu-designator)
217
218/** Convert tokens back to a quoting character
219 *
220 * Non-string types convert to '?' to screw ups can be identified easily
221 */
223 [ 0 ... T_HASH ] = '?', /* GCC extension for range initialization, also allowed by clang */
224
225 [T_BARE_WORD] = '\0',
227 [T_SINGLE_QUOTED_STRING] = '\'',
228 [T_BACK_QUOTED_STRING] = '`',
230};
231
232#define T(_x) [T_OP_ ## _x] = true
233
235 T(INCRM), /* only used by LDAP :( */
236
237 T(ADD_EQ),
238 T(SUB_EQ),
239 T(MUL_EQ),
240 T(DIV_EQ),
241 T(AND_EQ),
242 T(OR_EQ),
243 T(RSHIFT_EQ),
244 T(LSHIFT_EQ),
245
246 T(SET),
247 T(EQ),
248 T(PREPEND),
249};
250
252 T(ADD_EQ), /* append */
253 T(SUB_EQ), /* remove */
254 T(AND_EQ), /* intersection */
255 T(OR_EQ), /* union */
256 T(LE), /* merge RHS */
257 T(GE), /* merge LHS */
258
259 T(SET),
260 T(EQ),
261 T(PREPEND), /* prepend */
262};
263
265 T(NE),
266 T(GE),
267 T(GT),
268 T(LE),
269 T(LT),
270 T(REG_EQ),
271 T(REG_NE),
272 T(CMP_TRUE),
273 T(CMP_FALSE),
274 T(CMP_EQ),
275 T(CMP_EQ_TYPE),
276 T(CMP_NE_TYPE),
277};
278
279#undef T
280#define T(_x) [T_ ## _x] = true
281
283 T(ADD),
284 T(SUB),
285 T(MUL),
286 T(DIV),
287 T(AND),
288 T(OR),
289 T(MOD),
290 T(RSHIFT),
291 T(LSHIFT),
292};
293
294
295#undef T
296#define T(_x) [T_## _x] = true
298 T(BARE_WORD),
299 T(DOUBLE_QUOTED_STRING),
300 T(SINGLE_QUOTED_STRING),
301 T(BACK_QUOTED_STRING),
302};
303
304/*
305 * This works only as long as special tokens
306 * are max. 2 characters, but it's fast.
307 */
308#define TOKEN_MATCH(bptr, tptr) \
309 ( (tptr)[0] == (bptr)[0] && \
310 ((tptr)[1] == (bptr)[1] || (tptr)[1] == 0))
311
312/*
313 * Read a word from a buffer and advance pointer.
314 * This function knows about escapes and quotes.
315 *
316 * At end-of-line, buf[0] is set to '\0'.
317 * Returns 0 or special token value.
318 */
319static fr_token_t getthing(char const **ptr, char *buf, int buflen, bool tok,
320 fr_table_num_ordered_t const *tokenlist, size_t tokenlist_len, bool unescape)
321{
322 char *s;
323 char const *p;
324 char quote;
325 bool triple = false;
326 size_t i;
327 fr_token_t token;
328
329 buf[0] = '\0';
330
331 /* Skip whitespace */
332 p = *ptr;
333
335
336 if (!*p) {
337 *ptr = p;
338 return T_EOL;
339 }
340
341 /*
342 * Might be a 1 or 2 character token.
343 */
344 if (tok) {
345 for (i = 0; i < tokenlist_len; i++) {
346 if (TOKEN_MATCH(p, tokenlist[i].name.str)) {
347 strcpy(buf, tokenlist[i].name.str);
348 p += tokenlist[i].name.len;
349
350 /*
351 * Try to shut up Coverity, which claims fr_token_t can be between 0..63, not
352 * 0..48???
353 */
354 if ((tokenlist[i].value < 0) || (tokenlist[i].value >= T_TOKEN_LAST)) return T_INVALID;
355
356 token = tokenlist[i].value;
357 goto done;
358 }
359 }
360 }
361
362 /* Read word. */
363 quote = '\0';
364 switch (*p) {
365 default:
366 token = T_BARE_WORD;
367 break;
368
369 case '\'':
371 break;
372
373 case '"':
375 break;
376
377 case '`':
378 token = T_BACK_QUOTED_STRING;
379 break;
380 }
381
382 if (token != T_BARE_WORD) {
383 quote = *p;
384
385 /*
386 * Triple-quoted strings are copied over verbatim, without escapes.
387 */
388 if ((buflen >= 3) && (p[1] == quote) && (p[2] == quote)) {
389 p += 3;
390 triple = true;
391 } else {
392 p++;
393 }
394 }
395 s = buf;
396
397 while (*p && buflen-- > 1) {
398 /*
399 * We're looking for strings. Stop on spaces, or
400 * (if given a token list), on a token, or on a
401 * comma.
402 */
403 if (!quote) {
404 if (isspace((uint8_t) *p)) break;
405
406
407 if (tok) {
408 for (i = 0; i < tokenlist_len; i++) {
409 if (TOKEN_MATCH(p, tokenlist[i].name.str)) {
410 *s++ = 0;
411 goto done;
412 }
413 }
414 }
415 if (*p == ',') break;
416
417 /*
418 * Copy the character over.
419 */
420 *s++ = *p++;
421 continue;
422 } /* else there was a quotation character */
423
424 /*
425 * Un-escaped quote character. We're done.
426 */
427 if (*p == quote) {
428 if (!triple) {
429 p++;
430 *s++ = 0;
431 goto done;
432 }
433
434 if ((buflen >= 3) && (p[1] == quote) && (p[2] == quote)) {
435 p += 3;
436 *s++ = 0;
437 goto done;
438 }
439
440 *s++ = *p++;
441 continue;
442 }
443
444 /*
445 * Everything but backslash gets copied over.
446 */
447 if (*p != '\\') {
448 *s++ = *p++;
449 continue;
450 }
451
452 /*
453 * There's nothing after the backslash, it's an error.
454 */
455 if (!p[1]) {
456 fr_strerror_const("Unterminated string");
457 *s = '\0';
458 return T_INVALID;
459 }
460
461 if (unescape) {
462 p++;
463
464 switch (*p) {
465 case 'r':
466 *s++ = '\r';
467 break;
468 case 'n':
469 *s++ = '\n';
470 break;
471 case 't':
472 *s++ = '\t';
473 break;
474
475 default:
476 /*
477 * An octal escape is exactly three octal digits, and fits in one byte.
478 */
479 if ((p[0] >= '0') && (p[0] <= '3') &&
480 (p[1] >= '0') && (p[1] <= '7') &&
481 (p[2] >= '0') && (p[2] <= '7')) {
482 *s++ = (char) (((p[0] - '0') << 6) | ((p[1] - '0') << 3) | (p[2] - '0'));
483 p += 2;
484 } else
485 *s++ = *p;
486 break;
487 }
488 p++;
489
490 } else {
491 /*
492 * Convert backslash-quote to quote, but
493 * leave everything else alone.
494 */
495 if (p[1] == quote) { /* convert '\'' --> ' */
496 p++;
497 } else {
498 if (buflen < 2) {
499 fr_strerror_const("Truncated input");
500 *s = '\0';
501 return T_INVALID;
502 }
503
504 *(s++) = *(p++);
505 }
506 *(s++) = *(p++);
507 }
508 }
509
510 *s++ = 0;
511
512 if (quote) {
513 fr_strerror_const("Unterminated string");
514 return T_INVALID;
515 }
516
517done:
518 /* Skip whitespace again. */
520
521 *ptr = p;
522
523 return token;
524}
525
526/*
527 * Read a "word" - this means we don't honor
528 * tokens as delimiters.
529 */
530int getword(char const **ptr, char *buf, int buflen, bool unescape)
531{
532 return getthing(ptr, buf, buflen, false, fr_tokens_table, fr_tokens_table_len, unescape) == T_EOL ? 0 : 1;
533}
534
535
536/*
537 * Read the next word, use tokens as delimiters.
538 */
539fr_token_t gettoken(char const **ptr, char *buf, int buflen, bool unescape)
540{
541 return getthing(ptr, buf, buflen, true, fr_tokens_table, fr_tokens_table_len, unescape);
542}
543
544/*
545 * Expect an operator.
546 */
547fr_token_t getop(char const **ptr)
548{
549 char op[3];
550 fr_token_t token;
551
552 token = getthing(ptr, op, sizeof(op), true, fr_tokens_table, fr_tokens_table_len, false);
553 if (!fr_assignment_op[token] && !fr_comparison_op[token]) {
554 fr_strerror_const("Expected operator");
555 return T_INVALID;
556 }
557 return token;
558}
559
560/*
561 * Expect a string.
562 */
563fr_token_t getstring(char const **ptr, char *buf, int buflen, bool unescape)
564{
565 char const *p;
566
567 if (!ptr || !*ptr || !buf) return T_INVALID;
568
569 p = *ptr;
570
572
573 *ptr = p;
574
575 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
576 return gettoken(ptr, buf, buflen, unescape);
577 }
578
579 return getthing(ptr, buf, buflen, false, fr_tokens_table, fr_tokens_table_len, unescape);
580}
581
582char const *fr_token_name(int token)
583{
584 return fr_table_str_by_value(fr_tokens_table, token, "<INVALID>");
585}
strcpy(log_entry->msg, buffer)
#define RCSID(id)
Definition build.h:603
#define DIAG_OFF_OPTIONAL(_x)
Definition build.h:577
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:245
#define NUM_ELEMENTS(_t)
Definition build.h:437
@ CMP_EQ
a == b
Definition build.h:116
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:234
const bool fr_list_assignment_op[T_TOKEN_LAST]
Definition token.c:251
static size_t fr_token_to_enum_str_table_len
Definition token.c:115
#define TOKEN_MATCH(bptr, tptr)
Definition token.c:308
char const * fr_token_name(int token)
Definition token.c:582
fr_token_t gettoken(char const **ptr, char *buf, int buflen, bool unescape)
Definition token.c:539
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:319
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:297
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:547
const char fr_token_quote[T_TOKEN_LAST]
Convert tokens back to a quoting character.
Definition token.c:222
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:563
int getword(char const **ptr, char *buf, int buflen, bool unescape)
Definition token.c:530
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:264
const bool fr_binary_op[T_TOKEN_LAST]
Definition token.c:282
#define T(_x)
Definition token.c:232
@ 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
enum fr_token fr_token_t
#define T_TOKEN_LAST
Definition token.h:127
#define fr_strerror_const(_msg)
Definition strerror.h:223