Coverage Report

Created: 2025-06-13 07:09

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