(root)/
gettext-0.22.4/
gettext-tools/
src/
format-lua.c
       1  /* Lua format strings.
       2     Copyright (C) 2012-2013, 2018-2020 Free Software Foundation, Inc.
       3     Written by Ľubomír Remák <lubomirr@lubomirr.eu>, 2012.
       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 "gettext.h"
      27  #include "xalloc.h"
      28  #include "format-invalid.h"
      29  #include "c-ctype.h"
      30  #include "xvasprintf.h"
      31  
      32  #define _(str) gettext (str)
      33  
      34  /* The Lua format strings are described in the Lua manual,
      35     which can be found at:
      36     https://www.lua.org/manual/5.2/manual.html
      37  
      38     A directive
      39     - starts with '%'
      40     - is optionally followed by any of the characters '0', '-', ' ', or
      41       each of which acts as a flag,
      42     - is optionally followed by a width specification: a nonempty digit
      43       sequence,
      44     - is optionally followed by '.' and a precision specification: a nonempty
      45       digit sequence,
      46     - is finished by a specifier
      47         - 's', 'q', that needs a string argument,
      48         - 'd', 'i', 'o', 'u', 'X', 'x', that need an integer argument,
      49         - 'A', 'a', 'E', 'e', 'f', 'G', 'g', that need a floating-point argument,
      50         - 'c', that needs a character argument.
      51     Additionally there is the directive '%%', which takes no argument.
      52  
      53     Note: Lua does not distinguish between integer, floating-point
      54     and character arguments, since it has a number data type only.
      55     However, we should not allow users to use %d instead of %c.
      56     The same applies to %s and %q - we should not allow intermixing them.
      57   */
      58  
      59  enum format_arg_type
      60  {
      61    FAT_INTEGER,
      62    FAT_CHARACTER,
      63    FAT_FLOAT,
      64    FAT_STRING,
      65    FAT_ESCAPED_STRING
      66  };
      67  
      68  struct spec
      69  {
      70    unsigned int directives;
      71    unsigned int format_args_count;
      72    enum format_arg_type *format_args;
      73  };
      74  
      75  /* Locale independent test for a decimal digit.
      76     Argument can be  'char' or 'unsigned char'.  (Whereas the argument of
      77     <ctype.h> isdigit must be an 'unsigned char'.)  */
      78  #undef isdigit
      79  #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
      80  
      81  static void format_free (void *descr);
      82  
      83  static void *
      84  format_parse (const char *format, bool translated, char *fdi,
      85                char **invalid_reason)
      86  {
      87  
      88    const char *format_start = format;
      89    const char *fatstr = format;
      90    struct spec *result = NULL;
      91    unsigned int format_args_allocated;
      92  
      93    result = XMALLOC (struct spec);
      94    result->directives = 0;
      95    result->format_args_count = 0;
      96    result->format_args = NULL;
      97    format_args_allocated = 0;
      98  
      99    for (; *fatstr != '\0';)
     100      {
     101        if (*fatstr++ == '%')
     102          {
     103            FDI_SET (fatstr - 1, FMTDIR_START);
     104            result->directives++;
     105  
     106            if (*fatstr != '%')
     107              {
     108                enum format_arg_type type;
     109  
     110                /* Remove width. */
     111                while (isdigit (*fatstr))
     112                  fatstr++;
     113  
     114                if (*fatstr == '.')
     115                  {
     116                    fatstr++;
     117  
     118                    /* Remove precision. */
     119                    while (isdigit (*fatstr))
     120                      fatstr++;
     121                  }
     122  
     123                switch (*fatstr)
     124                  {
     125                  case 'c':
     126                    type = FAT_CHARACTER;
     127                    break;
     128                  case 'd':
     129                  case 'i':
     130                  case 'o':
     131                  case 'u':
     132                  case 'X':
     133                  case 'x':
     134                    type = FAT_INTEGER;
     135                    break;
     136                  case 'a':
     137                  case 'A':
     138                  case 'E':
     139                  case 'e':
     140                  case 'f':
     141                  case 'g':
     142                  case 'G':
     143                    type = FAT_FLOAT;
     144                    break;
     145                  case 's':
     146                    type = FAT_STRING;
     147                    break;
     148                  case 'q':
     149                    type = FAT_ESCAPED_STRING;
     150                    break;
     151                  default:
     152                    if (*fatstr == '\0')
     153                      {
     154                        *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
     155                        FDI_SET (fatstr - 1, FMTDIR_ERROR);
     156                      }
     157                    else
     158                      {
     159                        *invalid_reason =
     160                          INVALID_CONVERSION_SPECIFIER (result->
     161                                                        format_args_count + 1,
     162                                                        *fatstr);
     163                        FDI_SET (fatstr, FMTDIR_ERROR);
     164                      }
     165                    goto fmt_error;
     166                  }
     167  
     168                if (result->format_args_count == format_args_allocated)
     169                  {
     170                    format_args_allocated = 2 * format_args_allocated + 10;
     171                    result->format_args =
     172                      xrealloc (result->format_args,
     173                                format_args_allocated *
     174                                sizeof (enum format_arg_type));
     175                  }
     176                result->format_args[result->format_args_count++] = type;
     177              }
     178            FDI_SET (fatstr, FMTDIR_END);
     179            fatstr++;
     180          }
     181      }
     182  
     183    return result;
     184  
     185  fmt_error:
     186    format_free (result);
     187    return NULL;
     188  }
     189  
     190  static void
     191  format_free (void *descr)
     192  {
     193    struct spec *spec = (struct spec *) descr;
     194  
     195    if (spec->format_args != NULL)
     196      free (spec->format_args);
     197    free (spec);
     198  }
     199  
     200  static int
     201  format_get_number_of_directives (void *descr)
     202  {
     203    struct spec *spec = (struct spec *) descr;
     204  
     205    return spec->directives;
     206  }
     207  
     208  static bool
     209  format_check (void *msgid_descr, void *msgstr_descr, bool equality,
     210                formatstring_error_logger_t error_logger,
     211                const char *pretty_msgid, const char *pretty_msgstr)
     212  {
     213    struct spec *spec1 = (struct spec *) msgid_descr;
     214    struct spec *spec2 = (struct spec *) msgstr_descr;
     215  
     216    if (spec1->format_args_count + spec2->format_args_count > 0)
     217      {
     218        unsigned int i, n1, n2;
     219  
     220        n1 = spec1->format_args_count;
     221        n2 = spec2->format_args_count;
     222  
     223        for (i = 0; i < n1 || i < n2; i++)
     224          {
     225            if (i >= n1)
     226              {
     227                if (error_logger)
     228                  error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
     229                                i + 1, pretty_msgstr, pretty_msgid);
     230                return true;
     231              }
     232            else if (i >= n2)
     233              {
     234                if (error_logger)
     235                  error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
     236                                i + 1, pretty_msgstr);
     237                return true;
     238              }
     239            else if (spec1->format_args[i] != spec2->format_args[i])
     240              {
     241                if (error_logger)
     242                  error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
     243                                pretty_msgid, pretty_msgstr, i + 1);
     244                return true;
     245              }
     246          }
     247      }
     248  
     249    return false;
     250  }
     251  
     252  struct formatstring_parser formatstring_lua =
     253  {
     254    format_parse,
     255    format_free,
     256    format_get_number_of_directives,
     257    NULL,
     258    format_check
     259  };
     260  
     261  #ifdef TEST
     262  
     263  /* Test program: Print the argument list specification returned by
     264     format_parse for strings read from standard input.  */
     265  
     266  #include <stdio.h>
     267  
     268  static void
     269  format_print (void *descr)
     270  {
     271    struct spec *spec = (struct spec *) descr;
     272    unsigned int i;
     273  
     274    if (spec == NULL)
     275      {
     276        printf ("INVALID");
     277        return;
     278      }
     279  
     280    printf ("(");
     281    for (i = 0; i < spec->format_args_count; i++)
     282      {
     283        if (i > 0)
     284          printf (" ");
     285        switch (spec->format_args[i])
     286          {
     287          case FAT_INTEGER:
     288            printf ("i");
     289            break;
     290          case FAT_FLOAT:
     291            printf ("f");
     292            break;
     293          case FAT_CHARACTER:
     294            printf ("c");
     295            break;
     296          case FAT_STRING:
     297            printf ("s");
     298            break;
     299          case FAT_ESCAPED_STRING:
     300            printf ("q");
     301            break;
     302          default:
     303            abort ();
     304          }
     305      }
     306    printf (")");
     307  }
     308  
     309  int
     310  main ()
     311  {
     312    for (;;)
     313      {
     314        char *line = NULL;
     315        size_t line_size = 0;
     316        int line_len;
     317        char *invalid_reason;
     318        void *descr;
     319  
     320        line_len = getline (&line, &line_size, stdin);
     321        if (line_len < 0)
     322          break;
     323        if (line_len > 0 && line[line_len - 1] == '\n')
     324          line[--line_len] = '\0';
     325  
     326        invalid_reason = NULL;
     327        descr = format_parse (line, false, NULL, &invalid_reason);
     328  
     329        format_print (descr);
     330        printf ("\n");
     331        if (descr == NULL)
     332          printf ("%s\n", invalid_reason);
     333  
     334        free (invalid_reason);
     335        free (line);
     336      }
     337  
     338    return 0;
     339  }
     340  
     341  /*
     342   * For Emacs M-x compile
     343   * Local Variables:
     344   * 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-lua.c ../gnulib-lib/libgettextlib.la"
     345   * End:
     346   */
     347  
     348  #endif /* TEST */