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