(root)/
gettext-0.22.4/
gettext-tools/
src/
format-qt.c
       1  /* Qt format strings.
       2     Copyright (C) 2003-2004, 2006-2007, 2009, 2019 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2003.
       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 "xalloc.h"
      27  #include "xvasprintf.h"
      28  #include "gettext.h"
      29  
      30  #define _(str) gettext (str)
      31  
      32  /* Qt format strings are processed by QString::arg and are documented in
      33     qt-4.3.0/doc/html/qstring.html.
      34     A directive
      35       - starts with '%',
      36       - is optionally followed by 'L' (indicates locale-dependent processing),
      37       - is followed by one or two digits ('0' to '9'). %0n is equivalent to %n.
      38     An unterminated directive ('%' or '%L' not followed by a digit or at the
      39     end) is not an error.
      40     The first .arg() invocation replaces the %n with the lowest numbered n,
      41     the next .arg() invocation then replaces the %n with the second-lowest
      42     numbered n, and so on.
      43     This is inherently buggy because a '%' in the first replacement confuses
      44     the second .arg() invocation.
      45     To reduce this problem and introduce another one, there are also .arg()
      46     methods that take up to 9 strings and perform the replacements in one swoop.
      47     But this method works only on strings that contain no 'L' flags and only
      48     single-digit argument designators.
      49     Although %0 is supported, usually %1 denotes the first argument, %2 the
      50     second argument etc.  */
      51  
      52  struct spec
      53  {
      54    /* Number of format directives.  */
      55    unsigned int directives;
      56  
      57    /* True if the string supports the multi-argument .arg() methods, i.e. if it
      58       contains no 'L' flags and only single-digit argument designators.  */
      59    bool simple;
      60  
      61    /* Booleans telling which %nn was seen.  */
      62    unsigned int arg_count;
      63    bool args_used[100];
      64  };
      65  
      66  
      67  static void *
      68  format_parse (const char *format, bool translated, char *fdi,
      69                char **invalid_reason)
      70  {
      71    const char *const format_start = format;
      72    struct spec spec;
      73    struct spec *result;
      74  
      75    spec.directives = 0;
      76    spec.simple = true;
      77    spec.arg_count = 0;
      78  
      79    for (; *format != '\0';)
      80      if (*format++ == '%')
      81        {
      82          const char *dir_start = format - 1;
      83          bool locale_flag = false;
      84  
      85          if (*format == 'L')
      86            {
      87              locale_flag = true;
      88              format++;
      89            }
      90          if (*format >= '0' && *format <= '9')
      91            {
      92              /* A directive.  */
      93              unsigned int number;
      94  
      95              FDI_SET (dir_start, FMTDIR_START);
      96              spec.directives++;
      97              if (locale_flag)
      98                spec.simple = false;
      99  
     100              number = *format - '0';
     101              if (format[1] >= '0' && format[1] <= '9')
     102                {
     103                  number = 10 * number + (format[1] - '0');
     104                  spec.simple = false;
     105                  format++;
     106                }
     107  
     108              while (spec.arg_count <= number)
     109                spec.args_used[spec.arg_count++] = false;
     110              spec.args_used[number] = true;
     111  
     112              FDI_SET (format, FMTDIR_END);
     113  
     114              format++;
     115            }
     116        }
     117  
     118    result = XMALLOC (struct spec);
     119    *result = spec;
     120    return result;
     121  }
     122  
     123  static void
     124  format_free (void *descr)
     125  {
     126    struct spec *spec = (struct spec *) descr;
     127  
     128    free (spec);
     129  }
     130  
     131  static int
     132  format_get_number_of_directives (void *descr)
     133  {
     134    struct spec *spec = (struct spec *) descr;
     135  
     136    return spec->directives;
     137  }
     138  
     139  static bool
     140  format_check (void *msgid_descr, void *msgstr_descr, bool equality,
     141                formatstring_error_logger_t error_logger,
     142                const char *pretty_msgid, const char *pretty_msgstr)
     143  {
     144    struct spec *spec1 = (struct spec *) msgid_descr;
     145    struct spec *spec2 = (struct spec *) msgstr_descr;
     146    bool err = false;
     147    unsigned int i;
     148  
     149    if (spec1->simple && !spec2->simple)
     150      {
     151        if (error_logger)
     152          error_logger (_("'%s' is a simple format string, but '%s' is not: it contains an 'L' flag or a double-digit argument number"),
     153                        pretty_msgid, pretty_msgstr);
     154        err = true;
     155      }
     156  
     157    if (!err)
     158      for (i = 0; i < spec1->arg_count || i < spec2->arg_count; i++)
     159        {
     160          bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]);
     161          bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]);
     162  
     163          /* The translator cannot omit a %n from the msgstr because that would
     164             yield a "Argument missing" warning at runtime.  */
     165          if (arg_used1 != arg_used2)
     166            {
     167              if (error_logger)
     168                {
     169                  if (arg_used1)
     170                    error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
     171                                  i, pretty_msgstr);
     172                  else
     173                    error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
     174                                  i, pretty_msgstr, pretty_msgid);
     175                }
     176              err = true;
     177              break;
     178            }
     179        }
     180  
     181    return err;
     182  }
     183  
     184  
     185  struct formatstring_parser formatstring_qt =
     186  {
     187    format_parse,
     188    format_free,
     189    format_get_number_of_directives,
     190    NULL,
     191    format_check
     192  };
     193  
     194  
     195  #ifdef TEST
     196  
     197  /* Test program: Print the argument list specification returned by
     198     format_parse for strings read from standard input.  */
     199  
     200  #include <stdio.h>
     201  
     202  static void
     203  format_print (void *descr)
     204  {
     205    struct spec *spec = (struct spec *) descr;
     206    unsigned int i;
     207  
     208    if (spec == NULL)
     209      {
     210        printf ("INVALID");
     211        return;
     212      }
     213  
     214    printf ("(");
     215    for (i = 0; i < spec->arg_count; i++)
     216      {
     217        if (i > 0)
     218          printf (" ");
     219        if (spec->args_used[i])
     220          printf ("*");
     221        else
     222          printf ("_");
     223      }
     224    printf (")");
     225  }
     226  
     227  int
     228  main ()
     229  {
     230    for (;;)
     231      {
     232        char *line = NULL;
     233        size_t line_size = 0;
     234        int line_len;
     235        char *invalid_reason;
     236        void *descr;
     237  
     238        line_len = getline (&line, &line_size, stdin);
     239        if (line_len < 0)
     240          break;
     241        if (line_len > 0 && line[line_len - 1] == '\n')
     242          line[--line_len] = '\0';
     243  
     244        invalid_reason = NULL;
     245        descr = format_parse (line, false, NULL, &invalid_reason);
     246  
     247        format_print (descr);
     248        printf ("\n");
     249        if (descr == NULL)
     250          printf ("%s\n", invalid_reason);
     251  
     252        free (invalid_reason);
     253        free (line);
     254      }
     255  
     256    return 0;
     257  }
     258  
     259  /*
     260   * For Emacs M-x compile
     261   * Local Variables:
     262   * 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-qt.c ../gnulib-lib/libgettextlib.la"
     263   * End:
     264   */
     265  
     266  #endif /* TEST */