~ubuntu-branches/ubuntu/breezy/gettext/breezy

« back to all changes in this revision

Viewing changes to gettext-tools/src/format-sh.c

  • Committer: Bazaar Package Importer
  • Author(s): Santiago Vila
  • Date: 2004-03-14 17:40:02 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040314174002-p1ad5ldve1hqzhye
Tags: 0.14.1-2
* Added libexpat1-dev to Build-Depends, for glade support.
* Added libc0.1-dev to Build-Depends, for GNU/kFreeBSD.
* Removed special-casing of knetbsd-gnu in debian/rules.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Shell format strings.
 
2
   Copyright (C) 2003-2004 Free Software Foundation, Inc.
 
3
   Written by Bruno Haible <bruno@clisp.org>, 2003.
 
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
18
 
 
19
#ifdef HAVE_CONFIG_H
 
20
# include <config.h>
 
21
#endif
 
22
 
 
23
#include <stdbool.h>
 
24
#include <stdlib.h>
 
25
#include <string.h>
 
26
 
 
27
#include "format.h"
 
28
#include "c-ctype.h"
 
29
#include "xalloc.h"
 
30
#include "format-invalid.h"
 
31
#include "error.h"
 
32
#include "error-progname.h"
 
33
#include "gettext.h"
 
34
 
 
35
#define _(str) gettext (str)
 
36
 
 
37
/* Shell format strings are simply strings subjects to variable substitution.
 
38
   A variable substitution starts with '$' and is finished by either
 
39
   - a nonempty sequence of alphanumeric ASCII characters, the first being
 
40
     not a digit, or
 
41
   - an opening brace '{', a nonempty sequence of alphanumeric ASCII
 
42
     characters, the first being not a digit, and a closing brace '}'.
 
43
   We don't support variable references like $1, $$ or $? since they make
 
44
   no sense when 'envsubst' is invoked.
 
45
   We don't support non-ASCII variable names, to avoid dependencies w.r.t. the
 
46
   current encoding: While "${\xe0}" looks like a variable access in ISO-8859-1
 
47
   encoding, it doesn't look like one in the BIG5, BIG5-HKSCS, GBK, GB18030,
 
48
   SHIFT_JIS, JOHAB encodings, because \xe0\x7d is a single character in these
 
49
   encodings.
 
50
   We don't support the POSIX syntax for default or alternate values:
 
51
     ${variable-default}        ${variable:-default}
 
52
     ${variable=default}        ${variable:=default}
 
53
     ${variable+replacement}    ${variable:+replacement}
 
54
     ${variable?ignored}        ${variable:?ignored}
 
55
   because the translator might be tempted to change the default value; if
 
56
   we allow it we have a security problem; if we don't allow it the translator
 
57
   will be surprised.
 
58
 */
 
59
 
 
60
struct named_arg
 
61
{
 
62
  char *name;
 
63
};
 
64
 
 
65
struct spec
 
66
{
 
67
  unsigned int directives;
 
68
  unsigned int named_arg_count;
 
69
  unsigned int allocated;
 
70
  struct named_arg *named;
 
71
};
 
72
 
 
73
 
 
74
static int
 
75
named_arg_compare (const void *p1, const void *p2)
 
76
{
 
77
  return strcmp (((const struct named_arg *) p1)->name,
 
78
                 ((const struct named_arg *) p2)->name);
 
79
}
 
80
 
 
81
#define INVALID_NON_ASCII_VARIABLE() \
 
82
  xstrdup (_("The string refers to a shell variable with a non-ASCII name."))
 
83
#define INVALID_SHELL_SYNTAX() \
 
84
  xstrdup (_("The string refers to a shell variable with complex shell brace syntax. This syntax is unsupported here due to security reasons."))
 
85
#define INVALID_CONTEXT_DEPENDENT_VARIABLE() \
 
86
  xstrdup (_("The string refers to a shell variable whose value may be different inside shell functions."))
 
87
#define INVALID_EMPTY_VARIABLE() \
 
88
  xstrdup (_("The string refers to a shell variable with an empty name."))
 
89
 
 
90
static void *
 
91
format_parse (const char *format, bool translated, char **invalid_reason)
 
