1/* Emacs Lisp format strings. 2 Copyright (C) 2001-2004, 2006 Free Software Foundation, Inc. 3 Written by Bruno Haible <haible@clisp.cons.org>, 2002. 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/* Emacs Lisp format strings are implemented in emacs-21.1/src/editfns.c, 36 xemacs-21.1.14/src/editfns.c and xemacs-21.1.14/src/doprnt.c. 37 A directive 38 - starts with '%' or '%m$' where m is a positive integer, 39 - is optionally followed by any of the characters '#', '0', '-', ' ', '+', 40 each of which acts as a flag, 41 - is optionally followed by a width specification: '*' (reads an argument) 42 or a nonempty digit sequence, 43 - is optionally followed by '.' and a precision specification: '*' (reads 44 an argument) or a nonempty digit sequence, 45 - is finished by a specifier 46 - '%', that needs no argument, 47 - 'c', that need a character argument, 48 - 'd', 'i', 'x', 'X', 'o', that need an integer argument, 49 - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument, 50 - 's', that need an argument and prints it using princ, 51 - 'S', that need an argument and prints it using prin1. 52 Numbered ('%m$') and unnumbered argument specifications can be used in the 53 same string. The effect of '%m$' is to set the current argument number to 54 m. The current argument number is incremented after processing a directive. 55 */ 56 57enum format_arg_type 58{ 59 FAT_NONE, 60 FAT_CHARACTER, 61 FAT_INTEGER, 62 FAT_FLOAT, 63 FAT_OBJECT_PRETTY, 64 FAT_OBJECT 65}; 66 67struct numbered_arg 68{ 69 unsigned int number; 70 enum format_arg_type type; 71}; 72 73struct spec 74{ 75 unsigned int directives; 76 unsigned int numbered_arg_count; 77 unsigned int allocated; 78 struct numbered_arg *numbered; 79}; 80 81/* Locale independent test for a decimal digit. 82 Argument can be 'char' or 'unsigned char'. (Whereas the argument of 83 <ctype.h> isdigit must be an 'unsigned char'.) */ 84#undef isdigit 85#define isdigit(c) ((unsigned int) ((c) - '0') < 10) 86 87 88static int 89numbered_arg_compare (const void *p1, const void *p2) 90{ 91 unsigned int n1 = ((const struct numbered_arg *) p1)->number; 92 unsigned int n2 = ((const struct numbered_arg *) p2)->number; 93 94 return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0); 95} 96 97static void * 98format_parse (const char *format, bool translated, char **invalid_reason) 99{ 100 struct spec spec; 101 struct spec *result; 102 unsigned int number; 103 104 spec.directives = 0; 105 spec.numbered_arg_count = 0; 106 spec.allocated = 0; 107 spec.numbered = NULL; 108 number = 1; 109 110 for (; *format != '\0';) 111 if (*format++ == '%') 112 { 113 /* A directive. */ 114 enum format_arg_type type; 115 116 spec.directives++; 117 118 if (isdigit (*format)) 119 { 120 const char *f = format; 121 unsigned int m = 0; 122 123 do 124 { 125 m = 10 * m + (*f - '0'); 126 f++; 127 } 128 while (isdigit (*f)); 129 130 if (*f == '$' && m > 0) 131 { 132 number = m; 133 format = ++f; 134 } 135 } 136 137 /* Parse flags. */ 138 while (*format == ' ' || *format == '+' || *format == '-' 139 || *format == '#' || *format == '0') 140 format++; 141 142 /* Parse width. */ 143 if (*format == '*') 144 { 145 format++; 146 147 if (spec.allocated == spec.numbered_arg_count) 148 { 149 spec.allocated = 2 * spec.allocated + 1; 150 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); 151 } 152 spec.numbered[spec.numbered_arg_count].number = number; 153 spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER; 154 spec.numbered_arg_count++; 155 156 number++; 157 } 158 else if (isdigit (*format)) 159 { 160 do format++; while (isdigit (*format)); 161 } 162 163 /* Parse precision. */ 164 if (*format == '.') 165 { 166 format++; 167 168 if (*format == '*') 169 { 170 format++; 171 172 if (spec.allocated == spec.numbered_arg_count) 173 { 174 spec.allocated = 2 * spec.allocated + 1; 175 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); 176 } 177 spec.numbered[spec.numbered_arg_count].number = number; 178 spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER; 179 spec.numbered_arg_count++; 180 181 number++; 182 } 183 else if (isdigit (*format)) 184 { 185 do format++; while (isdigit (*format)); 186 } 187 } 188 189 switch (*format) 190 { 191 case '%': 192 type = FAT_NONE; 193 break; 194 case 'c': 195 type = FAT_CHARACTER; 196 break; 197 case 'd': case 'i': case 'x': case 'X': case 'o': 198 type = FAT_INTEGER; 199 break; 200 case 'e': case 'E': case 'f': case 'g': case 'G': 201 type = FAT_FLOAT; 202 break; 203 case 's': 204 type = FAT_OBJECT_PRETTY; 205 break; 206 case 'S': 207 type = FAT_OBJECT; 208 break; 209 default: 210 *invalid_reason = 211 (*format == '\0' 212 ? INVALID_UNTERMINATED_DIRECTIVE () 213 : INVALID_CONVERSION_SPECIFIER (spec.directives, *format)); 214 goto bad_format; 215 } 216 217 if (type != FAT_NONE) 218 { 219 if (spec.allocated == spec.numbered_arg_count) 220 { 221 spec.allocated = 2 * spec.allocated + 1; 222 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); 223 } 224 spec.numbered[spec.numbered_arg_count].number = number; 225 spec.numbered[spec.numbered_arg_count].type = type; 226 spec.numbered_arg_count++; 227 228 number++; 229 } 230 231 format++; 232 } 233 234 /* Sort the numbered argument array, and eliminate duplicates. */ 235 if (spec.numbered_arg_count > 1) 236 { 237 unsigned int i, j; 238 bool err; 239 240 qsort (spec.numbered, spec.numbered_arg_count, 241 sizeof (struct numbered_arg), numbered_arg_compare); 242 243 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */ 244 err = false; 245 for (i = j = 0; i < spec.numbered_arg_count; i++) 246 if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number) 247 { 248 enum format_arg_type type1 = spec.numbered[i].type; 249 enum format_arg_type type2 = spec.numbered[j-1].type; 250 enum format_arg_type type_both; 251 252 if (type1 == type2) 253 type_both = type1; 254 else 255 { 256 /* Incompatible types. */ 257 type_both = FAT_NONE; 258 if (!err) 259 *invalid_reason = 260 INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number); 261 err = true; 262 } 263 264 spec.numbered[j-1].type = type_both; 265 } 266 else 267 { 268 if (j < i) 269 { 270 spec.numbered[j].number = spec.numbered[i].number; 271 spec.numbered[j].type = spec.numbered[i].type; 272 } 273 j++; 274 } 275 spec.numbered_arg_count = j; 276 if (err) 277 /* *invalid_reason has already been set above. */ 278 goto bad_format; 279 } 280 281 result = (struct spec *) xmalloc (sizeof (struct spec)); 282 *result = spec; 283 return result; 284 285 bad_format: 286 if (spec.numbered != NULL) 287 free (spec.numbered); 288 return NULL; 289} 290 291static void 292format_free (void *descr) 293{ 294 struct spec *spec = (struct spec *) descr; 295 296 if (spec->numbered != NULL) 297 free (spec->numbered); 298 free (spec); 299} 300 301static int 302format_get_number_of_directives (void *descr) 303{ 304 struct spec *spec = (struct spec *) descr; 305 306 return spec->directives; 307} 308 309static bool 310format_check (void *msgid_descr, void *msgstr_descr, bool equality, 311 formatstring_error_logger_t error_logger, 312 const char *pretty_msgstr) 313{ 314 struct spec *spec1 = (struct spec *) msgid_descr; 315 struct spec *spec2 = (struct spec *) msgstr_descr; 316 bool err = false; 317 318 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0) 319 { 320 unsigned int i, j; 321 unsigned int n1 = spec1->numbered_arg_count; 322 unsigned int n2 = spec2->numbered_arg_count; 323 324 /* Check the argument names are the same. 325 Both arrays are sorted. We search for the first difference. */ 326 for (i = 0, j = 0; i < n1 || j < n2; ) 327 { 328 int cmp = (i >= n1 ? 1 : 329 j >= n2 ? -1 : 330 spec1->numbered[i].number > spec2->numbered[j].number ? 1 : 331 spec1->numbered[i].number < spec2->numbered[j].number ? -1 : 332 0); 333 334 if (cmp > 0) 335 { 336 if (error_logger) 337 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"), 338 spec2->numbered[j].number, pretty_msgstr); 339 err = true; 340 break; 341 } 342 else if (cmp < 0) 343 { 344 if (equality) 345 { 346 if (error_logger) 347 error_logger (_("a format specification for argument %u doesn't exist in '%s'"), 348 spec1->numbered[i].number, pretty_msgstr); 349 err = true; 350 break; 351 } 352 else 353 i++; 354 } 355 else 356 j++, i++; 357 } 358 /* Check the argument types are the same. */ 359 if (!err) 360 for (i = 0, j = 0; j < n2; ) 361 { 362 if (spec1->numbered[i].number == spec2->numbered[j].number) 363 { 364 if (spec1->numbered[i].type != spec2->numbered[j].type) 365 { 366 if (error_logger) 367 error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"), 368 pretty_msgstr, spec2->numbered[j].number); 369 err = true; 370 break; 371 } 372 j++, i++; 373 } 374 else 375 i++; 376 } 377 } 378 379 return err; 380} 381 382 383struct formatstring_parser formatstring_elisp = 384{ 385 format_parse, 386 format_free, 387 format_get_number_of_directives, 388 NULL, 389 format_check 390}; 391 392 393#ifdef TEST 394 395/* Test program: Print the argument list specification returned by 396 format_parse for strings read from standard input. */ 397 398#include <stdio.h> 399#include "getline.h" 400 401static void 402format_print (void *descr) 403{ 404 struct spec *spec = (struct spec *) descr; 405 unsigned int last; 406 unsigned int i; 407 408 if (spec == NULL) 409 { 410 printf ("INVALID"); 411 return; 412 } 413 414 printf ("("); 415 last = 1; 416 for (i = 0; i < spec->numbered_arg_count; i++) 417 { 418 unsigned int number = spec->numbered[i].number; 419 420 if (i > 0) 421 printf (" "); 422 if (number < last) 423 abort (); 424 for (; last < number; last++) 425 printf ("_ "); 426 switch (spec->numbered[i].type) 427 { 428 case FAT_CHARACTER: 429 printf ("c"); 430 break; 431 case FAT_INTEGER: 432 printf ("i"); 433 break; 434 case FAT_FLOAT: 435 printf ("f"); 436 break; 437 case FAT_OBJECT_PRETTY: 438 printf ("s"); 439 break; 440 case FAT_OBJECT: 441 printf ("*"); 442 break; 443 default: 444 abort (); 445 } 446 last = number + 1; 447 } 448 printf (")"); 449} 450 451int 452main () 453{ 454 for (;;) 455 { 456 char *line = NULL; 457 size_t line_size = 0; 458 int line_len; 459 char *invalid_reason; 460 void *descr; 461 462 line_len = getline (&line, &line_size, stdin); 463 if (line_len < 0) 464 break; 465 if (line_len > 0 && line[line_len - 1] == '\n') 466 line[--line_len] = '\0'; 467 468 invalid_reason = NULL; 469 descr = format_parse (line, false, &invalid_reason); 470 471 format_print (descr); 472 printf ("\n"); 473 if (descr == NULL) 474 printf ("%s\n", invalid_reason); 475 476 free (invalid_reason); 477 free (line); 478 } 479 480 return 0; 481} 482 483/* 484 * For Emacs M-x compile 485 * Local Variables: 486 * 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-elisp.c ../lib/libgettextlib.la" 487 * End: 488 */ 489 490#endif /* TEST */ 491