MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
my_vsnprintf.c
1 /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #include <my_global.h>
17 #include <m_string.h>
18 #include <stdarg.h>
19 #include <m_ctype.h>
20 
21 
22 #define MAX_ARGS 32 /* max positional args count*/
23 #define MAX_PRINT_INFO 32 /* max print position count */
24 
25 #define LENGTH_ARG 1
26 #define WIDTH_ARG 2
27 #define PREZERO_ARG 4
28 #define ESCAPED_ARG 8
29 
30 typedef struct pos_arg_info ARGS_INFO;
31 typedef struct print_info PRINT_INFO;
32 
34 {
35  char arg_type; /* argument type */
36  uint have_longlong; /* used from integer values */
37  char *str_arg; /* string value of the arg */
38  longlong longlong_arg; /* integer value of the arg */
39  double double_arg; /* double value of the arg */
40 };
41 
42 
43 struct print_info
44 {
45  char arg_type; /* argument type */
46  size_t arg_idx; /* index of the positional arg */
47  size_t length; /* print width or arg index */
48  size_t width; /* print width or arg index */
49  uint flags;
50  const char *begin;
51  const char *end;
52 };
53 
54 
66 static const char *get_length(const char *fmt, size_t *length, uint *pre_zero)
67 {
68  for (; my_isdigit(&my_charset_latin1, *fmt); fmt++)
69  {
70  *length= *length * 10 + (uint)(*fmt - '0');
71  if (!*length)
72  *pre_zero|= PREZERO_ARG; /* first digit was 0 */
73  }
74  return fmt;
75 }
76 
77 
88 static const char *get_width(const char *fmt, size_t *width)
89 {
90  for (; my_isdigit(&my_charset_latin1, *fmt); fmt++)
91  {
92  *width= *width * 10 + (uint)(*fmt - '0');
93  }
94  return fmt;
95 }
96 
107 static const char *check_longlong(const char *fmt, uint *have_longlong)
108 {
109  *have_longlong= 0;
110  if (*fmt == 'l')
111  {
112  fmt++;
113  if (*fmt != 'l')
114  *have_longlong= (sizeof(long) == sizeof(longlong));
115  else
116  {
117  fmt++;
118  *have_longlong= 1;
119  }
120  }
121  else if (*fmt == 'z')
122  {
123  fmt++;
124  *have_longlong= (sizeof(size_t) == sizeof(longlong));
125  }
126  return fmt;
127 }
128 
129 
144 static char *backtick_string(const CHARSET_INFO *cs, char *to, char *end,
145  char *par, size_t par_len, char quote_char)
146 {
147  uint char_len;
148  char *start= to;
149  char *par_end= par + par_len;
150  size_t buff_length= (size_t) (end - to);
151 
152  if (buff_length <= par_len)
153  goto err;
154  *start++= quote_char;
155 
156  for ( ; par < par_end; par+= char_len)
157  {
158  uchar c= *(uchar *) par;
159  if (!(char_len= my_mbcharlen(cs, c)))
160  char_len= 1;
161  if (char_len == 1 && c == (uchar) quote_char )
162  {
163  if (start + 1 >= end)
164  goto err;
165  *start++= quote_char;
166  }
167  if (start + char_len >= end)
168  goto err;
169  start= strnmov(start, par, char_len);
170  }
171 
172  if (start + 1 >= end)
173  goto err;
174  *start++= quote_char;
175  return start;
176 
177 err:
178  *to='\0';
179  return to;
180 }
181 
182 
187 static char *process_str_arg(const CHARSET_INFO *cs, char *to, char *end,
188  size_t width, char *par, uint print_type)
189 {
190  int well_formed_error;
191  size_t plen, left_len= (size_t) (end - to) + 1;
192  if (!par)
193  par = (char*) "(null)";
194 
195  plen= strnlen(par, width);
196  if (left_len <= plen)
197  plen = left_len - 1;
198  plen= cs->cset->well_formed_len(cs, par, par + plen,
199  width, &well_formed_error);
200  if (print_type & ESCAPED_ARG)
201  to= backtick_string(cs, to, end, par, plen, '`');
202  else
203  to= strnmov(to,par,plen);
204  return to;
205 }
206 
207 
212 static char *process_bin_arg(char *to, char *end, size_t width, char *par)
213 {
214  DBUG_ASSERT(to <= end);
215  if (to + width + 1 > end)
216  width= end - to - 1; /* sign doesn't matter */
217  memmove(to, par, width);
218  to+= width;
219  return to;
220 }
221 
222 
227 static char *process_dbl_arg(char *to, char *end, size_t width,
228  double par, char arg_type)
229 {
230  if (width == SIZE_T_MAX)
231  width= FLT_DIG; /* width not set, use default */
232  else if (width >= NOT_FIXED_DEC)
233  width= NOT_FIXED_DEC - 1; /* max.precision for my_fcvt() */
234  width= MY_MIN(width, (size_t)(end-to) - 1);
235 
236  if (arg_type == 'f')
237  to+= my_fcvt(par, (int)width , to, NULL);
238  else
239  to+= my_gcvt(par, MY_GCVT_ARG_DOUBLE, (int) width , to, NULL);
240  return to;
241 }
242 
243 
248 static char *process_int_arg(char *to, char *end, size_t length,
249  longlong par, char arg_type, uint print_type)
250 {
251  size_t res_length, to_length;
252  char *store_start= to, *store_end;
253  char buff[32];
254 
255  if ((to_length= (size_t) (end-to)) < 16 || length)
256  store_start= buff;
257 
258  if (arg_type == 'd' || arg_type == 'i')
259  store_end= longlong10_to_str(par, store_start, -10);
260  else if (arg_type == 'u')
261  store_end= longlong10_to_str(par, store_start, 10);
262  else if (arg_type == 'p')
263  {
264  store_start[0]= '0';
265  store_start[1]= 'x';
266  store_end= ll2str(par, store_start + 2, 16, 0);
267  }
268  else if (arg_type == 'o')
269  {
270  store_end= ll2str(par, store_start, 8, 0);
271  }
272  else
273  {
274  DBUG_ASSERT(arg_type == 'X' || arg_type =='x');
275  store_end= ll2str(par, store_start, 16, (arg_type == 'X'));
276  }
277 
278  if ((res_length= (size_t) (store_end - store_start)) > to_length)
279  return to; /* num doesn't fit in output */
280  /* If %#d syntax was used, we have to pre-zero/pre-space the string */
281  if (store_start == buff)
282  {
283  length= MY_MIN(length, to_length);
284  if (res_length < length)
285  {
286  size_t diff= (length- res_length);
287  memset(to, (print_type & PREZERO_ARG) ? '0' : ' ', diff);
288  if (arg_type == 'p' && print_type & PREZERO_ARG)
289  {
290  if (diff > 1)
291  to[1]= 'x';
292  else
293  store_start[0]= 'x';
294  store_start[1]= '0';
295  }
296  to+= diff;
297  }
298  bmove(to, store_start, res_length);
299  }
300  to+= res_length;
301  return to;
302 }
303 
304 
319 static char *process_args(const CHARSET_INFO *cs, char *to, char *end,
320  const char* fmt, size_t arg_index, va_list ap)
321 {
322  ARGS_INFO args_arr[MAX_ARGS];
323  PRINT_INFO print_arr[MAX_PRINT_INFO];
324  uint idx= 0, arg_count= arg_index;
325 
326 start:
327  /* Here we are at the beginning of positional argument, right after $ */
328  arg_index--;
329  print_arr[idx].flags= 0;
330  if (*fmt == '`')
331  {
332  print_arr[idx].flags|= ESCAPED_ARG;
333  fmt++;
334  }
335  if (*fmt == '-')
336  fmt++;
337  print_arr[idx].length= print_arr[idx].width= 0;
338  /* Get print length */
339  if (*fmt == '*')
340  {
341  fmt++;
342  fmt= get_length(fmt, &print_arr[idx].length, &print_arr[idx].flags);
343  print_arr[idx].length--;
344  DBUG_ASSERT(*fmt == '$' && print_arr[idx].length < MAX_ARGS);
345  args_arr[print_arr[idx].length].arg_type= 'd';
346  args_arr[print_arr[idx].length].have_longlong= 0;
347  print_arr[idx].flags|= LENGTH_ARG;
348  arg_count= MY_MAX(arg_count, print_arr[idx].length + 1);
349  fmt++;
350  }
351  else
352  fmt= get_length(fmt, &print_arr[idx].length, &print_arr[idx].flags);
353 
354  if (*fmt == '.')
355  {
356  fmt++;
357  /* Get print width */
358  if (*fmt == '*')
359  {
360  fmt++;
361  fmt= get_width(fmt, &print_arr[idx].width);
362  print_arr[idx].width--;
363  DBUG_ASSERT(*fmt == '$' && print_arr[idx].width < MAX_ARGS);
364  args_arr[print_arr[idx].width].arg_type= 'd';
365  args_arr[print_arr[idx].width].have_longlong= 0;
366  print_arr[idx].flags|= WIDTH_ARG;
367  arg_count= MY_MAX(arg_count, print_arr[idx].width + 1);
368  fmt++;
369  }
370  else
371  fmt= get_width(fmt, &print_arr[idx].width);
372  }
373  else
374  print_arr[idx].width= SIZE_T_MAX;
375 
376  fmt= check_longlong(fmt, &args_arr[arg_index].have_longlong);
377  if (*fmt == 'p')
378  args_arr[arg_index].have_longlong= (sizeof(void *) == sizeof(longlong));
379  args_arr[arg_index].arg_type= print_arr[idx].arg_type= *fmt;
380 
381  print_arr[idx].arg_idx= arg_index;
382  print_arr[idx].begin= ++fmt;
383 
384  while (*fmt && *fmt != '%')
385  fmt++;
386 
387  if (!*fmt) /* End of format string */
388  {
389  uint i;
390  print_arr[idx].end= fmt;
391  /* Obtain parameters from the list */
392  for (i= 0 ; i < arg_count; i++)
393  {
394  switch (args_arr[i].arg_type) {
395  case 's':
396  case 'b':
397  args_arr[i].str_arg= va_arg(ap, char *);
398  break;
399  case 'f':
400  case 'g':
401  args_arr[i].double_arg= va_arg(ap, double);
402  break;
403  case 'd':
404  case 'i':
405  case 'u':
406  case 'x':
407  case 'X':
408  case 'o':
409  case 'p':
410  if (args_arr[i].have_longlong)
411  args_arr[i].longlong_arg= va_arg(ap,longlong);
412  else if (args_arr[i].arg_type == 'd' || args_arr[i].arg_type == 'i')
413  args_arr[i].longlong_arg= va_arg(ap, int);
414  else
415  args_arr[i].longlong_arg= va_arg(ap, uint);
416  break;
417  case 'c':
418  args_arr[i].longlong_arg= va_arg(ap, int);
419  break;
420  default:
421  DBUG_ASSERT(0);
422  }
423  }
424  /* Print result string */
425  for (i= 0; i <= idx; i++)
426  {
427  size_t width= 0, length= 0;
428  switch (print_arr[i].arg_type) {
429  case 's':
430  {
431  char *par= args_arr[print_arr[i].arg_idx].str_arg;
432  width= (print_arr[i].flags & WIDTH_ARG)
433  ? (size_t)args_arr[print_arr[i].width].longlong_arg
434  : print_arr[i].width;
435  to= process_str_arg(cs, to, end, width, par, print_arr[i].flags);
436  break;
437  }
438  case 'b':
439  {
440  char *par = args_arr[print_arr[i].arg_idx].str_arg;
441  width= (print_arr[i].flags & WIDTH_ARG)
442  ? (size_t)args_arr[print_arr[i].width].longlong_arg
443  : print_arr[i].width;
444  to= process_bin_arg(to, end, width, par);
445  break;
446  }
447  case 'c':
448  {
449  if (to == end)
450  break;
451  *to++= (char) args_arr[print_arr[i].arg_idx].longlong_arg;
452  break;
453  }
454  case 'f':
455  case 'g':
456  {
457  double d= args_arr[print_arr[i].arg_idx].double_arg;
458  width= (print_arr[i].flags & WIDTH_ARG) ?
459  (uint)args_arr[print_arr[i].width].longlong_arg : print_arr[i].width;
460  to= process_dbl_arg(to, end, width, d, print_arr[i].arg_type);
461  break;
462  }
463  case 'd':
464  case 'i':
465  case 'u':
466  case 'x':
467  case 'X':
468  case 'o':
469  case 'p':
470  {
471  /* Integer parameter */
472  longlong larg;
473  length= (print_arr[i].flags & LENGTH_ARG)
474  ? (size_t)args_arr[print_arr[i].length].longlong_arg
475  : print_arr[i].length;
476 
477  if (args_arr[print_arr[i].arg_idx].have_longlong)
478  larg = args_arr[print_arr[i].arg_idx].longlong_arg;
479  else if (print_arr[i].arg_type == 'd' || print_arr[i].arg_type == 'i' )
480  larg = (int) args_arr[print_arr[i].arg_idx].longlong_arg;
481  else
482  larg= (uint) args_arr[print_arr[i].arg_idx].longlong_arg;
483 
484  to= process_int_arg(to, end, length, larg, print_arr[i].arg_type,
485  print_arr[i].flags);
486  break;
487  }
488  default:
489  break;
490  }
491 
492  if (to == end)
493  break;
494 
495  length= MY_MIN(end - to , print_arr[i].end - print_arr[i].begin);
496  if (to + length < end)
497  length++;
498  to= strnmov(to, print_arr[i].begin, length);
499  }
500  DBUG_ASSERT(to <= end);
501  *to='\0'; /* End of errmessage */
502  return to;
503  }
504  else
505  {
506  /* Process next positional argument*/
507  DBUG_ASSERT(*fmt == '%');
508  print_arr[idx].end= fmt - 1;
509  idx++;
510  fmt++;
511  arg_index= 0;
512  fmt= get_width(fmt, &arg_index);
513  DBUG_ASSERT(*fmt == '$');
514  fmt++;
515  arg_count= MY_MAX(arg_count, arg_index);
516  goto start;
517  }
518 
519  return 0;
520 }
521 
522 
523 
539 size_t my_vsnprintf_ex(const CHARSET_INFO *cs, char *to, size_t n,
540  const char* fmt, va_list ap)
541 {
542  char *start=to, *end=to+n-1;
543  size_t length, width;
544  uint print_type, have_longlong;
545 
546  for (; *fmt ; fmt++)
547  {
548  if (*fmt != '%')
549  {
550  if (to == end) /* End of buffer */
551  break;
552  *to++= *fmt; /* Copy ordinary char */
553  continue;
554  }
555  fmt++; /* skip '%' */
556 
557  length= width= 0;
558  print_type= 0;
559 
560  /* Read max fill size (only used with %d and %u) */
561  if (my_isdigit(&my_charset_latin1, *fmt))
562  {
563  fmt= get_length(fmt, &length, &print_type);
564  if (*fmt == '$')
565  {
566  to= process_args(cs, to, end, (fmt+1), length, ap);
567  return (size_t) (to - start);
568  }
569  }
570  else
571  {
572  if (*fmt == '`')
573  {
574  print_type|= ESCAPED_ARG;
575  fmt++;
576  }
577  if (*fmt == '-')
578  fmt++;
579  if (*fmt == '*')
580  {
581  fmt++;
582  length= va_arg(ap, int);
583  }
584  else
585  fmt= get_length(fmt, &length, &print_type);
586  }
587 
588  if (*fmt == '.')
589  {
590  fmt++;
591  if (*fmt == '*')
592  {
593  fmt++;
594  width= va_arg(ap, int);
595  }
596  else
597  fmt= get_width(fmt, &width);
598  }
599  else
600  width= SIZE_T_MAX;
601 
602  fmt= check_longlong(fmt, &have_longlong);
603 
604  if (*fmt == 's') /* String parameter */
605  {
606  reg2 char *par= va_arg(ap, char *);
607  to= process_str_arg(cs, to, end, width, par, print_type);
608  continue;
609  }
610  else if (*fmt == 'b') /* Buffer parameter */
611  {
612  char *par = va_arg(ap, char *);
613  to= process_bin_arg(to, end, width, par);
614  continue;
615  }
616  else if (*fmt == 'f' || *fmt == 'g')
617  {
618  double d= va_arg(ap, double);
619  to= process_dbl_arg(to, end, width, d, *fmt);
620  continue;
621  }
622  else if (*fmt == 'd' || *fmt == 'i' || *fmt == 'u' || *fmt == 'x' ||
623  *fmt == 'X' || *fmt == 'p' || *fmt == 'o')
624  {
625  /* Integer parameter */
626  longlong larg;
627  if (*fmt == 'p')
628  have_longlong= (sizeof(void *) == sizeof(longlong));
629 
630  if (have_longlong)
631  larg = va_arg(ap,longlong);
632  else if (*fmt == 'd' || *fmt == 'i')
633  larg = va_arg(ap, int);
634  else
635  larg= va_arg(ap, uint);
636 
637  to= process_int_arg(to, end, length, larg, *fmt, print_type);
638  continue;
639  }
640  else if (*fmt == 'c') /* Character parameter */
641  {
642  register int larg;
643  if (to == end)
644  break;
645  larg = va_arg(ap, int);
646  *to++= (char) larg;
647  continue;
648  }
649 
650  /* We come here on '%%', unknown code or too long parameter */
651  if (to == end)
652  break;
653  *to++='%'; /* % used as % or unknown code */
654  }
655  DBUG_ASSERT(to <= end);
656  *to='\0'; /* End of errmessage */
657  return (size_t) (to - start);
658 }
659 
660 
661 /*
662  Limited snprintf() implementations
663 
664  exported to plugins as a service, see the detailed documentation
665  around my_snprintf_service_st
666 */
667 
668 size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap)
669 {
670  return my_vsnprintf_ex(&my_charset_latin1, to, n, fmt, ap);
671 }
672 
673 
674 size_t my_snprintf(char* to, size_t n, const char* fmt, ...)
675 {
676  size_t result;
677  va_list args;
678  va_start(args,fmt);
679  result= my_vsnprintf(to, n, fmt, args);
680  va_end(args);
681  return result;
682 }
683