1 | /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. |
2 | Copyright (c) 2009, 2013, Monty Program Ab. |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ |
16 | |
17 | /* Functions to handle typelib */ |
18 | |
19 | #include "mysys_priv.h" |
20 | #include <m_string.h> |
21 | #include <m_ctype.h> |
22 | |
23 | |
24 | #define is_field_separator(F, X) \ |
25 | ((F & FIND_TYPE_COMMA_TERM) && ((X) == ',' || (X) == '=')) |
26 | |
27 | int find_type_with_warning(const char *x, TYPELIB *typelib, const char *option) |
28 | { |
29 | int res; |
30 | const char **ptr; |
31 | |
32 | if ((res= find_type((char *) x, typelib, FIND_TYPE_BASIC)) <= 0) |
33 | { |
34 | ptr= typelib->type_names; |
35 | if (!*x) |
36 | fprintf(stderr, "No option given to %s\n" , option); |
37 | else |
38 | fprintf(stderr, "Unknown option to %s: %s\n" , option, x); |
39 | fprintf(stderr, "Alternatives are: '%s'" , *ptr); |
40 | while (*++ptr) |
41 | fprintf(stderr, ",'%s'" , *ptr); |
42 | fprintf(stderr, "\n" ); |
43 | } |
44 | return res; |
45 | } |
46 | |
47 | |
48 | /** |
49 | Search after a string in a list of strings. Endspace in x is not compared. |
50 | |
51 | @param x pointer to string to find |
52 | (not necessarily zero-terminated). |
53 | by return it'll be advanced to point to the terminator. |
54 | @param typelib TYPELIB (struct of pointer to values + count) |
55 | @param flags flags to tune behaviour: a combination of |
56 | FIND_TYPE_NO_PREFIX |
57 | FIND_TYPE_COMMA_TERM. |
58 | @param eol a pointer to the end of the string. |
59 | |
60 | @retval |
61 | -1 Too many matching values |
62 | @retval |
63 | 0 No matching value |
64 | @retval |
65 | >0 Offset+1 in typelib for matched string |
66 | */ |
67 | |
68 | |
69 | static int find_type_eol(const char **x, const TYPELIB *typelib, uint flags, |
70 | const char *eol) |
71 | { |
72 | int find,pos; |
73 | int UNINIT_VAR(findpos); /* guarded by find */ |
74 | const char *UNINIT_VAR(termptr); |
75 | const char *i; |
76 | const char *j; |
77 | CHARSET_INFO *cs= &my_charset_latin1; |
78 | DBUG_ENTER("find_type_eol" ); |
79 | DBUG_PRINT("enter" ,("x: '%s' lib: %p" , *x, typelib)); |
80 | |
81 | DBUG_ASSERT(!(flags & ~(FIND_TYPE_NO_PREFIX | FIND_TYPE_COMMA_TERM))); |
82 | |
83 | if (!typelib->count) |
84 | { |
85 | DBUG_PRINT("exit" ,("no count" )); |
86 | DBUG_RETURN(0); |
87 | } |
88 | find=0; |
89 | for (pos=0 ; (j=typelib->type_names[pos]) ; pos++) |
90 | { |
91 | for (i=*x ; |
92 | i < eol && !is_field_separator(flags, *i) && |
93 | my_toupper(cs, *i) == my_toupper(cs, *j) ; i++, j++) ; |
94 | if (! *j) |
95 | { |
96 | while (i < eol && *i == ' ') |
97 | i++; /* skip_end_space */ |
98 | if (i >= eol || is_field_separator(flags, *i)) |
99 | { |
100 | *x= i; |
101 | DBUG_RETURN(pos+1); |
102 | } |
103 | } |
104 | if ((i >= eol && !is_field_separator(flags, *i)) && |
105 | (!*j || !(flags & FIND_TYPE_NO_PREFIX))) |
106 | { |
107 | find++; |
108 | findpos=pos; |
109 | termptr=i; |
110 | } |
111 | } |
112 | if (find == 0 || *x == eol) |
113 | { |
114 | DBUG_PRINT("exit" ,("Couldn't find type" )); |
115 | DBUG_RETURN(0); |
116 | } |
117 | else if (find != 1 || (flags & FIND_TYPE_NO_PREFIX)) |
118 | { |
119 | DBUG_PRINT("exit" ,("Too many possibilities" )); |
120 | DBUG_RETURN(-1); |
121 | } |
122 | *x= termptr; |
123 | DBUG_RETURN(findpos+1); |
124 | } /* find_type_eol */ |
125 | |
126 | |
127 | /** |
128 | Search after a string in a list of strings. Endspace in x is not compared. |
129 | |
130 | Same as find_type_eol, but for zero-terminated strings, |
131 | and without advancing the pointer. |
132 | */ |
133 | int find_type(const char *x, const TYPELIB *typelib, uint flags) |
134 | { |
135 | return find_type_eol(&x, typelib, flags, x + strlen(x)); |
136 | } |
137 | |
138 | /** |
139 | Get name of type nr |
140 | |
141 | @note |
142 | first type is 1, 0 = empty field |
143 | */ |
144 | |
145 | void make_type(register char * to, register uint nr, |
146 | register TYPELIB *typelib) |
147 | { |
148 | DBUG_ENTER("make_type" ); |
149 | if (!nr) |
150 | to[0]=0; |
151 | else |
152 | (void) strmov(to,get_type(typelib,nr-1)); |
153 | DBUG_VOID_RETURN; |
154 | } /* make_type */ |
155 | |
156 | |
157 | /** |
158 | Get type |
159 | |
160 | @note |
161 | first type is 0 |
162 | */ |
163 | |
164 | const char *get_type(TYPELIB *typelib, uint nr) |
165 | { |
166 | if (nr < (uint) typelib->count && typelib->type_names) |
167 | return(typelib->type_names[nr]); |
168 | return "?" ; |
169 | } |
170 | |
171 | |
172 | /** |
173 | Create an integer value to represent the supplied comma-separated |
174 | string where each string in the TYPELIB denotes a bit position. |
175 | |
176 | @param x string to decompose |
177 | @param lib TYPELIB (struct of pointer to values + count) |
178 | @param err index (not char position) of string element which was not |
179 | found or 0 if there was no error |
180 | |
181 | @retval |
182 | a integer representation of the supplied string |
183 | */ |
184 | |
185 | my_ulonglong find_typeset(char *x, TYPELIB *lib, int *err) |
186 | { |
187 | my_ulonglong result; |
188 | int find; |
189 | char *i; |
190 | DBUG_ENTER("find_set" ); |
191 | DBUG_PRINT("enter" ,("x: '%s' lib: %p" , x, lib)); |
192 | |
193 | if (!lib->count) |
194 | { |
195 | DBUG_PRINT("exit" ,("no count" )); |
196 | DBUG_RETURN(0); |
197 | } |
198 | result= 0; |
199 | *err= 0; |
200 | while (*x) |
201 | { |
202 | (*err)++; |
203 | i= x; |
204 | while (*x && *x != ',') |
205 | x++; |
206 | if (x[0] && x[1]) /* skip separator if found */ |
207 | x++; |
208 | if ((find= find_type(i, lib, FIND_TYPE_COMMA_TERM) - 1) < 0) |
209 | DBUG_RETURN(0); |
210 | result|= (1ULL << find); |
211 | } |
212 | *err= 0; |
213 | DBUG_RETURN(result); |
214 | } /* find_set */ |
215 | |
216 | |
217 | /** |
218 | Create a copy of a specified TYPELIB structure. |
219 | |
220 | @param root pointer to a MEM_ROOT object for allocations |
221 | @param from pointer to a source TYPELIB structure |
222 | |
223 | @retval |
224 | pointer to the new TYPELIB structure on successful copy |
225 | @retval |
226 | NULL otherwise |
227 | */ |
228 | |
229 | TYPELIB *copy_typelib(MEM_ROOT *root, TYPELIB *from) |
230 | { |
231 | TYPELIB *to; |
232 | uint i; |
233 | |
234 | if (!from) |
235 | return NULL; |
236 | |
237 | if (!(to= (TYPELIB*) alloc_root(root, sizeof(TYPELIB)))) |
238 | return NULL; |
239 | |
240 | if (!(to->type_names= (const char **) |
241 | alloc_root(root, (sizeof(char *) + sizeof(int)) * (from->count + 1)))) |
242 | return NULL; |
243 | to->type_lengths= (unsigned int *)(to->type_names + from->count + 1); |
244 | to->count= from->count; |
245 | if (from->name) |
246 | { |
247 | if (!(to->name= strdup_root(root, from->name))) |
248 | return NULL; |
249 | } |
250 | else |
251 | to->name= NULL; |
252 | |
253 | for (i= 0; i < from->count; i++) |
254 | { |
255 | if (!(to->type_names[i]= strmake_root(root, from->type_names[i], |
256 | from->type_lengths[i]))) |
257 | return NULL; |
258 | to->type_lengths[i]= from->type_lengths[i]; |
259 | } |
260 | to->type_names[to->count]= NULL; |
261 | to->type_lengths[to->count]= 0; |
262 | |
263 | return to; |
264 | } |
265 | |
266 | |
267 | static const char *on_off_default_names[]= { "off" ,"on" ,"default" , 0}; |
268 | static TYPELIB on_off_default_typelib= {array_elements(on_off_default_names)-1, |
269 | "" , on_off_default_names, 0}; |
270 | |
271 | /** |
272 | Parse a TYPELIB name from the buffer |
273 | |
274 | @param lib Set of names to scan for. |
275 | @param strpos INOUT Start of the buffer (updated to point to the next |
276 | character after the name) |
277 | @param end End of the buffer |
278 | |
279 | @note |
280 | The buffer is assumed to contain one of the names specified in the TYPELIB, |
281 | followed by comma, '=', or end of the buffer. |
282 | |
283 | @retval |
284 | 0 No matching name |
285 | @retval |
286 | >0 Offset+1 in typelib for matched name |
287 | */ |
288 | |
289 | static uint parse_name(const TYPELIB *lib, const char **pos, const char *end) |
290 | { |
291 | uint find= find_type_eol(pos, lib, |
292 | FIND_TYPE_COMMA_TERM | FIND_TYPE_NO_PREFIX, end); |
293 | return find; |
294 | } |
295 | |
296 | /** |
297 | Parse and apply a set of flag assingments |
298 | |
299 | @param lib Flag names |
300 | @param default_name Number of "default" in the typelib |
301 | @param cur_set Current set of flags (start from this state) |
302 | @param default_set Default set of flags (use this for assign-default |
303 | keyword and flag=default assignments) |
304 | @param str String to be parsed |
305 | @param length Length of the string |
306 | @param err_pos OUT If error, set to point to start of wrong set string |
307 | NULL on success |
308 | @param err_len OUT If error, set to the length of wrong set string |
309 | |
310 | @details |
311 | Parse a set of flag assignments, that is, parse a string in form: |
312 | |
313 | param_name1=value1,param_name2=value2,... |
314 | |
315 | where the names are specified in the TYPELIB, and each value can be |
316 | either 'on','off', or 'default'. Setting the same name twice is not |
317 | allowed. |
318 | |
319 | Besides param=val assignments, we support the "default" keyword (keyword |
320 | #default_name in the typelib). It can be used one time, if specified it |
321 | causes us to build the new set over the default_set rather than cur_set |
322 | value. |
323 | |
324 | @note |
325 | it's not charset aware |
326 | |
327 | @retval |
328 | Parsed set value if (*errpos == NULL), otherwise undefined |
329 | */ |
330 | |
331 | my_ulonglong find_set_from_flags(const TYPELIB *lib, uint default_name, |
332 | my_ulonglong cur_set, my_ulonglong default_set, |
333 | const char *str, uint length, |
334 | char **err_pos, uint *err_len) |
335 | { |
336 | const char *end= str + length; |
337 | my_ulonglong flags_to_set= 0, flags_to_clear= 0, res; |
338 | my_bool set_defaults= 0; |
339 | |
340 | *err_pos= 0; /* No error yet */ |
341 | if (str != end) |
342 | { |
343 | const char *start= str; |
344 | for (;;) |
345 | { |
346 | const char *pos= start; |
347 | uint flag_no, value; |
348 | |
349 | if (!(flag_no= parse_name(lib, &pos, end))) |
350 | goto err; |
351 | |
352 | if (flag_no == default_name) |
353 | { |
354 | /* Using 'default' twice isn't allowed. */ |
355 | if (set_defaults) |
356 | goto err; |
357 | set_defaults= TRUE; |
358 | } |
359 | else |
360 | { |
361 | my_ulonglong bit= (1ULL << (flag_no - 1)); |
362 | /* parse the '=on|off|default' */ |
363 | if ((flags_to_clear | flags_to_set) & bit || |
364 | pos >= end || *pos++ != '=' || |
365 | !(value= parse_name(&on_off_default_typelib, &pos, end))) |
366 | goto err; |
367 | |
368 | if (value == 1) /* this is '=off' */ |
369 | flags_to_clear|= bit; |
370 | else if (value == 2) /* this is '=on' */ |
371 | flags_to_set|= bit; |
372 | else /* this is '=default' */ |
373 | { |
374 | if (default_set & bit) |
375 | flags_to_set|= bit; |
376 | else |
377 | flags_to_clear|= bit; |
378 | } |
379 | } |
380 | if (pos >= end) |
381 | break; |
382 | |
383 | if (*pos++ != ',') |
384 | goto err; |
385 | |
386 | start=pos; |
387 | continue; |
388 | err: |
389 | *err_pos= (char*)start; |
390 | *err_len= (uint)(end - start); |
391 | break; |
392 | } |
393 | } |
394 | res= set_defaults? default_set : cur_set; |
395 | res|= flags_to_set; |
396 | res&= ~flags_to_clear; |
397 | return res; |
398 | } |
399 | |
400 | |