1/* Java format strings.
2   Copyright (C) 2001-2004 Free Software Foundation, Inc.
3   Written by Bruno Haible <haible@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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22#include <alloca.h>
23
24#include <stdbool.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "format.h"
29#include "c-ctype.h"
30#include "xalloc.h"
31#include "xallocsa.h"
32#include "xerror.h"
33#include "format-invalid.h"
34#include "gettext.h"
35
36#define _(str) gettext (str)
37
38/* Java format strings are described in java/text/MessageFormat.html.
39   See also the ICU documentation class_MessageFormat.html.
40
41   messageFormatPattern := string ( "{" messageFormatElement "}" string )*
42
43   messageFormatElement := argument { "," elementFormat }
44
45   elementFormat := "time" { "," datetimeStyle }
46                  | "date" { "," datetimeStyle }
47                  | "number" { "," numberStyle }
48                  | "choice" { "," choiceStyle }
49
50   datetimeStyle := "short"
51                    | "medium"
52                    | "long"
53                    | "full"
54                    | dateFormatPattern
55
56   numberStyle := "currency"
57                 | "percent"
58                 | "integer"
59                 | numberFormatPattern
60
61   choiceStyle := choiceFormatPattern
62
63   dateFormatPattern see SimpleDateFormat.applyPattern
64
65   numberFormatPattern see DecimalFormat.applyPattern
66
67   choiceFormatPattern see ChoiceFormat constructor
68
69   In strings, literal curly braces can be used if quoted between single
70   quotes.  A real single quote is represented by ''.
71
72   If a pattern is used, then unquoted braces in the pattern, if any, must
73   match: that is, "ab {0} de" and "ab '}' de" are ok, but "ab {0'}' de" and
74   "ab } de" are not.
75
76   The argument is a number from 0 to 9, which corresponds to the arguments
77   presented in an array to be formatted.
78
79   It is ok to have unused arguments in the array.
80
81   Adding a dateFormatPattern / numberFormatPattern / choiceFormatPattern
82   to an elementFormat is equivalent to creating a SimpleDateFormat /
83   DecimalFormat / ChoiceFormat and use of setFormat. For example,
84
85     MessageFormat form =
86       new MessageFormat("The disk \"{1}\" contains {0,choice,0#no files|1#one file|2#{0,number} files}.");
87
88   is equivalent to
89
90     MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
91     form.setFormat(1, // Number of {} occurrence in the string!
92                    new ChoiceFormat(new double[] { 0, 1, 2 },
93                                     new String[] { "no files", "one file",
94                                                    "{0,number} files" }));
95
96   Note: The behaviour of quotes inside a choiceFormatPattern is not clear.
97   Example 1:
98     "abc{1,choice,0#{1,number,00';'000}}def"
99       JDK 1.1.x: exception
100       JDK 1.3.x: behaves like "abc{1,choice,0#{1,number,00;000}}def"
101   Example 2:
102     "abc{1,choice,0#{1,number,00';'}}def"
103       JDK 1.1.x: interprets the semicolon as number suffix
104       JDK 1.3.x: behaves like "abc{1,choice,0#{1,number,00;}}def"
105 */
106
107enum format_arg_type
108{
109  FAT_NONE,
110  FAT_OBJECT,	/* java.lang.Object */
111  FAT_NUMBER,	/* java.lang.Number */
112  FAT_DATE	/* java.util.Date */
113};
114
115struct numbered_arg
116{
117  unsigned int number;
118  enum format_arg_type type;
119};
120
121struct spec
122{
123  unsigned int directives;
124  unsigned int numbered_arg_count;
125  unsigned int allocated;
126  struct numbered_arg *numbered;
127};
128
129
130/* Forward declaration of local functions.  */
131static bool date_format_parse (const char *format);
132static bool number_format_parse (const char *format);
133static bool choice_format_parse (const char *format, struct spec *spec,
134				 char **invalid_reason);
135
136
137/* Quote handling:
138   - When we see a single-quote, ignore it, but toggle the quoting flag.
139   - When we see a double single-quote, ignore the first of the two.
140   Assumes local variables format, quoting.  */
141#define HANDLE_QUOTE \
142  if (*format == '\'' && *++format != '\'') \
143    quoting = !quoting;
144
145/* Note that message_format_parse and choice_format_parse are mutually
146   recursive.  This is because MessageFormat can use some ChoiceFormats,
147   and a ChoiceFormat is made up from several MessageFormats.  */
148
149/* Return true if a format is a valid messageFormatPattern.
150   Extracts argument type information into spec.  */
151static bool
152message_format_parse (const char *format, struct spec *spec,
153		      char **invalid_reason)
154{
155  bool quoting = false;
156
157  for (;;)
158    {
159      HANDLE_QUOTE;
160      if (!quoting && *format == '{')
161	{
162	  unsigned int depth;
163	  const char *element_start;
164	  const char *element_end;
165	  size_t n;
166	  char *element_alloced;
167	  char *element;
168	  unsigned int number;
169	  enum format_arg_type type;
170
171	  spec->directives++;
172
173	  element_start = ++format;
174	  depth = 0;
175	  for (; *format != '\0'; format++)
176	    {
177	      if (*format == '{')
178		depth++;
179	      else if (*format == '}')
180		{
181		  if (depth == 0)
182		    break;
183		  else
184		    depth--;
185		}
186	    }
187	  if (*format == '\0')
188	    {
189	      *invalid_reason =
190		xstrdup (_("The string ends in the middle of a directive: found '{' without matching '}'."));
191	      return false;
192	    }
193	  element_end = format++;
194
195	  n = element_end - element_start;
196	  element = element_alloced = (char *) xallocsa (n + 1);
197	  memcpy (element, element_start, n);
198	  element[n] = '\0';
199
200	  if (!c_isdigit (*element))
201	    {
202	      *invalid_reason =
203		xasprintf (_("In the directive number %u, '{' is not followed by an argument number."), spec->directives);
204	      freesa (element_alloced);
205	      return false;
206	    }
207	  number = 0;
208	  do
209	    {
210	      number = 10 * number + (*element - '0');
211	      element++;
212	    }
213	  while (c_isdigit (*element));
214
215	  type = FAT_OBJECT;
216	  if (*element == '\0')
217	    ;
218	  else if (strncmp (element, ",time", 5) == 0
219		   || strncmp (element, ",date", 5) == 0)
220	    {
221	      type = FAT_DATE;
222	      element += 5;
223	      if (*element == '\0')
224		;
225	      else if (*element == ',')
226		{
227		  element++;
228		  if (strcmp (element, "short") == 0
229		      || strcmp (element, "medium") == 0
230		      || strcmp (element, "long") == 0
231		      || strcmp (element, "full") == 0
232		      || date_format_parse (element))
233		    ;
234		  else
235		    {
236		      *invalid_reason =
237			xasprintf (_("In the directive number %u, the substring \"%s\" is not a valid date/time style."), spec->directives, element);
238		      freesa (element_alloced);
239		      return false;
240		    }
241		}
242	      else
243		{
244		  *element = '\0';
245		  element -= 4;
246		  *invalid_reason =
247		    xasprintf (_("In the directive number %u, \"%s\" is not followed by a comma."), spec->directives, element);
248		  freesa (element_alloced);
249		  return false;
250		}
251	    }
252	  else if (strncmp (element, ",number", 7) == 0)
253	    {
254	      type = FAT_NUMBER;
255	      element += 7;
256	      if (*element == '\0')
257		;
258	      else if (*element == ',')
259		{
260		  element++;
261		  if (strcmp (element, "currency") == 0
262		      || strcmp (element, "percent") == 0
263		      || strcmp (element, "integer") == 0
264		      || number_format_parse (element))
265		    ;
266		  else
267		    {
268		      *invalid_reason =
269			xasprintf (_("In the directive number %u, the substring \"%s\" is not a valid number style."), spec->directives, element);
270		      freesa (element_alloced);
271		      return false;
272		    }
273		}
274	      else
275		{
276		  *element = '\0';
277		  element -= 6;
278		  *invalid_reason =
279		    xasprintf (_("In the directive number %u, \"%s\" is not followed by a comma."), spec->directives, element);
280		  freesa (element_alloced);
281		  return false;
282		}
283	    }
284	  else if (strncmp (element, ",choice", 7) == 0)
285	    {
286	      type = FAT_NUMBER; /* because ChoiceFormat extends NumberFormat */
287	      element += 7;
288	      if (*element == '\0')
289		;
290	      else if (*element == ',')
291		{
292		  element++;
293		  if (choice_format_parse (element, spec, invalid_reason))
294		    ;
295		  else
296		    {
297		      freesa (element_alloced);
298		      return false;
299		    }
300		}
301	      else
302		{
303		  *element = '\0';
304		  element -= 6;
305		  *invalid_reason =
306		    xasprintf (_("In the directive number %u, \"%s\" is not followed by a comma."), spec->directives, element);
307		  freesa (element_alloced);
308		  return false;
309		}
310	    }
311	  else
312	    {
313	      *invalid_reason =
314		xasprintf (_("In the directive number %u, the argument number is not followed by a comma and one of \"%s\", \"%s\", \"%s\", \"%s\"."), spec->directives, "time", "date", "number", "choice");
315	      freesa (element_alloced);
316	      return false;
317	    }
318	  freesa (element_alloced);
319
320	  if (spec->allocated == spec->numbered_arg_count)
321	    {
322	      spec->allocated = 2 * spec->allocated + 1;
323	      spec->numbered = (struct numbered_arg *) xrealloc (spec->numbered, spec->allocated * sizeof (struct numbered_arg));
324	    }
325	  spec->numbered[spec->numbered_arg_count].number = number;
326	  spec->numbered[spec->numbered_arg_count].type = type;
327	  spec->numbered_arg_count++;
328	}
329      /* The doc says "ab}de" is invalid.  Even though JDK accepts it.  */
330      else if (!quoting && *format == '}')
331	{
332	  *invalid_reason =
333	    xstrdup (_("The string starts in the middle of a directive: found '}' without matching '{'."));
334	  return false;
335	}
336      else if (*format != '\0')
337	format++;
338      else
339	break;
340    }
341
342  return true;
343}
344
345/* Return true if a format is a valid dateFormatPattern.  */
346static bool
347date_format_parse (const char *format)
348{
349  /* Any string is valid.  Single-quote starts a quoted section, to be
350     terminated at the next single-quote or string end.  Double single-quote
351     gives a single single-quote.  Non-quoted ASCII letters are first grouped
352     into blocks of equal letters.  Then each block (e.g. 'yyyy') is
353     interpreted according to some rules.  */
354  return true;
355}
356
357/* Return true if a format is a valid numberFormatPattern.  */
358static bool
359number_format_parse (const char *format)
360{
361  /* Pattern Syntax:
362       pattern     := pos_pattern{';' neg_pattern}
363       pos_pattern := {prefix}number{suffix}
364       neg_pattern := {prefix}number{suffix}
365       number      := integer{'.' fraction}{exponent}
366       prefix      := '\u0000'..'\uFFFD' - special_characters
367       suffix      := '\u0000'..'\uFFFD' - special_characters
368       integer     := min_int | '#' | '#' integer | '#' ',' integer
369       min_int     := '0' | '0' min_int | '0' ',' min_int
370       fraction    := '0'* '#'*
371       exponent    := 'E' '0' '0'*
372     Notation:
373       X*       0 or more instances of X
374       { X }    0 or 1 instances of X
375       X | Y    either X or Y
376       X..Y     any character from X up to Y, inclusive
377       S - T    characters in S, except those in T
378     Single-quote starts a quoted section, to be terminated at the next
379     single-quote or string end.  Double single-quote gives a single
380     single-quote.
381   */
382  bool quoting = false;
383  bool seen_semicolon = false;
384
385  HANDLE_QUOTE;
386  for (;;)
387    {
388      /* Parse prefix.  */
389      while (*format != '\0'
390	     && !(!quoting && (*format == '0' || *format == '#')))
391	{
392	  if (format[0] == '\\')
393	    {
394	      if (format[1] == 'u'
395		  && c_isxdigit (format[2])
396		  && c_isxdigit (format[3])
397		  && c_isxdigit (format[4])
398		  && c_isxdigit (format[5]))
399		format += 6;
400	      else
401		format += 2;
402	    }
403	  else
404	    format += 1;
405	  HANDLE_QUOTE;
406	}
407
408      /* Parse integer.  */
409      if (!(!quoting && (*format == '0' || *format == '#')))
410	return false;
411      while (!quoting && *format == '#')
412	{
413	  format++;
414	  HANDLE_QUOTE;
415	  if (!quoting && *format == ',')
416	    {
417	      format++;
418	      HANDLE_QUOTE;
419	    }
420	}
421      while (!quoting && *format == '0')
422	{
423	  format++;
424	  HANDLE_QUOTE;
425	  if (!quoting && *format == ',')
426	    {
427	      format++;
428	      HANDLE_QUOTE;
429	    }
430	}
431
432      /* Parse fraction.  */
433      if (!quoting && *format == '.')
434	{
435	  format++;
436	  HANDLE_QUOTE;
437	  while (!quoting && *format == '0')
438	    {
439	      format++;
440	      HANDLE_QUOTE;
441	    }
442	  while (!quoting && *format == '#')
443	    {
444	      format++;
445	      HANDLE_QUOTE;
446	    }
447	}
448
449      /* Parse exponent.  */
450      if (!quoting && *format == 'E')
451	{
452	  const char *format_save = format;
453	  format++;
454	  HANDLE_QUOTE;
455	  if (!quoting && *format == '0')
456	    {
457	      do
458		{
459		  format++;
460		  HANDLE_QUOTE;
461		}
462	      while (!quoting && *format == '0');
463	    }
464	  else
465	    {
466	      /* Back up.  */
467	      format = format_save;
468	      quoting = false;
469	    }
470	}
471
472      /* Parse suffix.  */
473      while (*format != '\0'
474	     && (seen_semicolon || !(!quoting && *format == ';')))
475	{
476	  if (format[0] == '\\')
477	    {
478	      if (format[1] == 'u'
479		  && c_isxdigit (format[2])
480		  && c_isxdigit (format[3])
481		  && c_isxdigit (format[4])
482		  && c_isxdigit (format[5]))
483		format += 6;
484	      else
485		format += 2;
486	    }
487	  else
488	    format += 1;
489	  HANDLE_QUOTE;
490	}
491
492      if (seen_semicolon || !(!quoting && *format == ';'))
493	break;
494    }
495
496  return (*format == '\0');
497}
498
499/* Return true if a format is a valid choiceFormatPattern.
500   Extracts argument type information into spec.  */
501static bool
502choice_format_parse (const char *format, struct spec *spec,
503		     char **invalid_reason)
504{
505  /* Pattern syntax:
506       pattern   := | choice | choice '|' pattern
507       choice    := number separator messageformat
508       separator := '<' | '#' | '\u2264'
509     Single-quote starts a quoted section, to be terminated at the next
510     single-quote or string end.  Double single-quote gives a single
511     single-quote.
512   */
513  bool quoting = false;
514
515  HANDLE_QUOTE;
516  if (*format == '\0')
517    return true;
518  for (;;)
519    {
520      /* Don't bother looking too precisely into the syntax of the number.
521	 It can contain various Unicode characters.  */
522      bool number_nonempty;
523      char *msgformat;
524      char *mp;
525      bool msgformat_valid;
526
527      /* Parse number.  */
528      number_nonempty = false;
529      while (*format != '\0'
530	     && !(!quoting && (*format == '<' || *format == '#'
531			       || strncmp (format, "\\u2264", 6) == 0
532			       || *format == '|')))
533	{
534	  if (format[0] == '\\')
535	    {
536	      if (format[1] == 'u'
537		  && c_isxdigit (format[2])
538		  && c_isxdigit (format[3])
539		  && c_isxdigit (format[4])
540		  && c_isxdigit (format[5]))
541		format += 6;
542	      else
543		format += 2;
544	    }
545	  else
546	    format += 1;
547	  number_nonempty = true;
548	  HANDLE_QUOTE;
549	}
550
551      /* Short clause at end of pattern is valid and is ignored!  */
552      if (*format == '\0')
553	break;
554
555      if (!number_nonempty)
556	{
557	  *invalid_reason =
558	    xasprintf (_("In the directive number %u, a choice contains no number."), spec->directives);
559	  return false;
560	}
561
562      if (*format == '<' || *format == '#')
563	format += 1;
564      else if (strncmp (format, "\\u2264", 6) == 0)
565	format += 6;
566      else
567	{
568	  *invalid_reason =
569	    xasprintf (_("In the directive number %u, a choice contains a number that is not followed by '<', '#' or '%s'."), spec->directives, "\\u2264");
570	  return false;
571	}
572      HANDLE_QUOTE;
573
574      msgformat = (char *) xallocsa (strlen (format) + 1);
575      mp = msgformat;
576
577      while (*format != '\0' && !(!quoting && *format == '|'))
578	{
579	  *mp++ = *format++;
580	  HANDLE_QUOTE;
581	}
582      *mp = '\0';
583
584      msgformat_valid = message_format_parse (msgformat, spec, invalid_reason);
585
586      freesa (msgformat);
587
588      if (!msgformat_valid)
589	return false;
590
591      if (*format == '\0')
592	break;
593
594      format++;
595      HANDLE_QUOTE;
596    }
597
598  return true;
599}
600
601static int
602numbered_arg_compare (const void *p1, const void *p2)
603{
604  unsigned int n1 = ((const struct numbered_arg *) p1)->number;
605  unsigned int n2 = ((const struct numbered_arg *) p2)->number;
606
607  return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
608}
609
610static void *
611format_parse (const char *format, bool translated, char **invalid_reason)
612{
613  struct spec spec;
614  struct spec *result;
615
616  spec.directives = 0;
617  spec.numbered_arg_count = 0;
618  spec.allocated = 0;
619  spec.numbered = NULL;
620
621  if (!message_format_parse (format, &spec, invalid_reason))
622    goto bad_format;
623
624  /* Sort the numbered argument array, and eliminate duplicates.  */
625  if (spec.numbered_arg_count > 1)
626    {
627      unsigned int i, j;
628      bool err;
629
630      qsort (spec.numbered, spec.numbered_arg_count,
631	     sizeof (struct numbered_arg), numbered_arg_compare);
632
633      /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
634      err = false;
635      for (i = j = 0; i < spec.numbered_arg_count; i++)
636	if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
637	  {
638	    enum format_arg_type type1 = spec.numbered[i].type;
639	    enum format_arg_type type2 = spec.numbered[j-1].type;
640	    enum format_arg_type type_both;
641
642	    if (type1 == type2 || type2 == FAT_OBJECT)
643	      type_both = type1;
644	    else if (type1 == FAT_OBJECT)
645	      type_both = type2;
646	    else
647	      {
648		/* Incompatible types.  */
649		type_both = FAT_NONE;
650		if (!err)
651		  *invalid_reason =
652		    INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
653		err = true;
654	      }
655
656	    spec.numbered[j-1].type = type_both;
657	  }
658	else
659	  {
660	    if (j < i)
661	      {
662		spec.numbered[j].number = spec.numbered[i].number;
663		spec.numbered[j].type = spec.numbered[i].type;
664	      }
665	    j++;
666	  }
667      spec.numbered_arg_count = j;
668      if (err)
669	/* *invalid_reason has already been set above.  */
670	goto bad_format;
671    }
672
673  result = (struct spec *) xmalloc (sizeof (struct spec));
674  *result = spec;
675  return result;
676
677 bad_format:
678  if (spec.numbered != NULL)
679    free (spec.numbered);
680  return NULL;
681}
682
683static void
684format_free (void *descr)
685{
686  struct spec *spec = (struct spec *) descr;
687
688  if (spec->numbered != NULL)
689    free (spec->numbered);
690  free (spec);
691}
692
693static int
694format_get_number_of_directives (void *descr)
695{
696  struct spec *spec = (struct spec *) descr;
697
698  return spec->directives;
699}
700
701static bool
702format_check (void *msgid_descr, void *msgstr_descr, bool equality,
703	      formatstring_error_logger_t error_logger,
704	      const char *pretty_msgstr)
705{
706  struct spec *spec1 = (struct spec *) msgid_descr;
707  struct spec *spec2 = (struct spec *) msgstr_descr;
708  bool err = false;
709
710  if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
711    {
712      unsigned int i, j;
713      unsigned int n1 = spec1->numbered_arg_count;
714      unsigned int n2 = spec2->numbered_arg_count;
715
716      /* Check the argument names are the same.
717	 Both arrays are sorted.  We search for the first difference.  */
718      for (i = 0, j = 0; i < n1 || j < n2; )
719	{
720	  int cmp = (i >= n1 ? 1 :
721		     j >= n2 ? -1 :
722		     spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
723		     spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
724		     0);
725
726	  if (cmp > 0)
727	    {
728	      if (error_logger)
729		error_logger (_("a format specification for argument {%u}, as in '%s', doesn't exist in 'msgid'"),
730			      spec2->numbered[j].number, pretty_msgstr);
731	      err = true;
732	      break;
733	    }
734	  else if (cmp < 0)
735	    {
736	      if (equality)
737		{
738		  if (error_logger)
739		    error_logger (_("a format specification for argument {%u} doesn't exist in '%s'"),
740				  spec1->numbered[i].number, pretty_msgstr);
741		  err = true;
742		  break;
743		}
744	      else
745		i++;
746	    }
747	  else
748	    j++, i++;
749	}
750      /* Check the argument types are the same.  */
751      if (!err)
752	for (i = 0, j = 0; j < n2; )
753	  {
754	    if (spec1->numbered[i].number == spec2->numbered[j].number)
755	      {
756		if (spec1->numbered[i].type != spec2->numbered[j].type)
757		  {
758		    if (error_logger)
759		      error_logger (_("format specifications in 'msgid' and '%s' for argument {%u} are not the same"),
760				    pretty_msgstr, spec2->numbered[j].number);
761		    err = true;
762		    break;
763		  }
764		j++, i++;
765	      }
766	    else
767	      i++;
768	  }
769    }
770
771  return err;
772}
773
774
775struct formatstring_parser formatstring_java =
776{
777  format_parse,
778  format_free,
779  format_get_number_of_directives,
780  format_check
781};
782
783
784#ifdef TEST
785
786/* Test program: Print the argument list specification returned by
787   format_parse for strings read from standard input.  */
788
789#include <stdio.h>
790#include "getline.h"
791
792static void
793format_print (void *descr)
794{
795  struct spec *spec = (struct spec *) descr;
796  unsigned int last;
797  unsigned int i;
798
799  if (spec == NULL)
800    {
801      printf ("INVALID");
802      return;
803    }
804
805  printf ("(");
806  last = 0;
807  for (i = 0; i < spec->numbered_arg_count; i++)
808    {
809      unsigned int number = spec->numbered[i].number;
810
811      if (i > 0)
812	printf (" ");
813      if (number < last)
814	abort ();
815      for (; last < number; last++)
816	printf ("_ ");
817      switch (spec->numbered[i].type)
818	{
819	case FAT_OBJECT:
820	  printf ("*");
821	  break;
822	case FAT_NUMBER:
823	  printf ("Number");
824	  break;
825	case FAT_DATE:
826	  printf ("Date");
827	  break;
828	default:
829	  abort ();
830	}
831      last = number + 1;
832    }
833  printf (")");
834}
835
836int
837main ()
838{
839  for (;;)
840    {
841      char *line = NULL;
842      size_t line_size = 0;
843      int line_len;
844      char *invalid_reason;
845      void *descr;
846
847      line_len = getline (&line, &line_size, stdin);
848      if (line_len < 0)
849	break;
850      if (line_len > 0 && line[line_len - 1] == '\n')
851	line[--line_len] = '\0';
852
853      invalid_reason = NULL;
854      descr = format_parse (line, false, &invalid_reason);
855
856      format_print (descr);
857      printf ("\n");
858      if (descr == NULL)
859	printf ("%s\n", invalid_reason);
860
861      free (invalid_reason);
862      free (line);
863    }
864
865  return 0;
866}
867
868/*
869 * For Emacs M-x compile
870 * Local Variables:
871 * 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-java.c ../lib/libgettextlib.la"
872 * End:
873 */
874
875#endif /* TEST */
876