1/* C# 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 26#include "format.h" 27#include "c-ctype.h" 28#include "xalloc.h" 29#include "xerror.h" 30#include "gettext.h" 31 32#define _(str) gettext (str) 33 34/* C# format strings are described in the description of the .NET System.String 35 class and implemented in 36 pnetlib-0.5.6/runtime/System/String.cs 37 and 38 mcs-0.28/class/corlib/System/String.cs 39 A format string consists of literal text (that is output verbatim), doubled 40 braces ('{{' and '}}', that lead to a single brace when output), and 41 directives. 42 A directive 43 - starts with '{', 44 - is followed by a nonnegative integer m, 45 - is optionally followed by ',' and an integer denoting a width, 46 - is optionally followed by ':' and a sequence of format specifiers. 47 (But the interpretation of the format specifiers is up to the IFormattable 48 implementation, depending on the argument's runtime value. New classes 49 implementing IFormattable can be defined by the user.) 50 - is finished with '}'. 51 */ 52 53struct spec 54{ 55 unsigned int directives; 56 unsigned int numbered_arg_count; 57}; 58 59static void * 60format_parse (const char *format, bool translated, char **invalid_reason) 61{ 62 struct spec spec; 63 struct spec *result; 64 65 spec.directives = 0; 66 spec.numbered_arg_count = 0; 67 68 for (; *format != '\0';) 69 { 70 char c = *format++; 71 72 if (c == '{') 73 { 74 if (*format == '{') 75 format++; 76 else 77 { 78 /* A directive. */ 79 unsigned int number; 80 81 spec.directives++; 82 83 if (!c_isdigit (*format)) 84 { 85 *invalid_reason = 86 xasprintf (_("In the directive number %u, '{' is not followed by an argument number."), spec.directives); 87 return NULL; 88 } 89 number = 0; 90 do 91 { 92 number = 10 * number + (*format - '0'); 93 format++; 94 } 95 while (c_isdigit (*format)); 96 97 if (*format == ',') 98 { 99 /* Parse width. */ 100 format++; 101 if (*format == '-') 102 format++; 103 if (!c_isdigit (*format)) 104 { 105 *invalid_reason = 106 xasprintf (_("In the directive number %u, ',' is not followed by a number."), spec.directives); 107 return NULL; 108 } 109 do 110 format++; 111 while (c_isdigit (*format)); 112 } 113 114 if (*format == ':') 115 { 116 /* Parse format specifiers. */ 117 do 118 format++; 119 while (*format != '\0' && *format != '}'); 120 } 121 122 if (*format == '\0') 123 { 124 *invalid_reason = 125 xstrdup (_("The string ends in the middle of a directive: found '{' without matching '}'.")); 126 return NULL; 127 } 128 129 if (*format != '}') 130 { 131 *invalid_reason = 132 (c_isprint (*format) 133 ? xasprintf (_("The directive number %u ends with an invalid character '%c' instead of '}'."), spec.directives, *format) 134 : xasprintf (_("The directive number %u ends with an invalid character instead of '}'."), spec.directives)); 135 return NULL; 136 } 137 138 format++; 139 140 if (spec.numbered_arg_count <= number) 141 spec.numbered_arg_count = number + 1; 142 } 143 } 144 else if (c == '}') 145 { 146 if (*format == '}') 147 format++; 148 else 149 { 150 *invalid_reason = 151 (spec.directives == 0 152 ? xstrdup (_("The string starts in the middle of a directive: found '}' without matching '{'.")) 153 : xasprintf (_("The string contains a lone '}' after directive number %u."), spec.directives)); 154 return NULL; 155 } 156 } 157 } 158 159 result = (struct spec *) xmalloc (sizeof (struct spec)); 160 *result = spec; 161 return result; 162} 163 164static void 165format_free (void *descr) 166{ 167 struct spec *spec = (struct spec *) descr; 168 169 free (spec); 170} 171 172static int 173format_get_number_of_directives (void *descr) 174{ 175 struct spec *spec = (struct spec *) descr; 176 177 return spec->directives; 178} 179 180static bool 181format_check (void *msgid_descr, void *msgstr_descr, bool equality, 182 formatstring_error_logger_t error_logger, 183 const char *pretty_msgstr) 184{ 185 struct spec *spec1 = (struct spec *) msgid_descr; 186 struct spec *spec2 = (struct spec *) msgstr_descr; 187 bool err = false; 188 189 /* Check that the argument counts are the same. */ 190 if (equality 191 ? spec1->numbered_arg_count != spec2->numbered_arg_count 192 : spec1->numbered_arg_count < spec2->numbered_arg_count) 193 { 194 if (error_logger) 195 error_logger (_("number of format specifications in 'msgid' and '%s' does not match"), 196 pretty_msgstr); 197 err = true; 198 } 199 200 return err; 201} 202 203 204struct formatstring_parser formatstring_csharp = 205{ 206 format_parse, 207 format_free, 208 format_get_number_of_directives, 209 format_check 210}; 211 212 213#ifdef TEST 214 215/* Test program: Print the argument list specification returned by 216 format_parse for strings read from standard input. */ 217 218#include <stdio.h> 219#include "getline.h" 220 221static void 222format_print (void *descr) 223{ 224 struct spec *spec = (struct spec *) descr; 225 unsigned int i; 226 227 if (spec == NULL) 228 { 229 printf ("INVALID"); 230 return; 231 } 232 233 printf ("("); 234 for (i = 0; i < spec->numbered_arg_count; i++) 235 { 236 if (i > 0) 237 printf (" "); 238 printf ("*"); 239 } 240 printf (")"); 241} 242 243int 244main () 245{ 246 for (;;) 247 { 248 char *line = NULL; 249 size_t line_size = 0; 250 int line_len; 251 char *invalid_reason; 252 void *descr; 253 254 line_len = getline (&line, &line_size, stdin); 255 if (line_len < 0) 256 break; 257 if (line_len > 0 && line[line_len - 1] == '\n') 258 line[--line_len] = '\0'; 259 260 invalid_reason = NULL; 261 descr = format_parse (line, false, &invalid_reason); 262 263 format_print (descr); 264 printf ("\n"); 265 if (descr == NULL) 266 printf ("%s\n", invalid_reason); 267 268 free (invalid_reason); 269 free (line); 270 } 271 272 return 0; 273} 274 275/* 276 * For Emacs M-x compile 277 * Local Variables: 278 * 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-csharp.c ../lib/libgettextlib.la" 279 * End: 280 */ 281 282#endif /* TEST */ 283