(root)/
gettext-0.22.4/
gettext-tools/
src/
format-pascal.c
       1  /* Object Pascal format strings.
       2     Copyright (C) 2001-2004, 2006-2007, 2009-2010, 2018-2020, 2023 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  #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 a nonempty digit
      53           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    unsigned int number;
      80    enum format_arg_type type;
      81  };
      82  
      83  struct spec
      84  {
      85    unsigned int directives;
      86    unsigned int numbered_arg_count;
      87    struct numbered_arg *numbered;
      88  };
      89  
      90  /* Locale independent test for a decimal digit.
      91     Argument can be  'char' or 'unsigned char'.  (Whereas the argument of
      92     <ctype.h> isdigit must be an 'unsigned char'.)  */
      93  #undef isdigit
      94  #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
      95  
      96  
      97  static int
      98  numbered_arg_compare (const void *p1, const void *p2)
      99  {
     100    unsigned int n1 = ((const struct numbered_arg *) p1)->number;
     101    unsigned int n2 = ((const struct numbered_arg *) p2)->number;
     102  
     103    return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
     104  }
     105  
     106  static void *
     107  format_parse (const char *format, bool translated, char *fdi,
     108                char **invalid_reason)
     109  {
     110    const char *const format_start = format;
     111    unsigned int directives;
     112    unsigned int numbered_arg_count;
     113    struct numbered_arg *numbered;
     114    unsigned int numbered_allocated;
     115    unsigned int unnumbered_arg_count;
     116    struct spec *result;
     117  
     118    enum arg_index
     119    {
     120      index_numbered,     /* index given by a fixed integer */
     121      index_unnumbered,   /* index given by unnumbered_arg_count++ */
     122      index_unknown       /* index is only known at run time */
     123    };
     124  
     125    directives = 0;
     126    numbered_arg_count = 0;
     127    numbered = NULL;
     128    numbered_allocated = 0;
     129    unnumbered_arg_count = 0;
     130  
     131    for (; *format != '\0';)
     132      if (*format++ == '%')
     133        {
     134          /* A directive.  */
     135          FDI_SET (format - 1, FMTDIR_START);
     136          directives++;
     137  
     138          if (*format != '%')
     139            {
     140              /* A complex directive.  */
     141              enum arg_index main_arg = index_unnumbered;
     142              unsigned int main_number = 0;
     143              enum format_arg_type type;
     144  
     145              if (isdigit (*format) || *format == ':')
     146                {
     147                  const char *f = format;
     148                  unsigned int m = 0;
     149  
     150                  while (isdigit (*f))
     151                    {
     152                      m = 10 * m + (*f - '0');
     153                      f++;
     154                    }
     155  
     156                  if (*f == ':')
     157                    {
     158                      main_number = m;
     159                      main_arg = index_numbered;
     160                      format = ++f;
     161                    }
     162                }
     163              else if (*format == '*')
     164                {
     165                  if (format[1] == ':')
     166                    {
     167                      main_arg = index_unknown;
     168                      format += 2;
     169                    }
     170                }
     171  
     172              /* Parse flags.  */
     173              if (*format == '-')
     174                format++;
     175  
     176              /* Parse width.  */
     177              if (isdigit (*format))
     178                {
     179                  do
     180                    format++;
     181                  while (isdigit (*format));
     182                }
     183              else if (*format == '*')
     184                {
     185                  /* Unnumbered argument of type FAT_INTEGER.   */
     186                  if (numbered_allocated == numbered_arg_count)
     187                    {
     188                      numbered_allocated = 2 * numbered_allocated + 1;
     189                      numbered = (struct numbered_arg *) xrealloc (numbered, numbered_allocated * sizeof (struct numbered_arg));
     190                    }
     191                  numbered[numbered_arg_count].number = unnumbered_arg_count;
     192                  numbered[numbered_arg_count].type = FAT_INTEGER;
     193                  numbered_arg_count++;
     194                  unnumbered_arg_count++;
     195  
     196                  format++;
     197                }
     198  
     199              /* Parse precision.  */
     200              if (*format == '.')
     201                {
     202                  format++;
     203  
     204                  if (isdigit (*format))
     205                    {
     206                      do
     207                        format++;
     208                      while (isdigit (*format));
     209                    }
     210                  else if (*format == '*')
     211                    {
     212                      /* Unnumbered argument of type FAT_INTEGER.   */
     213                      if (numbered_allocated == unnumbered_arg_count)
     214                        {
     215                          numbered_allocated = 2 * numbered_allocated + 1;
     216                          numbered = (struct numbered_arg *) xrealloc (numbered, numbered_allocated * sizeof (struct numbered_arg));
     217                        }
     218                      numbered[numbered_arg_count].number = unnumbered_arg_count;
     219                      numbered[numbered_arg_count].type = FAT_INTEGER;
     220                      numbered_arg_count++;
     221                      unnumbered_arg_count++;
     222  
     223                      format++;
     224                    }
     225                  else
     226                    --format;     /* will jump to bad_format */
     227                }
     228  
     229              switch (c_tolower (*format))
     230                {
     231                case 'd': case 'u': case 'x':
     232                  type = FAT_INTEGER;
     233                  break;
     234                case 'e': case 'f': case 'g': case 'n': case 'm':
     235                  type = FAT_FLOAT;
     236                  break;
     237                case 's':
     238                  type = FAT_STRING;
     239                  break;
     240                case 'p':
     241                  type = FAT_POINTER;
     242                  break;
     243                default:
     244                  if (*format == '\0')
     245                    {
     246                      *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
     247                      FDI_SET (format - 1, FMTDIR_ERROR);
     248                    }
     249                  else
     250                    {
     251                      *invalid_reason =
     252                        INVALID_CONVERSION_SPECIFIER (directives, *format);
     253                      FDI_SET (format, FMTDIR_ERROR);
     254                    }
     255                  goto bad_format;
     256                }
     257  
     258              if (numbered_allocated == numbered_arg_count)
     259                {
     260                  numbered_allocated = 2 * numbered_allocated + 1;
     261                  numbered = (struct numbered_arg *) xrealloc (numbered, numbered_allocated * sizeof (struct numbered_arg));
     262                }
     263              switch (main_arg)
     264                {
     265                case index_unnumbered:
     266                  numbered[numbered_arg_count].number = unnumbered_arg_count;
     267                  numbered[numbered_arg_count].type = type;
     268                  unnumbered_arg_count++;
     269                  break;
     270                case index_numbered:
     271                  numbered[numbered_arg_count].number = main_number;
     272                  numbered[numbered_arg_count].type = type;
     273                  break;
     274                case index_unknown:
     275                  numbered[numbered_arg_count].number = unnumbered_arg_count;
     276                  numbered[numbered_arg_count].type = FAT_INTEGER;
     277                  unnumbered_arg_count++;
     278                  break;
     279                default:
     280                  abort ();
     281                }
     282              numbered_arg_count++;
     283            }
     284  
     285          FDI_SET (format, FMTDIR_END);
     286  
     287          format++;
     288        }
     289  
     290    /* Sort the numbered argument array, and eliminate duplicates.  */
     291    if (numbered_arg_count > 1)
     292      {
     293        unsigned int i, j;
     294        bool err;
     295  
     296        qsort (numbered, numbered_arg_count,
     297               sizeof (struct numbered_arg), numbered_arg_compare);
     298  
     299        /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
     300        err = false;
     301        for (i = j = 0; i < numbered_arg_count; i++)
     302          if (j > 0 && numbered[i].number == numbered[j-1].number)
     303            {
     304              enum format_arg_type type1 = numbered[i].type;
     305              enum format_arg_type type2 = numbered[j-1].type;
     306              enum format_arg_type type_both;
     307  
     308              if (type1 == type2)
     309                type_both = type1;
     310              else
     311                {
     312                  /* Incompatible types.  */
     313                  type_both = type1;
     314                  if (!err)
     315                    *invalid_reason =
     316                      INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
     317                  err = true;
     318                }
     319  
     320              numbered[j-1].type = type_both;
     321            }
     322          else
     323            {
     324              if (j < i)
     325                {
     326                  numbered[j].number = numbered[i].number;
     327                  numbered[j].type = numbered[i].type;
     328                }
     329              j++;
     330            }
     331        numbered_arg_count = j;
     332        if (err)
     333          /* *invalid_reason has already been set above.  */
     334          goto bad_format;
     335      }
     336  
     337    result = XMALLOC (struct spec);
     338    result->directives = directives;
     339    result->numbered_arg_count = numbered_arg_count;
     340    result->numbered = numbered;
     341    return result;
     342  
     343   bad_format:
     344    if (numbered != NULL)
     345      free (numbered);
     346    return NULL;
     347  }
     348  
     349  static void
     350  format_free (void *descr)
     351  {
     352    struct spec *spec = (struct spec *) descr;
     353  
     354    if (spec->numbered != NULL)
     355      free (spec->numbered);
     356    free (spec);
     357  }
     358  
     359  static int
     360  format_get_number_of_directives (void *descr)
     361  {
     362    struct spec *spec = (struct spec *) descr;
     363  
     364    return spec->directives;
     365  }
     366  
     367  static bool
     368  format_check (void *msgid_descr, void *msgstr_descr, bool equality,
     369                formatstring_error_logger_t error_logger,
     370                const char *pretty_msgid, const char *pretty_msgstr)
     371  {
     372    struct spec *spec1 = (struct spec *) msgid_descr;
     373    struct spec *spec2 = (struct spec *) msgstr_descr;
     374    bool err = false;
     375  
     376    if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
     377      {
     378        unsigned int i, j;
     379        unsigned int n1 = spec1->numbered_arg_count;
     380        unsigned int n2 = spec2->numbered_arg_count;
     381  
     382        /* Check that the argument numbers are the same.
     383           Both arrays are sorted.  We search for the first difference.  */
     384        for (i = 0, j = 0; i < n1 || j < n2; )
     385          {
     386            int cmp = (i >= n1 ? 1 :
     387                       j >= n2 ? -1 :
     388                       spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
     389                       spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
     390                       0);
     391  
     392            if (cmp > 0)
     393              {
     394                if (error_logger)
     395                  error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
     396                                spec2->numbered[j].number, pretty_msgstr,
     397                                pretty_msgid);
     398                err = true;
     399                break;
     400              }
     401            else if (cmp < 0)
     402              {
     403                if (equality)
     404                  {
     405                    if (error_logger)
     406                      error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
     407                                    spec1->numbered[i].number, pretty_msgstr);
     408                    err = true;
     409                    break;
     410                  }
     411                else
     412                  i++;
     413              }
     414            else
     415              j++, i++;
     416          }
     417        /* Check the argument types are the same.  */
     418        if (!err)
     419          for (i = 0, j = 0; j < n2; )
     420            {
     421              if (spec1->numbered[i].number == spec2->numbered[j].number)
     422                {
     423                  if (spec1->numbered[i].type != spec2->numbered[j].type)
     424                    {
     425                      if (error_logger)
     426                        error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
     427                                      pretty_msgid, pretty_msgstr,
     428                                      spec2->numbered[j].number);
     429                      err = true;
     430                      break;
     431                    }
     432                  j++, i++;
     433                }
     434              else
     435                i++;
     436            }
     437      }
     438  
     439    return err;
     440  }
     441  
     442  
     443  struct formatstring_parser formatstring_pascal =
     444  {
     445    format_parse,
     446    format_free,
     447    format_get_number_of_directives,
     448    NULL,
     449    format_check
     450  };
     451  
     452  
     453  #ifdef TEST
     454  
     455  /* Test program: Print the argument list specification returned by
     456     format_parse for strings read from standard input.  */
     457  
     458  #include <stdio.h>
     459  
     460  static void
     461  format_print (void *descr)
     462  {
     463    struct spec *spec = (struct spec *) descr;
     464    unsigned int last;
     465    unsigned int i;
     466  
     467    if (spec == NULL)
     468      {
     469        printf ("INVALID");
     470        return;
     471      }
     472  
     473    printf ("(");
     474    last = 0;
     475    for (i = 0; i < spec->numbered_arg_count; i++)
     476      {
     477        unsigned int number = spec->numbered[i].number;
     478  
     479        if (i > 0)
     480          printf (" ");
     481        if (number < last)
     482          abort ();
     483        for (; last < number; last++)
     484          printf ("_ ");
     485        switch (spec->numbered[i].type)
     486          {
     487          case FAT_INTEGER:
     488            printf ("i");
     489            break;
     490          case FAT_FLOAT:
     491            printf ("f");
     492            break;
     493          case FAT_STRING:
     494            printf ("s");
     495            break;
     496          case FAT_POINTER:
     497            printf ("p");
     498            break;
     499          default:
     500            abort ();
     501          }
     502        last = number + 1;
     503      }
     504    printf (")");
     505  }
     506  
     507  int
     508  main ()
     509  {
     510    for (;;)
     511      {
     512        char *line = NULL;
     513        size_t line_size = 0;
     514        int line_len;
     515        char *invalid_reason;
     516        void *descr;
     517  
     518        line_len = getline (&line, &line_size, stdin);
     519        if (line_len < 0)
     520          break;
     521        if (line_len > 0 && line[line_len - 1] == '\n')
     522          line[--line_len] = '\0';
     523  
     524        invalid_reason = NULL;
     525        descr = format_parse (line, false, NULL, &invalid_reason);
     526  
     527        format_print (descr);
     528        printf ("\n");
     529        if (descr == NULL)
     530          printf ("%s\n", invalid_reason);
     531  
     532        free (invalid_reason);
     533        free (line);
     534      }
     535  
     536    return 0;
     537  }
     538  
     539  /*
     540   * For Emacs M-x compile
     541   * Local Variables:
     542   * 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"
     543   * End:
     544   */
     545  
     546  #endif /* TEST */