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
27int 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
69static 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*/
133int 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
145void 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
164const 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
185my_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
229TYPELIB *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
267static const char *on_off_default_names[]= { "off","on","default", 0};
268static 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
289static 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
331my_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