Coverage Report

Created: 2026-01-25 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gettext-0.26/gettext-tools/src/format-pascal.c
Line
Count
Source
1
/* Object Pascal format strings.
2
   Copyright (C) 2001-2025 Free Software Foundation, Inc.
3
   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4
5
   This program is free software: you can redistribute it and/or modify
6
   it under the terms of the GNU General Public License as published by
7
   the Free Software Foundation; either version 3 of the License, or
8
   (at your option) any later version.
9
10
   This program is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
   GNU General Public License for more details.
14
15
   You should have received a copy of the GNU General Public License
16
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
17
18
#ifdef HAVE_CONFIG_H
19
# include <config.h>
20
#endif
21
22
#include <stdbool.h>
23
#include <stdlib.h>
24
25
#include "format.h"
26
#include "c-ctype.h"
27
#include "xalloc.h"
28
#include "xvasprintf.h"
29
#include "format-invalid.h"
30
#include "gettext.h"
31
32
0
#define _(str) gettext (str)
33
34
/* Object Pascal format strings are usable with the "format" function in the
35
   "sysutils" unit.  They are described in
36
   <https://www.freepascal.org/docs-html/rtl/sysutils/format.html>
37
   and are implemented in fpc-2.4.0/rtl/objpas/sysutils/sysformt.inc.
38
   Another implementation exists in Borland Delphi.  The GNU Pascal's
39
   "sysutils" doesn't (yet?) have the "format" function.
40
41
   A directive
42
   - starts with '%',
43
   - either
44
     - is finished with '%', or
45
     - - is optionally followed by an index specification: '*' (reads an
46
         argument, must be of type integer) or a nonempty digit sequence
47
         or nothing (equivalent to 0), followed by ':',
48
       - is optionally followed by '-', which acts as a flag,
49
       - is optionally followed by a width specification: '*' (reads an
50
         argument, must be of type integer) or a nonempty digit sequence,
51
       - is optionally followed by '.' and a precision specification: '*'
52
         (reads an argument, must be of type integer) or an optional nonempty
53
         digit sequence,
54
       - is finished by a case-insensitive specifier. If no index was
55
         specified, it reads an argument; otherwise is uses the index-th
56
         argument, 0-based.
57
         - 'd', 'u', 'x', needs an 'integer' or 'int64' or 'qword' argument,
58
         - 'e', 'f', 'g', 'n', 'm', need an 'extended' or 'currency' floating-
59
           point argument,
60
         - 's', needs a 'string', 'char', 'pchar', 'widestring', 'widechar',
61
           'pwidechar' or 'ansistring' argument,
62
         - 'p', needs a 'pointer' argument.
63
   Numbered and unnumbered argument specifications can be used in the same
64
   string.  Numbered argument specifications have no influence on the
65
   "current argument index", that is incremented each time an argument is read.
66
 */
67
68
enum format_arg_type
69
{
70
  FAT_INTEGER,         /* integer, int64, qword */
71
  FAT_FLOAT,           /* extended, currency */
72
  FAT_STRING,          /* string, char, pchar, widestring, widechar, pwidechar,
73
                          ansistring */
74
  FAT_POINTER
75
};
76
77
struct numbered_arg
78
{
79
  size_t number;
80
  enum format_arg_type type;
81
};
82
83
struct spec
84
{
85
  size_t directives;
86
  size_t numbered_arg_count;
87
  struct numbered_arg *numbered;
88
};
89
90
91
static int
92
numbered_arg_compare (const void *p1, const void *p2)
93
0
{
94
0
  size_t n1 = ((const struct numbered_arg *) p1)->number;
95
0
  size_t n2 = ((const struct numbered_arg *) p2)->number;
96
97
0
  return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
98
0
}
99
100
static void *
101
format_parse (const char *format, bool translated, char *fdi,
102
              char **invalid_reason)
