From branch_1_1
[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  *   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.
11  *
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.
16  *
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
20  *
21  * Copyright 2000,2006  The FreeRADIUS server project
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <freeradius-devel/token.h>
32
33 static const LRAD_NAME_NUMBER tokens[] = {
34         { "=~", T_OP_REG_EQ,    }, /* order is important! */
35         { "!~", T_OP_REG_NE,    },
36         { "{",  T_LCBRACE,      },
37         { "}",  T_RCBRACE,      },
38         { "(",  T_LBRACE,       },
39         { ")",  T_RBRACE,       },
40         { ",",  T_COMMA,        },
41         { "+=", T_OP_ADD,       },
42         { "-=", T_OP_SUB,       },
43         { ":=", T_OP_SET,       },
44         { "=*", T_OP_CMP_TRUE,  },
45         { "!*", T_OP_CMP_FALSE, },
46         { "==", T_OP_CMP_EQ,    },
47         { "=",  T_OP_EQ,        },
48         { "!=", T_OP_NE,        },
49         { ">=", T_OP_GE,        },
50         { ">",  T_OP_GT,        },
51         { "<=", T_OP_LE,        },
52         { "<",  T_OP_LT,        },
53         { "#",  T_HASH,         },
54         { ";",  T_SEMICOLON,    },
55         { NULL, 0,              },
56 };
57
58 /*
59  *      This works only as long as special tokens
60  *      are max. 2 characters, but it's fast.
61  */
62 #define TOKEN_MATCH(bptr, tptr) \
63         ( (tptr)[0] == (bptr)[0] && \
64          ((tptr)[1] == (bptr)[1] || (tptr)[1] == 0))
65
66 /*
67  *      Read a word from a buffer and advance pointer.
68  *      This function knows about escapes and quotes.
69  *
70  *      At end-of-line, buf[0] is set to '\0'.
71  *      Returns 0 or special token value.
72  */
73 static LRAD_TOKEN getthing(char **ptr, char *buf, int buflen, int tok,
74                            const LRAD_NAME_NUMBER *tokenlist)
75 {
76         char    *s, *p;
77         int     quote;
78         int     escape;
79         int     x;
80         const LRAD_NAME_NUMBER*t;
81         LRAD_TOKEN rcode;
82
83         buf[0] = 0;
84
85         /* Skip whitespace */
86         p = *ptr;
87         while (*p && isspace((int) *p))
88                 p++;
89
90         if (*p == 0) {
91                 *ptr = p;
92                 return T_EOL;
93         }
94
95         /*
96          *      Might be a 1 or 2 character token.
97          */
98         if (tok) for (t = tokenlist; t->name; t++) {
99                 if (TOKEN_MATCH(p, t->name)) {
100                         strcpy(buf, t->name);
101                         p += strlen(t->name);
102                         while (isspace((int) *p))
103                                 p++;
104                         *ptr = p;
105                         return (LRAD_TOKEN) t->number;
106                 }
107         }
108
109         /* Read word. */
110         quote = 0;
111         if ((*p == '"') ||
112             (*p == '\'') ||
113             (*p == '`')) {
114                 quote = *p;
115                 p++;
116         }
117         s = buf;
118         escape = 0;
119
120         while (*p && buflen-- > 1) {
121                 if (escape) {
122                         escape = 0;
123                         switch(*p) {
124                                 case 'r':
125                                         *s++ = '\r';
126                                         break;
127                                 case 'n':
128                                         *s++ = '\n';
129                                         break;
130                                 case 't':
131                                         *s++ = '\t';
132                                         break;
133                                 case '"':
134                                         *s++ = '"';
135                                         break;
136                                 case '\'':
137                                         *s++ = '\'';
138                                         break;
139                                 case '`':
140                                         *s++ = '`';
141                                         break;
142                                 default:
143                                         if (*p >= '0' && *p <= '9' &&
144                                             sscanf(p, "%3o", &x) == 1) {
145                                                 *s++ = x;
146                                                 p += 2;
147                                         } else
148                                                 *s++ = *p;
149                                         break;
150                         }
151                         p++;
152                         continue;
153                 }
154                 if (*p == '\\') {
155                         p++;
156                         escape = 1;
157                         continue;
158                 }
159                 if (quote && (*p == quote)) {
160                         p++;
161                         break;
162                 }
163                 if (!quote) {
164                         if (isspace((int) *p))
165                                 break;
166                         if (tok) {
167                                 for (t = tokenlist; t->name; t++)
168                                         if (TOKEN_MATCH(p, t->name))
169                                                 break;
170                                 if (t->name != NULL)
171                                         break;
172                         }
173                 }
174                 *s++ = *p++;
175         }
176         *s++ = 0;
177
178         /* Skip whitespace again. */
179         while (*p && isspace((int) *p))
180                 p++;
181         *ptr = p;
182
183         /* we got SOME form of output string, even if it is empty */
184         switch (quote) {
185         default:
186           rcode = T_BARE_WORD;
187           break;
188
189         case '\'':
190           rcode = T_SINGLE_QUOTED_STRING;
191           break;
192
193         case '"':
194           rcode = T_DOUBLE_QUOTED_STRING;
195           break;
196
197         case '`':
198           rcode = T_BACK_QUOTED_STRING;
199           break;
200         }
201
202         return rcode;
203 }
204
205 /*
206  *      Read a "word" - this means we don't honor
207  *      tokens as delimiters.
208  */
209 int getword(char **ptr, char *buf, int buflen)
210 {
211         return getthing(ptr, buf, buflen, 0, tokens) == T_EOL ? 0 : 1;
212 }
213
214 /*
215  *      Read a bare "word" - this means we don't honor
216  *      tokens as delimiters.
217  */
218 int getbareword(char **ptr, char *buf, int buflen)
219 {
220         LRAD_TOKEN token;
221
222         token = getthing(ptr, buf, buflen, 0, NULL);
223         if (token != T_BARE_WORD) {
224                 return 0;
225         }
226
227         return 1;
228 }
229
230 /*
231  *      Read the next word, use tokens as delimiters.
232  */
233 LRAD_TOKEN gettoken(char **ptr, char *buf, int buflen)
234 {
235         return getthing(ptr, buf, buflen, 1, tokens);
236 }
237
238 /*
239  *      Convert a string to an integer
240  */
241 int lrad_str2int(const LRAD_NAME_NUMBER *table, const char *name, int def)
242 {
243         const LRAD_NAME_NUMBER *this;
244
245         for (this = table; this->name != NULL; this++) {
246                 if (strcasecmp(this->name, name) == 0) {
247                         return this->number;
248                 }
249         }
250
251         return def;
252 }
253
254 /*
255  *      Convert an integer to a string.
256  */
257 const char *lrad_int2str(const LRAD_NAME_NUMBER *table, int number,
258                          const char *def)
259 {
260         const LRAD_NAME_NUMBER *this;
261
262         for (this = table; this->name != NULL; this++) {
263                 if (this->number == number) {
264                         return this->name;
265                 }
266         }
267
268         return def;
269 }