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