103
0
{
104
0
  const char *const format_start = format;
105
0
  size_t directives;
106
0
  size_t numbered_arg_count;
107
0
  struct numbered_arg *numbered;
108
0
  size_t numbered_allocated;
109
0
  size_t unnumbered_arg_count;
110
0
  struct spec *result;
111
112
0
  enum arg_index
113
0
  {
114
0
    index_numbered,     /* index given by a fixed integer */
115
0
    index_unnumbered,   /* index given by unnumbered_arg_count++ */
116
0
    index_unknown       /* index is only known at run time */
117
0
  };
118
119
0
  directives = 0;
120
0
  numbered_arg_count = 0;
121
0
  numbered = NULL;
122
0
  numbered_allocated = 0;
123
0
  unnumbered_arg_count = 0;
124
125
0
  for (; *format != '\0';)
126
0
    if (*format++ == '%')
127
0
      {
128
        /* A directive.  */
129
0
        FDI_SET (format - 1, FMTDIR_START);
130
0
        directives++;
131
132
0
        if (*format != '%')
133
0
          {
134
            /* A complex directive.  */
135
0
            enum arg_index main_arg = index_unnumbered;
136
0
            size_t main_number = 0;
137
0
            enum format_arg_type type;
138
139
0
            if (c_isdigit (*format) || *format == ':')
140
0
              {
141
0
                const char *f = format;
142
0
                size_t m = 0;
143
144
0
                while (c_isdigit (*f))
145
0
                  {
146
0
                    m = 10 * m + (*f - '0');
147
0
                    f++;
148
0
                  }
149
150
0
                if (*f == ':')
151
0
                  {
152
0
                    main_number = m;
153
0
                    main_arg = index_numbered;
154
0
                    format = ++f;
155
0
                  }
156
0
              }
157
0
            else if (*format == '*')
158
0
              {
159
0
                if (format[1] == ':')
160
0
                  {
161
0
                    main_arg = index_unknown;
162
0
                    format += 2;
163
0
                  }
164
0
              }
165
166
            /* Parse flags.  */
167
0
            if (*format == '-')
168
0
              format++;
169
170
            /* Parse width.  */
171
0
            if (c_isdigit (*format))
172
0
              {
173
0
                do
174
0
                  format++;
175
0
                while (c_isdigit (*format));
176
0
              }
177
0
            else if (*format == '*')
178
0
              {
179
                /* Unnumbered argument of type FAT_INTEGER.   */
180
0
                if (numbered_allocated == numbered_arg_count)
181
0
                  {
182
0
                    numbered_allocated = 2 * numbered_allocated + 1;
183
0
                    numbered = (struct numbered_arg *) xrealloc (numbered, numbered_allocated * sizeof (struct numbered_arg));
184
0
                  }
185
0
                numbered[numbered_arg_count].number = unnumbered_arg_count;
186
0
                numbered[numbered_arg_count].type = FAT_INTEGER;
187
0
                numbered_arg_count++;
188
0
                unnumbered_arg_count++;
189
190
0
                format++;
191
0
              }
192
193
            /* Parse precision.  */
194
0
            if (*format == '.')
195
0
              {
196
0
                format++;
197
198
0
                if (c_isdigit (*format))
199
0
                  {
200
0
                    do
201
0
                      format++;
202
0
                    while (c_isdigit (*format));
203
0
                  }
204
0
                else if (*format == '*')
205
0
                  {
206
                    /* Unnumbered argument of type FAT_INTEGER.   */
207
0
                    if (numbered_allocated == unnumbered_arg_count)
208
0
                      {
209
0
                        numbered_allocated = 2 * numbered_allocated + 1;
210
0
                        numbered = (struct numbered_arg *) xrealloc (numbered, numbered_allocated * sizeof (struct numbered_arg));
211
0
                      }
212
0
                    numbered[numbered_arg_count].number = unnumbered_arg_count;
213
0
                    numbered[numbered_arg_count].type = FAT_INTEGER;
214
0
                    numbered_arg_count++;
215
0
                    unnumbered_arg_count++;
216
217
0
                    format++;
218
0
                  }
219
0
              }
220
221
0
            switch (c_tolower (*format))
222
0
              {
223
0
              case 'd': case 'u': case 'x':
224
0
                type = FAT_INTEGER;
225
0
                break;
226
0
              case 'e': case 'f': case 'g': case 'n': case 'm':
227
0
                type = FAT_FLOAT;
228
0
                break;
229
0
              case 's':
230
0
                type = FAT_STRING;
231
0
                break;
232
0
              case 'p':
233
0
                type = FAT_POINTER;
234
0
                break;
235
0
              default:
236
0
                if (*format == '\0')
237
0
                  {
238
0
                    *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
239
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
240
0
                  }
241
0
                else
242
0
                  {
243
0
                    *invalid_reason =
244
0
                      INVALID_CONVERSION_SPECIFIER (directives, *format);
245
0
                    FDI_SET (format, FMTDIR_ERROR);
246
0
                  }
247
0
                goto bad_format;
248
0
              }
249
250
0
            if (numbered_allocated == numbered_arg_count)
251
0
              {
252
0
                numbered_allocated = 2 * numbered_allocated + 1;
253
0
                numbered = (struct numbered_arg *) xrealloc (numbered, numbered_allocated * sizeof (struct numbered_arg));
254
0
              }
255
0
            switch (main_arg)
256
0
              {
257
0
              case index_unnumbered:
258
0
                numbered[numbered_arg_count].number = unnumbered_arg_count;
259
0
                numbered[numbered_arg_count].type = type;
260
0
                unnumbered_arg_count++;
261
0
                break;
262
0
              case index_numbered:
263
0
                numbered[numbered_arg_count].number = main_number;
264
0
                numbered[numbered_arg_count].type = type;
265
0
                break;
266
0
              case index_unknown:
267
0
                numbered[numbered_arg_count].number = unnumbered_arg_count;
268
0
                numbered[numbered_arg_count].type = FAT_INTEGER;
269
0
                unnumbered_arg_count++;
270
0
                break;
271
0
              default:
272
0
                abort ();
273
0
              }
274
0
            numbered_arg_count++;
275
0
          }
276
277
0
        FDI_SET (format, FMTDIR_END);
278
279
0
        format++;
280
0
      }
281
282
  /* Sort the numbered argument array, and eliminate duplicates.  */
283
0
  if (numbered_arg_count > 1)
284
0
    {
285
0
      size_t i, j;
286
0
      bool err;
287
288
0
      qsort (numbered, numbered_arg_count,
289
0
             sizeof (struct numbered_arg), numbered_arg_compare);
290
291
      /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
292
0
      err = false;
293
0
      for (i = j = 0; i < numbered_arg_count; i++)
294
0
        if (j > 0 && numbered[i].number == numbered[j-1].number)
295
0
          {
296
0
            enum format_arg_type type1 = numbered[i].type;
297
0
            enum format_arg_type type2 = numbered[j-1].type;
298
0
            enum format_arg_type type_both;
299
300
0
            if (type1 == type2)
301
0
              type_both = type1;
302
0
            else
303
0
              {
304
                /* Incompatible types.  */
305
0
                type_both = type1;
306
0
                if (!err)
307
0
                  *invalid_reason =
308
0
                    INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
309
0
                err = true;
310
0
              }
311
312
0
            numbered[j-1].type = type_both;
313
0
          }
314
0
        else
315
0
          {
316
0
            if (j < i)
317
0
              {
318
0
                numbered[j].number = numbered[i].number;
319
0
                numbered[j].type = numbered[i].type;
320
0
              }
321
0
            j++;
322
0
          }
323
0
      numbered_arg_count = j;
324
0
      if (err)
325
        /* *invalid_reason has already been set above.  */
326
0
        goto bad_format;
327
0
    }
328
329
0
  result = XMALLOC (struct spec);
330
0
  result->directives = directives;
331
0
  result->numbered_arg_count = numbered_arg_count;
332
0
  result->numbered = numbered;
333
0
  return result;
334
335
0
 bad_format:
336
0
  if (numbered != NULL)
337
0
    free (numbered);
338
0
  return NULL;
339
0
}
340
341
static void
342
format_free (void *descr)
343
0
{
344
0
  struct spec *spec = (struct spec *) descr;
345
346
0
  if (spec->numbered != NULL)
347
0
    free (spec->numbered);
348
0
  free (spec);
349
0
}
350
351
static int
352
format_get_number_of_directives (void *descr)
353
0
{
354
0
  struct spec *spec = (struct spec *) descr;
355
356
0
  return spec->directives;
357
0
}
358
359
static bool
360
format_check (void *msgid_descr, void *msgstr_descr, bool equality,
361
              formatstring_error_logger_t error_logger, void *error_logger_data,
362
              const char *pretty_msgid, const char *pretty_msgstr)
