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