do NOT return EOL if the parsed string is empty. An empty string
[freeradius.git] / src / lib / token.c
1 /*
2  * token.c      Read the next token from a string.
3  *              Yes it's pretty primitive but effective.
4  *
5  * Version:     $Id$
6  *
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include "token.h"
14
15 static const char rcsid[] = "$Id$";
16
17 typedef struct {
18         const char *str;
19         int token;
20 } TOKEN;
21
22 static const TOKEN tokens[] = {
23         { "=~", T_OP_REG_EQ,    }, /* order is important! */
24         { "!~", T_OP_REG_NE,    },
25         { "{",  T_LCBRACE,      },
26         { "}",  T_RCBRACE,      },
27         { "(",  T_LBRACE,       },
28         { ")",  T_RBRACE,       },
29         { ",",  T_COMMA,        },
30         { "+=", T_OP_ADD,       },
31         { "-=", T_OP_SUB,       },
32         { ":=", T_OP_SET,       },
33         { "==", T_OP_CMP_EQ,    },
34         { "=",  T_OP_EQ,        },
35         { "!=", T_OP_NE,        },
36         { ">=", T_OP_GE,        },
37         { ">",  T_OP_GT,        },
38         { "<=", T_OP_LE,        },
39         { "<",  T_OP_LT,        },
40         { "#",  T_HASH,         },
41         { NULL, 0,              },
42 };
43
44 /*
45  *      This works only as long as special tokens
46  *      are max. 2 characters, but it's fast.
47  */
48 #define TOKEN_MATCH(bptr, tptr) \
49         ( (tptr)[0] == (bptr)[0] && \
50          ((tptr)[1] == (bptr)[1] || (tptr)[1] == 0))
51
52 /*
53  *      Read a word from a buffer and advance pointer.
54  *      This function knows about escapes and quotes.
55  *
56  *      At end-of-line, buf[0] is set to '\0'.
57  *      Returns 0 or special token value.
58  */
59 static int getthing(char **ptr, char *buf, int buflen, int tok)
60 {
61         char    *s, *p;
62         int     quote;
63         int     escape;
64         int     x;
65         const TOKEN     *t;
66
67         buf[0] = 0;
68
69         /* Skip whitespace */
70         p = *ptr;
71         while (*p && isspace(*p))
72                 p++;
73
74         if (*p == 0) {
75                 *ptr = p;
76                 return T_EOL;
77         }
78
79         /*
80          *      Might be a 1 or 2 character token.
81          */
82         if (tok) for (t = tokens; t->str; t++) {
83                 if (TOKEN_MATCH(p, t->str)) {
84                         strcpy(buf, t->str);
85                         p += strlen(t->str);
86                         while (isspace(*p))
87                                 p++;
88                         *ptr = p;
89                         return t->token;
90                 }
91         }
92
93         /* Read word. */
94         quote = 0;
95         if (*p == '"') {
96                 quote = 1;
97                 p++;
98         }
99         s = buf;
100         escape = 0;
101
102         while (*p && buflen-- > 0) {
103                 if (escape) {
104                         escape = 0;
105                         switch(*p) {
106                                 case 'r':
107                                         *s++ = '\r';
108                                         break;
109                                 case 'n':
110                                         *s++ = '\n';
111                                         break;
112                                 case 't':
113                                         *s++ = '\t';
114                                         break;
115                                 case '"':
116                                         *s++ = '"';
117                                         break;
118                                 default:
119                                         if (*p >= '0' && *p <= '9' &&
120                                             sscanf(p, "%3o", &x) == 1) {
121                                                 *s++ = x;
122                                                 p += 2;
123                                         } else
124                                                 *s++ = *p;
125                                         break;
126                         }
127                         p++;
128                         continue;
129                 }
130                 if (*p == '\\') {
131                         p++;
132                         escape = 1;
133                         continue;
134                 }
135                 if (quote && *p == '"') {
136                         p++;
137                         break;
138                 }
139                 if (!quote) {
140                         if (isspace(*p))
141                                 break;
142                         if (tok) {
143                                 for (t = tokens; t->str; t++)
144                                         if (TOKEN_MATCH(p, t->str))
145                                                 break;
146                                 if (t->str != NULL)
147                                         break;
148                         }
149                 }
150                 *s++ = *p++;
151         }
152         *s++ = 0;
153
154         /* Skip whitespace again. */
155         while (*p && isspace(*p))
156                 p++;
157         *ptr = p;
158
159         /* we got SOME form out output string, even if it is empty */
160         return 0;
161 }
162
163 /*
164  *      Read a "word" - this means we don't honor
165  *      tokens as delimiters.
166  */
167 int getword(char **ptr, char *buf, int buflen)
168 {
169         return getthing(ptr, buf, buflen, 0) == T_EOL ? 0 : 1;
170 }
171
172 /*
173  *      Read the next word, use tokens as delimiters.
174  */
175 int gettoken(char **ptr, char *buf, int buflen)
176 {
177         return getthing(ptr, buf, buflen, 1);
178 }