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