| 1 | /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. |
| 2 | Copyright (c) 2009-2011, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ |
| 16 | |
| 17 | #include "strings_def.h" |
| 18 | #include <m_ctype.h> |
| 19 | #include <my_sys.h> |
| 20 | #include <my_base.h> |
| 21 | #include <my_handler_errors.h> |
| 22 | #include <mysql_com.h> /* For FLOATING_POINT_DECIMALS */ |
| 23 | |
| 24 | #define MAX_ARGS 32 /* max positional args count*/ |
| 25 | #define MAX_PRINT_INFO 32 /* max print position count */ |
| 26 | #define MAX_WIDTH 65535 |
| 27 | |
| 28 | #define LENGTH_ARG 1 |
| 29 | #define WIDTH_ARG 2 |
| 30 | #define PREZERO_ARG 4 |
| 31 | #define ESCAPED_ARG 8 |
| 32 | |
| 33 | typedef struct pos_arg_info ARGS_INFO; |
| 34 | typedef struct print_info PRINT_INFO; |
| 35 | |
| 36 | struct pos_arg_info |
| 37 | { |
| 38 | char arg_type; /* argument type */ |
| 39 | uint have_longlong; /* used from integer values */ |
| 40 | char *str_arg; /* string value of the arg */ |
| 41 | longlong longlong_arg; /* integer value of the arg */ |
| 42 | double double_arg; /* double value of the arg */ |
| 43 | }; |
| 44 | |
| 45 | |
| 46 | struct print_info |
| 47 | { |
| 48 | char arg_type; /* argument type */ |
| 49 | size_t arg_idx; /* index of the positional arg */ |
| 50 | size_t length; /* print width or arg index */ |
| 51 | size_t width; /* print width or arg index */ |
| 52 | uint flags; |
| 53 | const char *begin; /**/ |
| 54 | const char *end; /**/ |
| 55 | }; |
| 56 | |
| 57 | |
| 58 | /** |
| 59 | Calculates print length or index of positional argument |
| 60 | |
| 61 | @param fmt processed string |
| 62 | @param length print length or index of positional argument |
| 63 | @param pre_zero returns flags with PREZERO_ARG set if necessary |
| 64 | |
| 65 | @retval |
| 66 | string position right after length digits |
| 67 | */ |
| 68 | |
| 69 | static const char *get_length(const char *fmt, size_t *length, uint *pre_zero) |
| 70 | { |
| 71 | |
| 72 | for (; my_isdigit(&my_charset_latin1, *fmt); fmt++) |
| 73 | { |
| 74 | *length= *length * 10 + (uint)(*fmt - '0'); |
| 75 | if (!*length) |
| 76 | *pre_zero|= PREZERO_ARG; /* first digit was 0 */ |
| 77 | } |
| 78 | return fmt; |
| 79 | } |
| 80 | |
| 81 | |
| 82 | /* |
| 83 | Get argument for '*' parameter |
| 84 | |
| 85 | @param fmt processed string |
| 86 | @param args_arr Arguments to printf |
| 87 | @param arg_count Number of arguments to printf |
| 88 | @param length returns length of argument |
| 89 | @param flag returns flags with PREZERO_ARG set if necessary |
| 90 | |
| 91 | @return new fmt |
| 92 | */ |
| 93 | |
| 94 | static const char *get_length_arg(const char *fmt, ARGS_INFO *args_arr, |
| 95 | size_t *arg_count, size_t *length, uint *flags) |
| 96 | { |
| 97 | fmt= get_length(fmt+1, length, flags); |
| 98 | *arg_count= MY_MAX(*arg_count, *length); |
| 99 | (*length)--; |
| 100 | DBUG_ASSERT(*fmt == '$' && *length < MAX_ARGS); |
| 101 | args_arr[*length].arg_type= 'd'; |
| 102 | args_arr[*length].have_longlong= 0; |
| 103 | return fmt+1; |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | Calculates print width or index of positional argument |
| 108 | |
| 109 | @param fmt processed string |
| 110 | @param have_longlong TRUE if longlong is required |
| 111 | |
| 112 | @retval |
| 113 | string position right after modifier symbol |
| 114 | */ |
| 115 | |
| 116 | static const char *check_longlong(const char *fmt, uint *have_longlong) |
| 117 | { |
| 118 | *have_longlong= 0; |
| 119 | if (*fmt == 'l') |
| 120 | { |
| 121 | fmt++; |
| 122 | if (*fmt != 'l') |
| 123 | *have_longlong= (sizeof(long) == sizeof(longlong)); |
| 124 | else |
| 125 | { |
| 126 | fmt++; |
| 127 | *have_longlong= 1; |
| 128 | } |
| 129 | } |
| 130 | else if (*fmt == 'z') |
| 131 | { |
| 132 | fmt++; |
| 133 | *have_longlong= (sizeof(size_t) == sizeof(longlong)); |
| 134 | } |
| 135 | else if (*fmt == 'p') |
| 136 | *have_longlong= (sizeof(void *) == sizeof(longlong)); |
| 137 | return fmt; |
| 138 | } |
| 139 | |
| 140 | |
| 141 | /** |
| 142 | Returns escaped string |
| 143 | |
| 144 | @param cs string charset |
| 145 | @param to buffer where escaped string will be placed |
| 146 | @param end end of buffer |
| 147 | @param par string to escape |
| 148 | @param par_len string length |
| 149 | @param quote_char character for quoting |
| 150 | |
| 151 | @retval |
| 152 | position in buffer which points on the end of escaped string |
| 153 | */ |
| 154 | |
| 155 | static char *backtick_string(CHARSET_INFO *cs, char *to, const char *end, |
| 156 | char *par, size_t par_len, char quote_char) |
| 157 | { |
| 158 | uint char_len; |
| 159 | char *start= to; |
| 160 | char *par_end= par + par_len; |
| 161 | size_t buff_length= (size_t) (end - to); |
| 162 | |
| 163 | if (buff_length <= par_len) |
| 164 | goto err; |
| 165 | *start++= quote_char; |
| 166 | |
| 167 | for ( ; par < par_end; par+= char_len) |
| 168 | { |
| 169 | uchar c= *(uchar *) par; |
| 170 | char_len= my_charlen_fix(cs, par, par_end); |
| 171 | if (char_len == 1 && c == (uchar) quote_char ) |
| 172 | { |
| 173 | if (start + 1 >= end) |
| 174 | goto err; |
| 175 | *start++= quote_char; |
| 176 | } |
| 177 | if (start + char_len >= end) |
| 178 | goto err; |
| 179 | start= strnmov(start, par, char_len); |
| 180 | } |
| 181 | |
| 182 | if (start + 1 >= end) |
| 183 | goto err; |
| 184 | *start++= quote_char; |
| 185 | return start; |
| 186 | |
| 187 | err: |
| 188 | *to='\0'; |
| 189 | return to; |
| 190 | } |
| 191 | |
| 192 | |
| 193 | /** |
| 194 | Prints string argument |
| 195 | */ |
| 196 | |
| 197 | static char *process_str_arg(CHARSET_INFO *cs, char *to, const char *end, |
| 198 | size_t width, char *par, uint print_type) |
| 199 | { |
| 200 | int well_formed_error; |
| 201 | size_t plen, left_len= (size_t) (end - to) + 1; |
| 202 | if (!par) |
| 203 | par = (char*) "(null)" ; |
| 204 | |
| 205 | plen= strnlen(par, width); |
| 206 | if (left_len <= plen) |
| 207 | plen = left_len - 1; |
| 208 | plen= my_well_formed_length(cs, par, par + plen, width, &well_formed_error); |
| 209 | if (print_type & ESCAPED_ARG) |
| 210 | to= backtick_string(cs, to, end, par, plen, '`'); |
| 211 | else |
| 212 | to= strnmov(to,par,plen); |
| 213 | return to; |
| 214 | } |
| 215 | |
| 216 | |
| 217 | /** |
| 218 | Prints binary argument |
| 219 | */ |
| 220 | |
| 221 | static char *process_bin_arg(char *to, char *end, size_t width, char *par) |
| 222 | { |
| 223 | DBUG_ASSERT(to <= end); |
| 224 | if (to + width + 1 > end) |
| 225 | width= end - to - 1; /* sign doesn't matter */ |
| 226 | memmove(to, par, width); |
| 227 | to+= width; |
| 228 | return to; |
| 229 | } |
| 230 | |
| 231 | |
| 232 | /** |
| 233 | Prints double or float argument |
| 234 | */ |
| 235 | |
| 236 | static char *process_dbl_arg(char *to, char *end, size_t width, |
| 237 | double par, char arg_type) |
| 238 | { |
| 239 | if (width == MAX_WIDTH) |
| 240 | width= FLT_DIG; /* width not set, use default */ |
| 241 | else if (width >= FLOATING_POINT_DECIMALS) |
| 242 | width= FLOATING_POINT_DECIMALS - 1; /* max.precision for my_fcvt() */ |
| 243 | width= MY_MIN(width, (size_t)(end-to) - 1); |
| 244 | |
| 245 | if (arg_type == 'f') |
| 246 | to+= my_fcvt(par, (int)width , to, NULL); |
| 247 | else |
| 248 | to+= my_gcvt(par, MY_GCVT_ARG_DOUBLE, (int) width , to, NULL); |
| 249 | return to; |
| 250 | } |
| 251 | |
| 252 | |
| 253 | /** |
| 254 | Prints integer argument |
| 255 | */ |
| 256 | |
| 257 | static char *process_int_arg(char *to, const char *end, size_t length, |
| 258 | longlong par, char arg_type, uint print_type) |
| 259 | { |
| 260 | size_t res_length, to_length; |
| 261 | char *store_start= to, *store_end; |
| 262 | char buff[32]; |
| 263 | |
| 264 | if ((to_length= (size_t) (end-to)) < 16 || length) |
| 265 | store_start= buff; |
| 266 | |
| 267 | if (arg_type == 'd' || arg_type == 'i') |
| 268 | store_end= longlong10_to_str(par, store_start, -10); |
| 269 | else if (arg_type == 'u') |
| 270 | store_end= longlong10_to_str(par, store_start, 10); |
| 271 | else if (arg_type == 'p') |
| 272 | { |
| 273 | store_start[0]= '0'; |
| 274 | store_start[1]= 'x'; |
| 275 | store_end= ll2str(par, store_start + 2, 16, 0); |
| 276 | } |
| 277 | else if (arg_type == 'o') |
| 278 | { |
| 279 | store_end= ll2str(par, store_start, 8, 0); |
| 280 | } |
| 281 | else |
| 282 | { |
| 283 | DBUG_ASSERT(arg_type == 'X' || arg_type =='x'); |
| 284 | store_end= ll2str(par, store_start, 16, (arg_type == 'X')); |
| 285 | } |
| 286 | |
| 287 | if ((res_length= (size_t) (store_end - store_start)) > to_length) |
| 288 | return to; /* num doesn't fit in output */ |
| 289 | /* If %#d syntax was used, we have to pre-zero/pre-space the string */ |
| 290 | if (store_start == buff) |
| 291 | { |
| 292 | length= MY_MIN(length, to_length); |
| 293 | if (res_length < length) |
| 294 | { |
| 295 | size_t diff= (length- res_length); |
| 296 | bfill(to, diff, (print_type & PREZERO_ARG) ? '0' : ' '); |
| 297 | if (arg_type == 'p' && print_type & PREZERO_ARG) |
| 298 | { |
| 299 | if (diff > 1) |
| 300 | to[1]= 'x'; |
| 301 | else |
| 302 | store_start[0]= 'x'; |
| 303 | store_start[1]= '0'; |
| 304 | } |
| 305 | to+= diff; |
| 306 | } |
| 307 | bmove(to, store_start, res_length); |
| 308 | } |
| 309 | to+= res_length; |
| 310 | return to; |
| 311 | } |
| 312 | |
| 313 | |
| 314 | /** |
| 315 | Processed positional arguments. |
| 316 | |
| 317 | @param cs string charset |
| 318 | @param to buffer where processed string will be place |
| 319 | @param end end of buffer |
| 320 | @param par format string |
| 321 | @param arg_index arg index of the first occurrence of positional arg |
| 322 | @param ap list of parameters |
| 323 | |
| 324 | @retval |
| 325 | end of buffer where processed string is placed |
| 326 | */ |
| 327 | |
| 328 | static char *process_args(CHARSET_INFO *cs, char *to, char *end, |
| 329 | const char* fmt, size_t arg_index, va_list ap) |
| 330 | { |
| 331 | ARGS_INFO args_arr[MAX_ARGS]; |
| 332 | PRINT_INFO print_arr[MAX_PRINT_INFO]; |
| 333 | size_t idx= 0, arg_count= arg_index; |
| 334 | |
| 335 | start: |
| 336 | /* Here we are at the beginning of positional argument, right after $ */ |
| 337 | arg_index--; |
| 338 | print_arr[idx].flags= 0; |
| 339 | if (*fmt == '`') |
| 340 | { |
| 341 | print_arr[idx].flags|= ESCAPED_ARG; |
| 342 | fmt++; |
| 343 | } |
| 344 | if (*fmt == '-') |
| 345 | fmt++; |
| 346 | print_arr[idx].length= print_arr[idx].width= 0; |
| 347 | /* Get print length */ |
| 348 | if (*fmt == '*') |
| 349 | { |
| 350 | fmt= get_length_arg(fmt, args_arr, &arg_count, &print_arr[idx].length, |
| 351 | &print_arr[idx].flags); |
| 352 | print_arr[idx].flags|= LENGTH_ARG; |
| 353 | } |
| 354 | else |
| 355 | fmt= get_length(fmt, &print_arr[idx].length, &print_arr[idx].flags); |
| 356 | |
| 357 | if (*fmt == '.') |
| 358 | { |
| 359 | uint unused_flags= 0; |
| 360 | fmt++; |
| 361 | /* Get print width */ |
| 362 | if (*fmt == '*') |
| 363 | { |
| 364 | fmt= get_length_arg(fmt, args_arr, &arg_count, &print_arr[idx].width, |
| 365 | &unused_flags); |
| 366 | print_arr[idx].flags|= WIDTH_ARG; |
| 367 | } |
| 368 | else |
| 369 | fmt= get_length(fmt, &print_arr[idx].width, &unused_flags); |
| 370 | } |
| 371 | else |
| 372 | print_arr[idx].width= MAX_WIDTH; |
| 373 | |
| 374 | fmt= check_longlong(fmt, &args_arr[arg_index].have_longlong); |
| 375 | args_arr[arg_index].arg_type= print_arr[idx].arg_type= *fmt; |
| 376 | |
| 377 | print_arr[idx].arg_idx= arg_index; |
| 378 | print_arr[idx].begin= ++fmt; |
| 379 | |
| 380 | while (*fmt && *fmt != '%') |
| 381 | fmt++; |
| 382 | |
| 383 | if (!*fmt) /* End of format string */ |
| 384 | { |
| 385 | uint i; |
| 386 | print_arr[idx].end= fmt; |
| 387 | /* Obtain parameters from the list */ |
| 388 | for (i= 0 ; i < arg_count; i++) |
| 389 | { |
| 390 | switch (args_arr[i].arg_type) { |
| 391 | case 's': |
| 392 | case 'b': |
| 393 | args_arr[i].str_arg= va_arg(ap, char *); |
| 394 | break; |
| 395 | case 'f': |
| 396 | case 'g': |
| 397 | args_arr[i].double_arg= va_arg(ap, double); |
| 398 | break; |
| 399 | case 'd': |
| 400 | case 'i': |
| 401 | case 'u': |
| 402 | case 'x': |
| 403 | case 'X': |
| 404 | case 'o': |
| 405 | case 'p': |
| 406 | if (args_arr[i].have_longlong) |
| 407 | args_arr[i].longlong_arg= va_arg(ap,longlong); |
| 408 | else if (args_arr[i].arg_type == 'd' || args_arr[i].arg_type == 'i') |
| 409 | args_arr[i].longlong_arg= va_arg(ap, int); |
| 410 | else |
| 411 | args_arr[i].longlong_arg= va_arg(ap, uint); |
| 412 | break; |
| 413 | case 'M': |
| 414 | case 'c': |
| 415 | args_arr[i].longlong_arg= va_arg(ap, int); |
| 416 | break; |
| 417 | default: |
| 418 | DBUG_ASSERT(0); |
| 419 | } |
| 420 | } |
| 421 | /* Print result string */ |
| 422 | for (i= 0; i <= idx; i++) |
| 423 | { |
| 424 | size_t width= 0, length= 0; |
| 425 | switch (print_arr[i].arg_type) { |
| 426 | case 's': |
| 427 | { |
| 428 | char *par= args_arr[print_arr[i].arg_idx].str_arg; |
| 429 | width= (print_arr[i].flags & WIDTH_ARG) |
| 430 | ? (size_t)args_arr[print_arr[i].width].longlong_arg |
| 431 | : print_arr[i].width; |
| 432 | to= process_str_arg(cs, to, end, width, par, print_arr[i].flags); |
| 433 | break; |
| 434 | } |
| 435 | case 'b': |
| 436 | { |
| 437 | char *par = args_arr[print_arr[i].arg_idx].str_arg; |
| 438 | width= (print_arr[i].flags & WIDTH_ARG) |
| 439 | ? (size_t)args_arr[print_arr[i].width].longlong_arg |
| 440 | : print_arr[i].width; |
| 441 | to= process_bin_arg(to, end, width, par); |
| 442 | break; |
| 443 | } |
| 444 | case 'c': |
| 445 | { |
| 446 | if (to == end) |
| 447 | break; |
| 448 | *to++= (char) args_arr[print_arr[i].arg_idx].longlong_arg; |
| 449 | break; |
| 450 | } |
| 451 | case 'f': |
| 452 | case 'g': |
| 453 | { |
| 454 | double d= args_arr[print_arr[i].arg_idx].double_arg; |
| 455 | width= (print_arr[i].flags & WIDTH_ARG) ? |
| 456 | (uint)args_arr[print_arr[i].width].longlong_arg : print_arr[i].width; |
| 457 | to= process_dbl_arg(to, end, width, d, print_arr[i].arg_type); |
| 458 | break; |
| 459 | } |
| 460 | case 'd': |
| 461 | case 'i': |
| 462 | case 'u': |
| 463 | case 'x': |
| 464 | case 'X': |
| 465 | case 'o': |
| 466 | case 'p': |
| 467 | { |
| 468 | /* Integer parameter */ |
| 469 | longlong larg; |
| 470 | length= (print_arr[i].flags & LENGTH_ARG) |
| 471 | ? (size_t)args_arr[print_arr[i].length].longlong_arg |
| 472 | : print_arr[i].length; |
| 473 | |
| 474 | larg = args_arr[print_arr[i].arg_idx].longlong_arg; |
| 475 | to= process_int_arg(to, end, length, larg, print_arr[i].arg_type, |
| 476 | print_arr[i].flags); |
| 477 | break; |
| 478 | } |
| 479 | case 'M': |
| 480 | { |
| 481 | longlong larg; |
| 482 | const char *real_end; |
| 483 | |
| 484 | width= (print_arr[i].flags & WIDTH_ARG) |
| 485 | ? (size_t)args_arr[print_arr[i].width].longlong_arg |
| 486 | : print_arr[i].width; |
| 487 | |
| 488 | real_end= MY_MIN(to + width, end); |
| 489 | |
| 490 | larg = args_arr[print_arr[i].arg_idx].longlong_arg; |
| 491 | to= process_int_arg(to, real_end, 0, larg, 'd', print_arr[i].flags); |
| 492 | if (real_end - to >= 3) |
| 493 | { |
| 494 | char errmsg_buff[MYSYS_STRERROR_SIZE]; |
| 495 | *to++= ' '; |
| 496 | *to++= '"'; |
| 497 | my_strerror(errmsg_buff, sizeof(errmsg_buff), (int) larg); |
| 498 | to= process_str_arg(cs, to, real_end, width, errmsg_buff, |
| 499 | print_arr[i].flags); |
| 500 | if (real_end > to) *to++= '"'; |
| 501 | } |
| 502 | break; |
| 503 | } |
| 504 | default: |
| 505 | break; |
| 506 | } |
| 507 | |
| 508 | if (to == end) |
| 509 | break; |
| 510 | |
| 511 | /* Copy data after the % format expression until next % */ |
| 512 | length= MY_MIN(end - to , print_arr[i].end - print_arr[i].begin); |
| 513 | if (to + length < end) |
| 514 | length++; |
| 515 | to= strnmov(to, print_arr[i].begin, length); |
| 516 | } |
| 517 | DBUG_ASSERT(to <= end); |
| 518 | *to='\0'; /* End of errmessage */ |
| 519 | return to; |
| 520 | } |
| 521 | else |
| 522 | { |
| 523 | uint unused_flags= 0; |
| 524 | /* Process next positional argument*/ |
| 525 | DBUG_ASSERT(*fmt == '%'); |
| 526 | print_arr[idx].end= fmt - 1; |
| 527 | idx++; |
| 528 | fmt++; |
| 529 | arg_index= 0; |
| 530 | fmt= get_length(fmt, &arg_index, &unused_flags); |
| 531 | DBUG_ASSERT(*fmt == '$'); |
| 532 | fmt++; |
| 533 | arg_count= MY_MAX(arg_count, arg_index); |
| 534 | goto start; |
| 535 | } |
| 536 | |
| 537 | return 0; |
| 538 | } |
| 539 | |
| 540 | |
| 541 | |
| 542 | /** |
| 543 | Produces output string according to a format string |
| 544 | |
| 545 | See the detailed documentation around my_snprintf_service_st |
| 546 | |
| 547 | @param cs string charset |
| 548 | @param to buffer where processed string will be place |
| 549 | @param n size of buffer |
| 550 | @param par format string |
| 551 | @param ap list of parameters |
| 552 | |
| 553 | @retval |
| 554 | length of result string |
| 555 | */ |
| 556 | |
| 557 | size_t my_vsnprintf_ex(CHARSET_INFO *cs, char *to, size_t n, |
| 558 | const char* fmt, va_list ap) |
| 559 | { |
| 560 | char *start=to, *end=to+n-1; |
| 561 | size_t length, width; |
| 562 | uint print_type, have_longlong; |
| 563 | |
| 564 | for (; *fmt ; fmt++) |
| 565 | { |
| 566 | if (*fmt != '%') |
| 567 | { |
| 568 | if (to == end) /* End of buffer */ |
| 569 | break; |
| 570 | *to++= *fmt; /* Copy ordinary char */ |
| 571 | continue; |
| 572 | } |
| 573 | fmt++; /* skip '%' */ |
| 574 | |
| 575 | length= width= 0; |
| 576 | print_type= 0; |
| 577 | |
| 578 | /* Read max fill size (only used with %d and %u) */ |
| 579 | if (my_isdigit(&my_charset_latin1, *fmt)) |
| 580 | { |
| 581 | fmt= get_length(fmt, &length, &print_type); |
| 582 | if (*fmt == '$') |
| 583 | { |
| 584 | to= process_args(cs, to, end, (fmt+1), length, ap); |
| 585 | return (size_t) (to - start); |
| 586 | } |
| 587 | } |
| 588 | else |
| 589 | { |
| 590 | if (*fmt == '`') |
| 591 | { |
| 592 | print_type|= ESCAPED_ARG; |
| 593 | fmt++; |
| 594 | } |
| 595 | if (*fmt == '-') |
| 596 | fmt++; |
| 597 | if (*fmt == '*') |
| 598 | { |
| 599 | fmt++; |
| 600 | length= va_arg(ap, int); |
| 601 | } |
| 602 | else |
| 603 | fmt= get_length(fmt, &length, &print_type); |
| 604 | } |
| 605 | |
| 606 | if (*fmt == '.') |
| 607 | { |
| 608 | uint unused_flags= 0; |
| 609 | fmt++; |
| 610 | if (*fmt == '*') |
| 611 | { |
| 612 | fmt++; |
| 613 | width= va_arg(ap, int); |
| 614 | } |
| 615 | else |
| 616 | fmt= get_length(fmt, &width, &unused_flags); |
| 617 | } |
| 618 | else |
| 619 | width= MAX_WIDTH; |
| 620 | |
| 621 | fmt= check_longlong(fmt, &have_longlong); |
| 622 | |
| 623 | if (*fmt == 's') /* String parameter */ |
| 624 | { |
| 625 | reg2 char *par= va_arg(ap, char *); |
| 626 | to= process_str_arg(cs, to, end, width, par, print_type); |
| 627 | continue; |
| 628 | } |
| 629 | else if (*fmt == 'b') /* Buffer parameter */ |
| 630 | { |
| 631 | char *par = va_arg(ap, char *); |
| 632 | to= process_bin_arg(to, end, width, par); |
| 633 | continue; |
| 634 | } |
| 635 | else if (*fmt == 'f' || *fmt == 'g') |
| 636 | { |
| 637 | double d= va_arg(ap, double); |
| 638 | to= process_dbl_arg(to, end, width, d, *fmt); |
| 639 | continue; |
| 640 | } |
| 641 | else if (*fmt == 'd' || *fmt == 'i' || *fmt == 'u' || *fmt == 'x' || |
| 642 | *fmt == 'X' || *fmt == 'p' || *fmt == 'o') |
| 643 | { |
| 644 | /* Integer parameter */ |
| 645 | longlong larg; |
| 646 | |
| 647 | if (have_longlong) |
| 648 | larg = va_arg(ap,longlong); |
| 649 | else if (*fmt == 'd' || *fmt == 'i') |
| 650 | larg = va_arg(ap, int); |
| 651 | else |
| 652 | larg= va_arg(ap, uint); |
| 653 | |
| 654 | to= process_int_arg(to, end, length, larg, *fmt, print_type); |
| 655 | continue; |
| 656 | } |
| 657 | else if (*fmt == 'c') /* Character parameter */ |
| 658 | { |
| 659 | register int larg; |
| 660 | if (to == end) |
| 661 | break; |
| 662 | larg = va_arg(ap, int); |
| 663 | *to++= (char) larg; |
| 664 | continue; |
| 665 | } |
| 666 | else if (*fmt == 'M') |
| 667 | { |
| 668 | int larg= va_arg(ap, int); |
| 669 | const char *real_end= MY_MIN(to + width, end); |
| 670 | |
| 671 | to= process_int_arg(to, real_end, 0, larg, 'd', print_type); |
| 672 | if (real_end - to >= 3) |
| 673 | { |
| 674 | char errmsg_buff[MYSYS_STRERROR_SIZE]; |
| 675 | *to++= ' '; |
| 676 | *to++= '"'; |
| 677 | my_strerror(errmsg_buff, sizeof(errmsg_buff), (int) larg); |
| 678 | to= process_str_arg(cs, to, real_end, width, errmsg_buff, print_type); |
| 679 | if (real_end > to) *to++= '"'; |
| 680 | } |
| 681 | continue; |
| 682 | } |
| 683 | |
| 684 | /* We come here on '%%', unknown code or too long parameter */ |
| 685 | if (to >= end) |
| 686 | break; |
| 687 | *to++='%'; /* % used as % or unknown code */ |
| 688 | } |
| 689 | DBUG_ASSERT(to <= end); |
| 690 | *to='\0'; /* End of errmessage */ |
| 691 | return (size_t) (to - start); |
| 692 | } |
| 693 | |
| 694 | |
| 695 | /* |
| 696 | Limited snprintf() implementations |
| 697 | |
| 698 | exported to plugins as a service, see the detailed documentation |
| 699 | around my_snprintf_service_st |
| 700 | */ |
| 701 | |
| 702 | size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap) |
| 703 | { |
| 704 | return my_vsnprintf_ex(&my_charset_latin1, to, n, fmt, ap); |
| 705 | } |
| 706 | |
| 707 | |
| 708 | size_t my_snprintf(char* to, size_t n, const char* fmt, ...) |
| 709 | { |
| 710 | size_t result; |
| 711 | va_list args; |
| 712 | va_start(args,fmt); |
| 713 | result= my_vsnprintf(to, n, fmt, args); |
| 714 | va_end(args); |
| 715 | return result; |
| 716 | } |
| 717 | |
| 718 | |
| 719 | /** |
| 720 | Writes output to the stream according to a format string. |
| 721 | |
| 722 | @param stream file to write to |
| 723 | @param format string format |
| 724 | @param args list of parameters |
| 725 | |
| 726 | @retval |
| 727 | number of the characters written. |
| 728 | */ |
| 729 | |
| 730 | int my_vfprintf(FILE *stream, const char* format, va_list args) |
| 731 | { |
| 732 | char cvtbuf[1024]; |
| 733 | int alloc= 0; |
| 734 | char *p= cvtbuf; |
| 735 | size_t cur_len= sizeof(cvtbuf), actual; |
| 736 | int ret; |
| 737 | |
| 738 | /* |
| 739 | We do not know how much buffer we need. |
| 740 | So start with a reasonably-sized stack-allocated buffer, and increase |
| 741 | it exponentially until it is big enough. |
| 742 | */ |
| 743 | for (;;) |
| 744 | { |
| 745 | size_t new_len; |
| 746 | actual= my_vsnprintf(p, cur_len, format, args); |
| 747 | if (actual < cur_len - 1) |
| 748 | break; |
| 749 | /* |
| 750 | Not enough space (or just enough with nothing to spare - but we cannot |
| 751 | distinguish this case from the return value). Allocate a bigger buffer |
| 752 | and try again. |
| 753 | */ |
| 754 | if (alloc) |
| 755 | my_free(p); |
| 756 | else |
| 757 | alloc= 1; |
| 758 | new_len= cur_len*2; |
| 759 | if (new_len < cur_len) |
| 760 | return 0; /* Overflow */ |
| 761 | cur_len= new_len; |
| 762 | p= my_malloc(cur_len, MYF(MY_FAE)); |
| 763 | if (!p) |
| 764 | return 0; |
| 765 | } |
| 766 | ret= (int) actual; |
| 767 | if (fputs(p, stream) < 0) |
| 768 | ret= -1; |
| 769 | if (alloc) |
| 770 | my_free(p); |
| 771 | return ret; |
| 772 | } |
| 773 | |
| 774 | int my_fprintf(FILE *stream, const char* format, ...) |
| 775 | { |
| 776 | int result; |
| 777 | va_list args; |
| 778 | va_start(args, format); |
| 779 | result= my_vfprintf(stream, format, args); |
| 780 | va_end(args); |
| 781 | return result; |
| 782 | } |
| 783 | |
| 784 | |
| 785 | /* |
| 786 | Return system error text for given error number |
| 787 | |
| 788 | @param buf Buffer (of size MYSYS_STRERROR_SIZE) |
| 789 | @param len Length of buffer |
| 790 | @param nr Error number |
| 791 | */ |
| 792 | |
| 793 | const char* my_strerror(char *buf, size_t len, int nr) |
| 794 | { |
| 795 | char *msg= NULL; |
| 796 | |
| 797 | buf[0]= '\0'; /* failsafe */ |
| 798 | |
| 799 | if (nr <= 0) |
| 800 | { |
| 801 | strmake(buf, (nr == 0 ? |
| 802 | "Internal error/check (Not system error)" : |
| 803 | "Internal error < 0 (Not system error)" ), |
| 804 | len-1); |
| 805 | return buf; |
| 806 | } |
| 807 | |
| 808 | /* |
| 809 | These (handler-) error messages are shared by perror, as required |
| 810 | by the principle of least surprise. |
| 811 | */ |
| 812 | if ((nr >= HA_ERR_FIRST) && (nr <= HA_ERR_LAST)) |
| 813 | { |
| 814 | msg= (char *) handler_error_messages[nr - HA_ERR_FIRST]; |
| 815 | strmake(buf, msg, len - 1); |
| 816 | } |
| 817 | else |
| 818 | { |
| 819 | /* |
| 820 | On Windows, do things the Windows way. On a system that supports both |
| 821 | the GNU and the XSI variant, use whichever was configured (GNU); if |
| 822 | this choice is not advertised, use the default (POSIX/XSI). Testing |
| 823 | for __GNUC__ is not sufficient to determine whether this choice exists. |
| 824 | */ |
| 825 | #if defined(__WIN__) |
| 826 | strerror_s(buf, len, nr); |
| 827 | #elif ((defined _POSIX_C_SOURCE && (_POSIX_C_SOURCE >= 200112L)) || \ |
| 828 | (defined _XOPEN_SOURCE && (_XOPEN_SOURCE >= 600))) && \ |
| 829 | ! defined _GNU_SOURCE |
| 830 | strerror_r(nr, buf, len); /* I can build with or without GNU */ |
| 831 | #elif defined(__GLIBC__) && defined (_GNU_SOURCE) |
| 832 | char *r= strerror_r(nr, buf, len); |
| 833 | if (r != buf) /* Want to help, GNU? */ |
| 834 | strmake(buf, r, len - 1); /* Then don't. */ |
| 835 | #else |
| 836 | strerror_r(nr, buf, len); |
| 837 | #endif |
| 838 | } |
| 839 | |
| 840 | /* |
| 841 | strerror() return values are implementation-dependent, so let's |
| 842 | be pragmatic. |
| 843 | */ |
| 844 | if (!buf[0]) |
| 845 | strmake(buf, "unknown error" , len - 1); |
| 846 | return buf; |
| 847 | } |
| 848 | |