92
{
 
93
  struct spec spec;
 
94
  struct spec *result;
 
95
 
 
96
  spec.directives = 0;
 
97
  spec.named_arg_count = 0;
 
98
  spec.allocated = 0;
 
99
  spec.named = NULL;
 
100
 
 
101
  for (; *format != '\0';)
 
102
    if (*format++ == '$')
 
103
      {
 
104
        /* A variable substitution.  */
 
105
        char *name;
 
106
 
 
107
        spec.directives++;
 
108
 
 
109
        if (*format == '{')
 
110
          {
 
111
            const char *name_start;
 
112
            const char *name_end;
 
113
            size_t n;
 
114
 
 
115
            name_start = ++format;
 
116
            for (; *format != '\0'; format++)
 
117
              {
 
118
                if (*format == '}')
 
119
                  break;
 
120
                if (!c_isascii (*format))
 
121
                  {
 
122
                    *invalid_reason = INVALID_NON_ASCII_VARIABLE ();
 
123
                    goto bad_format;
 
124
                  }
 
125
                if (format > name_start
 
126
                    && (*format == '-' || *format == '=' || *format == '+'
 
127
                        || *format == '?' || *format == ':'))
 
128
                  {
 
129
                    *invalid_reason = INVALID_SHELL_SYNTAX ();
 
130
                    goto bad_format;
 
131
                  }
 
132
                if (!(c_isalnum (*format) || *format == '_')
 
133
                    || (format == name_start && c_isdigit (*format)))
 
134
                  {
 
135
                    *invalid_reason = INVALID_CONTEXT_DEPENDENT_VARIABLE ();
 
136
                    goto bad_format;
 
137
                  }
 
138
              }
 
139
            if (*format == '\0')
 
140
              {
 
141
                *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
 
142
                goto bad_format;
 
143
              }
 
144
            name_end = format++;
 
145
 
 
146
            n = name_end - name_start;
 
147
            if (n == 0)
 
148
              {
 
149
                *invalid_reason = INVALID_EMPTY_VARIABLE ();
 
150
                goto bad_format;
 
151
              }
 
152
            name = (char *) xmalloc (n + 1);
 
153
            memcpy (name, name_start, n);
 
154
            name[n] = '\0';
 
155
          }
 
156
        else if (c_isalpha (*format) || *format == '_')
 
157
          {
 
158
            const char *name_start;
 
159
            const char *name_end;
 
160
            size_t n;
 
161
 
 
162
            name_start = format;
 
163
            do
 
164
              format++;
 
165
            while (*format != '\0' && (c_isalnum (*format) || *format == '_'));
 
166
            name_end = format;
 
167
 
 
168
            n = name_end - name_start;
 
169
            name = (char *) xmalloc (n + 1);
 
170
            memcpy (name, name_start, n);
 
171
            name[n] = '\0';
 
172
          }
 
173
        else if (*format != '\0')
 
174
          {
 
175
            if (!c_isascii (*format))
 
176
              {
 
177
                *invalid_reason = INVALID_NON_ASCII_VARIABLE ();
 
178
                goto bad_format;
 
179
              }
 
180
            else
 
181
              {
 
182
                *invalid_reason = INVALID_CONTEXT_DEPENDENT_VARIABLE ();
 
183
                goto bad_format;
 
184
              }
 
185
          }
 
186
        else
 
187
          {
 
188
            *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
 
189
            goto bad_format;
 
190
          }
 
191
 
 
192
        /* Named argument.  */
 
193
        if (spec.allocated == spec.named_arg_count)
 
194
          {
 
195
            spec.allocated = 2 * spec.allocated + 1;
 
196
            spec.named = (struct named_arg *) xrealloc (spec.named, spec.allocated * sizeof (struct named_arg));
 
197
          }
 
198
        spec.named[spec.named_arg_count].name = name;
 
199
        spec.named_arg_count++;
 
200
      }
 
201
 
 
202
  /* Sort the named argument array, and eliminate duplicates.  */
 
203
  if (spec.named_arg_count > 1)
 
204
    {
 
205
      unsigned int i, j;
 
206
 
 
207
      qsort (spec.named, spec.named_arg_count, sizeof (struct named_arg),
 
208
             named_arg_compare);
 
209
 
 
210
      /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
 
211
      for (i = j = 0; i < spec.named_arg_count; i++)
 
212
        if (j > 0 && strcmp (spec.named[i].name, spec.named[j-1].name) == 0)
 
213
          free (spec.named[i].name);
 
214
        else
 
215
          {
 
216
            if (j < i)
 
217
              spec.named[j].name = spec.named[i].name;
 
218
            j++;
 
219
          }
 
220
      spec.named_arg_count = j;
 
221
    }
 
222
 
 
223
  result = (struct spec *) xmalloc (sizeof (struct spec));
 
224
  *result = spec;
 
225
  return result;
 
226
 
 
227
 bad_format:
 
228
  if (spec.named != NULL)
 
229
    {
 
230
      unsigned int i;
 
231
      for (i = 0; i < spec.named_arg_count; i++)
 
232
        free (spec.named[i].name);
 
233
      free (spec.named);
 
234
    }
 
235
  return NULL;
 
236
}
 
237
 
 
238
static void
 
239
format_free (void *descr)
 
240
{
 
241
  struct spec *spec = (struct spec *) descr;
 
242
 
 
243
  if (spec->named != NULL)
 
244
    {
 
245
      unsigned int i;
 
246
      for (i = 0; i < spec->named_arg_count; i++)
 
247
        free (spec->named[i].name);
 
248
      free (spec->named);
 
249
    }
 
250
  free (spec);
 
251
}
 
252
 
 
253
static int
 
