1/* windmc.c -- a program to compile Windows message files. 2 Copyright 2007 3 Free Software Foundation, Inc. 4 Written by Kai Tietz, Onevision. 5 6 This file is part of GNU Binutils. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 21 02110-1301, USA. */ 22 23/* This program can read and comile Windows message format. 24 25 It is based on information taken from the following sources: 26 27 * Microsoft documentation. 28 29 * The wmc program, written by Bertho A. Stultiens (BS). */ 30 31#include "sysdep.h" 32#include <assert.h> 33#include <time.h> 34#include "bfd.h" 35#include "getopt.h" 36#include "bucomm.h" 37#include "libiberty.h" 38#include "safe-ctype.h" 39#include "obstack.h" 40 41#include "windmc.h" 42#include "windint.h" 43 44/* Defines a message compiler element item with length and offset 45 information. */ 46typedef struct mc_msg_item 47{ 48 rc_uint_type res_len; 49 rc_uint_type res_off; 50 struct bin_messagetable_item *res; 51} mc_msg_item; 52 53/* Defined in bfd/binary.c. Used to set architecture and machine of input 54 binary files. */ 55extern enum bfd_architecture bfd_external_binary_architecture; 56extern unsigned long bfd_external_machine; 57 58int target_is_bigendian = 0; 59const char *def_target_arch; 60 61/* Globals and static variable definitions. */ 62 63/* bfd global helper struct variable. */ 64static struct 65{ 66 bfd *abfd; 67 asection *sec; 68} mc_bfd; 69 70/* Memory list. */ 71mc_node *mc_nodes = NULL; 72static mc_node_lang **mc_nodes_lang = NULL; 73static int mc_nodes_lang_count = 0; 74static mc_keyword **mc_severity_codes = NULL; 75static int mc_severity_codes_count = 0; 76static mc_keyword **mc_facility_codes = NULL; 77static int mc_facility_codes_count = 0; 78 79/* When we are building a resource tree, we allocate everything onto 80 an obstack, so that we can free it all at once if we want. */ 81#define obstack_chunk_alloc xmalloc 82#define obstack_chunk_free free 83 84/* The resource building obstack. */ 85static struct obstack res_obstack; 86 87/* Flag variables. */ 88/* Set by -C. Set the default code page to be used for input text file. */ 89static rc_uint_type mcset_codepage_in = 0; 90 91/* Set by -O. Set the default code page to be used for output text files. */ 92static rc_uint_type mcset_codepage_out = 0; 93 94/* Set by -b. .BIN filename should have .mc filename_ included for uniqueness. */ 95static int mcset_prefix_bin = 0; 96 97/* The base name of the .mc file. */ 98static const char *mcset_mc_basename = "unknown"; 99 100/* Set by -e <ext>. Specify the extension for the header file. */ 101static const char *mcset_header_ext = ".h"; 102 103/* Set by -h <path>. Gives the path of where to create the C include file. */ 104static const char *mcset_header_dir = "./"; 105 106/* Set by -r <path>. Gives the path of where to create the RC include file 107 and the binary message resource files it includes. */ 108static const char *mcset_rc_dir = "./"; 109 110/* Modified by -a & -u. By -u input file is unicode, by -a is ASCII (default). */ 111static int mcset_text_in_is_unicode = 0; 112 113/* Modified by -A & -U. By -U bin file is unicode (default), by -A is ASCII. */ 114static int mcset_bin_out_is_unicode = 1; 115 116/* Set by -c. Sets the Customer bit in all the message ID's. */ 117int mcset_custom_bit = 0; 118 119/* Set by -o. Generate OLE2 header file. Use HRESULT definition instead of 120 status code definition. */ 121static int mcset_use_hresult = 0; 122 123/* Set by -m <msglen>. Generate a warning if the size of any message exceeds 124 maxmsglen characters. */ 125rc_uint_type mcset_max_message_length = 0; 126 127/* Set by -d. Sets message values in header to decimal initially. */ 128int mcset_out_values_are_decimal = 0; 129 130/* Set by -n. terminates all strings with null's in the message tables. */ 131static int mcset_automatic_null_termination = 0; 132 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