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