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