summaryrefslogtreecommitdiffstats
path: root/src/lib/token.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/token.c')
-rw-r--r--src/lib/token.c481
1 files changed, 481 insertions, 0 deletions
diff --git a/src/lib/token.c b/src/lib/token.c
new file mode 100644
index 0000000..a797862
--- /dev/null
+++ b/src/lib/token.c
@@ -0,0 +1,481 @@
+/*
+ * token.c Read the next token from a string.
+ * Yes it's pretty primitive but effective.
+ *
+ * Version: $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2000,2006 The FreeRADIUS server project
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/libradius.h>
+
+#include <ctype.h>
+
+const FR_NAME_NUMBER fr_tokens[] = {
+ { "=~", T_OP_REG_EQ, }, /* order is important! */
+ { "!~", T_OP_REG_NE, },
+ { "{", T_LCBRACE, },
+ { "}", T_RCBRACE, },
+ { "(", T_LBRACE, },
+ { ")", T_RBRACE, },
+ { ",", T_COMMA, },
+ { "++", T_OP_INCRM, },
+ { "+=", T_OP_ADD, },
+ { "-=", T_OP_SUB, },
+ { ":=", T_OP_SET, },
+ { "=*", T_OP_CMP_TRUE, },
+ { "!*", T_OP_CMP_FALSE, },
+ { "==", T_OP_CMP_EQ, },
+ { "^=", T_OP_PREPEND, },
+ { "=", T_OP_EQ, },
+ { "!=", T_OP_NE, },
+ { ">=", T_OP_GE, },
+ { ">", T_OP_GT, },
+ { "<=", T_OP_LE, },
+ { "<", T_OP_LT, },
+ { "#", T_HASH, },
+ { ";", T_SEMICOLON, },
+ { NULL, 0, },
+};
+
+const bool fr_assignment_op[] = {
+ false, /* invalid token */
+ false, /* end of line */
+ false, /* { */
+ false, /* } */
+ false, /* ( */
+ false, /* ) 5 */
+ false, /* , */
+ false, /* ; */
+
+ true, /* ++ */
+ true, /* += */
+ true, /* -= 10 */
+ true, /* := */
+ true, /* = */
+ false, /* != */
+ false, /* >= */
+ false, /* > 15 */
+ false, /* <= */
+ false, /* < */
+ false, /* =~ */
+ false, /* !~ */
+ false, /* =* 20 */
+ false, /* !* */
+ false, /* == */
+ true, /* ^= */
+ false, /* # */
+ false, /* bare word 25 */
+ false, /* "foo" */
+ false, /* 'foo' */
+ false, /* `foo` */
+ false
+};
+
+const bool fr_equality_op[] = {
+ false, /* invalid token */
+ false, /* end of line */
+ false, /* { */
+ false, /* } */
+ false, /* ( */
+ false, /* ) 5 */
+ false, /* , */
+ false, /* ; */
+
+ false, /* ++ */
+ false, /* += */
+ false, /* -= 10 */
+ false, /* := */
+ false, /* = */
+ true, /* != */
+ true, /* >= */
+ true, /* > 15 */
+ true, /* <= */
+ true, /* < */
+ true, /* =~ */
+ true, /* !~ */
+ true, /* =* 20 */
+ true, /* !* */
+ true, /* == */
+ false, /* ^= */
+ false, /* # */
+ false, /* bare word 25 */
+ false, /* "foo" */
+ false, /* 'foo' */
+ false, /* `foo` */
+ false
+};
+
+const bool fr_str_tok[] = {
+ false, /* invalid token */
+ false, /* end of line */
+ false, /* { */
+ false, /* } */
+ false, /* ( */
+ false, /* ) 5 */
+ false, /* , */
+ false, /* ; */
+
+ false, /* ++ */
+ false, /* += */
+ false, /* -= 10 */
+ false, /* := */
+ false, /* = */
+ false, /* != */
+ false, /* >= */
+ false, /* > 15 */
+ false, /* <= */
+ false, /* < */
+ false, /* =~ */
+ false, /* !~ */
+ false, /* =* 20 */
+ false, /* !* */
+ false, /* == */
+ false, /* ^= */
+ false, /* # */
+ true, /* bare word 25 */
+ true, /* "foo" */
+ true, /* 'foo' */
+ true, /* `foo` */
+ false
+};
+
+/*
+ * This works only as long as special tokens
+ * are max. 2 characters, but it's fast.
+ */
+#define TOKEN_MATCH(bptr, tptr) \
+ ( (tptr)[0] == (bptr)[0] && \
+ ((tptr)[1] == (bptr)[1] || (tptr)[1] == 0))
+
+/*
+ * Read a word from a buffer and advance pointer.
+ * This function knows about escapes and quotes.
+ *
+ * At end-of-line, buf[0] is set to '\0'.
+ * Returns 0 or special token value.
+ */
+static FR_TOKEN getthing(char const **ptr, char *buf, int buflen, bool tok,
+ FR_NAME_NUMBER const *tokenlist, bool unescape)
+{
+ char *s;
+ char const *p;
+ char quote;
+ bool end = false;
+ unsigned int x;
+ FR_NAME_NUMBER const *t;
+ FR_TOKEN rcode;
+
+ buf[0] = '\0';
+
+ /* Skip whitespace */
+ p = *ptr;
+
+ while (*p && isspace((int) *p)) p++;
+
+ if (!*p) {
+ *ptr = p;
+ return T_EOL;
+ }
+
+ /*
+ * Might be a 1 or 2 character token.
+ */
+ if (tok) for (t = tokenlist; t->name; t++) {
+ if (TOKEN_MATCH(p, t->name)) {
+ strcpy(buf, t->name);
+ p += strlen(t->name);
+
+ rcode = t->number;
+ goto done;
+ }
+ }
+
+ /* Read word. */
+ quote = '\0';
+ switch (*p) {
+ default:
+ rcode = T_BARE_WORD;
+ break;
+
+ case '\'':
+ rcode = T_SINGLE_QUOTED_STRING;
+ break;
+
+ case '"':
+ rcode = T_DOUBLE_QUOTED_STRING;
+ break;
+
+ case '`':
+ rcode = T_BACK_QUOTED_STRING;
+ break;
+ }
+
+ if (rcode != T_BARE_WORD) {
+ quote = *p;
+ end = false;
+ p++;
+ }
+ s = buf;
+
+ while (*p && buflen-- > 1) {
+ /*
+ * We're looking for strings. Stop on spaces, or
+ * (if given a token list), on a token, or on a
+ * comma.
+ */
+ if (!quote) {
+ if (isspace((int) *p)) {
+ break;
+ }
+
+ if (tok) {
+ for (t = tokenlist; t->name; t++) {
+ if (TOKEN_MATCH(p, t->name)) {
+ *s++ = 0;
+ goto done;
+ }
+ }
+ }
+ if (*p == ',') break;
+
+ /*
+ * Copy the character over.
+ */
+ *s++ = *p++;
+ continue;
+ } /* else there was a quotation character */
+
+ /*
+ * Un-escaped quote character. We're done.
+ */
+ if (*p == quote) {
+ end = true;
+ p++;
+ break;
+ }
+
+ /*
+ * Everything but backslash gets copied over.
+ */
+ if (*p != '\\') {
+ *s++ = *p++;
+ continue;
+ }
+
+ /*
+ * There's nothing after the backslash, it's an error.
+ */
+ if (!p[1]) {
+ fr_strerror_printf("Unterminated string");
+ return T_INVALID;
+ }
+
+ if (unescape) {
+ p++;
+
+ switch (*p) {
+ case 'r':
+ *s++ = '\r';
+ break;
+ case 'n':
+ *s++ = '\n';
+ break;
+ case 't':
+ *s++ = '\t';
+ break;
+
+ default:
+ if (*p >= '0' && *p <= '9' &&
+ sscanf(p, "%3o", &x) == 1) {
+ *s++ = x;
+ p += 2;
+ } else
+ *s++ = *p;
+ break;
+ }
+ p++;
+
+ } else {
+ /*
+ * Convert backslash-quote to quote, but
+ * leave everything else alone.
+ */
+ if (p[1] == quote) { /* convert '\'' --> ' */
+ p++;
+ } else {
+ if (buflen < 2) {
+ fr_strerror_printf("Truncated input");
+ return T_INVALID;
+ }
+
+ *(s++) = *(p++);
+ }
+ *(s++) = *(p++);
+ }
+ }
+
+ *s++ = 0;
+
+ if (quote && !end) {
+ fr_strerror_printf("Unterminated string");
+ return T_INVALID;
+ }
+
+done:
+ /* Skip whitespace again. */
+ while (*p && isspace((int) *p)) p++;
+
+ *ptr = p;
+
+ return rcode;
+}
+
+/*
+ * Read a "word" - this means we don't honor
+ * tokens as delimiters.
+ */
+int getword(char const **ptr, char *buf, int buflen, bool unescape)
+{
+ return getthing(ptr, buf, buflen, false, fr_tokens, unescape) == T_EOL ? 0 : 1;
+}
+
+
+/*
+ * Read the next word, use tokens as delimiters.
+ */
+FR_TOKEN gettoken(char const **ptr, char *buf, int buflen, bool unescape)
+{
+ return getthing(ptr, buf, buflen, true, fr_tokens, unescape);
+}
+
+/*
+ * Expect an operator.
+ */
+FR_TOKEN getop(char const **ptr)
+{
+ char op[3];
+ FR_TOKEN rcode;
+
+ rcode = getthing(ptr, op, sizeof(op), true, fr_tokens, false);
+ if (!fr_assignment_op[rcode] && !fr_equality_op[rcode]) {
+ fr_strerror_printf("Expected operator");
+ return T_INVALID;
+ }
+ return rcode;
+}
+
+/*
+ * Expect a string.
+ */
+FR_TOKEN getstring(char const **ptr, char *buf, int buflen, bool unescape)
+{
+ char const *p;
+
+ if (!ptr || !*ptr || !buf) return T_INVALID;
+
+ p = *ptr;
+
+ while (*p && (isspace((int)*p))) p++;
+
+ *ptr = p;
+
+ if ((*p == '"') || (*p == '\'') || (*p == '`')) {
+ return gettoken(ptr, buf, buflen, unescape);
+ }
+
+ return getthing(ptr, buf, buflen, false, fr_tokens, unescape);
+}
+
+/*
+ * Convert a string to an integer
+ */
+int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
+{
+ FR_NAME_NUMBER const *this;
+
+ if (!name) {
+ return def;
+ }
+
+ for (this = table; this->name != NULL; this++) {
+ if (strcasecmp(this->name, name) == 0) {
+ return this->number;
+ }
+ }
+
+ return def;
+}
+
+/*
+ * Convert a string matching part of name to an integer.
+ */
+int fr_substr2int(FR_NAME_NUMBER const *table, char const *name, int def, int len)
+{
+ FR_NAME_NUMBER const *this;
+ size_t max;
+
+ if (!name) {
+ return def;
+ }
+
+ for (this = table; this->name != NULL; this++) {
+ size_t tlen;
+
+ tlen = strlen(this->name);
+
+ /*
+ * Don't match "request" to user input "req".
+ */
+ if ((len > 0) && (len < (int) tlen)) continue;
+
+ /*
+ * Match up to the length of the table entry if len is < 0.
+ */
+ max = (len < 0) ? tlen : (unsigned)len;
+
+ if (strncasecmp(this->name, name, max) == 0) {
+ return this->number;
+ }
+ }
+
+ return def;
+}
+
+/*
+ * Convert an integer to a string.
+ */
+char const *fr_int2str(FR_NAME_NUMBER const *table, int number,
+ char const *def)
+{
+ FR_NAME_NUMBER const *this;
+
+ for (this = table; this->name != NULL; this++) {
+ if (this->number == number) {
+ return this->name;
+ }
+ }
+
+ return def;
+}
+
+char const *fr_token_name(int token)
+{
+ return fr_int2str(fr_tokens, token, "???");
+}