(root)/
gettext-0.22.4/
gettext-tools/
src/
format-csharp.c
       1  /* C# format strings.
       2     Copyright (C) 2003-2004, 2006-2007, 2009, 2018-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 "c-ctype.h"
      27  #include "xalloc.h"
      28  #include "xvasprintf.h"
      29  #include "gettext.h"
      30  
      31  #define _(str) gettext (str)
      32  
      33  /* C# format strings are described in the description of the .NET System.String
      34     class and implemented in mcs-0.28/class/corlib/System/String.cs .
      35     A format string consists of literal text (that is output verbatim), doubled
      36     braces ('{{' and '}}', that lead to a single brace when output), and
      37     directives.
      38     A directive
      39     - starts with '{',
      40     - is followed by a nonnegative integer m,
      41     - is optionally followed by ',' and an integer denoting a width,
      42     - is optionally followed by ':' and a sequence of format specifiers.
      43       (But the interpretation of the format specifiers is up to the IFormattable
      44       implementation, depending on the argument's runtime value. New classes
      45       implementing IFormattable can be defined by the user.)
      46     - is finished with '}'.
      47   */
      48  
      49  struct spec
      50  {
      51    unsigned int directives;
      52    unsigned int numbered_arg_count;
      53  };
      54  
      55  static void *
      56  format_parse (const char *format, bool translated, char *fdi,
      57                char **invalid_reason)
      58  {
      59    const char *const format_start = format;
      60    struct spec spec;
      61    struct spec *result;
      62  
      63    spec.directives = 0;
      64    spec.numbered_arg_count = 0;
      65  
      66    for (; *format != '\0';)
      67      {
      68        char c = *format++;
      69  
      70        if (c == '{')
      71          {
      72            FDI_SET (format - 1, FMTDIR_START);
      73            if (*format == '{')
      74              format++;
      75            else
      76              {
      77                /* A directive.  */
      78                unsigned int number;
      79  
      80                spec.directives++;
      81  
      82                if (!c_isdigit (*format))
      83                  {
      84                    *invalid_reason =
      85                      xasprintf (_("In the directive number %u, '{' is not followed by an argument number."), spec.directives);
      86                    FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
      87                    return NULL;
      88                  }
      89                number = 0;
      90                do
      91                  {
      92                    number = 10 * number + (*format - '0');
      93                    format++;
      94                  }
      95                while (c_isdigit (*format));
      96  
      97                if (*format == ',')
      98                  {
      99                    /* Parse width.  */
     100                    format++;
     101                    if (*format == '-')
     102                      format++;
     103                    if (!c_isdigit (*format))
     104                      {
     105                        *invalid_reason =
     106                          xasprintf (_("In the directive number %u, ',' is not followed by a number."), spec.directives);
     107                        FDI_SET (*format == '\0' ? format - 1 : format,
     108                                 FMTDIR_ERROR);
     109                        return NULL;
     110                      }
     111                    do
     112                      format++;
     113                    while (c_isdigit (*format));
     114                  }
     115  
     116                if (*format == ':')
     117                  {
     118                    /* Parse format specifiers.  */
     119                    do
     120                      format++;
     121                    while (*format != '\0' && *format != '}');
     122                  }
     123  
     124                if (*format == '\0')
     125                  {
     126                    *invalid_reason =
     127                      xstrdup (_("The string ends in the middle of a directive: found '{' without matching '}'."));
     128                    FDI_SET (format - 1, FMTDIR_ERROR);
     129                    return NULL;
     130                  }
     131  
     132                if (*format != '}')
     133                  {
     134                    *invalid_reason =
     135                      (c_isprint (*format)
     136                       ? xasprintf (_("The directive number %u ends with an invalid character '%c' instead of '}'."), spec.directives, *format)
     137                       : xasprintf (_("The directive number %u ends with an invalid character instead of '}'."), spec.directives));
     138                    FDI_SET (format, FMTDIR_ERROR);
     139                    return NULL;
     140                  }
     141  
     142                format++;
     143  
     144                if (spec.numbered_arg_count <= number)
     145                  spec.numbered_arg_count = number + 1;
     146              }
     147            FDI_SET (format - 1, FMTDIR_END);
     148          }
     149        else if (c == '}')
     150          {
     151            FDI_SET (format - 1, FMTDIR_START);
     152            if (*format == '}')
     153              format++;
     154            else
     155              {
     156                *invalid_reason =
     157                  (spec.directives == 0
     158                   ? xstrdup (_("The string starts in the middle of a directive: found '}' without matching '{'."))
     159                   : xasprintf (_("The string contains a lone '}' after directive number %u."), spec.directives));
     160                FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
     161                return NULL;
     162              }
     163            FDI_SET (format - 1, FMTDIR_END);
     164          }
     165      }
     166  
     167    result = XMALLOC (struct spec);
     168    *result = spec;
     169    return result;
     170  }
     171  
     172  static void
     173  format_free (void *descr)
     174  {
     175    struct spec *spec = (struct spec *) descr;
     176  
     177    free (spec);
     178  }
     179  
     180  static int
     181  format_get_number_of_directives (void *descr)
     182  {
     183    struct spec *spec = (struct spec *) descr;
     184  
     185    return spec->directives;
     186  }
     187  
     188  static bool
     189  format_check (void *msgid_descr, void *msgstr_descr, bool equality,
     190                formatstring_error_logger_t error_logger,
     191                const char *pretty_msgid, const char *pretty_msgstr)
     192  {
     193    struct spec *spec1 = (struct spec *) msgid_descr;
     194    struct spec *spec2 = (struct spec *) msgstr_descr;
     195    bool err = false;
     196  
     197    /* Check that the argument counts are the same.  */
     198    if (equality
     199        ? spec1->numbered_arg_count != spec2->numbered_arg_count
     200        : spec1->numbered_arg_count < spec2->numbered_arg_count)
     201      {
     202        if (error_logger)
     203          error_logger (_("number of format specifications in '%s' and '%s' does not match"),
     204                        pretty_msgid, pretty_msgstr);
     205        err = true;
     206      }
     207  
     208    return err;
     209  }
     210  
     211  
     212  struct formatstring_parser formatstring_csharp =
     213  {
     214    format_parse,
     215    format_free,
     216    format_get_number_of_directives,
     217    NULL,
     218    format_check
     219  };
     220  
     221  
     222  #ifdef TEST
     223  
     224  /* Test program: Print the argument list specification returned by
     225     format_parse for strings read from standard input.  */
     226  
     227  #include <stdio.h>
     228  
     229  static void
     230  format_print (void *descr)
     231  {
     232    struct spec *spec = (struct spec *) descr;
     233    unsigned int i;
     234  
     235    if (spec == NULL)
     236      {
     237        printf ("INVALID");
     238        return;
     239      }
     240  
     241    printf ("(");
     242    for (i = 0; i < spec->numbered_arg_count; i++)
     243      {
     244        if (i > 0)
     245          printf (" ");
     246        printf ("*");
     247      }
     248    printf (")");
     249  }
     250  
     251  int
     252  main ()
     253  {
     254    for (;;)
     255      {
     256        char *line = NULL;
     257        size_t line_size = 0;
     258        int line_len;
     259        char *invalid_reason;
     260        void *descr;
     261  
     262        line_len = getline (&line, &line_size, stdin);
     263        if (line_len < 0)
     264          break;
     265        if (line_len > 0 && line[line_len - 1] == '\n')
     266          line[--line_len] = '\0';
     267  
     268        invalid_reason = NULL;
     269        descr = format_parse (line, false, NULL, &invalid_reason);
     270  
     271        format_print (descr);
     272        printf ("\n");
     273        if (descr == NULL)
     274          printf ("%s\n", invalid_reason);
     275  
     276        free (invalid_reason);
     277        free (line);
     278      }
     279  
     280    return 0;
     281  }
     282  
     283  /*
     284   * For Emacs M-x compile
     285   * Local Variables:
     286   * 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-csharp.c ../gnulib-lib/libgettextlib.la"
     287   * End:
     288   */
     289  
     290  #endif /* TEST */