windmc.c revision 214571
1243789Sdim/* windmc.c -- a program to compile Windows message files. 2243789Sdim Copyright 2007 3243789Sdim Free Software Foundation, Inc. 4243789Sdim Written by Kai Tietz, Onevision. 5243789Sdim 6243789Sdim This file is part of GNU Binutils. 7243789Sdim 8243789Sdim This program is free software; you can redistribute it and/or modify 9243789Sdim it under the terms of the GNU General Public License as published by 10243789Sdim the Free Software Foundation; either version 2 of the License, or 11243789Sdim (at your option) any later version. 12243789Sdim 13243789Sdim This program is distributed in the hope that it will be useful, 14243789Sdim but WITHOUT ANY WARRANTY; without even the implied warranty of 15243789Sdim MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16243789Sdim GNU General Public License for more details. 17243789Sdim 18243789Sdim You should have received a copy of the GNU General Public License 19243789Sdim along with this program; if not, write to the Free Software 20243789Sdim Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 21243789Sdim 02110-1301, USA. */ 22249423Sdim 23243789Sdim/* This program can read and comile Windows message format. 24243789Sdim 25243789Sdim It is based on information taken from the following sources: 26243789Sdim 27243789Sdim * Microsoft documentation. 28243789Sdim 29243789Sdim * The wmc program, written by Bertho A. Stultiens (BS). */ 30243789Sdim 31243789Sdim#include "sysdep.h" 32243789Sdim#include <assert.h> 33243789Sdim#include <time.h> 34243789Sdim#include "bfd.h" 35243789Sdim#include "getopt.h" 36243789Sdim#include "bucomm.h" 37243789Sdim#include "libiberty.h" 38243789Sdim#include "safe-ctype.h" 39243789Sdim#include "obstack.h" 40243789Sdim 41243789Sdim#include "windmc.h" 42243789Sdim#include "windint.h" 43243789Sdim 44243789Sdim/* Defines a message compiler element item with length and offset 45243789Sdim information. */ 46243789Sdimtypedef struct mc_msg_item 47243789Sdim{ 48243789Sdim rc_uint_type res_len; 49243789Sdim rc_uint_type res_off; 50243789Sdim struct bin_messagetable_item *res; 51243789Sdim} mc_msg_item; 52243789Sdim 53243789Sdim/* Defined in bfd/binary.c. Used to set architecture and machine of input 54243789Sdim binary files. */ 55243789Sdimextern enum bfd_architecture bfd_external_binary_architecture; 56243789Sdimextern unsigned long bfd_external_machine; 57243789Sdim 58243789Sdimint target_is_bigendian = 0; 59243789Sdimconst char *def_target_arch; 60243789Sdim 61243789Sdim/* Globals and static variable definitions. */ 62243789Sdim 63243789Sdim/* bfd global helper struct variable. */ 64243789Sdimstatic struct 65243789Sdim{ 66243789Sdim bfd *abfd; 67249423Sdim asection *sec; 68249423Sdim} mc_bfd; 69249423Sdim 70249423Sdim/* Memory list. */ 71249423Sdimmc_node *mc_nodes = NULL; 72243789Sdimstatic mc_node_lang **mc_nodes_lang = NULL; 73243789Sdimstatic int mc_nodes_lang_count = 0; 74243789Sdimstatic mc_keyword **mc_severity_codes = NULL; 75243789Sdimstatic int mc_severity_codes_count = 0; 76243789Sdimstatic mc_keyword **mc_facility_codes = NULL; 77243789Sdimstatic int mc_facility_codes_count = 0; 78243789Sdim 79243789Sdim/* When we are building a resource tree, we allocate everything onto 80243789Sdim an obstack, so that we can free it all at once if we want. */ 81243789Sdim#define obstack_chunk_alloc xmalloc 82243789Sdim#define obstack_chunk_free free 83243789Sdim 84243789Sdim/* The resource building obstack. */ 85243789Sdimstatic struct obstack res_obstack; 86243789Sdim 87243789Sdim/* Flag variables. */ 88249423Sdim/* Set by -C. Set the default code page to be used for input text file. */ 89249423Sdimstatic rc_uint_type mcset_codepage_in = 0; 90249423Sdim 91249423Sdim/* Set by -O. Set the default code page to be used for output text files. */ 92249423Sdimstatic rc_uint_type mcset_codepage_out = 0; 93249423Sdim 94249423Sdim/* Set by -b. .BIN filename should have .mc filename_ included for uniqueness. */ 95249423Sdimstatic int mcset_prefix_bin = 0; 96249423Sdim 97249423Sdim/* The base name of the .mc file. */ 98249423Sdimstatic const char *mcset_mc_basename = "unknown"; 99249423Sdim 100249423Sdim/* Set by -e <ext>. Specify the extension for the header file. */ 101249423Sdimstatic const char *mcset_header_ext = ".h"; 102249423Sdim 103249423Sdim/* Set by -h <path>. Gives the path of where to create the C include file. */ 104249423Sdimstatic const char *mcset_header_dir = "./"; 105243789Sdim 106243789Sdim/* Set by -r <path>. Gives the path of where to create the RC include file 107243789Sdim and the binary message resource files it includes. */ 108243789Sdimstatic const char *mcset_rc_dir = "./"; 109249423Sdim 110249423Sdim/* Modified by -a & -u. By -u input file is unicode, by -a is ASCII (default). */ 111249423Sdimstatic int mcset_text_in_is_unicode = 0; 112249423Sdim 113249423Sdim/* Modified by -A & -U. By -U bin file is unicode (default), by -A is ASCII. */ 114249423Sdimstatic int mcset_bin_out_is_unicode = 1; 115249423Sdim 116249423Sdim/* Set by -c. Sets the Customer bit in all the message ID's. */ 117249423Sdimint mcset_custom_bit = 0; 118249423Sdim 119249423Sdim/* Set by -o. Generate OLE2 header file. Use HRESULT definition instead of 120249423Sdim status code definition. */ 121249423Sdimstatic int mcset_use_hresult = 0; 122249423Sdim 123249423Sdim/* Set by -m <msglen>. Generate a warning if the size of any message exceeds 124249423Sdim maxmsglen characters. */ 125249423Sdimrc_uint_type mcset_max_message_length = 0; 126249423Sdim 127249423Sdim/* Set by -d. Sets message values in header to decimal initially. */ 128243789Sdimint mcset_out_values_are_decimal = 0; 129243789Sdim 130243789Sdim/* Set by -n. terminates all strings with null's in the message tables. */ 131243789Sdimstatic int mcset_automatic_null_termination = 0; 132243789Sdim 133/* The type used for message id output in header. */ 134unichar *mcset_msg_id_typedef = NULL; 135 136/* Set by -x path. Geberated debug C file for mapping ID's to text. */ 137static const char *mcset_dbg_dir = NULL; 138 139/* getopt long name definitions. */ 140static const struct option long_options[] = 141{ 142 {"binprefix", no_argument, 0, 'b'}, 143 {"target", required_argument, 0, 'F'}, 144 {"extension", required_argument, 0, 'e'}, 145 {"headerdir", required_argument, 0, 'h'}, 146 {"rcdir", required_argument, 0, 'r'}, 147 {"verbose", no_argument, 0, 'v'}, 148 {"codepage_in", required_argument, 0, 'C'}, 149 {"codepage_out", required_argument, 0, 'O'}, 150 {"maxlength", required_argument, 0, 'm'}, 151 {"ascii_in", no_argument, 0, 'a'}, 152 {"ascii_out", no_argument, 0, 'A'}, 153 {"unicode_in", no_argument, 0, 'u'}, 154 {"unicode_out", no_argument, 0, 'U'}, 155 {"customflag", no_argument, 0, 'c'}, 156 {"decimal_values", no_argument, 0, 'd'}, 157 {"hresult_use", no_argument, 0, 'o'}, 158 {"nullterminate", no_argument, 0, 'n'}, 159 {"xdbg", required_argument, 0, 'x'}, 160 {"version", no_argument, 0, 'V'}, 161 {"help", no_argument, 0, 'H'}, 162 {0, no_argument, 0, 0} 163}; 164 165 166/* Initialize the resource building obstack. */ 167static void 168res_init (void) 169{ 170 obstack_init (&res_obstack); 171} 172 173/* Allocate space on the resource building obstack. */ 174void * 175res_alloc (rc_uint_type bytes) 176{ 177 return (void *) obstack_alloc (&res_obstack, (size_t) bytes); 178} 179 180static FILE * 181mc_create_path_text_file (const char *path, const char *ext) 182{ 183 FILE *ret; 184 size_t len = 1; 185 char *hsz; 186 187 len += (path != NULL ? strlen (path) : 0); 188 len += strlen (mcset_mc_basename); 189 len += (ext != NULL ? strlen (ext) : 0); 190 hsz = xmalloc (len); 191 sprintf (hsz, "%s%s%s", (path != NULL ? path : ""), mcset_mc_basename, 192 (ext != NULL ? ext : "")); 193 if ((ret = fopen (hsz, "wb")) == NULL) 194 fatal (_("can't create %s file ,%s' for output.\n"), (ext ? ext : "text"), hsz); 195 free (hsz); 196 return ret; 197} 198 199static void 200usage (FILE *stream, int status) 201{ 202 fprintf (stream, _("Usage: %s [option(s)] [input-file]\n"), 203 program_name); 204 fprintf (stream, _(" The options are:\n\ 205 -a --ascii_in Read input file as ASCII file\n\ 206 -A --ascii_out Write binary messages as ASCII\n\ 207 -b --binprefix .bin filename is prefixed by .mc filename_ for uniqueness.\n\ 208 -c --customflag Set custom flags for messages\n\ 209 -C --codepage_in=<val> Set codepage when reading mc text file\n\ 210 -d --decimal_values Print values to text files decimal\n\ 211 -e --extension=<extension> Set header extension used on export header file\n\ 212 -F --target <target> Specify output target for endianess.\n\ 213 -h --headerdir=<directory> Set the export directory for headers\n\ 214 -u --unicode_in Read input file as UTF16 file\n\ 215 -U --unicode_out Write binary messages as UFT16\n\ 216 -m --maxlength=<val> Set the maximal allowed message length\n\ 217 -n --nullterminate Automatic add a zero termination to strings\n\ 218 -o --hresult_use Use HRESULT definition instead of status code definition\n\ 219 -O --codepage_out=<val> Set codepage used for writing text file\n\ 220 -r --rcdir=<directory> Set the export directory for rc files\n\ 221 -x --xdbg=<directory> Where to create the .dbg C include file\n\ 222 that maps message ID's to their symbolic name.\n\ 223")); 224 fprintf (stream, _("\ 225 -H --help Print this help message\n\ 226 -v --verbose Verbose - tells you what it's doing\n\ 227 -V --version Print version information\n")); 228 229 list_supported_targets (program_name, stream); 230 231 if (REPORT_BUGS_TO[0] && status == 0) 232 fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO); 233 234 exit (status); 235} 236 237static void 238set_endianess (bfd *abfd, const char *target) 239{ 240 const bfd_target *target_vec; 241 242 def_target_arch = NULL; 243 target_vec = bfd_find_target (target, abfd); 244 if (! target_vec) 245 fatal ("Can't detect target endianess and architecture."); 246 target_is_bigendian = ((target_vec->byteorder == BFD_ENDIAN_BIG) ? 1 : 0); 247 { 248 const char *tname = target_vec->name; 249 const char **arch = bfd_arch_list (); 250 251 if (arch && tname) 252 { 253 if (strchr (tname, '-') != NULL) 254 tname = strchr (tname, '-') + 1; 255 while (*arch != NULL) 256 { 257 const char *in_a = strstr (*arch, tname); 258 char end_ch = (in_a ? in_a[strlen (tname)] : 0); 259 if (in_a && (in_a == *arch || in_a[-1] == ':') 260 && end_ch == 0) 261 { 262 def_target_arch = *arch; 263 break; 264 } 265 arch++; 266 } 267 } 268 if (! def_target_arch) 269 fatal ("Can't detect architecture."); 270 } 271} 272 273static int 274probe_codepage (rc_uint_type *cp, int *is_uni, const char *pswitch, int defmode) 275{ 276 if (*is_uni == -1) 277 { 278 if (*cp != CP_UTF16) 279 *is_uni = defmode; 280 else 281 *is_uni = 1; 282 } 283 if (*is_uni) 284 { 285 if (*cp != 0 && *cp != CP_UTF16) 286 { 287 fprintf (stderr, _("%s: warning: "), program_name); 288 fprintf (stderr, _("A codepage was specified switch ,%s' and UTF16.\n"), pswitch); 289 fprintf (stderr, _("\tcodepage settings are ignored.\n")); 290 } 291 *cp = CP_UTF16; 292 return 1; 293 } 294 if (*cp == CP_UTF16) 295 { 296 *is_uni = 1; 297 return 1; 298 } 299 if (*cp == 0) 300 *cp = 1252; 301 if (! unicode_is_valid_codepage (*cp)) 302 fatal ("Code page 0x%x is unknown.", (unsigned int) *cp); 303 *is_uni = 0; 304 return 1; 305} 306 307mc_node * 308mc_add_node (void) 309{ 310 mc_node *ret; 311 312 ret = res_alloc (sizeof (mc_node)); 313 memset (ret, 0, sizeof (mc_node)); 314 if (! mc_nodes) 315 mc_nodes = ret; 316 else 317 { 318 mc_node *h = mc_nodes; 319 320 while (h->next != NULL) 321 h = h->next; 322 h->next = ret; 323 } 324 return ret; 325} 326 327mc_node_lang * 328mc_add_node_lang (mc_node *root, const mc_keyword *lang, rc_uint_type vid) 329{ 330 mc_node_lang *ret, *h, *p; 331 332 if (! lang || ! root) 333 fatal (_("try to add a ill language.")); 334 ret = res_alloc (sizeof (mc_node_lang)); 335 memset (ret, 0, sizeof (mc_node_lang)); 336 ret->lang = lang; 337 ret->vid = vid; 338 if ((h = root->sub) == NULL) 339 root->sub = ret; 340 else 341 { 342 p = NULL; 343 while (h != NULL) 344 { 345 if (h->lang->nval > lang->nval) 346 break; 347 if (h->lang->nval == lang->nval) 348 { 349 if (h->vid > vid) 350 break; 351 if (h->vid == vid) 352 fatal ("double defined message id %ld.\n", (long) vid); 353 } 354 h = (p = h)->next; 355 } 356 ret->next = h; 357 if (! p) 358 root->sub = ret; 359 else 360 p->next = ret; 361 } 362 return ret; 363} 364 365static char * 366convert_unicode_to_ACP (const unichar *usz) 367{ 368 char *s; 369 rc_uint_type l; 370 371 if (! usz) 372 return NULL; 373 codepage_from_unicode (&l, usz, &s, mcset_codepage_out); 374 if (! s) 375 fatal ("unicode string not mappable to ASCII codepage 0x%lx.\n", (long) mcset_codepage_out); 376 return s; 377} 378 379static void 380write_dbg_define (FILE *fp, const unichar *sym_name, const unichar *typecast) 381{ 382 char *sym; 383 384 if (!sym_name || sym_name[0] == 0) 385 return; 386 sym = convert_unicode_to_ACP (sym_name); 387 fprintf (fp, " {("); 388 if (typecast) 389 unicode_print (fp, typecast, unichar_len (typecast)); 390 else 391 fprintf (fp, "DWORD"); 392 fprintf (fp, ") %s, \"%s\" },\n", sym, sym); 393} 394 395static void 396write_header_define (FILE *fp, const unichar *sym_name, rc_uint_type vid, const unichar *typecast, mc_node_lang *nl) 397{ 398 char *sym; 399 char *tdef = NULL; 400 401 if (!sym_name || sym_name[0] == 0) 402 { 403 if (nl != NULL) 404 { 405 if (mcset_out_values_are_decimal) 406 fprintf (fp, "//\n// MessageId: 0x%lu\n//\n", (unsigned long) vid); 407 else 408 fprintf (fp, "//\n// MessageId: 0x%lx\n//\n", (unsigned long) vid); 409 } 410 return; 411 } 412 sym = convert_unicode_to_ACP (sym_name); 413 if (typecast && typecast[0] != 0) 414 tdef = convert_unicode_to_ACP (typecast); 415 fprintf (fp, "//\n// MessageId: %s\n//\n", sym); 416 if (! mcset_out_values_are_decimal) 417 fprintf (fp, "#define %s %s%s%s 0x%lx\n\n", sym, 418 (tdef ? "(" : ""), (tdef ? tdef : ""), (tdef ? ")" : ""), 419 (unsigned long) vid); 420 else 421 fprintf (fp, "#define %s %s%s%s 0x%lu\n\n", sym, 422 (tdef ? "(" : ""), (tdef ? tdef : ""), (tdef ? ")" : ""), 423 (unsigned long) vid); 424} 425 426static int 427sort_mc_node_lang (const void *l, const void *r) 428{ 429 const mc_node_lang *l1 = *((const mc_node_lang **)l); 430 const mc_node_lang *r1 = *((const mc_node_lang **)r); 431 432 if (l == r) 433 return 0; 434 if (l1->lang != r1->lang) 435 { 436 if (l1->lang->nval < r1->lang->nval) 437 return -1; 438 return 1; 439 } 440 if (l1->vid == r1->vid) 441 return 0; 442 if (l1->vid < r1->vid) 443 return -1; 444 return 1; 445} 446 447static int 448sort_keyword_by_nval (const void *l, const void *r) 449{ 450 const mc_keyword *l1 = *((const mc_keyword **)l); 451 const mc_keyword *r1 = *((const mc_keyword **)r); 452 rc_uint_type len1, len2; 453 int e; 454 455 if (l == r) 456 return 0; 457 if (l1->nval != r1->nval) 458 { 459 if (l1->nval < r1->nval) 460 return -1; 461 return 1; 462 } 463 len1 = unichar_len (l1->usz); 464 len2 = unichar_len (r1->usz); 465 if (len1 <= len2) 466 e = memcmp (l1->usz, r1->usz, sizeof (unichar) * len1); 467 else 468 e = memcmp (l1->usz, r1->usz, sizeof (unichar) * len2); 469 if (e) 470 return e; 471 if (len1 < len2) 472 return -1; 473 else if (len1 > len2) 474 return 1; 475 return 0; 476} 477 478static void 479do_sorts (void) 480{ 481 mc_node *h; 482 mc_node_lang *n; 483 const mc_keyword *k; 484 int i; 485 486 /* Sort message by their language and id ascending. */ 487 mc_nodes_lang_count = 0; 488 489 h = mc_nodes; 490 while (h != NULL) 491 { 492 n = h->sub; 493 while (n != NULL) 494 { 495 mc_nodes_lang_count +=1; 496 n = n->next; 497 } 498 h = h->next; 499 } 500 501 if (mc_nodes_lang_count != 0) 502 { 503 h = mc_nodes; 504 i = 0; 505 mc_nodes_lang = xmalloc (sizeof (mc_node_lang *) * mc_nodes_lang_count); 506 507 while (h != NULL) 508 { 509 n = h->sub; 510 while (n != NULL) 511 { 512 mc_nodes_lang[i++] = n; 513 n = n->next; 514 } 515 h = h->next; 516 } 517 qsort (mc_nodes_lang, (size_t) mc_nodes_lang_count, sizeof (mc_node_lang *), sort_mc_node_lang); 518 } 519 /* Sort facility code definitions by there id ascending. */ 520 i = 0; 521 while ((k = enum_facility (i)) != NULL) 522 ++i; 523 mc_facility_codes_count = i; 524 if (i != 0) 525 { 526 mc_facility_codes = xmalloc (sizeof (mc_keyword *) * i); 527 i = 0; 528 while ((k = enum_facility (i)) != NULL) 529 mc_facility_codes[i++] = (mc_keyword *) k; 530 qsort (mc_facility_codes, (size_t) mc_facility_codes_count, sizeof (mc_keyword *), sort_keyword_by_nval); 531 } 532 533 /* Sort severity code definitions by there id ascending. */ 534 i = 0; 535 while ((k = enum_severity (i)) != NULL) 536 ++i; 537 mc_severity_codes_count = i; 538 if (i != 0) 539 { 540 mc_severity_codes = xmalloc (sizeof (mc_keyword *) * i); 541 i = 0; 542 while ((k = enum_severity (i)) != NULL) 543 mc_severity_codes[i++] = (mc_keyword *) k; 544 qsort (mc_severity_codes, (size_t) mc_severity_codes_count, sizeof (mc_keyword *), sort_keyword_by_nval); 545 } 546} 547 548static int 549mc_get_block_count (mc_node_lang **nl, int elems) 550{ 551 rc_uint_type exid; 552 int i, ret; 553 554 if (! nl) 555 return 0; 556 i = 0; 557 ret = 0; 558 while (i < elems) 559 { 560 ret++; 561 exid = nl[i++]->vid; 562 while (i < elems && nl[i]->vid == exid + 1) 563 exid = nl[i++]->vid; 564 } 565 return ret; 566} 567 568static bfd * 569windmc_open_as_binary (const char *filename) 570{ 571 bfd *abfd; 572 573 abfd = bfd_openw (filename, "binary"); 574 if (! abfd) 575 fatal ("can't open `%s' for output", filename); 576 577 return abfd; 578} 579 580static void 581target_put_16 (void *p, rc_uint_type value) 582{ 583 assert (!! p); 584 585 if (target_is_bigendian) 586 bfd_putb16 (value, p); 587 else 588 bfd_putl16 (value, p); 589} 590 591static void 592target_put_32 (void *p, rc_uint_type value) 593{ 594 assert (!! p); 595 596 if (target_is_bigendian) 597 bfd_putb32 (value, p); 598 else 599 bfd_putl32 (value, p); 600} 601 602static struct bin_messagetable_item * 603mc_generate_bin_item (mc_node_lang *n, rc_uint_type *res_len) 604{ 605 struct bin_messagetable_item *ret = NULL; 606 rc_uint_type len; 607 608 *res_len = 0; 609 if (mcset_bin_out_is_unicode == 1) 610 { 611 unichar *ht = n->message; 612 rc_uint_type txt_len; 613 614 txt_len = unichar_len (n->message); 615 if (mcset_automatic_null_termination && txt_len != 0) 616 { 617 while (txt_len > 0 && ht[txt_len - 1] > 0 && ht[txt_len - 1] < 0x20) 618 ht[--txt_len] = 0; 619 } 620 txt_len *= sizeof (unichar); 621 len = BIN_MESSAGETABLE_ITEM_SIZE + txt_len + sizeof (unichar); 622 ret = res_alloc ((len + 3) & ~3); 623 memset (ret, 0, (len + 3) & ~3); 624 target_put_16 (ret->length, (len + 3) & ~3); 625 target_put_16 (ret->flags, MESSAGE_RESOURCE_UNICODE); 626 txt_len = 0; 627 while (*ht != 0) 628 { 629 target_put_16 (ret->data + txt_len, *ht++); 630 txt_len += 2; 631 } 632 } 633 else 634 { 635 rc_uint_type txt_len, l; 636 char *cvt_txt; 637 638 codepage_from_unicode( &l, n->message, &cvt_txt, n->lang->lang_info.wincp); 639 if (! cvt_txt) 640 fatal ("Failed to convert message to language codepage.\n"); 641 txt_len = strlen (cvt_txt); 642 if (mcset_automatic_null_termination && txt_len > 0) 643 { 644 while (txt_len > 0 && cvt_txt[txt_len - 1] > 0 && cvt_txt[txt_len - 1] < 0x20) 645 cvt_txt[--txt_len] = 0; 646 } 647 len = BIN_MESSAGETABLE_ITEM_SIZE + txt_len + 1; 648 ret = res_alloc ((len + 3) & ~3); 649 memset (ret, 0, (len + 3) & ~3); 650 target_put_16 (ret->length, (len + 3) & ~3); 651 target_put_16 (ret->flags, 0); 652 strcpy ((char *) ret->data, cvt_txt); 653 } 654 *res_len = (len + 3) & ~3; 655 return ret; 656} 657 658static void 659mc_write_blocks (struct bin_messagetable *mtbl, mc_node_lang **nl, mc_msg_item *ml, int elems) 660{ 661 int i, idx = 0; 662 rc_uint_type exid; 663 664 if (! nl) 665 return; 666 i = 0; 667 while (i < elems) 668 { 669 target_put_32 (mtbl->items[idx].lowid, nl[i]->vid); 670 target_put_32 (mtbl->items[idx].highid, nl[i]->vid); 671 target_put_32 (mtbl->items[idx].offset, ml[i].res_off); 672 exid = nl[i++]->vid; 673 while (i < elems && nl[i]->vid == exid + 1) 674 { 675 target_put_32 (mtbl->items[idx].highid, nl[i]->vid); 676 exid = nl[i++]->vid; 677 } 678 ++idx; 679 } 680} 681 682static void 683set_windmc_bfd_content (const void *data, rc_uint_type off, rc_uint_type length) 684{ 685 if (! bfd_set_section_contents (mc_bfd.abfd, mc_bfd.sec, data, off, length)) 686 bfd_fatal ("bfd_set_section_contents"); 687} 688 689static void 690windmc_write_bin (const char *filename, mc_node_lang **nl, int elems) 691{ 692 unsigned long sec_length = 1; 693 int block_count, i; 694 mc_msg_item *mi; 695 struct bin_messagetable *mtbl; 696 rc_uint_type dta_off, dta_start; 697 698 if (elems <= 0) 699 return; 700 mc_bfd.abfd = windmc_open_as_binary (filename); 701 mc_bfd.sec = bfd_make_section (mc_bfd.abfd, ".data"); 702 if (mc_bfd.sec == NULL) 703 bfd_fatal ("bfd_make_section"); 704 if (! bfd_set_section_flags (mc_bfd.abfd, mc_bfd.sec, 705 (SEC_HAS_CONTENTS | SEC_ALLOC 706 | SEC_LOAD | SEC_DATA))) 707 bfd_fatal ("bfd_set_section_flags"); 708 /* Requiring this is probably a bug in BFD. */ 709 mc_bfd.sec->output_section = mc_bfd.sec; 710 711 block_count = mc_get_block_count (nl, elems); 712 713 dta_off = (rc_uint_type) ((BIN_MESSAGETABLE_BLOCK_SIZE * block_count) + BIN_MESSAGETABLE_SIZE - 4); 714 dta_start = dta_off = (dta_off + 3) & ~3; 715 mi = xmalloc (sizeof (mc_msg_item) * elems); 716 mtbl = xmalloc (dta_start); 717 718 /* Clear header region. */ 719 memset (mtbl, 0, dta_start); 720 target_put_32 (mtbl->cblocks, block_count); 721 /* Prepare items section for output. */ 722 for (i = 0; i < elems; i++) 723 { 724 mi[i].res_off = dta_off; 725 mi[i].res = mc_generate_bin_item (nl[i], &mi[i].res_len); 726 dta_off += mi[i].res_len; 727 } 728 sec_length = (dta_off + 3) & ~3; 729 if (! bfd_set_section_size (mc_bfd.abfd, mc_bfd.sec, sec_length)) 730 bfd_fatal ("bfd_set_section_size"); 731 /* Make sure we write the complete block. */ 732 set_windmc_bfd_content ("\0", sec_length - 1, 1); 733 734 /* Write block information. */ 735 mc_write_blocks (mtbl, nl, mi, elems); 736 737 set_windmc_bfd_content (mtbl, 0, dta_start); 738 739 /* Write items. */ 740 for (i = 0; i < elems; i++) 741 set_windmc_bfd_content (mi[i].res, mi[i].res_off, mi[i].res_len); 742 743 free (mtbl); 744 free (mi); 745 bfd_close (mc_bfd.abfd); 746 mc_bfd.abfd = NULL; 747 mc_bfd.sec = NULL; 748} 749 750static void 751write_bin (void) 752{ 753 mc_node_lang *n = NULL; 754 int i, c; 755 756 if (! mc_nodes_lang_count) 757 return; 758 759 i = 0; 760 while (i < mc_nodes_lang_count) 761 { 762 char *nd; 763 char *filename; 764 765 if (n && n->lang == mc_nodes_lang[i]->lang) 766 { 767 i++; 768 continue; 769 } 770 n = mc_nodes_lang[i]; 771 c = i + 1; 772 while (c < mc_nodes_lang_count && n->lang == mc_nodes_lang[c]->lang) 773 c++; 774 nd = convert_unicode_to_ACP (n->lang->sval); 775 776 /* Prepare filename for binary output. */ 777 filename = xmalloc (strlen (nd) + 4 + 1 + strlen (mcset_mc_basename) + 1 + strlen (mcset_rc_dir)); 778 strcpy (filename, mcset_rc_dir); 779 if (mcset_prefix_bin) 780 sprintf (filename + strlen (filename), "%s_", mcset_mc_basename); 781 strcat (filename, nd); 782 strcat (filename, ".bin"); 783 784 /* Write message file. */ 785 windmc_write_bin (filename, &mc_nodes_lang[i], (c - i)); 786 787 free (filename); 788 i = c; 789 } 790} 791 792static void 793write_rc (FILE *fp) 794{ 795 mc_node_lang *n; 796 int i, l; 797 798 fprintf (fp, 799 "/* Do not edit this file manually.\n" 800 " This file is autogenerated by windmc. */\n\n"); 801 if (! mc_nodes_lang_count) 802 return; 803 n = NULL; 804 i = 0; 805 for (l = 0; l < mc_nodes_lang_count; l++) 806 { 807 if (n && n->lang == mc_nodes_lang[l]->lang) 808 continue; 809 ++i; 810 n = mc_nodes_lang[l]; 811 fprintf (fp, "\n// Country: %s\n// Language: %s\n#pragma code_page(%u)\n", 812 n->lang->lang_info.country, n->lang->lang_info.name, 813 (unsigned) n->lang->lang_info.wincp); 814 fprintf (fp, "LANGUAGE 0x%lx, 0x%lx\n", (long) (n->lang->nval & 0x3ff), 815 (long) ((n->lang->nval & 0xffff) >> 10)); 816 fprintf (fp, "1 MESSAGETABLE \""); 817 if (mcset_prefix_bin) 818 fprintf (fp, "%s_", mcset_mc_basename); 819 unicode_print (fp, n->lang->sval, unichar_len (n->lang->sval)); 820 fprintf (fp, ".bin\"\n"); 821 } 822} 823 824static void 825write_dbg (FILE *fp) 826{ 827 mc_node *h; 828 829 fprintf (fp, 830 "/* Do not edit this file manually.\n" 831 " This file is autogenerated by windmc.\n\n" 832 " This file maps each message ID value in to a text string that contains\n" 833 " the symbolic name used for it. */\n\n"); 834 835 fprintf (fp, 836 "struct %sSymbolicName\n" 837 "{\n ", mcset_mc_basename); 838 if (mcset_msg_id_typedef) 839 unicode_print (fp, mcset_msg_id_typedef, unichar_len (mcset_msg_id_typedef)); 840 else 841 fprintf (fp, "DWORD"); 842 fprintf (fp, 843 " MessageId;\n" 844 " char *SymbolicName;\n" 845 "} %sSymbolicNames[] =\n" 846 "{\n", mcset_mc_basename); 847 h = mc_nodes; 848 while (h != NULL) 849 { 850 if (h->symbol) 851 write_dbg_define (fp, h->symbol, mcset_msg_id_typedef); 852 h = h->next; 853 } 854 fprintf (fp, " { ("); 855 if (mcset_msg_id_typedef) 856 unicode_print (fp, mcset_msg_id_typedef, unichar_len (mcset_msg_id_typedef)); 857 else 858 fprintf (fp, "DWORD"); 859 fprintf (fp, 860 ") 0xffffffff, NULL }\n" 861 "};\n"); 862} 863 864static void 865write_header (FILE *fp) 866{ 867 char *s; 868 int i; 869 const mc_keyword *key; 870 mc_node *h; 871 872 fprintf (fp, 873 "/* Do not edit this file manually.\n" 874 " This file is autogenerated by windmc. */\n\n" 875 "//\n// The values are 32 bit layed out as follows:\n//\n" 876 "// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1\n" 877 "// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0\n" 878 "// +---+-+-+-----------------------+-------------------------------+\n" 879 "// |Sev|C|R| Facility | Code |\n" 880 "// +---+-+-+-----------------------+-------------------------------+\n//\n" 881 "// where\n//\n" 882 "// C - is the Customer code flag\n//\n" 883 "// R - is a reserved bit\n//\n" 884 "// Code - is the facility's status code\n//\n"); 885 886 h = mc_nodes; 887 888 fprintf (fp, "// Sev - is the severity code\n//\n"); 889 if (mc_severity_codes_count != 0) 890 { 891 for (i = 0; i < mc_severity_codes_count; i++) 892 { 893 key = mc_severity_codes[i]; 894 fprintf (fp, "// %s - %02lx\n", convert_unicode_to_ACP (key->usz), 895 (unsigned long) key->nval); 896 if (key->sval && key->sval[0] != 0) 897 { 898 if (! mcset_out_values_are_decimal) 899 fprintf (fp, "#define %s 0x%lx\n", convert_unicode_to_ACP (key->sval), 900 (unsigned long) key->nval); 901 else 902 fprintf (fp, "#define %s 0x%lu\n", convert_unicode_to_ACP (key->sval), 903 (unsigned long) key->nval); 904 } 905 } 906 fprintf (fp, "//\n"); 907 } 908 fprintf (fp, "// Facility - is the facility code\n//\n"); 909 if (mc_facility_codes_count != 0) 910 { 911 for (i = 0; i < mc_facility_codes_count; i++) 912 { 913 key = mc_facility_codes[i]; 914 fprintf (fp, "// %s - %04lx\n", convert_unicode_to_ACP (key->usz), 915 (unsigned long) key->nval); 916 if (key->sval && key->sval[0] != 0) 917 { 918 if (! mcset_out_values_are_decimal) 919 fprintf (fp, "#define %s 0x%lx\n", convert_unicode_to_ACP (key->sval), 920 (unsigned long) key->nval); 921 else 922 fprintf (fp, "#define %s 0x%lu\n", convert_unicode_to_ACP (key->sval), 923 (unsigned long) key->nval); 924 } 925 } 926 fprintf (fp, "//\n"); 927 } 928 fprintf (fp, "\n"); 929 while (h != NULL) 930 { 931 if (h->user_text) 932 { 933 s = convert_unicode_to_ACP (h->user_text); 934 if (s) 935 fprintf (fp, "%s", s); 936 } 937 if (h->symbol) 938 write_header_define (fp, h->symbol, h->vid, mcset_msg_id_typedef, h->sub); 939 h = h->next; 940 } 941} 942 943static const char * 944mc_unify_path (const char *path) 945{ 946 char *end; 947 char *hsz; 948 949 if (! path || *path == 0) 950 return "./"; 951 hsz = xmalloc (strlen (path) + 2); 952 strcpy (hsz, path); 953 end = hsz + strlen (hsz); 954 if (hsz[-1] != '/' && hsz[-1] != '\\') 955 strcpy (end, "/"); 956 while ((end = strchr (hsz, '\\')) != NULL) 957 *end = '/'; 958 return hsz; 959} 960 961int main (int, char **); 962 963int 964main (int argc, char **argv) 965{ 966 FILE *h_fp; 967 int c; 968 char *target, *input_filename; 969 int verbose; 970 971#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) 972 setlocale (LC_MESSAGES, ""); 973#endif 974#if defined (HAVE_SETLOCALE) 975 setlocale (LC_CTYPE, ""); 976#endif 977 bindtextdomain (PACKAGE, LOCALEDIR); 978 textdomain (PACKAGE); 979 980 program_name = argv[0]; 981 xmalloc_set_program_name (program_name); 982 983 expandargv (&argc, &argv); 984 985 bfd_init (); 986 set_default_bfd_target (); 987 988 target = NULL; 989 verbose = 0; 990 input_filename = NULL; 991 992 res_init (); 993 994 while ((c = getopt_long (argc, argv, "C:F:O:h:e:m:r:x:aAbcdHunoUvV", long_options, 995 (int *) 0)) != EOF) 996 { 997 switch (c) 998 { 999 case 'b': 1000 mcset_prefix_bin = 1; 1001 break; 1002 case 'e': 1003 { 1004 mcset_header_ext = optarg; 1005 if (mcset_header_ext[0] != '.' && mcset_header_ext[0] != 0) 1006 { 1007 char *hsz = xmalloc (strlen (mcset_header_ext) + 2); 1008 1009 sprintf (hsz, ".%s", mcset_header_ext); 1010 mcset_header_ext = hsz; 1011 } 1012 } 1013 break; 1014 case 'h': 1015 mcset_header_dir = mc_unify_path (optarg); 1016 break; 1017 case 'r': 1018 mcset_rc_dir = mc_unify_path (optarg); 1019 break; 1020 case 'a': 1021 mcset_text_in_is_unicode = 0; 1022 break; 1023 case 'x': 1024 if (*optarg != 0) 1025 mcset_dbg_dir = mc_unify_path (optarg); 1026 break; 1027 case 'A': 1028 mcset_bin_out_is_unicode = 0; 1029 break; 1030 case 'd': 1031 mcset_out_values_are_decimal = 1; 1032 break; 1033 case 'u': 1034 mcset_text_in_is_unicode = 1; 1035 break; 1036 case 'U': 1037 mcset_bin_out_is_unicode = 1; 1038 break; 1039 case 'c': 1040 mcset_custom_bit = 1; 1041 break; 1042 case 'n': 1043 mcset_automatic_null_termination = 1; 1044 break; 1045 case 'o': 1046 mcset_use_hresult = 1; 1047 fatal ("option -o is not implemented until yet.\n"); 1048 break; 1049 case 'F': 1050 target = optarg; 1051 break; 1052 case 'v': 1053 verbose ++; 1054 break; 1055 case 'm': 1056 mcset_max_message_length = strtol (optarg, (char **) NULL, 10); 1057 break; 1058 case 'C': 1059 mcset_codepage_in = strtol (optarg, (char **) NULL, 10); 1060 break; 1061 case 'O': 1062 mcset_codepage_out = strtol (optarg, (char **) NULL, 10); 1063 break; 1064 case '?': 1065 case 'H': 1066 usage (stdout, 0); 1067 break; 1068 case 'V': 1069 print_version ("windmc"); 1070 break; 1071 1072 default: 1073 usage (stderr, 1); 1074 break; 1075 } 1076 } 1077 if (input_filename == NULL && optind < argc) 1078 { 1079 input_filename = argv[optind]; 1080 ++optind; 1081 } 1082 1083 set_endianess (NULL, target); 1084 1085 if (input_filename == NULL) 1086 { 1087 fprintf (stderr, "Error: No input file was specified.\n"); 1088 usage (stderr, 1); 1089 } 1090 mc_set_inputfile (input_filename); 1091 1092 if (!probe_codepage (&mcset_codepage_in, &mcset_text_in_is_unicode, "codepage_in", 0)) 1093 usage (stderr, 1); 1094 if (mcset_codepage_out == 0) 1095 mcset_codepage_out = 1252; 1096 if (! unicode_is_valid_codepage (mcset_codepage_out)) 1097 fatal ("Code page 0x%x is unknown.", (unsigned int) mcset_codepage_out); 1098 if (mcset_codepage_out == CP_UTF16) 1099 fatal ("UTF16 is no valid text output code page."); 1100 if (verbose) 1101 { 1102 fprintf (stderr, "// Default target is %s and it is %s endian.\n", 1103 def_target_arch, (target_is_bigendian ? "big" : "little")); 1104 fprintf (stderr, "// Input codepage: 0x%x\n", (unsigned int) mcset_codepage_in); 1105 fprintf (stderr, "// Output codepage: 0x%x\n", (unsigned int) mcset_codepage_out); 1106 } 1107 1108 if (argc != optind) 1109 usage (stderr, 1); 1110 1111 /* Initialize mcset_mc_basename. */ 1112 { 1113 const char *bn, *bn2; 1114 char *hsz; 1115 1116 bn = strrchr (input_filename, '/'); 1117 bn2 = strrchr (input_filename, '\\'); 1118 if (! bn) 1119 bn = bn2; 1120 if (bn && bn2 && bn < bn2) 1121 bn = bn2; 1122 if (! bn) 1123 bn = input_filename; 1124 else 1125 bn++; 1126 mcset_mc_basename = hsz = xstrdup (bn); 1127 1128 /* Cut of right-hand extension. */ 1129 if ((hsz = strrchr (hsz, '.')) != NULL) 1130 *hsz = 0; 1131 } 1132 1133 /* Load the input file and do code page transformations to UTF16. */ 1134 { 1135 unichar *u; 1136 rc_uint_type ul; 1137 char *buff; 1138 long flen; 1139 FILE *fp = fopen (input_filename, "rb"); 1140 1141 if (!fp) 1142 fatal (_("unable to open file ,%s' for input.\n"), input_filename); 1143 1144 fseek (fp, 0, SEEK_END); 1145 flen = ftell (fp); 1146 fseek (fp, 0, SEEK_SET); 1147 buff = malloc (flen + 3); 1148 memset (buff, 0, flen + 3); 1149 fread (buff, 1, flen, fp); 1150 fclose (fp); 1151 if (mcset_text_in_is_unicode != 1) 1152 { 1153 unicode_from_codepage (&ul, &u, buff, mcset_codepage_in); 1154 if (! u) 1155 fatal ("Failed to convert input to UFT16\n"); 1156 mc_set_content (u); 1157 } 1158 else 1159 { 1160 if ((flen & 1) != 0) 1161 fatal (_("input file does not seems to be UFT16.\n")); 1162 mc_set_content ((unichar *) buff); 1163 } 1164 free (buff); 1165 } 1166 1167 while (yyparse ()) 1168 ; 1169 1170 do_sorts (); 1171 1172 h_fp = mc_create_path_text_file (mcset_header_dir, mcset_header_ext); 1173 write_header (h_fp); 1174 fclose (h_fp); 1175 1176 h_fp = mc_create_path_text_file (mcset_rc_dir, ".rc"); 1177 write_rc (h_fp); 1178 fclose (h_fp); 1179 1180 if (mcset_dbg_dir != NULL) 1181 { 1182 h_fp = mc_create_path_text_file (mcset_dbg_dir, ".dbg"); 1183 write_dbg (h_fp); 1184 fclose (h_fp); 1185 } 1186 write_bin (); 1187 1188 if (mc_nodes_lang) 1189 free (mc_nodes_lang); 1190 if (mc_severity_codes) 1191 free (mc_severity_codes); 1192 if (mc_facility_codes) 1193 free (mc_facility_codes); 1194 1195 xexit (0); 1196 return 0; 1197} 1198