Added new tokens: bare word, double quoted string, single quoted
[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_TRUE,  },
34         { "!*", T_OP_CMP_FALSE, },
35         { "==", T_OP_CMP_EQ,    },
36         { "=",  T_OP_EQ,        },
37         { "!=", T_OP_NE,        },
38         { ">=", T_OP_GE,        },
39         { ">",  T_OP_GT,        },
40         { "<=", T_OP_LE,        },
41         { "<",  T_OP_LT,        },
42         { "#",  T_HASH,         },
43         { ";",  T_SEMICOLON,    },
44         { NULL, 0,              },
45 };
46
47 /*
48  *      This works only as long as special tokens
49  *      are max. 2 characters, but it's fast.
50  */
51 #define TOKEN_MATCH(bptr, tptr) \
52         ( (tptr)[0] == (bptr)[0] && \
53          ((tptr)[1] == (bptr)[1] || (tptr)[1] == 0))
54
55 /*
56  *      Read a word from a buffer and advance pointer.
57  *      This function knows about escapes and quotes.
58  *
59  *      At end-of-line, buf[0] is set to '\0'.
60  *      Returns 0 or special token value.
61  */
62 static LRAD_TOKEN getthing(char **ptr, char *buf, int buflen, int tok,
63                            const TOKEN *tokenlist)
64 {
65         char    *s, *p;
66         int     quote;
67         int     escape;
68         int     x;
69         const TOKEN     *t;
70         LRAD_TOKEN rcode;
71
72         buf[0] = 0;
73
74         /* Skip whitespace */
75         p = *ptr;
76         while (*p && isspace((int) *p))
77                 p++;
78
79         if (*p == 0) {
80                 *ptr = p;
81                 return T_EOL;
82         }
83
84         /*
85          *      Might be a 1 or 2 character token.
86          */
87         if (tok) for (t = tokenlist; t->str; t++) {
88                 if (TOKEN_MATCH(p, t->str)) {
89                         strcpy(buf, t->str);
90                         p += strlen(t->str);
91                         while (isspace((int) *p))
92                                 p++;
93                         *ptr = p;
94                         return t->token;
95                 }
96         }
97
98         /* Read word. */
99         quote = 0;
100         if ((*p == '"') ||
101             (*p == '\'') ||
102             (*p == '`')) {
103                 quote = *p;
104                 p++;
105         }
106         s = buf;
107         escape = 0;
108
109         while (*p && buflen-- > 0) {
110                 if (escape) {
111                         escape = 0;
112                         switch(*p) {
113                                 case 'r':
114                                         *s++ = '\r';
115                                         break;
116                                 case 'n':
117                                         *s++ = '\n';
118                                         break;
119                                 case 't':
120                                         *s++ = '\t';
121                                         break;
122                                 case '"':
123                                         *s++ = '"';
124                                         break;
125                                 case '\'':
126                                         *s++ = '\'';
127                                         break;
128                                 case '`':
129                                         *s++ = '`';
130                                         break;
131                                 default:
132                                         if (*p >= '0' && *p <= '9' &&
133                                             sscanf(p, "%3o", &x) == 1) {
134                                                 *s++ = x;
135                                                 p += 2;
136                                         } else
137                                                 *s++ = *p;
138                                         break;
139                         }
140                         p++;
141                         continue;
142                 }
143                 if (*p == '\\') {
144                         p++;
145                         escape = 1;
146                         continue;
147                 }
148                 if (quote && (*p == quote)) {
149                         p++;
150                         break;
151                 }
152                 if (!quote) {
153                         if (isspace((int) *p))
154                                 break;
155                         if (tok) {
156                                 for (t = tokenlist; t->str; t++)
157                                         if (TOKEN_MATCH(p, t->str))
158                                                 break;
159                                 if (t->str != NULL)
160                                         break;
161                         }
162                 }
163                 *s++ = *p++;
164         }
165         *s++ = 0;
166
167         /* Skip whitespace again. */
168         while (*p && isspace((int) *p))
169                 p++;
170         *ptr = p;
171
172         /* we got SOME form of output string, even if it is empty */
173         switch (quote) {
174         default:
175           rcode = T_BARE_WORD;
176           break;
177
178         case '\'':
179           rcode = T_SINGLE_QUOTED_STRING;
180           break;
181
182         case '"':
183           rcode = T_DOUBLE_QUOTED_STRING;
184           break;
185
186         case '`':
187           rcode = T_BACK_QUOTED_STRING;
188           break;
189         }
190
191         return rcode;
192 }
193
194 /*
195  *      Read a "word" - this means we don't honor
196  *      tokens as delimiters.
197  */
198 int getword(char **ptr, char *buf, int buflen)
199 {
200         return getthing(ptr, buf, buflen, 0, tokens) == T_EOL ? 0 : 1;
201 }
202
203 /*
204  *      Read a bare "word" - this means we don't honor
205  *      tokens as delimiters.
206  */
207 int getbareword(char **ptr, char *buf, int buflen)
208 {
209         LRAD_TOKEN token;
210
211         token = getthing(ptr, buf, buflen, 0, NULL);
212         if (token != T_BARE_WORD) {
213                 return 0;
214         }
215
216         return 1;
217 }
218
219 /*
220  *      Read the next word, use tokens as delimiters.
221  */
222 LRAD_TOKEN gettoken(char **ptr, char *buf, int buflen)
223 {
224         return getthing(ptr, buf, buflen, 1, tokens);
225 }