1/* Object Pascal format strings. 2 Copyright (C) 2001-2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "xvasprintf.h" 30#include "format-invalid.h" 31#include "gettext.h" 32 33#define _(str) gettext (str) 34 35/* Object Pascal format strings are usable with the "format" function in the 36 "sysutils" unit. They are implemented in fpc-1.0.4/rtl/objpas/sysstr.inc. 37 Another implementation exists in Borland Delphi. The GNU Pascal's 38 "sysutils" doesn't (yet?) have the "format" function. 39 40 A directive 41 - starts with '%', 42 - either 43 - is finished with '%', or 44 - - is optionally followed by an index specification: '*' (reads an 45 argument, must be of type integer) or a nonempty digit sequence, 46 followed by ':', 47 - is optionally followed by '-', which acts as a flag, 48 - is optionally followed by a width specification: '*' (reads an 49 argument, must be of type integer) or a nonempty digit sequence, 50 - is optionally followed by '.' and a precision specification: '*' 51 (reads an argument, must be of type integer) or a nonempty digit 52 sequence, 53 - is finished by a case-insensitive specifier. If no index was 54 specified, it reads an argument; otherwise is uses the index-th 55 argument, 0-based. 56 - 'd', needs an 'integer' or 'int64' argument, 57 - 'e', 'f', 'g', 'n', 'm', need an 'extended' floating-point argument, 58 - 's', needs a 'string', 'char', 'pchar' or 'ansistring' argument, 59 - 'p', needs a 'pointer' argument, 60 - 'x', needs an integer argument. 61 Numbered and unnumbered argument specifications can be used in the same 62 string. Numbered argument specifications have no influence on the 63 "current argument index", that is incremented each time an argument is read. 64 */ 65 66enum format_arg_type 67{ 68 FAT_INTEGER, /* integer */ 69 FAT_INTEGER64, /* integer, int64 */ 70 FAT_FLOAT, /* extended */ 71 FAT_STRING, /* string, char, pchar, ansistring */ 72 FAT_POINTER 73}; 74 75struct numbered_arg 76{ 77 unsigned int number; 78 enum format_arg_type type; 79}; 80 81struct spec 82{ 83 unsigned int directives; 84 unsigned int numbered_arg_count; 85 unsigned int allocated; 86 struct numbered_arg *numbered; 87}; 88 89/* Locale independent test for a decimal digit. 90 Argument can be 'char' or 'unsigned char'. (Whereas the argument of 91 <ctype.h> isdigit must be an 'unsigned char'.) */ 92#undef isdigit 93#define isdigit(c) ((unsigned int) ((c) - '0') < 10) 94 95 96static int 97numbered_arg_compare (const void *p1, const void *p2) 98{ 99 unsigned int n1 = ((const struct numbered_arg *) p1)->number; 100 unsigned int n2 = ((const struct numbered_arg *) p2)->number; 101 102 return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0); 103} 104 105static void * 106format_parse (const char *format, bool translated, char **invalid_reason) 107{ 108 unsigned int directives; 109 unsigned int numbered_arg_count; 110 unsigned int allocated; 111 struct numbered_arg *numbered; 112 unsigned int unnumbered_arg_count; 113 struct spec *result; 114 115 enum arg_index 116 { 117 index_numbered, /* index given by a fixed integer */ 118 index_unnumbered, /* index given by unnumbered_arg_count++ */ 119 index_unknown /* index is only known at run time */ 120 }; 121 122 directives = 0; 123 numbered_arg_count = 0; 124 allocated = 0; 125 numbered = NULL; 126 unnumbered_arg_count = 0; 127 128 for (; *format != '\0';) 129 if (*format++ == '%') 130 { 131 /* A directive. */ 132 directives++; 133 134 if (*format != '%') 135 { 136 /* A complex directive. */ 137 enum arg_index main_arg = index_unnumbered; 138 unsigned int main_number = 0; 139 enum format_arg_type type; 140 141 if (isdigit (*format)) 142 { 143 const char *f = format; 144 unsigned int m = 0; 145 146 do 147 { 148 m = 10 * m + (*f - '0'); 149 f++; 150 } 151 while (isdigit (*f)); 152 153 if (*f == ':') 154 { 155 main_number = m; 156 main_arg = index_numbered; 157 format = ++f; 158 } 159 } 160 else if (*format == '*') 161 { 162 if (format[1] == ':') 163 { 164 main_arg = index_unknown; 165 format += 2; 166 } 167 } 168 169 /* Parse flags. */ 170 if (*format == '-') 171 format++; 172 173 /* Parse width. */ 174 if (isdigit (*format)) 175 { 176 do 177 format++; 178 while (isdigit (*format)); 179 } 180 else if (*format == '*') 181 { 182 /* Unnumbered argument of type FAT_INTEGER. */ 183 if (allocated == numbered_arg_count) 184 { 185 allocated = 2 * allocated + 1; 186 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg)); 187 } 188 numbered[numbered_arg_count].number = unnumbered_arg_count; 189 numbered[numbered_arg_count].type = FAT_INTEGER; 190 numbered_arg_count++; 191 unnumbered_arg_count++; 192 193 format++; 194 } 195 196 /* Parse precision. */ 197 if (*format == '.') 198 { 199 format++; 200 201 if (isdigit (*format)) 202 { 203 do 204 format++; 205 while (isdigit (*format)); 206 } 207 else if (*format == '*') 208 { 209 /* Unnumbered argument of type FAT_INTEGER. */ 210 if (allocated == unnumbered_arg_count) 211 { 212 allocated = 2 * allocated + 1; 213 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg)); 214 } 215 numbered[numbered_arg_count].number = unnumbered_arg_count; 216 numbered[numbered_arg_count].type = FAT_INTEGER; 217 numbered_arg_count++; 218 unnumbered_arg_count++; 219 220 format++; 221 } 222 else 223 --format; /* will jump to bad_format */ 224 } 225 226 switch (c_tolower (*format)) 227 { 228 case 'd': 229 type = FAT_INTEGER64; 230 break; 231 case 'e': case 'f': case 'g': case 'n': case 'm': 232 type = FAT_FLOAT; 233 break; 234 case 's': 235 type = FAT_STRING; 236 break; 237 case 'p': 238 type = FAT_POINTER; 239 break; 240 case 'x': 241 type = FAT_INTEGER; 242 break; 243 default: 244 *invalid_reason = 245 (*format == '\0' 246 ? INVALID_UNTERMINATED_DIRECTIVE () 247 : INVALID_CONVERSION_SPECIFIER (directives, *format)); 248 goto bad_format; 249 } 250 251 if (allocated == numbered_arg_count) 252 { 253 allocated = 2 * allocated + 1; 254 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg)); 255 } 256 switch (main_arg) 257 { 258 case index_unnumbered: 259 numbered[numbered_arg_count].number = unnumbered_arg_count; 260 numbered[numbered_arg_count].type = type; 261 unnumbered_arg_count++; 262 break; 263 case index_numbered: 264 numbered[numbered_arg_count].number = main_number; 265 numbered[numbered_arg_count].type = type; 266 break; 267 case index_unknown: 268 numbered[numbered_arg_count].number = unnumbered_arg_count; 269 numbered[numbered_arg_count].type = FAT_INTEGER; 270 unnumbered_arg_count++; 271 break; 272 default: 273 abort (); 274 } 275 numbered_arg_count++; 276 } 277 278 format++; 279 } 280 281 /* Sort the numbered argument array, and eliminate duplicates. */ 282 if (numbered_arg_count > 1) 283 { 284 unsigned int i, j; 285 bool err; 286 287 qsort (numbered, numbered_arg_count, 288 sizeof (struct numbered_arg), numbered_arg_compare); 289 290 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */ 291 err = false; 292 for (i = j = 0; i < numbered_arg_count; i++) 293 if (j > 0 && numbered[i].number == numbered[j-1].number) 294 { 295 enum format_arg_type type1 = numbered[i].type; 296 enum format_arg_type type2 = numbered[j-1].type; 297 enum format_arg_type type_both; 298 299 if (type1 == type2) 300 type_both = type1; 301 else if ((type1 == FAT_INTEGER && type2 == FAT_INTEGER64) 302 || (type1 == FAT_INTEGER64 && type2 == FAT_INTEGER)) 303 type_both = FAT_INTEGER; 304 else 305 { 306 /* Incompatible types. */ 307 type_both = type1; 308 if (!err) 309 *invalid_reason = 310 INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number); 311 err = true; 312 } 313 314 numbered[j-1].type = type_both; 315 } 316 else 317 { 318 if (j < i) 319 { 320 numbered[j].number = numbered[i].number; 321 numbered[j].type = numbered[i].type; 322 } 323 j++; 324 } 325 numbered_arg_count = j; 326 if (err) 327 /* *invalid_reason has already been set above. */ 328 goto bad_format; 329 } 330 331 result = (struct spec *) xmalloc (sizeof (struct spec)); 332 result->directives = directives; 333 result->numbered_arg_count = numbered_arg_count; 334 result->allocated = allocated; 335 result->numbered = numbered; 336 return result; 337 338 bad_format: 339 if (numbered != NULL) 340 free (numbered); 341 return NULL; 342} 343 344static void 345format_free (void *descr) 346{ 347 struct spec *spec = (struct spec *) descr; 348 349 if (spec->numbered != NULL) 350 free (spec->numbered); 351 free (spec); 352} 353 354static int 355format_get_number_of_directives (void *descr) 356{ 357 struct spec *spec = (struct spec *) descr; 358 359 return spec->directives; 360} 361 362static bool 363format_check (void *msgid_descr, void *msgstr_descr, bool equality, 364 formatstring_error_logger_t error_logger, 365 const char *pretty_msgstr) 366{ 367 struct spec *spec1 = (struct spec *) msgid_descr; 368 struct spec *spec2 = (struct spec *) msgstr_descr; 369 bool err = false; 370 371 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0) 372 { 373 unsigned int i, j; 374 unsigned int n1 = spec1->numbered_arg_count; 375 unsigned int n2 = spec2->numbered_arg_count; 376 377 /* Check the argument names are the same. 378 Both arrays are sorted. We search for the first difference. */ 379 for (i = 0, j = 0; i < n1 || j < n2; ) 380 { 381 int cmp = (i >= n1 ? 1 : 382 j >= n2 ? -1 : 383 spec1->numbered[i].number > spec2->numbered[j].number ? 1 : 384 spec1->numbered[i].number < spec2->numbered[j].number ? -1 : 385 0); 386 387 if (cmp > 0) 388 { 389 if (error_logger) 390 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"), 391 spec2->numbered[j].number, pretty_msgstr); 392 err = true; 393 break; 394 } 395 else if (cmp < 0) 396 { 397 if (equality) 398 { 399 if (error_logger) 400 error_logger (_("a format specification for argument %u doesn't exist in '%s'"), 401 spec1->numbered[i].number, pretty_msgstr); 402 err = true; 403 break; 404 } 405 else 406 i++; 407 } 408 else 409 j++, i++; 410 } 411 /* Check the argument types are the same. */ 412 if (!err) 413 for (i = 0, j = 0; j < n2; ) 414 { 415 if (spec1->numbered[i].number == spec2->numbered[j].number) 416 { 417 if (spec1->numbered[i].type != spec2->numbered[j].type) 418 { 419 if (error_logger) 420 error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"), 421 pretty_msgstr, spec2->numbered[j].number); 422 err = true; 423 break; 424 } 425 j++, i++; 426 } 427 else 428 i++; 429 } 430 } 431 432 return err; 433} 434 435 436struct formatstring_parser formatstring_pascal = 437{ 438 format_parse, 439 format_free, 440 format_get_number_of_directives, 441 NULL, 442 format_check 443}; 444 445 446#ifdef TEST 447 448/* Test program: Print the argument list specification returned by 449 format_parse for strings read from standard input. */ 450 451#include <stdio.h> 452#include "getline.h" 453 454static void 455format_print (void *descr) 456{ 457 struct spec *spec = (struct spec *) descr; 458 unsigned int last; 459 unsigned int i; 460 461 if (spec == NULL) 462 { 463 printf ("INVALID"); 464 return; 465 } 466 467 printf ("("); 468 last = 0; 469 for (i = 0; i < spec->numbered_arg_count; i++) 470 { 471 unsigned int number = spec->numbered[i].number; 472 473 if (i > 0) 474 printf (" "); 475 if (number < last) 476 abort (); 477 for (; last < number; last++) 478 printf ("_ "); 479 switch (spec->numbered[i].type) 480 { 481 case FAT_INTEGER: 482 printf ("i"); 483 break; 484 case FAT_INTEGER64: 485 printf ("I"); 486 break; 487 case FAT_FLOAT: 488 printf ("f"); 489 break; 490 case FAT_STRING: 491 printf ("s"); 492 break; 493 case FAT_POINTER: 494 printf ("p"); 495 break; 496 default: 497 abort (); 498 } 499 last = number + 1; 500 } 501 printf (")"); 502} 503 504int 505main () 506{ 507 for (;;) 508 { 509 char *line = NULL; 510 size_t line_size = 0; 511 int line_len; 512 char *invalid_reason; 513 void *descr; 514 515 line_len = getline (&line, &line_size, stdin); 516 if (line_len < 0) 517 break; 518 if (line_len > 0 && line[line_len - 1] == '\n') 519 line[--line_len] = '\0'; 520 521 invalid_reason = NULL; 522 descr = format_parse (line, false, &invalid_reason); 523 524 format_print (descr); 525 printf ("\n"); 526 if (descr == NULL) 527 printf ("%s\n", invalid_reason); 528 529 free (invalid_reason); 530 free (line); 531 } 532 533 return 0; 534} 535 536/* 537 * For Emacs M-x compile 538 * Local Variables: 539 * 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-pascal.c ../lib/libgettextlib.la" 540 * End: 541 */ 542 543#endif /* TEST */ 544