254
format_get_number_of_directives (void *descr)
 
255
{
 
256
  struct spec *spec = (struct spec *) descr;
 
257
 
 
258
  return spec->directives;
 
259
}
 
260
 
 
261
static bool
 
262
format_check (const lex_pos_ty *pos, void *msgid_descr, void *msgstr_descr,
 
263
              bool equality, bool noisy, const char *pretty_msgstr)
 
264
{
 
265
  struct spec *spec1 = (struct spec *) msgid_descr;
 
266
  struct spec *spec2 = (struct spec *) msgstr_descr;
 
267
  bool err = false;
 
268
 
 
269
  if (spec1->named_arg_count + spec2->named_arg_count > 0)
 
270
    {
 
271
      unsigned int i, j;
 
272
      unsigned int n1 = spec1->named_arg_count;
 
273
      unsigned int n2 = spec2->named_arg_count;
 
274
 
 
275
      /* Check the argument names are the same.
 
276
         Both arrays are sorted.  We search for the first difference.  */
 
277
      for (i = 0, j = 0; i < n1 || j < n2; )
 
278
        {
 
279
          int cmp = (i >= n1 ? 1 :
 
280
                     j >= n2 ? -1 :
 
281
                     strcmp (spec1->named[i].name, spec2->named[j].name));
 
282
 
 
283
          if (cmp > 0)
 
284
            {
 
285
              if (noisy)
 
286
                {
 
287
                  error_with_progname = false;
 
288
                  error_at_line (0, 0, pos->file_name, pos->line_number,
 
289
                                 _("a format specification for argument '%s', as in '%s', doesn't exist in 'msgid'"),
 
290
                                 spec2->named[j].name, pretty_msgstr);
 
291
                  error_with_progname = true;
 
292
                }
 
293
              err = true;
 
294
              break;
 
295
            }
 
296
          else if (cmp < 0)
 
297
            {
 
298
              if (equality)
 
299
                {
 
300
                  if (noisy)
 
301
                    {
 
302
                      error_with_progname = false;
 
303
                      error_at_line (0, 0, pos->file_name, pos->line_number,
 
304
                                     _("a format specification for argument '%s' doesn't exist in '%s'"),
 
305
                                     spec1->named[i].name, pretty_msgstr);
 
306
                      error_with_progname = true;
 
307
                    }
 
308
                  err = true;
 
309
                  break;
 
310
                }
 
311
              else
 
312
                i++;
 
313
            }
 
314
          else
 
315
            j++, i++;
 
316
        }
 
317
    }
 
318
 
 
319
  return err;
 
320
}
 
321
 
 
322
 
 
323
struct formatstring_parser formatstring_sh =
 
324
{
 
325
  format_parse,
 
326
  format_free,
 
327
  format_get_number_of_directives,
 
328
  format_check
 
329
};
 
330
 
 
331
 
 
332
#ifdef TEST
 
333
 
 
334
/* Test program: Print the argument list specification returned by
 
335
   format_parse for strings read from standard input.  */
 
336
 
 
337
#include <stdio.h>
 
338
#include "getline.h"
 
339
 
 
340
static void
 
341
format_print (void *descr)
 
342
{
 
343
  struct spec *spec = (struct spec *) descr;
 
344
  unsigned int i;
 
345
 
 
346
  if (spec == NULL)
 
347
    {
 
348
      printf ("INVALID");
 
349
      return;
 
350
    }
 
351
 
 
352
  printf ("{");
 
353
  for (i = 0; i < spec->named_arg_count; i++)
 
354
    {
 
355
      if (i > 0)
 
356
        printf (", ");
 
357
      printf ("'%s'", spec->named[i].name);
 
358
    }
 
359
  printf ("}");
 
360
}
 
361
 
 
362
int
 
363
main ()
 
364
{
 
365
  for (;;)
 
366
    {
 
367
      char *line = NULL;
 
368
      size_t line_size = 0;
 
369
      int line_len;
 
370
      char *invalid_reason;
 
371
      void *descr;
 
372
 
 
373
      line_len = getline (&line, &line_size, stdin);
 
374
      if (line_len < 0)
 
375
        break;
 
376
      if (line_len > 0 && line[line_len - 1] == '\n')
 
377
        line[--line_len] = '\0';
 
378
 
 
379
      invalid_reason = NULL;
 
380
      descr = format_parse (line, false, &invalid_reason);
 
381
 
 
382
      format_print (descr);
 
383
      printf ("\n");
 
384
      if (descr == NULL)
 
385
        printf ("%s\n", invalid_reason);
 
386
 
 
387
      free (invalid_reason);
 
388
      free (line);
 
389
    }
 
390
 
 
391
  return 0;
 
392
}
 
393
 
 
394
/*
 
395
 * For Emacs M-x compile
 
396
 * Local Variables:
 
397
 * 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"
 
398
 * End:
 
399
 */
 
400
 
 
401
#endif /* TEST */