1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include "gnu_msgfmt.h" 28 29 #define OPT_L 0x01 30 #define OPT_l 0x02 31 #define OPT_ll 0x04 32 #define OPT_w 0x08 33 #define OPT_h 0x10 34 #define OPT_hh 0x20 35 #define OPT_j 0x40 36 37 static int 38 extract_format(char *norm, const char *sfmt, size_t sz) 39 { 40 const unsigned char *fmt = (const unsigned char *)sfmt; 41 unsigned char c; 42 int t, arg, ap; 43 int dotseen; 44 char flag, conv; 45 int lastarg = -1; 46 int prevarg; 47 int max = 0; 48 int lflag; 49 50 for (; *fmt; fmt++) { 51 if (*fmt == '%') { 52 if (*++fmt == '%') 53 continue; 54 if (!*fmt) 55 break; 56 prevarg = lastarg; 57 arg = ++lastarg; 58 59 t = 0; 60 while (*fmt && isdigit(*fmt)) 61 t = t * 10 + *fmt++ - '0'; 62 63 if (*fmt == '$') { 64 lastarg = arg = t - 1; 65 fmt++; 66 } 67 68 if (!*fmt) 69 goto end; 70 71 dotseen = 0; 72 flag = 0; 73 lflag = 0; 74 again: 75 /* Skip flags */ 76 while ((c = *fmt) != '\0') { 77 if (c == '\'' || c == '+' || c == '-' || 78 c == ' ' || c == '#' || c == '0') { 79 fmt++; 80 continue; 81 } 82 break; 83 } 84 85 while (*fmt && isdigit(*fmt)) 86 fmt++; 87 88 if (*fmt == '*') { 89 if (isdigit(*(fmt + 1))) { 90 fmt++; 91 t = 0; 92 while (*fmt && isdigit(*fmt)) 93 t = t * 10 + *fmt++ - '0'; 94 95 if (*fmt == '$') { 96 /* 97 * %*4$ 98 */ 99 ap = t - 1; 100 if ((ap * 2 + 1 >= sz) || 101 (norm[ap * 2] && 102 norm[ap * 2] != '*')) { 103 /* error in format */ 104 return (-1); 105 } else { 106 if (ap >= max) 107 max = ap + 1; 108 norm[ap * 2] = '*'; 109 } 110 } 111 /* 112 * If digits follow a '*', it is 113 * not loaded as an argument, the 114 * digits are used instead. 115 */ 116 } else { 117 /* 118 * %* 119 */ 120 if (*(fmt + 1) == '$') { 121 fmt++; 122 } else { 123 ap = arg; 124 prevarg = arg; 125 lastarg = ++arg; 126 if ((ap * 2 + 1 >= sz) || 127 (norm[ap * 2] && 128 norm[ap * 2] != '*')) { 129 /* error in format */ 130 return (-1); 131 } else { 132 if (ap >= max) 133 max = ap + 1; 134 norm[ap * 2] = '*'; 135 } 136 } 137 } 138 fmt++; 139 } 140 141 if ((*fmt == '.') || (*fmt == '*')) { 142 if (dotseen) 143 return (-1); 144 dotseen = 1; 145 fmt++; 146 goto again; 147 } 148 149 if (!*fmt) 150 goto end; 151 152 while (*fmt) { 153 switch (*fmt) { 154 case 'l': 155 if (!(flag & OPT_ll)) { 156 if (lflag) { 157 flag &= ~OPT_l; 158 flag |= OPT_ll; 159 } else { 160 flag |= OPT_l; 161 } 162 } 163 lflag++; 164 break; 165 case 'L': 166 flag |= OPT_L; 167 break; 168 case 'w': 169 flag |= OPT_w; 170 break; 171 case 'h': 172 if (flag & (OPT_h|OPT_hh)) 173 flag |= OPT_hh; 174 else 175 flag |= OPT_h; 176 break; 177 case 'j': 178 flag |= OPT_j; 179 break; 180 case 'z': 181 case 't': 182 if (!(flag & OPT_ll)) { 183 flag |= OPT_l; 184 } 185 break; 186 case '\'': 187 case '+': 188 case '-': 189 case ' ': 190 case '#': 191 case '.': 192 case '*': 193 goto again; 194 default: 195 if (isdigit(*fmt)) 196 goto again; 197 else 198 goto done; 199 } 200 fmt++; 201 } 202 done: 203 if (!*fmt) 204 goto end; 205 206 if ((c = *fmt) == 'C') { 207 flag |= OPT_l; 208 conv = 'c'; 209 } else if (c == 'd') { 210 conv = 'd'; 211 } else if (c == 'S') { 212 flag |= OPT_l; 213 conv = 's'; 214 } else if (c == 's') { 215 conv = 's'; 216 } else if (c == 'i') { 217 conv = 'i'; 218 } else if (c == 'o') { 219 conv = 'o'; 220 } else if (c == 'u') { 221 conv = 'u'; 222 } else if (c == 'c') { 223 conv = 'c'; 224 } else if (c == 'x') { 225 conv = 'x'; 226 } else if (c == 'X') { 227 conv = 'X'; 228 } else if (c == 'e') { 229 conv = 'e'; 230 } else if (c == 'E') { 231 conv = 'E'; 232 } else if (c == 'f') { 233 conv = 'f'; 234 } else if (c == 'F') { 235 conv = 'F'; 236 } else if (c == 'a') { 237 conv = 'a'; 238 } else if (c == 'A') { 239 conv = 'A'; 240 } else if (c == 'g') { 241 conv = 'g'; 242 } else if (c == 'G') { 243 conv = 'G'; 244 } else if (c == 'p') { 245 conv = 'p'; 246 } else if (c == 'n') { 247 conv = 'n'; 248 } else { 249 lastarg = prevarg; 250 continue; 251 } 252 253 if ((arg * 2 + 1 >= sz) || 254 (norm[arg * 2] && 255 (norm[arg * 2] != conv))) { 256 return (-1); 257 } else { 258 if (arg >= max) 259 max = arg + 1; 260 norm[arg * 2] = conv; 261 } 262 norm[arg * 2 + 1] = flag; 263 } 264 } 265 266 end: 267 for (arg = 0; arg < max; arg++) { 268 if (norm[arg * 2] == '\0') 269 return (-1); 270 } 271 272 return (max); 273 } 274 275 276 void 277 check_format(struct entry *id, struct entry *str, int is_c_format) 278 { 279 int i, n; 280 int id_b_newline, id_e_newline; 281 int plural_b_newline, plural_e_newline; 282 int str_b_newline, str_e_newline; 283 int id_fmt, plural_fmt, str_fmt; 284 int *pstr_fmt; 285 char *msgid, *plural, *msgstr; 286 char *id_norm, *plural_norm, *str_norm; 287 char **pstr_norm; 288 size_t id_len, id_num; 289 size_t plural_off, plural_len, plural_num; 290 size_t str_len, str_num; 291 size_t osz, nsz; 292 struct loc *p; 293 294 if (id->len == 1) { 295 /* 296 * null string: header entry 297 * no check is performed 298 */ 299 return; 300 } 301 302 msgid = id->str; 303 id_num = id->num; 304 msgstr = str->str; 305 if (id->no > 1) { 306 /* plural */ 307 id_len = id->pos[0].len; 308 plural_off = id->pos[1].off; 309 plural_len = id->pos[1].len; 310 plural_num = id->pos[1].num; 311 plural = msgid + plural_off; 312 } else { 313 /* no plural form */ 314 id_len = id->len; 315 str_len = str->len; 316 str_num = str->num; 317 plural = NULL; 318 } 319 320 /* 321 * First checking the newline 322 */ 323 324 if (!plural) { 325 /* no plural form */ 326 id_b_newline = (msgid[0] == '\n'); 327 id_e_newline = (msgid[id_len - 1 - 1] == '\n'); 328 329 str_b_newline = (msgstr[0] == '\n'); 330 str_e_newline = (msgstr[str_len - 1 - 1] == '\n'); 331 if (id_b_newline && !str_b_newline) { 332 diag(gettext(ERR_BEGIN_NEWLINE_1), 333 id_num, str_num, cur_po); 334 po_error++; 335 } else if (!id_b_newline && str_b_newline) { 336 diag(gettext(ERR_BEGIN_NEWLINE_2), 337 id_num, str_num, cur_po); 338 po_error++; 339 } 340 if (id_e_newline && !str_e_newline) { 341 diag(gettext(ERR_END_NEWLINE_1), 342 id_num, str_num, cur_po); 343 po_error++; 344 } else if (!id_e_newline && str_e_newline) { 345 diag(gettext(ERR_END_NEWLINE_2), 346 id_num, str_num, cur_po); 347 po_error++; 348 } 349 } else { 350 /* plural form */ 351 id_b_newline = (msgid[0] == '\n'); 352 id_e_newline = (msgid[id_len - 1 - 1] == '\n'); 353 354 plural_b_newline = (plural[0] == '\n'); 355 plural_e_newline = (plural[plural_len - 1 -1 ] == '\n'); 356 357 /* between msgid and msgid_plural */ 358 if (id_b_newline && !plural_b_newline) { 359 diag(gettext(ERR_BEGIN_NEWLINE_3), 360 id_num, plural_num, cur_po); 361 po_error++; 362 } else if (!id_b_newline && plural_b_newline) { 363 diag(gettext(ERR_BEGIN_NEWLINE_4), 364 id_num, plural_num, cur_po); 365 po_error++; 366 } 367 if (id_e_newline && !plural_e_newline) { 368 diag(gettext(ERR_END_NEWLINE_3), 369 id_num, plural_num, cur_po); 370 po_error++; 371 } else if (!id_e_newline && plural_e_newline) { 372 diag(gettext(ERR_END_NEWLINE_4), 373 id_num, plural_num, cur_po); 374 po_error++; 375 } 376 377 for (i = 0; i < str->no; i++) { 378 p = str->pos + i; 379 str_b_newline = (msgstr[p->off] == '\n'); 380 str_e_newline = 381 (msgstr[p->off + p->len - 1 - 1] == '\n'); 382 383 if (id_b_newline && !str_b_newline) { 384 diag(gettext(ERR_BEGIN_NEWLINE_5), 385 id_num, p->num, cur_po, i); 386 po_error++; 387 } else if (!id_b_newline && str_b_newline) { 388 diag(gettext(ERR_BEGIN_NEWLINE_6), 389 id_num, p->num, cur_po, i); 390 po_error++; 391 } 392 393 if (id_e_newline && !str_e_newline) { 394 diag(gettext(ERR_END_NEWLINE_5), 395 id_num, p->num, cur_po, i); 396 po_error++; 397 } else if (!id_e_newline && str_e_newline) { 398 diag(gettext(ERR_END_NEWLINE_6), 399 id_num, p->num, cur_po, i); 400 po_error++; 401 } 402 } 403 } 404 405 /* 406 * if c-format is not specified, no printf-format check 407 * is performed. 408 */ 409 if (!is_c_format) { 410 return; 411 } 412 413 osz = id_len * 2; 414 id_norm = (char *)Xcalloc(1, osz); 415 id_fmt = extract_format(id_norm, msgid, osz); 416 if (id_fmt == -1) { 417 diag(gettext(ERR_INVALID_FMT), id_num, cur_po); 418 po_error++; 419 } 420 421 if (!plural) { 422 /* no plural */ 423 424 nsz = str_len * 2; 425 str_norm = (char *)Xcalloc(1, nsz); 426 str_fmt = extract_format(str_norm, msgstr, nsz); 427 if (str_fmt == -1) { 428 diag(gettext(ERR_INVALID_FMT), str_num, cur_po); 429 po_error++; 430 } 431 432 if (id_fmt != str_fmt) { 433 diag(gettext(ERR_INCMP_FMT), 434 id_num, str_num, cur_po); 435 diag(gettext(ERR_INCMP_FMT_DIFF_1), 436 id_fmt, str_fmt); 437 po_error++; 438 } else { 439 for (n = 0; n < id_fmt; n++) { 440 if ((id_norm[n * 2] != 441 str_norm[n * 2]) || 442 (id_norm[n * 2 + 1] != 443 str_norm[n * 2 + 1])) { 444 diag(gettext(ERR_INCMP_FMT), 445 id_num, str_num, cur_po); 446 diag(gettext(ERR_INCMP_FMT_DIFF_2), 447 n + 1); 448 po_error++; 449 } 450 } 451 } 452 free(str_norm); 453 free(id_norm); 454 455 return; 456 } 457 458 /* plural */ 459 nsz = plural_len * 2; 460 plural_norm = (char *)Xcalloc(1, nsz); 461 plural_fmt = extract_format(plural_norm, plural, nsz); 462 if (plural_fmt == -1) { 463 diag(gettext(ERR_INVALID_FMT), plural_num, cur_po); 464 po_error++; 465 } 466 467 pstr_norm = (char **)Xcalloc(str->no, sizeof (char *)); 468 pstr_fmt = (int *)Xcalloc(str->no, sizeof (int)); 469 for (i = 0; i < str->no; i++) { 470 p = str->pos + i; 471 nsz = p->len * 2; 472 pstr_norm[i] = (char *)Xcalloc(1, nsz); 473 pstr_fmt[i] = extract_format(pstr_norm[i], 474 msgstr + p->off, nsz); 475 if (pstr_fmt[i] == -1) { 476 diag(gettext(ERR_INVALID_FMT), 477 p->num, cur_po); 478 po_error++; 479 } 480 } 481 482 /* between msgid and msgid_plural */ 483 if (id_fmt != plural_fmt) { 484 diag(gettext(ERR_INCMP_FMT), 485 id_num, plural_num, cur_po); 486 diag(gettext(ERR_INCMP_FMT_DIFF_1), 487 id_fmt, plural_fmt); 488 po_error++; 489 } else { 490 for (n = 0; n < id_fmt; n++) { 491 if ((id_norm[n * 2] != 492 plural_norm[n * 2]) || 493 (id_norm[n * 2 + 1] != 494 plural_norm[n * 2 + 1])) { 495 diag(gettext(ERR_INCMP_FMT), 496 id_num, plural_num, cur_po); 497 diag(gettext(ERR_INCMP_FMT_DIFF_2), 498 n + 1); 499 po_error++; 500 } 501 } 502 } 503 free(plural_norm); 504 505 /* between msgid and msgstr */ 506 for (i = 0; i < str->no; i++) { 507 p = str->pos + i; 508 if (id_fmt != pstr_fmt[i]) { 509 diag(gettext(ERR_INCMP_FMT), 510 id_num, p->num, cur_po); 511 diag(gettext(ERR_INCMP_FMT_DIFF_1), 512 id_fmt, pstr_fmt[i]); 513 po_error++; 514 } else { 515 for (n = 0; n < id_fmt; n++) { 516 if ((id_norm[n * 2] != 517 pstr_norm[i][n * 2]) || 518 (id_norm[n * 2 + 1] != 519 pstr_norm[i][n * 2 + 1])) { 520 diag(gettext(ERR_INCMP_FMT), 521 id_num, p->num, cur_po); 522 diag(gettext(ERR_INCMP_FMT_DIFF_2), 523 n + 1); 524 po_error++; 525 } 526 } 527 } 528 free(pstr_norm[i]); 529 } 530 free(pstr_norm); 531 free(pstr_fmt); 532 free(id_norm); 533 } 534