363
0
{
364
0
  struct spec *spec1 = (struct spec *) msgid_descr;
365
0
  struct spec *spec2 = (struct spec *) msgstr_descr;
366
0
  bool err = false;
367
368
0
  if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
369
0
    {
370
0
      size_t i, j;
371
0
      size_t n1 = spec1->numbered_arg_count;
372
0
      size_t n2 = spec2->numbered_arg_count;
373
374
      /* Check that the argument numbers are the same.
375
         Both arrays are sorted.  We search for the first difference.  */
376
0
      for (i = 0, j = 0; i < n1 || j < n2; )
377
0
        {
378
0
          int cmp = (i >= n1 ? 1 :
379
0
                     j >= n2 ? -1 :
380
0
                     spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
381
0
                     spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
382
0
                     0);
383
384
0
          if (cmp > 0)
385
0
            {
386
0
              if (error_logger)
387
0
                error_logger (error_logger_data,
388
0
                              _("a format specification for argument %zu, as in '%s', doesn't exist in '%s'"),
389
0
                              spec2->numbered[j].number, pretty_msgstr,
390
0
                              pretty_msgid);
391
0
              err = true;
392
0
              break;
393
0
            }
394
0
          else if (cmp < 0)
395
0
            {
396
0
              if (equality)
397
0
                {
398
0
                  if (error_logger)
399
0
                    error_logger (error_logger_data,
400
0
                                  _("a format specification for argument %zu doesn't exist in '%s'"),
401
0
                                  spec1->numbered[i].number, pretty_msgstr);
402
0
                  err = true;
403
0
                  break;
404
0
                }
405
0
              else
406
0
                i++;
407
0
            }
408
0
          else
409
0
            j++, i++;
410
0
        }
411
      /* Check the argument types are the same.  */
412
0
      if (!err)
413
0
        for (i = 0, j = 0; j < n2; )
414
0
          {
415
0
            if (spec1->numbered[i].number == spec2->numbered[j].number)
416
0
              {
417
0
                if (spec1->numbered[i].type != spec2->numbered[j].type)
418
0
                  {
419
0
                    if (error_logger)
420
0
                      error_logger (error_logger_data,
421
0
                                    _("format specifications in '%s' and '%s' for argument %zu are not the same"),
422
0
                                    pretty_msgid, pretty_msgstr,
423
0
                                    spec2->numbered[j].number);
424
0
                    err = true;
425
0
                    break;
426
0
                  }
427
0
                j++, i++;
428
0
              }
429
0
            else
430
0
              i++;
431
0
          }
432
0
    }
433
434
0
  return err;
435
0
}
436
437
438
struct formatstring_parser formatstring_pascal =
439
{
440
  format_parse,
441
  format_free,
442
  format_get_number_of_directives,
443
  NULL,
444
  format_check
445
};
446
447
448
#ifdef TEST
449
450
/* Test program: Print the argument list specification returned by
451
   format_parse for strings read from standard input.  */
