(root)/
gettext-0.22.4/
gettext-tools/
src/
format-smalltalk.c
       1  /* Smalltalk and YCP format strings.
       2     Copyright (C) 2001-2004, 2006-2007, 2009, 2019-2020 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  /* YCP sformat strings are described in libycp documentation YCP-builtins.html.
      35     A directive starts with '%' and is followed by '%' or a nonzero digit ('1'
      36     to '9').
      37     GNU Smalltalk format strings are described in the CharArray documentation,
      38     methods 'bindWith:' and 'bindWithArguments:'. They have the same syntax.
      39   */
      40  
      41  struct spec
      42  {
      43    unsigned int directives;
      44    unsigned int arg_count;
      45    bool args_used[9];
      46  };
      47  
      48  
      49  static void *
      50  format_parse (const char *format, bool translated, char *fdi,
      51                char **invalid_reason)
      52  {
      53    const char *const format_start = format;
      54    struct spec spec;
      55    struct spec *result;
      56  
      57    spec.directives = 0;
      58    spec.arg_count = 0;
      59  
      60    for (; *format != '\0';)
      61      if (*format++ == '%')
      62        {
      63          /* A directive.  */
      64          FDI_SET (format - 1, FMTDIR_START);
      65          spec.directives++;
      66  
      67          if (*format == '%')
      68            format++;
      69          else if (*format >= '1' && *format <= '9')
      70            {
      71              unsigned int number = *format - '1';
      72  
      73              while (spec.arg_count <= number)
      74                spec.args_used[spec.arg_count++] = false;
      75              spec.args_used[number] = true;
      76  
      77              format++;
      78            }
      79          else
      80            {
      81              if (*format == '\0')
      82                {
      83                  *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
      84                  FDI_SET (format - 1, FMTDIR_ERROR);
      85                }
      86              else
      87                {
      88                  *invalid_reason =
      89                    (c_isprint (*format)
      90                     ? xasprintf (_("In the directive number %u, the character '%c' is not a digit between 1 and 9."), spec.directives, *format)
      91                     : xasprintf (_("The character that terminates the directive number %u is not a digit between 1 and 9."), spec.directives));
      92                  FDI_SET (format, FMTDIR_ERROR);
      93                }
      94              goto bad_format;
      95            }
      96  
      97          FDI_SET (format - 1, FMTDIR_END);
      98        }
      99  
     100    result = XMALLOC (struct spec);
     101    *result = spec;
     102    return result;
     103  
     104   bad_format:
     105    return NULL;
     106  }
     107  
     108  static void
     109  format_free (void *descr)
     110  {
     111    struct spec *spec = (struct spec *) descr;
     112  
     113    free (spec);
     114  }
     115  
     116  static int
     117  format_get_number_of_directives (void *descr)
     118  {
     119    struct spec *spec = (struct spec *) descr;
     120  
     121    return spec->directives;
     122  }
     123  
     124  static bool
     125  format_check (void *msgid_descr, void *msgstr_descr, bool equality,
     126                formatstring_error_logger_t error_logger,
     127                const char *pretty_msgid, const char *pretty_msgstr)
     128  {
     129    struct spec *spec1 = (struct spec *) msgid_descr;
     130    struct spec *spec2 = (struct spec *) msgstr_descr;
     131    bool err = false;
     132    unsigned int i;
     133  
     134    for (i = 0; i < spec1->arg_count || i < spec2->arg_count; i++)
     135      {
     136        bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]);
     137        bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]);
     138  
     139        if (equality ? (arg_used1 != arg_used2) : (!arg_used1 && arg_used2))
     140          {
     141            if (error_logger)
     142              {
     143                if (arg_used1)
     144                  error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
     145                                i + 1, pretty_msgstr);
     146                else
     147                  error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
     148                                i + 1, pretty_msgstr, pretty_msgid);
     149              }
     150            err = true;
     151            break;
     152          }
     153      }
     154  
     155    return err;
     156  }
     157  
     158  
     159  struct formatstring_parser formatstring_smalltalk =
     160  {
     161    format_parse,
     162    format_free,
     163    format_get_number_of_directives,
     164    NULL,
     165    format_check
     166  };
     167  
     168  
     169  struct formatstring_parser formatstring_ycp =
     170  {
     171    format_parse,
     172    format_free,
     173    format_get_number_of_directives,
     174    NULL,
     175    format_check
     176  };
     177  
     178  
     179  #ifdef TEST
     180  
     181  /* Test program: Print the argument list specification returned by
     182     format_parse for strings read from standard input.  */
     183  
     184  #include <stdio.h>
     185  
     186  static void
     187  format_print (void *descr)
     188  {
     189    struct spec *spec = (struct spec *) descr;
     190    unsigned int i;
     191  
     192    if (spec == NULL)
     193      {
     194        printf ("INVALID");
     195        return;
     196      }
     197  
     198    printf ("(");
     199    for (i = 0; i < spec->arg_count; i++)
     200      {
     201        if (i > 0)
     202          printf (" ");
     203        if (spec->args_used[i])
     204          printf ("*");
     205        else
     206          printf ("_");
     207      }
     208    printf (")");
     209  }
     210  
     211  int
     212  main ()
     213  {
     214    for (;;)
     215      {
     216        char *line = NULL;
     217        size_t line_size = 0;
     218        int line_len;
     219        char *invalid_reason;
     220        void *descr;
     221  
     222        line_len = getline (&line, &line_size, stdin);
     223        if (line_len < 0)
     224          break;
     225        if (line_len > 0 && line[line_len - 1] == '\n')
     226          line[--line_len] = '\0';
     227  
     228        invalid_reason = NULL;
     229        descr = format_parse (line, false, NULL, &invalid_reason);
     230  
     231        format_print (descr);
     232        printf ("\n");
     233        if (descr == NULL)
     234          printf ("%s\n", invalid_reason);
     235  
     236        free (invalid_reason);
     237        free (line);
     238      }
     239  
     240    return 0;
     241  }
     242  
     243  /*
     244   * For Emacs M-x compile
     245   * Local Variables:
     246   * 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-smalltalk.c ../gnulib-lib/libgettextlib.la"
     247   * End:
     248   */
     249  
     250  #endif /* TEST */