| 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 | |