Coverage Report

Created: 2024-06-18 07:03

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