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