1 /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    Without limiting anything contained in the foregoing, this file,
15    which is part of C Driver for MySQL (Connector/C), is also subject to the
16    Universal FOSS Exception, version 1.0, a copy of which can be found at
17    http://oss.oracle.com/licenses/universal-foss-exception.
18 
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22    GNU General Public License, version 2.0, for more details.
23 
24    You should have received a copy of the GNU General Public License
25    along with this program; if not, write to the Free Software
26    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
27 
28 /* Functions to handle typelib */
29 
30 #include "mysys_priv.h"
31 #include <m_string.h>
32 #include <m_ctype.h>
33 
34 
35 #define is_field_separator(X) ((X) == ',' || (X) == '=')
36 
find_type_or_exit(const char * x,TYPELIB * typelib,const char * option)37 int find_type_or_exit(const char *x, TYPELIB *typelib, const char *option)
38 {
39   int res;
40   const char **ptr;
41 
42   if ((res= find_type((char *) x, typelib, FIND_TYPE_BASIC)) <= 0)
43   {
44     ptr= typelib->type_names;
45     if (!*x)
46       fprintf(stderr, "No option given to %s\n", option);
47     else
48       fprintf(stderr, "Unknown option to %s: %s\n", option, x);
49     fprintf(stderr, "Alternatives are: '%s'", *ptr);
50     while (*++ptr)
51       fprintf(stderr, ",'%s'", *ptr);
52     fprintf(stderr, "\n");
53     exit(1);
54   }
55   return res;
56 }
57 
58 
59 /**
60   Search after a string in a list of strings. Endspace in x is not compared.
61 
62   @param x              String to find
63   @param typelib        TYPELIB (struct of pointer to values + count)
64   @param flags          flags to tune behaviour: a combination of
65                         FIND_TYPE_NO_PREFIX
66                         FIND_TYPE_ALLOW_NUMBER
67                         FIND_TYPE_COMMA_TERM.
68                         FIND_TYPE_NO_OVERWRITE can be passed but is
69                         superfluous (is always implicitely on).
70 
71   @retval
72     -1  Too many matching values
73   @retval
74     0   No matching value
75   @retval
76     >0  Offset+1 in typelib for matched string
77 */
78 
79 
find_type(const char * x,const TYPELIB * typelib,uint flags)80 int find_type(const char *x, const TYPELIB *typelib, uint flags)
81 {
82   int find,pos;
83   int UNINIT_VAR(findpos);                       /* guarded by find */
84   const char *i;
85   const char *j;
86   DBUG_ENTER("find_type");
87   DBUG_PRINT("enter",("x: '%s'  lib: 0x%lx", x, (long) typelib));
88 
89   DBUG_ASSERT(!(flags & ~(FIND_TYPE_NO_PREFIX | FIND_TYPE_ALLOW_NUMBER |
90                           FIND_TYPE_NO_OVERWRITE | FIND_TYPE_COMMA_TERM)));
91   if (!typelib->count)
92   {
93     DBUG_PRINT("exit",("no count"));
94     DBUG_RETURN(0);
95   }
96   find=0;
97   for (pos=0 ; (j=typelib->type_names[pos]) ; pos++)
98   {
99     for (i=x ;
100     	*i && (!(flags & FIND_TYPE_COMMA_TERM) || !is_field_separator(*i)) &&
101         my_toupper(&my_charset_latin1,*i) ==
102     		my_toupper(&my_charset_latin1,*j) ; i++, j++) ;
103     if (! *j)
104     {
105       while (*i == ' ')
106 	i++;					/* skip_end_space */
107       if (! *i || ((flags & FIND_TYPE_COMMA_TERM) && is_field_separator(*i)))
108 	DBUG_RETURN(pos+1);
109     }
110     if ((!*i &&
111          (!(flags & FIND_TYPE_COMMA_TERM) || !is_field_separator(*i))) &&
112         (!*j || !(flags & FIND_TYPE_NO_PREFIX)))
113     {
114       find++;
115       findpos=pos;
116     }
117   }
118   if (find == 0 && (flags & FIND_TYPE_ALLOW_NUMBER) && x[0] == '#' &&
119       strend(x)[-1] == '#' &&
120       (findpos=atoi(x+1)-1) >= 0 && (uint) findpos < typelib->count)
121     find=1;
122   else if (find == 0 || ! x[0])
123   {
124     DBUG_PRINT("exit",("Couldn't find type"));
125     DBUG_RETURN(0);
126   }
127   else if (find != 1 || (flags & FIND_TYPE_NO_PREFIX))
128   {
129     DBUG_PRINT("exit",("Too many possybilities"));
130     DBUG_RETURN(-1);
131   }
132   DBUG_RETURN(findpos+1);
133 } /* find_type */
134 
135 
136 /**
137   Get name of type nr
138 
139   @note
140   first type is 1, 0 = empty field
141 */
142 
make_type(char * to,uint nr,TYPELIB * typelib)143 void make_type(char * to, uint nr,
144 	       TYPELIB *typelib)
145 {
146   DBUG_ENTER("make_type");
147   if (!nr)
148     to[0]=0;
149   else
150     (void) strmov(to,get_type(typelib,nr-1));
151   DBUG_VOID_RETURN;
152 } /* make_type */
153 
154 
155 /**
156   Get type
157 
158   @note
159   first type is 0
160 */
161 
get_type(TYPELIB * typelib,uint nr)162 const char *get_type(TYPELIB *typelib, uint nr)
163 {
164   if (nr < (uint) typelib->count && typelib->type_names)
165     return(typelib->type_names[nr]);
166   return "?";
167 }
168 
169 
170 /**
171   Create an integer value to represent the supplied comma-seperated
172   string where each string in the TYPELIB denotes a bit position.
173 
174   @param x      string to decompose
175   @param lib    TYPELIB (struct of pointer to values + count)
176   @param err    index (not char position) of string element which was not
177                 found or 0 if there was no error
178 
179   @retval
180     a integer representation of the supplied string
181 */
182 
find_typeset(char * x,TYPELIB * lib,int * err)183 my_ulonglong find_typeset(char *x, TYPELIB *lib, int *err)
184 {
185   my_ulonglong result;
186   int find;
187   char *i;
188   DBUG_ENTER("find_set");
189   DBUG_PRINT("enter",("x: '%s'  lib: 0x%lx", x, (long) lib));
190 
191   if (!lib->count)
192   {
193     DBUG_PRINT("exit",("no count"));
194     DBUG_RETURN(0);
195   }
196   result= 0;
197   *err= 0;
198   while (*x)
199   {
200     (*err)++;
201     i= x;
202     while (*x && !is_field_separator(*x))
203       x++;
204     if (x[0] && x[1])      /* skip separator if found */
205       x++;
206     if ((find= find_type(i, lib, FIND_TYPE_COMMA_TERM) - 1) < 0)
207       DBUG_RETURN(0);
208     result|= (ULL(1) << find);
209   }
210   *err= 0;
211   DBUG_RETURN(result);
212 } /* find_set */
213 
214 
215 /**
216   Create a copy of a specified TYPELIB structure.
217 
218   @param root   pointer to a MEM_ROOT object for allocations
219   @param from   pointer to a source TYPELIB structure
220 
221   @retval
222     pointer to the new TYPELIB structure on successful copy
223   @retval
224     NULL otherwise
225 */
226 
copy_typelib(MEM_ROOT * root,TYPELIB * from)227 TYPELIB *copy_typelib(MEM_ROOT *root, TYPELIB *from)
228 {
229   TYPELIB *to;
230   uint i;
231 
232   if (!from)
233     return NULL;
234 
235   if (!(to= (TYPELIB*) alloc_root(root, sizeof(TYPELIB))))
236     return NULL;
237 
238   if (!(to->type_names= (const char **)
239         alloc_root(root, (sizeof(char *) + sizeof(int)) * (from->count + 1))))
240     return NULL;
241   to->type_lengths= (unsigned int *)(to->type_names + from->count + 1);
242   to->count= from->count;
243   if (from->name)
244   {
245     if (!(to->name= strdup_root(root, from->name)))
246       return NULL;
247   }
248   else
249     to->name= NULL;
250 
251   for (i= 0; i < from->count; i++)
252   {
253     if (!(to->type_names[i]= strmake_root(root, from->type_names[i],
254                                           from->type_lengths[i])))
255       return NULL;
256     to->type_lengths[i]= from->type_lengths[i];
257   }
258   to->type_names[to->count]= NULL;
259   to->type_lengths[to->count]= 0;
260 
261   return to;
262 }
263 
264 
265 static const char *on_off_default_names[]= { "off","on","default", 0};
266 static TYPELIB on_off_default_typelib= {array_elements(on_off_default_names)-1,
267                                         "", on_off_default_names, 0};
268 
269 /**
270   Parse a TYPELIB name from the buffer
271 
272   @param lib          Set of names to scan for.
273   @param strpos INOUT Start of the buffer (updated to point to the next
274                       character after the name)
275   @param end          End of the buffer
276 
277   @note
278   The buffer is assumed to contain one of the names specified in the TYPELIB,
279   followed by comma, '=', or end of the buffer.
280 
281   @retval
282     0   No matching name
283   @retval
284     >0  Offset+1 in typelib for matched name
285 */
286 
parse_name(const TYPELIB * lib,const char ** strpos,const char * end)287 static uint parse_name(const TYPELIB *lib, const char **strpos, const char *end)
288 {
289   const char *pos= *strpos;
290   uint find= find_type(pos, lib, FIND_TYPE_COMMA_TERM);
291   for (; pos != end && *pos != '=' && *pos !=',' ; pos++);
292   *strpos= pos;
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 
find_set_from_flags(const TYPELIB * lib,uint default_name,my_ulonglong cur_set,my_ulonglong default_set,const char * str,uint length,char ** err_pos,uint * err_len)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= 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