452
453
#include <stdio.h>
454
455
static void
456
format_print (void *descr)
457
{
458
  struct spec *spec = (struct spec *) descr;
459
  size_t last;
460
  size_t i;
461
462
  if (spec == NULL)
463
    {
464
      printf ("INVALID");
465
      return;
466
    }
467
468
  printf ("(");
469
  last = 0;
470
  for (i = 0; i < spec->numbered_arg_count; i++)
471
    {
472
      size_t number = spec->numbered[i].number;
473
474
      if (i > 0)
475
        printf (" ");
476
      if (number < last)
477
        abort ();
478
      for (; last < number; last++)
479
        printf ("_ ");
480
      switch (spec->numbered[i].type)
481
        {
482
        case FAT_INTEGER:
483
          printf ("i");
484
          break;
485
        case FAT_FLOAT:
486
          printf ("f");
487
          break;
488
        case FAT_STRING:
489
          printf ("s");
490
          break;
491
        case FAT_POINTER:
492
          printf ("p");
493
          break;
494
        default:
495
          abort ();
496
        }
497
      last = number + 1;
498
    }
499
  printf (")");
500
}
501
502
int
503
main ()
504
{
505
  for (;;)
506
    {
507
      char *line = NULL;
508
      size_t line_size = 0;
509
      int line_len;
510
      char *invalid_reason;
511
      void *descr;
512
513
      line_len = getline (&line, &line_size, stdin);
514
      if (line_len < 0)
515
        break;
516
      if (line_len > 0 && line[line_len - 1] == '\n')
517
        line[--line_len] = '\0';
518
519
      invalid_reason = NULL;
520
      descr = format_parse (line, false, NULL, &invalid_reason);
521
522
      format_print (descr);
523
      printf ("\n");
524
      if (descr == NULL)
525
        printf ("%s\n", invalid_reason);
526
527
      free (invalid_reason);
528
      free (line);
529
    }
530
531
  return 0;
532
}
533
534
/*
535
 * For Emacs M-x compile
536
 * Local Variables:
537
 * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../../gettext-runtime/intl -DHAVE_CONFIG_H -DTEST format-pascal.c ../gnulib-lib/libgettextlib.la"
538
 * End:
539
 */
540
541
#endif /* TEST */