1 /* Shell format strings.
2 Copyright (C) 2003-2004 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
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)
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
30 #include "format-invalid.h"
33 #define _(str) gettext (str)
35 /* Shell format strings are simply strings subjects to variable substitution.
36 A variable substitution starts with '$' and is finished by either
37 - a nonempty sequence of alphanumeric ASCII characters, the first being
39 - an opening brace '{', a nonempty sequence of alphanumeric ASCII
40 characters, the first being not a digit, and a closing brace '}'.
41 We don't support variable references like $1, $$ or $? since they make
42 no sense when 'envsubst' is invoked.
43 We don't support non-ASCII variable names, to avoid dependencies w.r.t. the
44 current encoding: While "${\xe0}" looks like a variable access in ISO-8859-1
45 encoding, it doesn't look like one in the BIG5, BIG5-HKSCS, GBK, GB18030,
46 SHIFT_JIS, JOHAB encodings, because \xe0\x7d is a single character in these
48 We don't support the POSIX syntax for default or alternate values:
49 ${variable-default} ${variable:-default}
50 ${variable=default} ${variable:=default}
51 ${variable+replacement} ${variable:+replacement}
52 ${variable?ignored} ${variable:?ignored}
53 because the translator might be tempted to change the default value; if
54 we allow it we have a security problem; if we don't allow it the translator
65 unsigned int directives
;
66 unsigned int named_arg_count
;
67 unsigned int allocated
;
68 struct named_arg
*named
;
73 named_arg_compare (const void *p1
, const void *p2
)
75 return strcmp (((const struct named_arg
*) p1
)->name
,
76 ((const struct named_arg
*) p2
)->name
);
79 #define INVALID_NON_ASCII_VARIABLE() \
80 xstrdup (_("The string refers to a shell variable with a non-ASCII name."))
81 #define INVALID_SHELL_SYNTAX() \
82 xstrdup (_("The string refers to a shell variable with complex shell brace syntax. This syntax is unsupported here due to security reasons."))
83 #define INVALID_CONTEXT_DEPENDENT_VARIABLE() \
84 xstrdup (_("The string refers to a shell variable whose value may be different inside shell functions."))
85 #define INVALID_EMPTY_VARIABLE() \
86 xstrdup (_("The string refers to a shell variable with an empty name."))
89 format_parse (const char *format
, bool translated
, char **invalid_reason
)
95 spec
.named_arg_count
= 0;
99 for (; *format
!= '\0';)
100 if (*format
++ == '$')
102 /* A variable substitution. */
109 const char *name_start
;
110 const char *name_end
;
113 name_start
= ++format
;
114 for (; *format
!= '\0'; format
++)
118 if (!c_isascii (*format
))
120 *invalid_reason
= INVALID_NON_ASCII_VARIABLE ();
123 if (format
> name_start
124 && (*format
== '-' || *format
== '=' || *format
== '+'
125 || *format
== '?' || *format
== ':'))
127 *invalid_reason
= INVALID_SHELL_SYNTAX ();
130 if (!(c_isalnum (*format
) || *format
== '_')
131 || (format
== name_start
&& c_isdigit (*format
)))
133 *invalid_reason
= INVALID_CONTEXT_DEPENDENT_VARIABLE ();
139 *invalid_reason
= INVALID_UNTERMINATED_DIRECTIVE ();
144 n
= name_end
- name_start
;
147 *invalid_reason
= INVALID_EMPTY_VARIABLE ();
150 name
= (char *) xmalloc (n
+ 1);
151 memcpy (name
, name_start
, n
);
154 else if (c_isalpha (*format
) || *format
== '_')
156 const char *name_start
;
157 const char *name_end
;
163 while (*format
!= '\0' && (c_isalnum (*format
) || *format
== '_'));
166 n
= name_end
- name_start
;
167 name
= (char *) xmalloc (n
+ 1);
168 memcpy (name
, name_start
, n
);
171 else if (*format
!= '\0')
173 if (!c_isascii (*format
))
175 *invalid_reason
= INVALID_NON_ASCII_VARIABLE ();
180 *invalid_reason
= INVALID_CONTEXT_DEPENDENT_VARIABLE ();
186 *invalid_reason
= INVALID_UNTERMINATED_DIRECTIVE ();
190 /* Named argument. */
191 if (spec
.allocated
== spec
.named_arg_count
)
193 spec
.allocated
= 2 * spec
.allocated
+ 1;
194 spec
.named
= (struct named_arg
*) xrealloc (spec
.named
, spec
.allocated
* sizeof (struct named_arg
));
196 spec
.named
[spec
.named_arg_count
].name
= name
;
197 spec
.named_arg_count
++;
200 /* Sort the named argument array, and eliminate duplicates. */
201 if (spec
.named_arg_count
> 1)
205 qsort (spec
.named
, spec
.named_arg_count
, sizeof (struct named_arg
),
208 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
209 for (i
= j
= 0; i
< spec
.named_arg_count
; i
++)
210 if (j
> 0 && strcmp (spec
.named
[i
].name
, spec
.named
[j
-1].name
) == 0)
211 free (spec
.named
[i
].name
);
215 spec
.named
[j
].name
= spec
.named
[i
].name
;
218 spec
.named_arg_count
= j
;
221 result
= (struct spec
*) xmalloc (sizeof (struct spec
));
226 if (spec
.named
!= NULL
)
229 for (i
= 0; i
< spec
.named_arg_count
; i
++)
230 free (spec
.named
[i
].name
);
237 format_free (void *descr
)
239 struct spec
*spec
= (struct spec
*) descr
;
241 if (spec
->named
!= NULL
)
244 for (i
= 0; i
< spec
->named_arg_count
; i
++)
245 free (spec
->named
[i
].name
);
252 format_get_number_of_directives (void *descr
)
254 struct spec
*spec
= (struct spec
*) descr
;
256 return spec
->directives
;
260 format_check (void *msgid_descr
, void *msgstr_descr
, bool equality
,
261 formatstring_error_logger_t error_logger
,
262 const char *pretty_msgstr
)
264 struct spec
*spec1
= (struct spec
*) msgid_descr
;
265 struct spec
*spec2
= (struct spec
*) msgstr_descr
;
268 if (spec1
->named_arg_count
+ spec2
->named_arg_count
> 0)
271 unsigned int n1
= spec1
->named_arg_count
;
272 unsigned int n2
= spec2
->named_arg_count
;
274 /* Check the argument names are the same.
275 Both arrays are sorted. We search for the first difference. */
276 for (i
= 0, j
= 0; i
< n1
|| j
< n2
; )
278 int cmp
= (i
>= n1
? 1 :
280 strcmp (spec1
->named
[i
].name
, spec2
->named
[j
].name
));
285 error_logger (_("a format specification for argument '%s', as in '%s', doesn't exist in 'msgid'"),
286 spec2
->named
[j
].name
, pretty_msgstr
);
295 error_logger (_("a format specification for argument '%s' doesn't exist in '%s'"),
296 spec1
->named
[i
].name
, pretty_msgstr
);
312 struct formatstring_parser formatstring_sh
=
316 format_get_number_of_directives
,
323 /* Test program: Print the argument list specification returned by
324 format_parse for strings read from standard input. */
330 format_print (void *descr
)
332 struct spec
*spec
= (struct spec
*) descr
;
342 for (i
= 0; i
< spec
->named_arg_count
; i
++)
346 printf ("'%s'", spec
->named
[i
].name
);
357 size_t line_size
= 0;
359 char *invalid_reason
;
362 line_len
= getline (&line
, &line_size
, stdin
);
365 if (line_len
> 0 && line
[line_len
- 1] == '\n')
366 line
[--line_len
] = '\0';
368 invalid_reason
= NULL
;
369 descr
= format_parse (line
, false, &invalid_reason
);
371 format_print (descr
);
374 printf ("%s\n", invalid_reason
);
376 free (invalid_reason
);
384 * For Emacs M-x compile
386 * 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-sh.c ../lib/libgettextlib.la"