2 * token.c Read the next token from a string.
3 * Yes it's pretty primitive but effective.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright 2000,2006 The FreeRADIUS server project
26 #include <freeradius-devel/libradius.h>
30 const FR_NAME_NUMBER fr_tokens[] = {
31 { "=~", T_OP_REG_EQ, }, /* order is important! */
32 { "!~", T_OP_REG_NE, },
38 { "++", T_OP_INCRM, },
42 { "=*", T_OP_CMP_TRUE, },
43 { "!*", T_OP_CMP_FALSE, },
44 { "==", T_OP_CMP_EQ, },
52 { ";", T_SEMICOLON, },
56 const bool fr_assignment_op[] = {
57 false, /* invalid token */
58 false, /* end of line */
82 false, /* bare word */
89 const bool fr_equality_op[] = {
90 false, /* invalid token */
91 false, /* end of line */
115 false, /* bare word */
116 false, /* "foo" 25 */
122 const bool fr_str_tok[] = {
123 false, /* invalid token */
124 false, /* end of line */
148 true, /* bare word */
156 * This works only as long as special tokens
157 * are max. 2 characters, but it's fast.
159 #define TOKEN_MATCH(bptr, tptr) \
160 ( (tptr)[0] == (bptr)[0] && \
161 ((tptr)[1] == (bptr)[1] || (tptr)[1] == 0))
164 * Read a word from a buffer and advance pointer.
165 * This function knows about escapes and quotes.
167 * At end-of-line, buf[0] is set to '\0'.
168 * Returns 0 or special token value.
170 static FR_TOKEN getthing(char const **ptr, char *buf, int buflen, bool tok,
171 FR_NAME_NUMBER const *tokenlist, bool unescape)
178 FR_NAME_NUMBER const *t;
183 /* Skip whitespace */
186 while (*p && isspace((int) *p)) p++;
194 * Might be a 1 or 2 character token.
196 if (tok) for (t = tokenlist; t->name; t++) {
197 if (TOKEN_MATCH(p, t->name)) {
198 strcpy(buf, t->name);
199 p += strlen(t->name);
214 rcode = T_SINGLE_QUOTED_STRING;
218 rcode = T_DOUBLE_QUOTED_STRING;
222 rcode = T_BACK_QUOTED_STRING;
226 if (rcode != T_BARE_WORD) {
233 while (*p && buflen-- > 1) {
235 * We're looking for strings. Stop on spaces, or
236 * (if given a token list), on a token, or on a
240 if (isspace((int) *p)) {
245 for (t = tokenlist; t->name; t++) {
246 if (TOKEN_MATCH(p, t->name)) {
252 if (*p == ',') break;
255 * Copy the character over.
261 if (unescape && quote && (*p == '\\')) {
279 if (*p >= '0' && *p <= '9' &&
280 sscanf(p, "%3o", &x) == 1) {
292 * Deal with quotes and escapes, but don't mash
293 * escaped characters into their non-escaped
296 if (!unescape && quote && (*p == '\\')) {
297 if (!p[1]) continue; /* force end of string */
299 if (p[1] == quote) { /* convert '\'' --> ' */
308 if (quote && (*p == quote)) {
319 fr_strerror_printf("Unterminated string");
324 /* Skip whitespace again. */
325 while (*p && isspace((int) *p)) p++;
333 * Read a "word" - this means we don't honor
334 * tokens as delimiters.
336 int getword(char const **ptr, char *buf, int buflen, bool unescape)
338 return getthing(ptr, buf, buflen, false, fr_tokens, unescape) == T_EOL ? 0 : 1;
343 * Read the next word, use tokens as delimiters.
345 FR_TOKEN gettoken(char const **ptr, char *buf, int buflen, bool unescape)
347 return getthing(ptr, buf, buflen, true, fr_tokens, unescape);
351 * Expect an operator.
353 FR_TOKEN getop(char const **ptr)
358 rcode = getthing(ptr, op, sizeof(op), true, fr_tokens, false);
359 if (!fr_assignment_op[rcode] && !fr_equality_op[rcode]) {
360 fr_strerror_printf("Expected operator");
369 FR_TOKEN getstring(char const **ptr, char *buf, int buflen, bool unescape)
373 if (!ptr || !*ptr || !buf) return T_INVALID;
377 while (*p && (isspace((int)*p))) p++;
381 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
382 return gettoken(ptr, buf, buflen, unescape);
385 return getthing(ptr, buf, buflen, false, fr_tokens, unescape);
389 * Convert a string to an integer
391 int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
393 FR_NAME_NUMBER const *this;
399 for (this = table; this->name != NULL; this++) {
400 if (strcasecmp(this->name, name) == 0) {
409 * Convert a string matching part of name to an integer.
411 int fr_substr2int(FR_NAME_NUMBER const *table, char const *name, int def, int len)
413 FR_NAME_NUMBER const *this;
420 for (this = table; this->name != NULL; this++) {
423 tlen = strlen(this->name);
426 * Don't match "request" to user input "req".
428 if ((len > 0) && (len < (int) tlen)) continue;
431 * Match up to the length of the table entry if len is < 0.
433 max = (len < 0) ? tlen : (unsigned)len;
435 if (strncasecmp(this->name, name, max) == 0) {
444 * Convert an integer to a string.
446 char const *fr_int2str(FR_NAME_NUMBER const *table, int number,
449 FR_NAME_NUMBER const *this;
451 for (this = table; this->name != NULL; this++) {
452 if (this->number == number) {
460 char const *fr_token_name(int token)
462 return fr_int2str(fr_tokens, token, "???");