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