1 /*
2  * Test reading data with SQLBindCol
3  */
4 #include "common.h"
5 #include <ctype.h>
6 #include "parser.h"
7 
8 #include <common/test_assert.h>
9 
10 unsigned int odbc_line_num;
11 
12 void
odbc_fatal(const char * msg,...)13 odbc_fatal(const char *msg, ...)
14 {
15 	va_list ap;
16 
17 	va_start(ap, msg);
18 	if (msg[0] == ':')
19 		fprintf(stderr, "Line %u", odbc_line_num);
20 	vfprintf(stderr, msg, ap);
21 	va_end(ap);
22 
23 	exit(1);
24 }
25 
26 #define SEP " \t\n"
27 
28 const char *
odbc_get_tok(char ** p)29 odbc_get_tok(char **p)
30 {
31 	char *s = *p, *end;
32 	s += strspn(s, SEP);
33 	if (!*s) return NULL;
34 	end = s + strcspn(s, SEP);
35 	*end = 0;
36 	*p = end+1;
37 	return s;
38 }
39 
40 static void
parse_cstr(char * s)41 parse_cstr(char *s)
42 {
43 	char hexbuf[4];
44 	char *d = s;
45 
46 	while (*s) {
47 		if (*s != '\\') {
48 			*d++ = *s++;
49 			continue;
50 		}
51 
52 		switch (*++s) {
53 		case '\"':
54 			*d++ = *s++;
55 			break;
56 		case '\\':
57 			*d++ = *s++;
58 			break;
59 		case 'x':
60 			if (strlen(s) < 3)
61 				odbc_fatal(": wrong string format\n");
62 			memcpy(hexbuf, ++s, 2);
63 			hexbuf[2] = 0;
64 			*d++ = (char) strtoul(hexbuf, NULL, 16);
65 			s += 2;
66 			break;
67 		default:
68 			odbc_fatal(": wrong string format\n");
69 		}
70 	}
71 	*d = 0;
72 }
73 
74 const char *
odbc_get_str(char ** p)75 odbc_get_str(char **p)
76 {
77 	char *s = *p, *end;
78 	s += strspn(s, SEP);
79 	if (!*s) odbc_fatal(": unable to get string\n");
80 
81 	if (strncmp(s, "\"\"\"", 3) == 0) {
82 		s += 3;
83 		end = strstr(s, "\"\"\"");
84 		if (!end) odbc_fatal(": string not terminated\n");
85 		*end = 0;
86 		*p = end+3;
87 	} else if (s[0] == '\"') {
88 		++s;
89 		end = strchr(s, '\"');
90 		if (!end) odbc_fatal(": string not terminated\n");
91 		*end = 0;
92 		parse_cstr(s);
93 		*p = end+1;
94 	} else {
95 		return odbc_get_tok(p);
96 	}
97 	return s;
98 }
99 
100 enum { MAX_BOOLS = 64 };
101 typedef struct {
102 	char *name;
103 	int value;
104 } bool_t;
105 static bool_t bools[MAX_BOOLS];
106 
107 void
odbc_set_bool(const char * name,int value)108 odbc_set_bool(const char *name, int value)
109 {
110 	unsigned n;
111 	value = !!value;
112 	for (n = 0; n < MAX_BOOLS && bools[n].name; ++n)
113 		if (!strcmp(bools[n].name, name)) {
114 			bools[n]. value = value;
115 			return;
116 		}
117 
118 	if (n == MAX_BOOLS)
119 		odbc_fatal(": no more boolean variable free\n");
120 	bools[n].name = strdup(name);
121 	if (!bools[n].name) odbc_fatal(": out of memory\n");
122 	bools[n].value = value;
123 }
124 
125 static int
get_bool(const char * name)126 get_bool(const char *name)
127 {
128 	unsigned n;
129 	if (!name)
130 		odbc_fatal(": boolean variable not provided\n");
131 	for (n = 0; n < MAX_BOOLS && bools[n].name; ++n)
132 		if (!strcmp(bools[n].name, name))
133 			return bools[n]. value;
134 
135 	odbc_fatal(": boolean variable %s not found\n", name);
136 	return 0;
137 }
138 
139 /** initialize booleans, call after connection */
140 void
odbc_init_bools(void)141 odbc_init_bools(void)
142 {
143 	int big_endian = 1;
144 
145 	if (((char *) &big_endian)[0] == 1)
146 		big_endian = 0;
147 	odbc_set_bool("bigendian", big_endian);
148 
149 	odbc_set_bool("msdb", odbc_db_is_microsoft());
150 	odbc_set_bool("freetds", odbc_driver_is_freetds());
151 }
152 
153 void
odbc_clear_bools(void)154 odbc_clear_bools(void)
155 {
156 	unsigned n;
157 	for (n = 0; n < MAX_BOOLS && bools[n].name; ++n) {
158 		free(bools[n].name);
159 		bools[n].name = NULL;
160 	}
161 }
162 
163 enum { MAX_CONDITIONS = 32 };
164 static char conds[MAX_CONDITIONS];
165 static unsigned cond_level = 0;
166 
167 static int
pop_condition(void)168 pop_condition(void)
169 {
170 	if (cond_level == 0) odbc_fatal(": no related if\n");
171 	return conds[--cond_level];
172 }
173 
174 static void
push_condition(int cond)175 push_condition(int cond)
176 {
177 	if (cond != 0 && cond != 1) odbc_fatal(": invalid cond value %d\n", cond);
178 	if (cond_level >= MAX_CONDITIONS) odbc_fatal(": too much nested conditions\n");
179 	conds[cond_level++] = cond;
180 }
181 
182 static int
get_not_cond(char ** p)183 get_not_cond(char **p)
184 {
185 	int cond;
186 	const char *tok = odbc_get_tok(p);
187 	if (!tok) odbc_fatal(": wrong condition syntax\n");
188 
189 	if (!strcmp(tok, "not"))
190 		cond = !get_bool(odbc_get_tok(p));
191 	else
192 		cond = get_bool(tok);
193 
194 	return cond;
195 }
196 
197 static int
get_condition(char ** p)198 get_condition(char **p)
199 {
200 	int cond1 = get_not_cond(p), cond2;
201 	const char *tok;
202 
203 	while ((tok=odbc_get_tok(p)) != NULL) {
204 
205 		cond2 = get_not_cond(p);
206 
207 		if (!strcmp(tok, "or"))
208 			cond1 = cond1 || cond2;
209 		else if (!strcmp(tok, "and"))
210 			cond1 = cond1 && cond2;
211 		else odbc_fatal(": wrong condition syntax\n");
212 	}
213 	return cond1;
214 }
215 
216 static FILE *parse_file;
217 static char line_buf[1024];
218 
219 void
odbc_init_parser(FILE * f)220 odbc_init_parser(FILE *f)
221 {
222 	if (parse_file)
223 		odbc_fatal("parser file already setup\n");
224 	parse_file = f;
225 	odbc_line_num = 0;
226 }
227 
228 const char *
odbc_get_cmd_line(char ** p_s,int * cond)229 odbc_get_cmd_line(char **p_s, int *cond)
230 {
231 	while (fgets(line_buf, sizeof(line_buf), parse_file)) {
232 		char *p = line_buf;
233 		const char *cmd;
234 
235 		++odbc_line_num;
236 
237 		cmd = odbc_get_tok(&p);
238 
239 		/* skip comments */
240 		if (!cmd || cmd[0] == '#' || cmd[0] == 0 || cmd[0] == '\n')
241 			continue;
242 
243 		/* conditional statement */
244 		if (!strcmp(cmd, "else")) {
245 			int c = pop_condition();
246 			push_condition(c);
247 			*cond = c && !*cond;
248 			continue;
249 		}
250 		if (!strcmp(cmd, "endif")) {
251 			*cond = pop_condition();
252 			continue;
253 		}
254 		if (!strcmp(cmd, "if")) {
255 			push_condition(*cond);
256 			if (*cond)
257 				*cond = get_condition(&p);
258 			continue;
259 		}
260 
261 		if (strcmp(cmd, "tds_version_cmp") == 0) {
262 			const char *bool_name = odbc_get_tok(&p);
263 			const char *cmp = odbc_get_tok(&p);
264 			const char *s_ver = odbc_get_tok(&p);
265 			int ver = odbc_tds_version();
266 			int expected;
267 			int res;
268 			unsigned M, m;
269 
270 			if (!cmp || !s_ver)
271 				odbc_fatal(": missing parameters\n");
272 			if (sscanf(s_ver, "%u.%u", &M, &m) != 2)
273 				odbc_fatal(": invalid version %s\n", s_ver);
274 			expected = M * 0x100u + m;
275 
276 			if (strcmp(cmp, ">") == 0)
277 				res = ver > expected;
278 			else if (strcmp(cmp, ">=") == 0)
279 				res = ver >= expected;
280 			else if (strcmp(cmp, "<") == 0)
281 				res = ver < expected;
282 			else if (strcmp(cmp, "<=") == 0)
283 				res = ver <= expected;
284 			else if (strcmp(cmp, "==") == 0)
285 				res = ver == expected;
286 			else if (strcmp(cmp, "!=") == 0)
287 				res = ver != expected;
288 			else
289 				odbc_fatal(": invalid operator %s\n", cmp);
290 
291 			if (*cond)
292 				odbc_set_bool(bool_name, res);
293 			continue;
294 		}
295 
296 		*p_s = p;
297 		return cmd;
298 	}
299 	return NULL;
300 }
301 
302