Back to index

cell-binutils  2.17cvs20070401
windres.c
Go to the documentation of this file.
00001 /* windres.c -- a program to manipulate Windows resources
00002    Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
00003    Free Software Foundation, Inc.
00004    Written by Ian Lance Taylor, Cygnus Support.
00005 
00006    This file is part of GNU Binutils.
00007 
00008    This program is free software; you can redistribute it and/or modify
00009    it under the terms of the GNU General Public License as published by
00010    the Free Software Foundation; either version 2 of the License, or
00011    (at your option) any later version.
00012 
00013    This program is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016    GNU General Public License for more details.
00017 
00018    You should have received a copy of the GNU General Public License
00019    along with this program; if not, write to the Free Software
00020    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
00021    02110-1301, USA.  */
00022 
00023 /* This program can read and write Windows resources in various
00024    formats.  In particular, it can act like the rc resource compiler
00025    program, and it can act like the cvtres res to COFF conversion
00026    program.
00027 
00028    It is based on information taken from the following sources:
00029 
00030    * Microsoft documentation.
00031 
00032    * The rcl program, written by Gunther Ebert
00033      <gunther.ebert@ixos-leipzig.de>.
00034 
00035    * The res2coff program, written by Pedro A. Aranda <paag@tid.es>.  */
00036 
00037 #include "config.h"
00038 #ifdef HAVE_UNISTD_H
00039 #include <unistd.h>
00040 #endif
00041 #include <assert.h>
00042 #include <time.h>
00043 #include "bfd.h"
00044 #include "getopt.h"
00045 #include "bucomm.h"
00046 #include "libiberty.h"
00047 #include "safe-ctype.h"
00048 #include "obstack.h"
00049 #include "windres.h"
00050 
00051 /* Used by resrc.c at least.  */
00052 
00053 int verbose = 0;
00054 
00055 /* An enumeration of format types.  */
00056 
00057 enum res_format
00058 {
00059   /* Unknown format.  */
00060   RES_FORMAT_UNKNOWN,
00061   /* Textual RC file.  */
00062   RES_FORMAT_RC,
00063   /* Binary RES file.  */
00064   RES_FORMAT_RES,
00065   /* COFF file.  */
00066   RES_FORMAT_COFF
00067 };
00068 
00069 /* A structure used to map between format types and strings.  */
00070 
00071 struct format_map
00072 {
00073   const char *name;
00074   enum res_format format;
00075 };
00076 
00077 /* A mapping between names and format types.  */
00078 
00079 static const struct format_map format_names[] =
00080 {
00081   { "rc", RES_FORMAT_RC },
00082   { "res", RES_FORMAT_RES },
00083   { "coff", RES_FORMAT_COFF },
00084   { NULL, RES_FORMAT_UNKNOWN }
00085 };
00086 
00087 /* A mapping from file extensions to format types.  */
00088 
00089 static const struct format_map format_fileexts[] =
00090 {
00091   { "rc", RES_FORMAT_RC },
00092   { "res", RES_FORMAT_RES },
00093   { "exe", RES_FORMAT_COFF },
00094   { "obj", RES_FORMAT_COFF },
00095   { "o", RES_FORMAT_COFF },
00096   { NULL, RES_FORMAT_UNKNOWN }
00097 };
00098 
00099 /* A list of include directories.  */
00100 
00101 struct include_dir
00102 {
00103   struct include_dir *next;
00104   char *dir;
00105 };
00106 
00107 static struct include_dir *include_dirs;
00108 
00109 /* Static functions.  */
00110 
00111 static void res_init (void);
00112 static int extended_menuitems (const struct menuitem *);
00113 static enum res_format format_from_name (const char *, int);
00114 static enum res_format format_from_filename (const char *, int);
00115 static void usage (FILE *, int);
00116 static int cmp_res_entry (const void *, const void *);
00117 static struct res_directory *sort_resources (struct res_directory *);
00118 static void reswr_init (void);
00119 static const char * quot (const char *);
00120 
00121 /* When we are building a resource tree, we allocate everything onto
00122    an obstack, so that we can free it all at once if we want.  */
00123 
00124 #define obstack_chunk_alloc xmalloc
00125 #define obstack_chunk_free free
00126 
00127 /* The resource building obstack.  */
00128 
00129 static struct obstack res_obstack;
00130 
00131 /* Initialize the resource building obstack.  */
00132 
00133 static void
00134 res_init (void)
00135 {
00136   obstack_init (&res_obstack);
00137 }
00138 
00139 /* Allocate space on the resource building obstack.  */
00140 
00141 void *
00142 res_alloc (size_t bytes)
00143 {
00144   return (void *) obstack_alloc (&res_obstack, bytes);
00145 }
00146 
00147 /* We also use an obstack to save memory used while writing out a set
00148    of resources.  */
00149 
00150 static struct obstack reswr_obstack;
00151 
00152 /* Initialize the resource writing obstack.  */
00153 
00154 static void
00155 reswr_init (void)
00156 {
00157   obstack_init (&reswr_obstack);
00158 }
00159 
00160 /* Allocate space on the resource writing obstack.  */
00161 
00162 void *
00163 reswr_alloc (size_t bytes)
00164 {
00165   return (void *) obstack_alloc (&reswr_obstack, bytes);
00166 }
00167 
00168 /* Open a file using the include directory search list.  */
00169 
00170 FILE *
00171 open_file_search (const char *filename, const char *mode, const char *errmsg,
00172                 char **real_filename)
00173 {
00174   FILE *e;
00175   struct include_dir *d;
00176 
00177   e = fopen (filename, mode);
00178   if (e != NULL)
00179     {
00180       *real_filename = xstrdup (filename);
00181       return e;
00182     }
00183 
00184   if (errno == ENOENT)
00185     {
00186       for (d = include_dirs; d != NULL; d = d->next)
00187        {
00188          char *n;
00189 
00190          n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2);
00191          sprintf (n, "%s/%s", d->dir, filename);
00192          e = fopen (n, mode);
00193          if (e != NULL)
00194            {
00195              *real_filename = n;
00196              return e;
00197            }
00198 
00199          if (errno != ENOENT)
00200            break;
00201        }
00202     }
00203 
00204   fatal (_("can't open %s `%s': %s"), errmsg, filename, strerror (errno));
00205 
00206   /* Return a value to avoid a compiler warning.  */
00207   return NULL;
00208 }
00209 
00210 /* Compare two resource ID's.  We consider name entries to come before
00211    numeric entries, because that is how they appear in the COFF .rsrc
00212    section.  */
00213 
00214 int
00215 res_id_cmp (struct res_id a, struct res_id b)
00216 {
00217   if (! a.named)
00218     {
00219       if (b.named)
00220        return 1;
00221       if (a.u.id > b.u.id)
00222        return 1;
00223       else if (a.u.id < b.u.id)
00224        return -1;
00225       else
00226        return 0;
00227     }
00228   else
00229     {
00230       unichar *as, *ase, *bs, *bse;
00231 
00232       if (! b.named)
00233        return -1;
00234 
00235       as = a.u.n.name;
00236       ase = as + a.u.n.length;
00237       bs = b.u.n.name;
00238       bse = bs + b.u.n.length;
00239 
00240       while (as < ase)
00241        {
00242          int i;
00243 
00244          if (bs >= bse)
00245            return 1;
00246          i = (int) *as - (int) *bs;
00247          if (i != 0)
00248            return i;
00249          ++as;
00250          ++bs;
00251        }
00252 
00253       if (bs < bse)
00254        return -1;
00255 
00256       return 0;
00257     }
00258 }
00259 
00260 /* Print a resource ID.  */
00261 
00262 void
00263 res_id_print (FILE *stream, struct res_id id, int quote)
00264 {
00265   if (! id.named)
00266     fprintf (stream, "%lu", id.u.id);
00267   else
00268     {
00269       if (quote)
00270        putc ('"', stream);
00271       unicode_print (stream, id.u.n.name, id.u.n.length);
00272       if (quote)
00273        putc ('"', stream);
00274     }
00275 }
00276 
00277 /* Print a list of resource ID's.  */
00278 
00279 void
00280 res_ids_print (FILE *stream, int cids, const struct res_id *ids)
00281 {
00282   int i;
00283 
00284   for (i = 0; i < cids; i++)
00285     {
00286       res_id_print (stream, ids[i], 1);
00287       if (i + 1 < cids)
00288        fprintf (stream, ": ");
00289     }
00290 }
00291 
00292 /* Convert an ASCII string to a resource ID.  */
00293 
00294 void
00295 res_string_to_id (struct res_id *res_id, const char *string)
00296 {
00297   res_id->named = 1;
00298   unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
00299 }
00300 
00301 /* Define a resource.  The arguments are the resource tree, RESOURCES,
00302    and the location at which to put it in the tree, CIDS and IDS.
00303    This returns a newly allocated res_resource structure, which the
00304    caller is expected to initialize.  If DUPOK is non-zero, then if a
00305    resource with this ID exists, it is returned.  Otherwise, a warning
00306    is issued, and a new resource is created replacing the existing
00307    one.  */
00308 
00309 struct res_resource *
00310 define_resource (struct res_directory **resources, int cids,
00311                const struct res_id *ids, int dupok)
00312 {
00313   struct res_entry *re = NULL;
00314   int i;
00315 
00316   assert (cids > 0);
00317   for (i = 0; i < cids; i++)
00318     {
00319       struct res_entry **pp;
00320 
00321       if (*resources == NULL)
00322        {
00323          static unsigned long timeval;
00324 
00325          /* Use the same timestamp for every resource created in a
00326              single run.  */
00327          if (timeval == 0)
00328            timeval = time (NULL);
00329 
00330          *resources = ((struct res_directory *)
00331                      res_alloc (sizeof **resources));
00332          (*resources)->characteristics = 0;
00333          (*resources)->time = timeval;
00334          (*resources)->major = 0;
00335          (*resources)->minor = 0;
00336          (*resources)->entries = NULL;
00337        }
00338 
00339       for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
00340        if (res_id_cmp ((*pp)->id, ids[i]) == 0)
00341          break;
00342 
00343       if (*pp != NULL)
00344        re = *pp;
00345       else
00346        {
00347          re = (struct res_entry *) res_alloc (sizeof *re);
00348          re->next = NULL;
00349          re->id = ids[i];
00350          if ((i + 1) < cids)
00351            {
00352              re->subdir = 1;
00353              re->u.dir = NULL;
00354            }
00355          else
00356            {
00357              re->subdir = 0;
00358              re->u.res = NULL;
00359            }
00360 
00361          *pp = re;
00362        }
00363 
00364       if ((i + 1) < cids)
00365        {
00366          if (! re->subdir)
00367            {
00368              fprintf (stderr, "%s: ", program_name);
00369              res_ids_print (stderr, i, ids);
00370              fprintf (stderr, _(": expected to be a directory\n"));
00371              xexit (1);
00372            }
00373 
00374          resources = &re->u.dir;
00375        }
00376     }
00377 
00378   if (re->subdir)
00379     {
00380       fprintf (stderr, "%s: ", program_name);
00381       res_ids_print (stderr, cids, ids);
00382       fprintf (stderr, _(": expected to be a leaf\n"));
00383       xexit (1);
00384     }
00385 
00386   if (re->u.res != NULL)
00387     {
00388       if (dupok)
00389        return re->u.res;
00390 
00391       fprintf (stderr, _("%s: warning: "), program_name);
00392       res_ids_print (stderr, cids, ids);
00393       fprintf (stderr, _(": duplicate value\n"));
00394     }
00395 
00396   re->u.res = ((struct res_resource *)
00397               res_alloc (sizeof (struct res_resource)));
00398   memset (re->u.res, 0, sizeof (struct res_resource));
00399 
00400   re->u.res->type = RES_TYPE_UNINITIALIZED;
00401   return re->u.res;
00402 }
00403 
00404 /* Define a standard resource.  This is a version of define_resource
00405    that just takes type, name, and language arguments.  */
00406 
00407 struct res_resource *
00408 define_standard_resource (struct res_directory **resources, int type,
00409                        struct res_id name, int language, int dupok)
00410 {
00411   struct res_id a[3];
00412 
00413   a[0].named = 0;
00414   a[0].u.id = type;
00415   a[1] = name;
00416   a[2].named = 0;
00417   a[2].u.id = language;
00418   return define_resource (resources, 3, a, dupok);
00419 }
00420 
00421 /* Comparison routine for resource sorting.  */
00422 
00423 static int
00424 cmp_res_entry (const void *p1, const void *p2)
00425 {
00426   const struct res_entry **re1, **re2;
00427 
00428   re1 = (const struct res_entry **) p1;
00429   re2 = (const struct res_entry **) p2;
00430   return res_id_cmp ((*re1)->id, (*re2)->id);
00431 }
00432 
00433 /* Sort the resources.  */
00434 
00435 static struct res_directory *
00436 sort_resources (struct res_directory *resdir)
00437 {
00438   int c, i;
00439   struct res_entry *re;
00440   struct res_entry **a;
00441 
00442   if (resdir->entries == NULL)
00443     return resdir;
00444 
00445   c = 0;
00446   for (re = resdir->entries; re != NULL; re = re->next)
00447     ++c;
00448 
00449   /* This is a recursive routine, so using xmalloc is probably better
00450      than alloca.  */
00451   a = (struct res_entry **) xmalloc (c * sizeof (struct res_entry *));
00452 
00453   for (i = 0, re = resdir->entries; re != NULL; re = re->next, i++)
00454     a[i] = re;
00455 
00456   qsort (a, c, sizeof (struct res_entry *), cmp_res_entry);
00457 
00458   resdir->entries = a[0];
00459   for (i = 0; i < c - 1; i++)
00460     a[i]->next = a[i + 1];
00461   a[i]->next = NULL;
00462 
00463   free (a);
00464 
00465   /* Now sort the subdirectories.  */
00466 
00467   for (re = resdir->entries; re != NULL; re = re->next)
00468     if (re->subdir)
00469       re->u.dir = sort_resources (re->u.dir);
00470 
00471   return resdir;
00472 }
00473 
00474 /* Return whether the dialog resource DIALOG is a DIALOG or a
00475    DIALOGEX.  */
00476 
00477 int
00478 extended_dialog (const struct dialog *dialog)
00479 {
00480   const struct dialog_control *c;
00481 
00482   if (dialog->ex != NULL)
00483     return 1;
00484 
00485   for (c = dialog->controls; c != NULL; c = c->next)
00486     if (c->data != NULL || c->help != 0)
00487       return 1;
00488 
00489   return 0;
00490 }
00491 
00492 /* Return whether MENUITEMS are a MENU or a MENUEX.  */
00493 
00494 int
00495 extended_menu (const struct menu *menu)
00496 {
00497   return extended_menuitems (menu->items);
00498 }
00499 
00500 static int
00501 extended_menuitems (const struct menuitem *menuitems)
00502 {
00503   const struct menuitem *mi;
00504 
00505   for (mi = menuitems; mi != NULL; mi = mi->next)
00506     {
00507       if (mi->help != 0 || mi->state != 0)
00508        return 1;
00509       if (mi->popup != NULL && mi->id != 0)
00510        return 1;
00511       if ((mi->type
00512           & ~ (MENUITEM_CHECKED
00513               | MENUITEM_GRAYED
00514               | MENUITEM_HELP
00515               | MENUITEM_INACTIVE
00516               | MENUITEM_MENUBARBREAK
00517               | MENUITEM_MENUBREAK))
00518          != 0)
00519        return 1;
00520       if (mi->popup != NULL)
00521        {
00522          if (extended_menuitems (mi->popup))
00523            return 1;
00524        }
00525     }
00526 
00527   return 0;
00528 }
00529 
00530 /* Convert a string to a format type, or exit if it can't be done.  */
00531 
00532 static enum res_format
00533 format_from_name (const char *name, int exit_on_error)
00534 {
00535   const struct format_map *m;
00536 
00537   for (m = format_names; m->name != NULL; m++)
00538     if (strcasecmp (m->name, name) == 0)
00539       break;
00540 
00541   if (m->name == NULL && exit_on_error)
00542     {
00543       non_fatal (_("unknown format type `%s'"), name);
00544       fprintf (stderr, _("%s: supported formats:"), program_name);
00545       for (m = format_names; m->name != NULL; m++)
00546        fprintf (stderr, " %s", m->name);
00547       fprintf (stderr, "\n");
00548       xexit (1);
00549     }
00550 
00551   return m->format;
00552 }
00553 
00554 /* Work out a format type given a file name.  If INPUT is non-zero,
00555    it's OK to look at the file itself.  */
00556 
00557 static enum res_format
00558 format_from_filename (const char *filename, int input)
00559 {
00560   const char *ext;
00561   FILE *e;
00562   unsigned char b1, b2, b3, b4, b5;
00563   int magic;
00564 
00565   /* If we have an extension, see if we recognize it as implying a
00566      particular format.  */
00567   ext = strrchr (filename, '.');
00568   if (ext != NULL)
00569     {
00570       const struct format_map *m;
00571 
00572       ++ext;
00573       for (m = format_fileexts; m->name != NULL; m++)
00574        if (strcasecmp (m->name, ext) == 0)
00575          return m->format;
00576     }
00577 
00578   /* If we don't recognize the name of an output file, assume it's a
00579      COFF file.  */
00580   if (! input)
00581     return RES_FORMAT_COFF;
00582 
00583   /* Read the first few bytes of the file to see if we can guess what
00584      it is.  */
00585   e = fopen (filename, FOPEN_RB);
00586   if (e == NULL)
00587     fatal ("%s: %s", filename, strerror (errno));
00588 
00589   b1 = getc (e);
00590   b2 = getc (e);
00591   b3 = getc (e);
00592   b4 = getc (e);
00593   b5 = getc (e);
00594 
00595   fclose (e);
00596 
00597   /* A PE executable starts with 0x4d 0x5a.  */
00598   if (b1 == 0x4d && b2 == 0x5a)
00599     return RES_FORMAT_COFF;
00600 
00601   /* A COFF .o file starts with a COFF magic number.  */
00602   magic = (b2 << 8) | b1;
00603   switch (magic)
00604     {
00605     case 0x14c: /* i386 */
00606     case 0x166: /* MIPS */
00607     case 0x184: /* Alpha */
00608     case 0x268: /* 68k */
00609     case 0x1f0: /* PowerPC */
00610     case 0x290: /* PA */
00611       return RES_FORMAT_COFF;
00612     }
00613 
00614   /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0.  */
00615   if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20)
00616     return RES_FORMAT_RES;
00617 
00618   /* If every character is printable or space, assume it's an RC file.  */
00619   if ((ISPRINT (b1) || ISSPACE (b1))
00620       && (ISPRINT (b2) || ISSPACE (b2))
00621       && (ISPRINT (b3) || ISSPACE (b3))
00622       && (ISPRINT (b4) || ISSPACE (b4))
00623       && (ISPRINT (b5) || ISSPACE (b5)))
00624     return RES_FORMAT_RC;
00625 
00626   /* Otherwise, we give up.  */
00627   fatal (_("can not determine type of file `%s'; use the -J option"),
00628         filename);
00629 
00630   /* Return something to silence the compiler warning.  */
00631   return RES_FORMAT_UNKNOWN;
00632 }
00633 
00634 /* Print a usage message and exit.  */
00635 
00636 static void
00637 usage (FILE *stream, int status)
00638 {
00639   fprintf (stream, _("Usage: %s [option(s)] [input-file] [output-file]\n"),
00640           program_name);
00641   fprintf (stream, _(" The options are:\n\
00642   -i --input=<file>            Name input file\n\
00643   -o --output=<file>           Name output file\n\
00644   -J --input-format=<format>   Specify input format\n\
00645   -O --output-format=<format>  Specify output format\n\
00646   -F --target=<target>         Specify COFF target\n\
00647      --preprocessor=<program>  Program to use to preprocess rc file\n\
00648   -I --include-dir=<dir>       Include directory when preprocessing rc file\n\
00649   -D --define <sym>[=<val>]    Define SYM when preprocessing rc file\n\
00650   -U --undefine <sym>          Undefine SYM when preprocessing rc file\n\
00651   -v --verbose                 Verbose - tells you what it's doing\n\
00652   -l --language=<val>          Set language when reading rc file\n\
00653      --use-temp-file           Use a temporary file instead of popen to read\n\
00654                                the preprocessor output\n\
00655      --no-use-temp-file        Use popen (default)\n"));
00656 #ifdef YYDEBUG
00657   fprintf (stream, _("\
00658      --yydebug                 Turn on parser debugging\n"));
00659 #endif
00660   fprintf (stream, _("\
00661   -r                           Ignored for compatibility with rc\n\
00662   @<file>                      Read options from <file>\n\
00663   -h --help                    Print this help message\n\
00664   -V --version                 Print version information\n"));
00665   fprintf (stream, _("\
00666 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
00667 extension if not specified.  A single file name is an input file.\n\
00668 No input-file is stdin, default rc.  No output-file is stdout, default rc.\n"));
00669 
00670   list_supported_targets (program_name, stream);
00671 
00672   if (REPORT_BUGS_TO[0] && status == 0)
00673     fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
00674 
00675   exit (status);
00676 }
00677 
00678 /* Quote characters that will confuse the shell when we run the preprocessor.  */
00679 
00680 static const char *
00681 quot (const char *string)
00682 {
00683   static char *buf = 0;
00684   static int buflen = 0;
00685   int slen = strlen (string);
00686   const char *src;
00687   char *dest;
00688 
00689   if ((buflen < slen * 2 + 2) || !buf)
00690     {
00691       buflen = slen * 2 + 2;
00692       if (buf)
00693        free (buf);
00694       buf = (char *) xmalloc (buflen);
00695     }
00696 
00697   for (src=string, dest=buf; *src; src++, dest++)
00698     {
00699       if (*src == '(' || *src == ')' || *src == ' ')
00700        *dest++ = '\\';
00701       *dest = *src;
00702     }
00703   *dest = 0;
00704   return buf;
00705 }
00706 
00707 /* Long options.  */
00708 
00709 /* 150 isn't special; it's just an arbitrary non-ASCII char value.  */
00710 
00711 #define OPTION_PREPROCESSOR 150
00712 #define OPTION_USE_TEMP_FILE       (OPTION_PREPROCESSOR + 1)
00713 #define OPTION_NO_USE_TEMP_FILE    (OPTION_USE_TEMP_FILE + 1)
00714 #define OPTION_YYDEBUG             (OPTION_NO_USE_TEMP_FILE + 1)
00715 
00716 static const struct option long_options[] =
00717 {
00718   {"input", required_argument, 0, 'i'},
00719   {"output", required_argument, 0, 'o'},
00720   {"input-format", required_argument, 0, 'J'},
00721   {"output-format", required_argument, 0, 'O'},
00722   {"target", required_argument, 0, 'F'},
00723   {"preprocessor", required_argument, 0, OPTION_PREPROCESSOR},
00724   {"include-dir", required_argument, 0, 'I'},
00725   {"define", required_argument, 0, 'D'},
00726   {"undefine", required_argument, 0, 'U'},
00727   {"verbose", no_argument, 0, 'v'},
00728   {"language", required_argument, 0, 'l'},
00729   {"use-temp-file", no_argument, 0, OPTION_USE_TEMP_FILE},
00730   {"no-use-temp-file", no_argument, 0, OPTION_NO_USE_TEMP_FILE},
00731   {"yydebug", no_argument, 0, OPTION_YYDEBUG},
00732   {"version", no_argument, 0, 'V'},
00733   {"help", no_argument, 0, 'h'},
00734   {0, no_argument, 0, 0}
00735 };
00736 
00737 /* This keeps gcc happy when using -Wmissing-prototypes -Wstrict-prototypes.  */
00738 int main (int, char **);
00739 
00740 /* The main function.  */
00741 
00742 int
00743 main (int argc, char **argv)
00744 {
00745   int c;
00746   char *input_filename;
00747   char *output_filename;
00748   enum res_format input_format;
00749   enum res_format input_format_tmp;
00750   enum res_format output_format;
00751   char *target;
00752   char *preprocessor;
00753   char *preprocargs;
00754   const char *quotedarg;
00755   int language;
00756   struct res_directory *resources;
00757   int use_temp_file;
00758 
00759 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
00760   setlocale (LC_MESSAGES, "");
00761 #endif
00762 #if defined (HAVE_SETLOCALE)
00763   setlocale (LC_CTYPE, "");
00764 #endif
00765   bindtextdomain (PACKAGE, LOCALEDIR);
00766   textdomain (PACKAGE);
00767 
00768   program_name = argv[0];
00769   xmalloc_set_program_name (program_name);
00770 
00771   expandargv (&argc, &argv);
00772 
00773   bfd_init ();
00774   set_default_bfd_target ();
00775 
00776   res_init ();
00777 
00778   input_filename = NULL;
00779   output_filename = NULL;
00780   input_format = RES_FORMAT_UNKNOWN;
00781   output_format = RES_FORMAT_UNKNOWN;
00782   target = NULL;
00783   preprocessor = NULL;
00784   preprocargs = NULL;
00785   language = 0x409;   /* LANG_ENGLISH, SUBLANG_ENGLISH_US.  */
00786   use_temp_file = 0;
00787 
00788   while ((c = getopt_long (argc, argv, "f:i:l:o:I:J:O:F:D:U:rhHvV", long_options,
00789                         (int *) 0)) != EOF)
00790     {
00791       switch (c)
00792        {
00793        case 'i':
00794          input_filename = optarg;
00795          break;
00796 
00797        case 'f':
00798          /* For compatibility with rc we accept "-fo <name>" as being the
00799             equivalent of "-o <name>".  We do not advertise this fact
00800             though, as we do not want users to use non-GNU like command
00801             line switches.  */
00802          if (*optarg != 'o')
00803            fatal (_("invalid option -f\n"));
00804          optarg++;
00805          if (* optarg == 0)
00806            {
00807              if (optind == argc)
00808               fatal (_("No filename following the -fo option.\n"));
00809              optarg = argv [optind++];
00810            }
00811          /* Fall through.  */
00812 
00813        case 'o':
00814          output_filename = optarg;
00815          break;
00816 
00817        case 'J':
00818          input_format = format_from_name (optarg, 1);
00819          break;
00820 
00821        case 'O':
00822          output_format = format_from_name (optarg, 1);
00823          break;
00824 
00825        case 'F':
00826          target = optarg;
00827          break;
00828 
00829        case OPTION_PREPROCESSOR:
00830          preprocessor = optarg;
00831          break;
00832 
00833        case 'D':
00834        case 'U':
00835          if (preprocargs == NULL)
00836            {
00837              quotedarg = quot (optarg);
00838              preprocargs = xmalloc (strlen (quotedarg) + 3);
00839              sprintf (preprocargs, "-%c%s", c, quotedarg);
00840            }
00841          else
00842            {
00843              char *n;
00844 
00845              quotedarg = quot (optarg);
00846              n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
00847              sprintf (n, "%s -%c%s", preprocargs, c, quotedarg);
00848              free (preprocargs);
00849              preprocargs = n;
00850            }
00851          break;
00852 
00853        case 'r':
00854          /* Ignored for compatibility with rc.  */
00855          break;
00856 
00857        case 'v':
00858          verbose ++;
00859          break;
00860 
00861        case 'I':
00862          /* For backward compatibility, should be removed in the future.  */
00863          input_format_tmp = format_from_name (optarg, 0);
00864          if (input_format_tmp != RES_FORMAT_UNKNOWN)
00865            {
00866              fprintf (stderr, _("Option -I is deprecated for setting the input format, please use -J instead.\n"));
00867              input_format = input_format_tmp;
00868              break;
00869            }
00870 
00871          if (preprocargs == NULL)
00872            {
00873              quotedarg = quot (optarg);
00874              preprocargs = xmalloc (strlen (quotedarg) + 3);
00875              sprintf (preprocargs, "-I%s", quotedarg);
00876            }
00877          else
00878            {
00879              char *n;
00880 
00881              quotedarg = quot (optarg);
00882              n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
00883              sprintf (n, "%s -I%s", preprocargs, quotedarg);
00884              free (preprocargs);
00885              preprocargs = n;
00886            }
00887 
00888          {
00889            struct include_dir *n, **pp;
00890 
00891            n = (struct include_dir *) xmalloc (sizeof *n);
00892            n->next = NULL;
00893            n->dir = optarg;
00894 
00895            for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
00896              ;
00897            *pp = n;
00898          }
00899 
00900          break;
00901 
00902        case 'l':
00903          language = strtol (optarg, (char **) NULL, 16);
00904          break;
00905 
00906        case OPTION_USE_TEMP_FILE:
00907          use_temp_file = 1;
00908          break;
00909 
00910        case OPTION_NO_USE_TEMP_FILE:
00911          use_temp_file = 0;
00912          break;
00913 
00914 #ifdef YYDEBUG
00915        case OPTION_YYDEBUG:
00916          yydebug = 1;
00917          break;
00918 #endif
00919 
00920        case 'h':
00921        case 'H':
00922          usage (stdout, 0);
00923          break;
00924 
00925        case 'V':
00926          print_version ("windres");
00927          break;
00928 
00929        default:
00930          usage (stderr, 1);
00931          break;
00932        }
00933     }
00934 
00935   if (input_filename == NULL && optind < argc)
00936     {
00937       input_filename = argv[optind];
00938       ++optind;
00939     }
00940 
00941   if (output_filename == NULL && optind < argc)
00942     {
00943       output_filename = argv[optind];
00944       ++optind;
00945     }
00946 
00947   if (argc != optind)
00948     usage (stderr, 1);
00949 
00950   if (input_format == RES_FORMAT_UNKNOWN)
00951     {
00952       if (input_filename == NULL)
00953        input_format = RES_FORMAT_RC;
00954       else
00955        input_format = format_from_filename (input_filename, 1);
00956     }
00957 
00958   if (output_format == RES_FORMAT_UNKNOWN)
00959     {
00960       if (output_filename == NULL)
00961        output_format = RES_FORMAT_RC;
00962       else
00963        output_format = format_from_filename (output_filename, 0);
00964     }
00965 
00966   /* Read the input file.  */
00967   switch (input_format)
00968     {
00969     default:
00970       abort ();
00971     case RES_FORMAT_RC:
00972       resources = read_rc_file (input_filename, preprocessor, preprocargs,
00973                             language, use_temp_file);
00974       break;
00975     case RES_FORMAT_RES:
00976       resources = read_res_file (input_filename);
00977       break;
00978     case RES_FORMAT_COFF:
00979       resources = read_coff_rsrc (input_filename, target);
00980       break;
00981     }
00982 
00983   if (resources == NULL)
00984     fatal (_("no resources"));
00985 
00986   /* Sort the resources.  This is required for COFF, convenient for
00987      rc, and unimportant for res.  */
00988   resources = sort_resources (resources);
00989 
00990   /* Write the output file.  */
00991   reswr_init ();
00992 
00993   switch (output_format)
00994     {
00995     default:
00996       abort ();
00997     case RES_FORMAT_RC:
00998       write_rc_file (output_filename, resources);
00999       break;
01000     case RES_FORMAT_RES:
01001       write_res_file (output_filename, resources);
01002       break;
01003     case RES_FORMAT_COFF:
01004       write_coff_file (output_filename, target, resources);
01005       break;
01006     }
01007 
01008   xexit (0);
01009   return 0;
01010 }