(root)/
gettext-0.22.4/
gettext-tools/
src/
format-gfc-internal.c
       1  /* GFC (GNU Fortran Compiler) internal format strings.
       2     Copyright (C) 2003-2009, 2019-2020, 2022 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2009.
       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  /* GFC internal format strings consist of format directives that are specific
      35     to the GNU Fortran Compiler frontend of GCC, implemented in
      36     gcc-4.3.3/gcc/fortran/error.c (function error_print).
      37  
      38     A directive
      39     - starts with '%',
      40     - either is finished by '%', that needs no argument,
      41     - or is continued like this:
      42         - optionally 'm$' where m is a positive integer,
      43         - finished by a specifier
      44             - 'C', that needs no argument but uses a particular variable
      45                    (but for the purposes of 'm$' numbering it consumes an
      46                    argument nevertheless, of 'void' type),
      47             - 'L', that needs a 'locus *' argument,
      48             - 'i', 'd', that need a signed integer argument,
      49             - 'u', that needs an unsigned integer argument,
      50             - 'li', 'ld', that need a signed long integer argument,
      51             - 'lu', that needs an unsigned long integer argument,
      52             - 'c', that needs a character argument,
      53             - 's', that needs a string argument.
      54  
      55     Numbered ('%m$') and unnumbered argument specifications can be used in the
      56     same string. The effect of '%m$' is to set the current argument number to
      57     m. The current argument number is incremented after processing a directive.
      58  
      59     When numbered argument specifications are used, specifying the Nth argument
      60     requires that all the leading arguments, from the first to the (N-1)th, are
      61     specified in the format string.  */
      62  
      63  enum format_arg_type
      64  {
      65    FAT_NONE              = 0,
      66    /* Basic types */
      67    FAT_VOID              = 1,
      68    FAT_INTEGER           = 2,
      69    FAT_CHAR              = 3,
      70    FAT_STRING            = 4,
      71    FAT_LOCUS             = 5,
      72    /* Flags */
      73    FAT_UNSIGNED          = 1 << 3,
      74    FAT_SIZE_LONG         = 1 << 4,
      75    /* Bitmasks */
      76    FAT_SIZE_MASK         = FAT_SIZE_LONG
      77  };
      78  #ifdef __cplusplus
      79  typedef int format_arg_type_t;
      80  #else
      81  typedef enum format_arg_type format_arg_type_t;
      82  #endif
      83  
      84  struct numbered_arg
      85  {
      86    unsigned int number;
      87    format_arg_type_t type;
      88  };
      89  
      90  struct unnumbered_arg
      91  {
      92    format_arg_type_t type;
      93  };
      94  
      95  struct spec
      96  {
      97    unsigned int directives;
      98    unsigned int unnumbered_arg_count;
      99    struct unnumbered_arg *unnumbered;
     100    bool uses_currentloc;
     101  };
     102  
     103  /* Locale independent test for a decimal digit.
     104     Argument can be  'char' or 'unsigned char'.  (Whereas the argument of
     105     <ctype.h> isdigit must be an 'unsigned char'.)  */
     106  #undef isdigit
     107  #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
     108  
     109  
     110  static int
     111  numbered_arg_compare (const void *p1, const void *p2)
     112  {
     113    unsigned int n1 = ((const struct numbered_arg *) p1)->number;
     114    unsigned int n2 = ((const struct numbered_arg *) p2)->number;
     115  
     116    return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
     117  }
     118  
     119  static void *
     120  format_parse (const char *format, bool translated, char *fdi,
     121                char **invalid_reason)
     122  {
     123    const char *const format_start = format;
     124    struct spec spec;
     125    unsigned int numbered_arg_count;
     126    unsigned int numbered_allocated;
     127    struct numbered_arg *numbered;
     128    struct spec *result;
     129    unsigned int number;
     130  
     131    spec.directives = 0;
     132    numbered_arg_count = 0;
     133    numbered_allocated = 0;
     134    numbered = NULL;
     135    spec.uses_currentloc = false;
     136    number = 1;
     137  
     138    for (; *format != '\0';)
     139      if (*format++ == '%')
     140        {
     141          /* A directive.  */
     142          FDI_SET (format - 1, FMTDIR_START);
     143          spec.directives++;
     144  
     145          if (*format != '%')
     146            {
     147              format_arg_type_t type;
     148  
     149              if (isdigit (*format))
     150                {
     151                  const char *f = format;
     152                  unsigned int m = 0;
     153  
     154                  do
     155                    {
     156                      m = 10 * m + (*f - '0');
     157                      f++;
     158                    }
     159                  while (isdigit (*f));
     160  
     161                  if (*f == '$')
     162                    {
     163                      if (m == 0)
     164                        {
     165                          *invalid_reason = INVALID_ARGNO_0 (spec.directives);
     166                          FDI_SET (f, FMTDIR_ERROR);
     167                          goto bad_format;
     168                        }
     169                      number = m;
     170                      format = ++f;
     171                    }
     172                }
     173  
     174              if (*format == 'C')
     175                {
     176                  type = FAT_VOID;
     177                  spec.uses_currentloc = true;
     178                }
     179              else if (*format == 'L')
     180                type = FAT_LOCUS;
     181              else if (*format == 'c')
     182                type = FAT_CHAR;
     183              else if (*format == 's')
     184                type = FAT_STRING;
     185              else
     186                {
     187                  format_arg_type_t size = 0;
     188  
     189                  if (*format == 'l')
     190                    {
     191                      ++format;
     192                      size = FAT_SIZE_LONG;
     193                    }
     194  
     195                  if (*format == 'i' || *format == 'd')
     196                    type = FAT_INTEGER | size;
     197                  else if (*format == 'u')
     198                    type = FAT_INTEGER | FAT_UNSIGNED | size;
     199                  else
     200                    {
     201                      if (*format == '\0')
     202                        {
     203                          *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
     204                          FDI_SET (format - 1, FMTDIR_ERROR);
     205                        }
     206                      else
     207                        {
     208                          *invalid_reason =
     209                            INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
     210                          FDI_SET (format, FMTDIR_ERROR);
     211                        }
     212                      goto bad_format;
     213                    }
     214                }
     215  
     216              if (numbered_allocated == numbered_arg_count)
     217                {
     218                  numbered_allocated = 2 * numbered_allocated + 1;
     219                  numbered = (struct numbered_arg *) xrealloc (numbered, numbered_allocated * sizeof (struct numbered_arg));
     220                }
     221              numbered[numbered_arg_count].number = number;
     222              numbered[numbered_arg_count].type = type;
     223              numbered_arg_count++;
     224  
     225              number++;
     226            }
     227  
     228          FDI_SET (format, FMTDIR_END);
     229  
     230          format++;
     231        }
     232  
     233    /* Sort the numbered argument array, and eliminate duplicates.  */
     234    if (numbered_arg_count > 1)
     235      {
     236        unsigned int i, j;
     237        bool err;
     238  
     239        qsort (numbered, numbered_arg_count,
     240               sizeof (struct numbered_arg), numbered_arg_compare);
     241  
     242        /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
     243        err = false;
     244        for (i = j = 0; i < numbered_arg_count; i++)
     245          if (j > 0 && numbered[i].number == numbered[j-1].number)
     246            {
     247              format_arg_type_t type1 = numbered[i].type;
     248              format_arg_type_t type2 = numbered[j-1].type;
     249              format_arg_type_t type_both;
     250  
     251              if (type1 == type2)
     252                type_both = type1;
     253              else
     254                {
     255                  /* Incompatible types.  */
     256                  type_both = FAT_NONE;
     257                  if (!err)
     258                    *invalid_reason =
     259                      INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
     260                  err = true;
     261                }
     262  
     263              numbered[j-1].type = type_both;
     264            }
     265          else
     266            {
     267              if (j < i)
     268                {
     269                  numbered[j].number = numbered[i].number;
     270                  numbered[j].type = numbered[i].type;
     271                }
     272              j++;
     273            }
     274        numbered_arg_count = j;
     275        if (err)
     276          /* *invalid_reason has already been set above.  */
     277          goto bad_format;
     278      }
     279  
     280    /* Verify that the format string uses all arguments up to the highest
     281       numbered one.  */
     282    {
     283      unsigned int i;
     284  
     285      for (i = 0; i < numbered_arg_count; i++)
     286        if (numbered[i].number != i + 1)
     287          {
     288            *invalid_reason =
     289              xasprintf (_("The string refers to argument number %u but ignores argument number %u."), numbered[i].number, i + 1);
     290            goto bad_format;
     291          }
     292    }
     293  
     294    /* So now the numbered arguments array is equivalent to a sequence
     295       of unnumbered arguments.  Eliminate the FAT_VOID placeholders.  */
     296    {
     297      unsigned int i;
     298  
     299      spec.unnumbered_arg_count = 0;
     300      for (i = 0; i < numbered_arg_count; i++)
     301        if (numbered[i].type != FAT_VOID)
     302          spec.unnumbered_arg_count++;
     303  
     304      if (spec.unnumbered_arg_count > 0)
     305        {
     306          unsigned int j;
     307  
     308          spec.unnumbered = XNMALLOC (spec.unnumbered_arg_count, struct unnumbered_arg);
     309          j = 0;
     310          for (i = 0; i < numbered_arg_count; i++)
     311            if (numbered[i].type != FAT_VOID)
     312              spec.unnumbered[j++].type = numbered[i].type;
     313        }
     314      else
     315        spec.unnumbered = NULL;
     316    }
     317    free (numbered);
     318  
     319    result = XMALLOC (struct spec);
     320    *result = spec;
     321    return result;
     322  
     323   bad_format:
     324    if (numbered != NULL)
     325      free (numbered);
     326    return NULL;
     327  }
     328  
     329  static void
     330  format_free (void *descr)
     331  {
     332    struct spec *spec = (struct spec *) descr;
     333  
     334    if (spec->unnumbered != NULL)
     335      free (spec->unnumbered);
     336    free (spec);
     337  }
     338  
     339  static int
     340  format_get_number_of_directives (void *descr)
     341  {
     342    struct spec *spec = (struct spec *) descr;
     343  
     344    return spec->directives;
     345  }
     346  
     347  static bool
     348  format_check (void *msgid_descr, void *msgstr_descr, bool equality,
     349                formatstring_error_logger_t error_logger,
     350                const char *pretty_msgid, const char *pretty_msgstr)
     351  {
     352    struct spec *spec1 = (struct spec *) msgid_descr;
     353    struct spec *spec2 = (struct spec *) msgstr_descr;
     354    bool err = false;
     355    unsigned int i;
     356  
     357    /* Check the argument types are the same.  */
     358    if (equality
     359        ? spec1->unnumbered_arg_count != spec2->unnumbered_arg_count
     360        : spec1->unnumbered_arg_count < spec2->unnumbered_arg_count)
     361      {
     362        if (error_logger)
     363          error_logger (_("number of format specifications in '%s' and '%s' does not match"),
     364                        pretty_msgid, pretty_msgstr);
     365        err = true;
     366      }
     367    else
     368      for (i = 0; i < spec2->unnumbered_arg_count; i++)
     369        if (spec1->unnumbered[i].type != spec2->unnumbered[i].type)
     370          {
     371            if (error_logger)
     372              error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
     373                            pretty_msgid, pretty_msgstr, i + 1);
     374            err = true;
     375          }
     376  
     377    /* Check that the use of currentloc is the same.  */
     378    if (spec1->uses_currentloc != spec2->uses_currentloc)
     379      {
     380        if (error_logger)
     381          {
     382            if (spec1->uses_currentloc)
     383              error_logger (_("'%s' uses %%C but '%s' doesn't"),
     384                            pretty_msgid, pretty_msgstr);
     385            else
     386              error_logger (_("'%s' does not use %%C but '%s' uses %%C"),
     387                            pretty_msgid, pretty_msgstr);
     388          }
     389        err = true;
     390      }
     391  
     392    return err;
     393  }
     394  
     395  
     396  struct formatstring_parser formatstring_gfc_internal =
     397  {
     398    format_parse,
     399    format_free,
     400    format_get_number_of_directives,
     401    NULL,
     402    format_check
     403  };
     404  
     405  
     406  #ifdef TEST
     407  
     408  /* Test program: Print the argument list specification returned by
     409     format_parse for strings read from standard input.  */
     410  
     411  #include <stdio.h>
     412  
     413  static void
     414  format_print (void *descr)
     415  {
     416    struct spec *spec = (struct spec *) descr;
     417    unsigned int i;
     418  
     419    if (spec == NULL)
     420      {
     421        printf ("INVALID");
     422        return;
     423      }
     424  
     425    printf ("(");
     426    for (i = 0; i < spec->unnumbered_arg_count; i++)
     427      {
     428        if (i > 0)
     429          printf (" ");
     430        if (spec->unnumbered[i].type & FAT_UNSIGNED)
     431          printf ("[unsigned]");
     432        switch (spec->unnumbered[i].type & FAT_SIZE_MASK)
     433          {
     434          case 0:
     435            break;
     436          case FAT_SIZE_LONG:
     437            printf ("[long]");
     438            break;
     439          default:
     440            abort ();
     441          }
     442        switch (spec->unnumbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_MASK))
     443          {
     444          case FAT_INTEGER:
     445            printf ("i");
     446            break;
     447          case FAT_CHAR:
     448            printf ("c");
     449            break;
     450          case FAT_STRING:
     451            printf ("s");
     452            break;
     453          case FAT_LOCUS:
     454            printf ("L");
     455            break;
     456          default:
     457            abort ();
     458          }
     459      }
     460    printf (")");
     461    if (spec->uses_currentloc)
     462      printf (" C");
     463  }
     464  
     465  int
     466  main ()
     467  {
     468    for (;;)
     469      {
     470        char *line = NULL;
     471        size_t line_size = 0;
     472        int line_len;
     473        char *invalid_reason;
     474        void *descr;
     475  
     476        line_len = getline (&line, &line_size, stdin);
     477        if (line_len < 0)
     478          break;
     479        if (line_len > 0 && line[line_len - 1] == '\n')
     480          line[--line_len] = '\0';
     481  
     482        invalid_reason = NULL;
     483        descr = format_parse (line, false, NULL, &invalid_reason);
     484  
     485        format_print (descr);
     486        printf ("\n");
     487        if (descr == NULL)
     488          printf ("%s\n", invalid_reason);
     489  
     490        free (invalid_reason);
     491        free (line);
     492      }
     493  
     494    return 0;
     495  }
     496  
     497  /*
     498   * For Emacs M-x compile
     499   * Local Variables:
     500   * 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-gfc-internal.c ../gnulib-lib/libgettextlib.la"
     501   * End:
     502   */
     503  
     504  #endif /* TEST */