(root)/
gettext-0.22.4/
gettext-tools/
src/
format-c.c
       1  /* C format strings.
       2     Copyright (C) 2001-2004, 2006-2007, 2009-2010, 2019, 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 "gettext.h"
      30  
      31  #define _(str) gettext (str)
      32  
      33  #include "format-invalid.h"
      34  
      35  #define INVALID_C99_MACRO(directive_number) \
      36    xasprintf (_("In the directive number %u, the token after '<' is not the name of a format specifier macro. The valid macro names are listed in ISO C 99 section 7.8.1."), directive_number)
      37  
      38  #define INVALID_ANGLE_BRACKET(directive_number) \
      39    xasprintf (_("In the directive number %u, the token after '<' is not followed by '>'."), directive_number)
      40  
      41  #define INVALID_SIZE_SPECIFIER(directive_number) \
      42    xasprintf (_("In the directive number %u, the argument size specifier is invalid."), directive_number)
      43  
      44  #define INVALID_IGNORED_ARGUMENT(referenced_arg, ignored_arg) \
      45    xasprintf (_("The string refers to argument number %u but ignores argument number %u."), referenced_arg, ignored_arg)
      46  
      47  /* Execute statement if memory allocation function returned NULL.  */
      48  #define IF_OOM(allocated_ptr, statement)  /* nothing, since we use xalloc.h */
      49  
      50  /* Specifies whether the system dependent segments in msgid and msgstr have
      51     been processed.  This means:
      52       - If false, ISO C 99 <inttypes.h> directives are denoted with angle
      53         brackets.  If true, they have already been expanded, leading in
      54         particular to %I64d directives on native Windows platforms.
      55       - If false, the 'I' flag may be present in msgstr (also on platforms
      56         other than glibc).  If true, the 'I' directive may be present in msgstr
      57         only on glibc >= 2.2 platforms.  */
      58  #define SYSDEP_SEGMENTS_PROCESSED false
      59  
      60  /* Include the bulk of the C format string parsing code.  */
      61  #include "format-c-parse.h"
      62  
      63  static void *
      64  format_parse (const char *format, bool translated, bool objc_extensions,
      65                char *fdi, char **invalid_reason)
      66  {
      67    struct spec result_buf;
      68    struct spec *result;
      69  
      70    result = format_parse_entrails (format, translated, objc_extensions, fdi, invalid_reason, &result_buf);
      71  
      72    if (result != NULL)
      73      {
      74        /* Copy the result to a heap-allocated object.  */
      75        struct spec *safe_result = XMALLOC (struct spec);
      76        *safe_result = *result;
      77        result = safe_result;
      78      }
      79    return result;
      80  }
      81  
      82  static void *
      83  format_c_parse (const char *format, bool translated, char *fdi,
      84                  char **invalid_reason)
      85  {
      86    return format_parse (format, translated, false, fdi, invalid_reason);
      87  }
      88  
      89  static void *
      90  format_objc_parse (const char *format, bool translated, char *fdi,
      91                     char **invalid_reason)
      92  {
      93    return format_parse (format, translated, true, fdi, invalid_reason);
      94  }
      95  
      96  static void
      97  format_free (void *descr)
      98  {
      99    struct spec *spec = (struct spec *) descr;
     100  
     101    if (spec->unnumbered != NULL)
     102      free (spec->unnumbered);
     103    if (spec->sysdep_directives != NULL)
     104      free (spec->sysdep_directives);
     105    free (spec);
     106  }
     107  
     108  static bool
     109  format_is_unlikely_intentional (void *descr)
     110  {
     111    struct spec *spec = (struct spec *) descr;
     112  
     113    return spec->unlikely_intentional;
     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    /* Check the argument types are the same.  */
     135    if (equality
     136        ? spec1->unnumbered_arg_count != spec2->unnumbered_arg_count
     137        : spec1->unnumbered_arg_count < spec2->unnumbered_arg_count)
     138      {
     139        if (error_logger)
     140          error_logger (_("number of format specifications in '%s' and '%s' does not match"),
     141                        pretty_msgid, pretty_msgstr);
     142        err = true;
     143      }
     144    else
     145      for (i = 0; i < spec2->unnumbered_arg_count; i++)
     146        if (spec1->unnumbered[i].type != spec2->unnumbered[i].type)
     147          {
     148            if (error_logger)
     149              error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
     150                            pretty_msgid, pretty_msgstr, i + 1);
     151            err = true;
     152          }
     153  
     154    return err;
     155  }
     156  
     157  
     158  struct formatstring_parser formatstring_c =
     159  {
     160    format_c_parse,
     161    format_free,
     162    format_get_number_of_directives,
     163    format_is_unlikely_intentional,
     164    format_check
     165  };
     166  
     167  
     168  struct formatstring_parser formatstring_objc =
     169  {
     170    format_objc_parse,
     171    format_free,
     172    format_get_number_of_directives,
     173    format_is_unlikely_intentional,
     174    format_check
     175  };
     176  
     177  
     178  void
     179  get_sysdep_c_format_directives (const char *string, bool translated,
     180                                  struct interval **intervalsp, size_t *lengthp)
     181  {
     182    /* Parse the format string with all possible extensions turned on.  (The
     183       caller has already verified that the format string is valid for the
     184       particular language.)  */
     185    char *invalid_reason = NULL;
     186    struct spec *descr =
     187      (struct spec *)
     188      format_parse (string, translated, true, NULL, &invalid_reason);
     189  
     190    if (descr != NULL && descr->sysdep_directives_count > 0)
     191      {
     192        unsigned int n = descr->sysdep_directives_count;
     193        struct interval *intervals = XNMALLOC (n, struct interval);
     194        unsigned int i;
     195  
     196        for (i = 0; i < n; i++)
     197          {
     198            intervals[i].startpos = descr->sysdep_directives[2 * i] - string;
     199            intervals[i].endpos = descr->sysdep_directives[2 * i + 1] - string;
     200          }
     201        *intervalsp = intervals;
     202        *lengthp = n;
     203      }
     204    else
     205      {
     206        *intervalsp = NULL;
     207        *lengthp = 0;
     208      }
     209  
     210    if (descr != NULL)
     211      format_free (descr);
     212    else
     213      free (invalid_reason);
     214  }
     215  
     216  
     217  #ifdef TEST
     218  
     219  /* Test program: Print the argument list specification returned by
     220     format_parse for strings read from standard input.  */
     221  
     222  #include <stdio.h>
     223  
     224  static void
     225  format_print (void *descr)
     226  {
     227    struct spec *spec = (struct spec *) descr;
     228    unsigned int i;
     229  
     230    if (spec == NULL)
     231      {
     232        printf ("INVALID");
     233        return;
     234      }
     235  
     236    printf ("(");
     237    for (i = 0; i < spec->unnumbered_arg_count; i++)
     238      {
     239        if (i > 0)
     240          printf (" ");
     241        if (spec->unnumbered[i].type & FAT_UNSIGNED)
     242          printf ("[unsigned]");
     243        switch (spec->unnumbered[i].type & FAT_SIZE_MASK)
     244          {
     245          case 0:
     246            break;
     247          case FAT_SIZE_SHORT:
     248            printf ("[short]");
     249            break;
     250          case FAT_SIZE_CHAR:
     251            printf ("[char]");
     252            break;
     253          case FAT_SIZE_LONG:
     254            printf ("[long]");
     255            break;
     256          case FAT_SIZE_LONGLONG:
     257            printf ("[long long]");
     258            break;
     259          case FAT_SIZE_8_T:
     260            printf ("[int8_t]");
     261            break;
     262          case FAT_SIZE_16_T:
     263            printf ("[int16_t]");
     264            break;
     265          case FAT_SIZE_32_T:
     266            printf ("[int32_t]");
     267            break;
     268          case FAT_SIZE_64_T:
     269            printf ("[int64_t]");
     270            break;
     271          case FAT_SIZE_LEAST8_T:
     272            printf ("[int_least8_t]");
     273            break;
     274          case FAT_SIZE_LEAST16_T:
     275            printf ("[int_least16_t]");
     276            break;
     277          case FAT_SIZE_LEAST32_T:
     278            printf ("[int_least32_t]");
     279            break;
     280          case FAT_SIZE_LEAST64_T:
     281            printf ("[int_least64_t]");
     282            break;
     283          case FAT_SIZE_FAST8_T:
     284            printf ("[int_fast8_t]");
     285            break;
     286          case FAT_SIZE_FAST16_T:
     287            printf ("[int_fast16_t]");
     288            break;
     289          case FAT_SIZE_FAST32_T:
     290            printf ("[int_fast32_t]");
     291            break;
     292          case FAT_SIZE_FAST64_T:
     293            printf ("[int_fast64_t]");
     294            break;
     295          case FAT_SIZE_INTMAX_T:
     296            printf ("[intmax_t]");
     297            break;
     298          case FAT_SIZE_INTPTR_T:
     299            printf ("[intptr_t]");
     300            break;
     301          case FAT_SIZE_SIZE_T:
     302            printf ("[size_t]");
     303            break;
     304          case FAT_SIZE_PTRDIFF_T:
     305            printf ("[ptrdiff_t]");
     306            break;
     307          default:
     308            abort ();
     309          }
     310        switch (spec->unnumbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_MASK))
     311          {
     312          case FAT_INTEGER:
     313            printf ("i");
     314            break;
     315          case FAT_DOUBLE:
     316            printf ("f");
     317            break;
     318          case FAT_CHAR:
     319            printf ("c");
     320            break;
     321          case FAT_STRING:
     322            printf ("s");
     323            break;
     324          case FAT_OBJC_OBJECT:
     325            printf ("@");
     326            break;
     327          case FAT_POINTER:
     328            printf ("p");
     329            break;
     330          case FAT_COUNT_POINTER:
     331            printf ("n");
     332            break;
     333          default:
     334            abort ();
     335          }
     336      }
     337    printf (")");
     338  }
     339  
     340  int
     341  main ()
     342  {
     343    for (;;)
     344      {
     345        char *line = NULL;
     346        size_t line_size = 0;
     347        int line_len;
     348        char *invalid_reason;
     349        void *descr;
     350  
     351        line_len = getline (&line, &line_size, stdin);
     352        if (line_len < 0)
     353          break;
     354        if (line_len > 0 && line[line_len - 1] == '\n')
     355          line[--line_len] = '\0';
     356  
     357        invalid_reason = NULL;
     358        descr = format_c_parse (line, false, NULL, &invalid_reason);
     359  
     360        format_print (descr);
     361        printf ("\n");
     362        if (descr == NULL)
     363          printf ("%s\n", invalid_reason);
     364  
     365        free (invalid_reason);
     366        free (line);
     367      }
     368  
     369    return 0;
     370  }
     371  
     372  /*
     373   * For Emacs M-x compile
     374   * Local Variables:
     375   * 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-c.c ../gnulib-lib/libgettextlib.la"
     376   * End:
     377   */
     378  
     379  #endif /* TEST */