Home | History | Annotate | Line # | Download | only in src
      1 /* Emacs Lisp format strings.
      2    Copyright (C) 2001-2004, 2006 Free Software Foundation, Inc.
      3    Written by Bruno Haible <haible (at) clisp.cons.org>, 2002.
      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 2, or (at your option)
      8    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, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
     18 
     19 #ifdef HAVE_CONFIG_H
     20 # include <config.h>
     21 #endif
     22 
     23 #include <stdbool.h>
     24 #include <stdlib.h>
     25 
     26 #include "format.h"
     27 #include "c-ctype.h"
     28 #include "xalloc.h"
     29 #include "xvasprintf.h"
     30 #include "format-invalid.h"
     31 #include "gettext.h"
     32 
     33 #define _(str) gettext (str)
     34 
     35 /* Emacs Lisp format strings are implemented in emacs-21.1/src/editfns.c,
     36    xemacs-21.1.14/src/editfns.c and xemacs-21.1.14/src/doprnt.c.
     37    A directive
     38    - starts with '%' or '%m$' where m is a positive integer,
     39    - is optionally followed by any of the characters '#', '0', '-', ' ', '+',
     40      each of which acts as a flag,
     41    - is optionally followed by a width specification: '*' (reads an argument)
     42      or a nonempty digit sequence,
     43    - is optionally followed by '.' and a precision specification: '*' (reads
     44      an argument) or a nonempty digit sequence,
     45    - is finished by a specifier
     46        - '%', that needs no argument,
     47        - 'c', that need a character argument,
     48        - 'd', 'i', 'x', 'X', 'o', that need an integer argument,
     49        - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument,
     50        - 's', that need an argument and prints it using princ,
     51        - 'S', that need an argument and prints it using prin1.
     52    Numbered ('%m$') and unnumbered argument specifications can be used in the
     53    same string. The effect of '%m$' is to set the current argument number to
     54    m. The current argument number is incremented after processing a directive.
     55  */
     56 
     57 enum format_arg_type
     58 {
     59   FAT_NONE,
     60   FAT_CHARACTER,
     61   FAT_INTEGER,
     62   FAT_FLOAT,
     63   FAT_OBJECT_PRETTY,
     64   FAT_OBJECT
     65 };
     66 
     67 struct numbered_arg
     68 {
     69   unsigned int number;
     70   enum format_arg_type type;
     71 };
     72 
     73 struct spec
     74 {
     75   unsigned int directives;
     76   unsigned int numbered_arg_count;
     77   unsigned int allocated;
     78   struct numbered_arg *numbered;
     79 };
     80 
     81 /* Locale independent test for a decimal digit.
     82    Argument can be  'char' or 'unsigned char'.  (Whereas the argument of
     83    <ctype.h> isdigit must be an 'unsigned char'.)  */
     84 #undef isdigit
     85 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
     86 
     87 
     88 static int
     89 numbered_arg_compare (const void *p1, const void *p2)
     90 {
     91   unsigned int n1 = ((const struct numbered_arg *) p1)->number;
     92   unsigned int n2 = ((const struct numbered_arg *) p2)->number;
     93 
     94   return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
     95 }
     96 
     97 static void *
     98 format_parse (const char *format, bool translated, char **invalid_reason)
     99 {
    100   struct spec spec;
    101   struct spec *result;
    102   unsigned int number;
    103 
    104   spec.directives = 0;
    105   spec.numbered_arg_count = 0;
    106   spec.allocated = 0;
    107   spec.numbered = NULL;
    108   number = 1;
    109 
    110   for (; *format != '\0';)
    111     if (*format++ == '%')
    112       {
    113 	/* A directive.  */
    114 	enum format_arg_type type;
    115 
    116 	spec.directives++;
    117 
    118 	if (isdigit (*format))
    119 	  {
    120 	    const char *f = format;
    121 	    unsigned int m = 0;
    122 
    123 	    do
    124 	      {
    125 		m = 10 * m + (*f - '0');
    126 		f++;
    127 	      }
    128 	    while (isdigit (*f));
    129 
    130 	    if (*f == '$' && m > 0)
    131 	      {
    132 		number = m;
    133 		format = ++f;
    134 	      }
    135 	  }
    136 
    137 	/* Parse flags.  */
    138 	while (*format == ' ' || *format == '+' || *format == '-'
    139 	       || *format == '#' || *format == '0')
    140 	  format++;
    141 
    142 	/* Parse width.  */
    143 	if (*format == '*')
    144 	  {
    145 	    format++;
    146 
    147 	    if (spec.allocated == spec.numbered_arg_count)
    148 	      {
    149 		spec.allocated = 2 * spec.allocated + 1;
    150 		spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
    151 	      }
    152 	    spec.numbered[spec.numbered_arg_count].number = number;
    153 	    spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
    154 	    spec.numbered_arg_count++;
    155 
    156 	    number++;
    157 	  }
    158 	else if (isdigit (*format))
    159 	  {
    160 	    do format++; while (isdigit (*format));
    161 	  }
    162 
    163 	/* Parse precision.  */
    164 	if (*format == '.')
    165 	  {
    166 	    format++;
    167 
    168 	    if (*format == '*')
    169 	      {
    170 		format++;
    171 
    172 		if (spec.allocated == spec.numbered_arg_count)
    173 		  {
    174 		    spec.allocated = 2 * spec.allocated + 1;
    175 		    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
    176 		  }
    177 		spec.numbered[spec.numbered_arg_count].number = number;
    178 		spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
    179 		spec.numbered_arg_count++;
    180 
    181 		number++;
    182 	      }
    183 	    else if (isdigit (*format))
    184 	      {
    185 		do format++; while (isdigit (*format));
    186 	      }
    187 	  }
    188 
    189 	switch (*format)
    190 	  {
    191 	  case '%':
    192 	    type = FAT_NONE;
    193 	    break;
    194 	  case 'c':
    195 	    type = FAT_CHARACTER;
    196 	    break;
    197 	  case 'd': case 'i': case 'x': case 'X': case 'o':
    198 	    type = FAT_INTEGER;
    199 	    break;
    200 	  case 'e': case 'E': case 'f': case 'g': case 'G':
    201 	    type = FAT_FLOAT;
    202 	    break;
    203 	  case 's':
    204 	    type = FAT_OBJECT_PRETTY;
    205 	    break;
    206 	  case 'S':
    207 	    type = FAT_OBJECT;
    208 	    break;
    209 	  default:
    210 	    *invalid_reason =
    211 	      (*format == '\0'
    212 	       ? INVALID_UNTERMINATED_DIRECTIVE ()
    213 	       : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
    214 	    goto bad_format;
    215 	  }
    216 
    217 	if (type != FAT_NONE)
    218 	  {
    219 	    if (spec.allocated == spec.numbered_arg_count)
    220 	      {
    221 		spec.allocated = 2 * spec.allocated + 1;
    222 		spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
    223 	      }
    224 	    spec.numbered[spec.numbered_arg_count].number = number;
    225 	    spec.numbered[spec.numbered_arg_count].type = type;
    226 	    spec.numbered_arg_count++;
    227 
    228 	    number++;
    229 	  }
    230 
    231 	format++;
    232       }
    233 
    234   /* Sort the numbered argument array, and eliminate duplicates.  */
    235   if (spec.numbered_arg_count > 1)
    236     {
    237       unsigned int i, j;
    238       bool err;
    239 
    240       qsort (spec.numbered, spec.numbered_arg_count,
    241 	     sizeof (struct numbered_arg), numbered_arg_compare);
    242 
    243       /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
    244       err = false;
    245       for (i = j = 0; i < spec.numbered_arg_count; i++)
    246 	if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
    247 	  {
    248 	    enum format_arg_type type1 = spec.numbered[i].type;
    249 	    enum format_arg_type type2 = spec.numbered[j-1].type;
    250 	    enum format_arg_type type_both;
    251 
    252 	    if (type1 == type2)
    253 	      type_both = type1;
    254 	    else
    255 	      {
    256 		/* Incompatible types.  */
    257 		type_both = FAT_NONE;
    258 		if (!err)
    259 		  *invalid_reason =
    260 		    INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
    261 		err = true;
    262 	      }
    263 
    264 	    spec.numbered[j-1].type = type_both;
    265 	  }
    266 	else
    267 	  {
    268 	    if (j < i)
    269 	      {
    270 		spec.numbered[j].number = spec.numbered[i].number;
    271 		spec.numbered[j].type = spec.numbered[i].type;
    272 	      }
    273 	    j++;
    274 	  }
    275       spec.numbered_arg_count = j;
    276       if (err)
    277 	/* *invalid_reason has already been set above.  */
    278 	goto bad_format;
    279     }
    280 
    281   result = (struct spec *) xmalloc (sizeof (struct spec));
    282   *result = spec;
    283   return result;
    284 
    285  bad_format:
    286   if (spec.numbered != NULL)
    287     free (spec.numbered);
    288   return NULL;
    289 }
    290 
    291 static void
    292 format_free (void *descr)
    293 {
    294   struct spec *spec = (struct spec *) descr;
    295 
    296   if (spec->numbered != NULL)
    297     free (spec->numbered);
    298   free (spec);
    299 }
    300 
    301 static int
    302 format_get_number_of_directives (void *descr)
    303 {
    304   struct spec *spec = (struct spec *) descr;
    305 
    306   return spec->directives;
    307 }
    308 
    309 static bool
    310 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
    311 	      formatstring_error_logger_t error_logger,
    312 	      const char *pretty_msgstr)
    313 {
    314   struct spec *spec1 = (struct spec *) msgid_descr;
    315   struct spec *spec2 = (struct spec *) msgstr_descr;
    316   bool err = false;
    317 
    318   if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
    319     {
    320       unsigned int i, j;
    321       unsigned int n1 = spec1->numbered_arg_count;
    322       unsigned int n2 = spec2->numbered_arg_count;
    323 
    324       /* Check the argument names are the same.
    325 	 Both arrays are sorted.  We search for the first difference.  */
    326       for (i = 0, j = 0; i < n1 || j < n2; )
    327 	{
    328 	  int cmp = (i >= n1 ? 1 :
    329 		     j >= n2 ? -1 :
    330 		     spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
    331 		     spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
    332 		     0);
    333 
    334 	  if (cmp > 0)
    335 	    {
    336 	      if (error_logger)
    337 		error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
    338 			      spec2->numbered[j].number, pretty_msgstr);
    339 	      err = true;
    340 	      break;
    341 	    }
    342 	  else if (cmp < 0)
    343 	    {
    344 	      if (equality)
    345 		{
    346 		  if (error_logger)
    347 		    error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
    348 				  spec1->numbered[i].number, pretty_msgstr);
    349 		  err = true;
    350 		  break;
    351 		}
    352 	      else
    353 		i++;
    354 	    }
    355 	  else
    356 	    j++, i++;
    357 	}
    358       /* Check the argument types are the same.  */
    359       if (!err)
    360 	for (i = 0, j = 0; j < n2; )
    361 	  {
    362 	    if (spec1->numbered[i].number == spec2->numbered[j].number)
    363 	      {
    364 		if (spec1->numbered[i].type != spec2->numbered[j].type)
    365 		  {
    366 		    if (error_logger)
    367 		      error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
    368 				    pretty_msgstr, spec2->numbered[j].number);
    369 		    err = true;
    370 		    break;
    371 		  }
    372 		j++, i++;
    373 	      }
    374 	    else
    375 	      i++;
    376 	  }
    377     }
    378 
    379   return err;
    380 }
    381 
    382 
    383 struct formatstring_parser formatstring_elisp =
    384 {
    385   format_parse,
    386   format_free,
    387   format_get_number_of_directives,
    388   NULL,
    389   format_check
    390 };
    391 
    392 
    393 #ifdef TEST
    394 
    395 /* Test program: Print the argument list specification returned by
    396    format_parse for strings read from standard input.  */
    397 
    398 #include <stdio.h>
    399 #include "getline.h"
    400 
    401 static void
    402 format_print (void *descr)
    403 {
    404   struct spec *spec = (struct spec *) descr;
    405   unsigned int last;
    406   unsigned int i;
    407 
    408   if (spec == NULL)
    409     {
    410       printf ("INVALID");
    411       return;
    412     }
    413 
    414   printf ("(");
    415   last = 1;
    416   for (i = 0; i < spec->numbered_arg_count; i++)
    417     {
    418       unsigned int number = spec->numbered[i].number;
    419 
    420       if (i > 0)
    421 	printf (" ");
    422       if (number < last)
    423 	abort ();
    424       for (; last < number; last++)
    425 	printf ("_ ");
    426       switch (spec->numbered[i].type)
    427 	{
    428 	case FAT_CHARACTER:
    429 	  printf ("c");
    430 	  break;
    431 	case FAT_INTEGER:
    432 	  printf ("i");
    433 	  break;
    434 	case FAT_FLOAT:
    435 	  printf ("f");
    436 	  break;
    437 	case FAT_OBJECT_PRETTY:
    438 	  printf ("s");
    439 	  break;
    440 	case FAT_OBJECT:
    441 	  printf ("*");
    442 	  break;
    443 	default:
    444 	  abort ();
    445 	}
    446       last = number + 1;
    447     }
    448   printf (")");
    449 }
    450 
    451 int
    452 main ()
    453 {
    454   for (;;)
    455     {
    456       char *line = NULL;
    457       size_t line_size = 0;
    458       int line_len;
    459       char *invalid_reason;
    460       void *descr;
    461 
    462       line_len = getline (&line, &line_size, stdin);
    463       if (line_len < 0)
    464 	break;
    465       if (line_len > 0 && line[line_len - 1] == '\n')
    466 	line[--line_len] = '\0';
    467 
    468       invalid_reason = NULL;
    469       descr = format_parse (line, false, &invalid_reason);
    470 
    471       format_print (descr);
    472       printf ("\n");
    473       if (descr == NULL)
    474 	printf ("%s\n", invalid_reason);
    475 
    476       free (invalid_reason);
    477       free (line);
    478     }
    479 
    480   return 0;
    481 }
    482 
    483 /*
    484  * For Emacs M-x compile
    485  * Local Variables:
    486  * compile-command: "/bin/sh ../libtool --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../lib -I../intl -DHAVE_CONFIG_H -DTEST format-elisp.c ../lib/libgettextlib.la"
    487  * End:
    488  */
    489 
    490 #endif /* TEST */
    491