Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / src / format-ycp.c
1 /* YCP and Smalltalk format strings.
2    Copyright (C) 2001-2004, 2006-2007, 2009, 2015 Free Software
3    Foundation, Inc.
4    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
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 *fdi,
52               char **invalid_reason)
53 {
54   const char *const format_start = format;
55   struct spec spec;
56   struct spec *result;
57
58   spec.directives = 0;
59   spec.arg_count = 0;
60
61   for (; *format != '\0';)
62     if (*format++ == '%')
63       {
64         /* A directive.  */
65         FDI_SET (format - 1, FMTDIR_START);
66         spec.directives++;
67
68         if (*format == '%')
69           format++;
70         else if (*format >= '1' && *format <= '9')
71           {
72             unsigned int number = *format - '1';
73
74             while (spec.arg_count <= number)
75               spec.args_used[spec.arg_count++] = false;
76             spec.args_used[number] = true;
77
78             format++;
79           }
80         else
81           {
82             if (*format == '\0')
83               {
84                 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
85                 FDI_SET (format - 1, FMTDIR_ERROR);
86               }
87             else
88               {
89                 *invalid_reason =
90                   (c_isprint (*format)
91                    ? xasprintf (_("In the directive number %u, the character '%c' is not a digit between 1 and 9."), spec.directives, *format)
92                    : xasprintf (_("The character that terminates the directive number %u is not a digit between 1 and 9."), spec.directives));
93                 FDI_SET (format, FMTDIR_ERROR);
94               }
95             goto bad_format;
96           }
97
98         FDI_SET (format - 1, FMTDIR_END);
99       }
100
101   result = XMALLOC (struct spec);
102   *result = spec;
103   return result;
104
105  bad_format:
106   return NULL;
107 }
108
109 static void
110 format_free (void *descr)
111 {
112   struct spec *spec = (struct spec *) descr;
113
114   free (spec);
115 }
116
117 static int
118 format_get_number_of_directives (void *descr)
119 {
120   struct spec *spec = (struct spec *) descr;
121
122   return spec->directives;
123 }
124
125 static bool
126 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
127               formatstring_error_logger_t error_logger,
128               const char *pretty_msgid, const char *pretty_msgstr)
129 {
130   struct spec *spec1 = (struct spec *) msgid_descr;
131   struct spec *spec2 = (struct spec *) msgstr_descr;
132   bool err = false;
133   unsigned int i;
134
135   for (i = 0; i < spec1->arg_count || i < spec2->arg_count; i++)
136     {
137       bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]);
138       bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]);
139
140       if (equality ? (arg_used1 != arg_used2) : (!arg_used1 && arg_used2))
141         {
142           if (error_logger)
143             {
144               if (arg_used1)
145                 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
146                               i + 1, pretty_msgstr);
147               else
148                 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
149                               i + 1, pretty_msgstr, pretty_msgid);
150             }
151           err = true;
152           break;
153         }
154     }
155
156   return err;
157 }
158
159
160 struct formatstring_parser formatstring_ycp =
161 {
162   format_parse,
163   format_free,
164   format_get_number_of_directives,
165   NULL,
166   format_check
167 };
168
169
170 struct formatstring_parser formatstring_smalltalk =
171 {
172   format_parse,
173   format_free,
174   format_get_number_of_directives,
175   NULL,
176   format_check
177 };
178
179
180 #ifdef TEST
181
182 /* Test program: Print the argument list specification returned by
183    format_parse for strings read from standard input.  */
184
185 #include <stdio.h>
186
187 static void
188 format_print (void *descr)
189 {
190   struct spec *spec = (struct spec *) descr;
191   unsigned int i;
192
193   if (spec == NULL)
194     {
195       printf ("INVALID");
196       return;
197     }
198
199   printf ("(");
200   for (i = 0; i < spec->arg_count; i++)
201     {
202       if (i > 0)
203         printf (" ");
204       if (spec->args_used[i])
205         printf ("*");
206       else
207         printf ("_");
208     }
209   printf (")");
210 }
211
212 int
213 main ()
214 {
215   for (;;)
216     {
217       char *line = NULL;
218       size_t line_size = 0;
219       int line_len;
220       char *invalid_reason;
221       void *descr;
222
223       line_len = getline (&line, &line_size, stdin);
224       if (line_len < 0)
225         break;
226       if (line_len > 0 && line[line_len - 1] == '\n')
227         line[--line_len] = '\0';
228
229       invalid_reason = NULL;
230       descr = format_parse (line, false, NULL, &invalid_reason);
231
232       format_print (descr);
233       printf ("\n");
234       if (descr == NULL)
235         printf ("%s\n", invalid_reason);
236
237       free (invalid_reason);
238       free (line);
239     }
240
241   return 0;
242 }
243
244 /*
245  * For Emacs M-x compile
246  * Local Variables:
247  * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-ycp.c ../gnulib-lib/libgettextlib.la"
248  * End:
249  */
250
251 #endif /* TEST */