Back to index

cell-binutils  2.17cvs20070401
resres.c
Go to the documentation of this file.
00001 /* resres.c: read_res_file and write_res_file implementation for windres.
00002    Copyright 1998, 1999, 2001, 2002 Free Software Foundation, Inc.
00003    Written by Anders Norlander <anorland@hem2.passagen.se>.
00004 
00005    This file is part of GNU Binutils.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
00020    02110-1301, USA.  */
00021 
00022 /* FIXME: This file does not work correctly in a cross configuration.
00023    It assumes that it can use fread and fwrite to read and write
00024    integers.  It does no swapping.  */
00025 
00026 #include "bfd.h"
00027 #include "bucomm.h"
00028 #include "libiberty.h"
00029 #include "windres.h"
00030 
00031 #include <assert.h>
00032 #include <time.h>
00033 
00034 struct res_hdr
00035   {
00036     unsigned long data_size;
00037     unsigned long header_size;
00038   };
00039 
00040 static void write_res_directory
00041   PARAMS ((const struct res_directory *,
00042           const struct res_id *, const struct res_id *,
00043           int *, int));
00044 static void write_res_resource
00045   PARAMS ((const struct res_id *, const struct res_id *,
00046           const struct res_resource *, int *));
00047 static void write_res_bin
00048   PARAMS ((const struct res_resource *, const struct res_id *,
00049           const struct res_id *, const struct res_res_info *));
00050 
00051 static void write_res_id PARAMS ((const struct res_id *));
00052 static void write_res_info PARAMS ((const struct res_res_info *));
00053 static void write_res_data PARAMS ((const void *, size_t, int));
00054 static void write_res_header
00055   PARAMS ((unsigned long, const struct res_id *, const struct res_id *,
00056           const struct res_res_info *));
00057 
00058 static int read_resource_entry PARAMS ((void));
00059 static void read_res_data PARAMS ((void *, size_t, int));
00060 static void read_res_id PARAMS ((struct res_id *));
00061 static unichar *read_unistring PARAMS ((int *));
00062 static void skip_null_resource PARAMS ((void));
00063 
00064 static unsigned long get_id_size PARAMS ((const struct res_id *));
00065 static void res_align_file PARAMS ((void));
00066 
00067 static void
00068   res_add_resource
00069   PARAMS ((struct res_resource *, const struct res_id *,
00070           const struct res_id *, int, int));
00071 
00072 void
00073   res_append_resource
00074   PARAMS ((struct res_directory **, struct res_resource *,
00075           int, const struct res_id *, int));
00076 
00077 static struct res_directory *resources = NULL;
00078 
00079 static FILE *fres;
00080 static const char *filename;
00081 
00082 extern char *program_name;
00083 
00084 /* Read resource file */
00085 struct res_directory *
00086 read_res_file (fn)
00087      const char *fn;
00088 {
00089   filename = fn;
00090   fres = fopen (filename, "rb");
00091   if (fres == NULL)
00092     fatal ("can't open `%s' for output: %s", filename, strerror (errno));
00093 
00094   skip_null_resource ();
00095 
00096   while (read_resource_entry ())
00097     ;
00098 
00099   fclose (fres);
00100 
00101   return resources;
00102 }
00103 
00104 /* Write resource file */
00105 void
00106 write_res_file (fn, resdir)
00107      const char *fn;
00108      const struct res_directory *resdir;
00109 {
00110   int language;
00111   static const unsigned char sign[] =
00112   {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
00113    0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
00114    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00115    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
00116   long fpos;
00117 
00118   filename = fn;
00119 
00120   fres = fopen (filename, "wb");
00121   if (fres == NULL)
00122     fatal ("can't open `%s' for output: %s", filename, strerror (errno));
00123 
00124   /* Write 32 bit resource signature */
00125   write_res_data (sign, sizeof (sign), 1);
00126 
00127   /* write resources */
00128 
00129   language = -1;
00130   write_res_directory (resdir, (const struct res_id *) NULL,
00131                      (const struct res_id *) NULL, &language, 1);
00132 
00133   /* end file on DWORD boundary */
00134   fpos = ftell (fres);
00135   if (fpos % 4)
00136     write_res_data (sign, fpos % 4, 1);
00137 
00138   fclose (fres);
00139 }
00140 
00141 /* Read a resource entry, returns 0 when all resources are read */
00142 static int
00143 read_resource_entry (void)
00144 {
00145   struct res_id type;
00146   struct res_id name;
00147   struct res_res_info resinfo;
00148   struct res_hdr reshdr;
00149   long version;
00150   void *buff;
00151 
00152   struct res_resource *r;
00153 
00154   res_align_file ();
00155 
00156   /* Read header */
00157   if (fread (&reshdr, sizeof (reshdr), 1, fres) != 1)
00158     return 0;
00159 
00160   /* read resource type */
00161   read_res_id (&type);
00162   /* read resource id */
00163   read_res_id (&name);
00164 
00165   res_align_file ();
00166 
00167   /* Read additional resource header */
00168   read_res_data (&resinfo.version, sizeof (resinfo.version), 1);
00169   read_res_data (&resinfo.memflags, sizeof (resinfo.memflags), 1);
00170   read_res_data (&resinfo.language, sizeof (resinfo.language), 1);
00171   read_res_data (&version, sizeof (version), 1);
00172   read_res_data (&resinfo.characteristics, sizeof (resinfo.characteristics), 1);
00173 
00174   res_align_file ();
00175 
00176   /* Allocate buffer for data */
00177   buff = res_alloc (reshdr.data_size);
00178   /* Read data */
00179   read_res_data (buff, reshdr.data_size, 1);
00180   /* Convert binary data to resource */
00181   r = bin_to_res (type, buff, reshdr.data_size, 0);
00182   r->res_info = resinfo;
00183   /* Add resource to resource directory */
00184   res_add_resource (r, &type, &name, resinfo.language, 0);
00185 
00186   return 1;
00187 }
00188 
00189 /* write resource directory to binary resource file */
00190 static void
00191 write_res_directory (rd, type, name, language, level)
00192      const struct res_directory *rd;
00193      const struct res_id *type;
00194      const struct res_id *name;
00195      int *language;
00196      int level;
00197 {
00198   const struct res_entry *re;
00199 
00200   for (re = rd->entries; re != NULL; re = re->next)
00201     {
00202       switch (level)
00203        {
00204        case 1:
00205          /* If we're at level 1, the key of this resource is the
00206             type.  This normally duplicates the information we have
00207             stored with the resource itself, but we need to remember
00208             the type if this is a user define resource type.  */
00209          type = &re->id;
00210          break;
00211 
00212        case 2:
00213          /* If we're at level 2, the key of this resource is the name
00214             we are going to use in the rc printout.  */
00215          name = &re->id;
00216          break;
00217 
00218        case 3:
00219          /* If we're at level 3, then this key represents a language.
00220             Use it to update the current language.  */
00221          if (!re->id.named
00222              && re->id.u.id != (unsigned long) *language
00223              && (re->id.u.id & 0xffff) == re->id.u.id)
00224            {
00225              *language = re->id.u.id;
00226            }
00227          break;
00228 
00229        default:
00230          break;
00231        }
00232 
00233       if (re->subdir)
00234        write_res_directory (re->u.dir, type, name, language, level + 1);
00235       else
00236        {
00237          if (level == 3)
00238            {
00239              /* This is the normal case: the three levels are
00240                 TYPE/NAME/LANGUAGE.  NAME will have been set at level
00241                 2, and represents the name to use.  We probably just
00242                 set LANGUAGE, and it will probably match what the
00243                 resource itself records if anything.  */
00244              write_res_resource (type, name, re->u.res, language);
00245            }
00246          else
00247            {
00248              fprintf (stderr, "// Resource at unexpected level %d\n", level);
00249              write_res_resource (type, (struct res_id *) NULL, re->u.res,
00250                               language);
00251            }
00252        }
00253     }
00254 
00255 }
00256 
00257 static void
00258 write_res_resource (type, name, res, language)
00259      const struct res_id *type;
00260      const struct res_id *name;
00261      const struct res_resource *res;
00262      int *language ATTRIBUTE_UNUSED;
00263 {
00264   int rt;
00265 
00266   switch (res->type)
00267     {
00268     default:
00269       abort ();
00270 
00271     case RES_TYPE_ACCELERATOR:
00272       rt = RT_ACCELERATOR;
00273       break;
00274 
00275     case RES_TYPE_BITMAP:
00276       rt = RT_BITMAP;
00277       break;
00278 
00279     case RES_TYPE_CURSOR:
00280       rt = RT_CURSOR;
00281       break;
00282 
00283     case RES_TYPE_GROUP_CURSOR:
00284       rt = RT_GROUP_CURSOR;
00285       break;
00286 
00287     case RES_TYPE_DIALOG:
00288       rt = RT_DIALOG;
00289       break;
00290 
00291     case RES_TYPE_FONT:
00292       rt = RT_FONT;
00293       break;
00294 
00295     case RES_TYPE_FONTDIR:
00296       rt = RT_FONTDIR;
00297       break;
00298 
00299     case RES_TYPE_ICON:
00300       rt = RT_ICON;
00301       break;
00302 
00303     case RES_TYPE_GROUP_ICON:
00304       rt = RT_GROUP_ICON;
00305       break;
00306 
00307     case RES_TYPE_MENU:
00308       rt = RT_MENU;
00309       break;
00310 
00311     case RES_TYPE_MESSAGETABLE:
00312       rt = RT_MESSAGETABLE;
00313       break;
00314 
00315     case RES_TYPE_RCDATA:
00316       rt = RT_RCDATA;
00317       break;
00318 
00319     case RES_TYPE_STRINGTABLE:
00320       rt = RT_STRING;
00321       break;
00322 
00323     case RES_TYPE_USERDATA:
00324       rt = 0;
00325       break;
00326 
00327     case RES_TYPE_VERSIONINFO:
00328       rt = RT_VERSION;
00329       break;
00330     }
00331 
00332   if (rt != 0
00333       && type != NULL
00334       && (type->named || type->u.id != (unsigned long) rt))
00335     {
00336       fprintf (stderr, "// Unexpected resource type mismatch: ");
00337       res_id_print (stderr, *type, 1);
00338       fprintf (stderr, " != %d", rt);
00339       abort ();
00340     }
00341 
00342   write_res_bin (res, type, name, &res->res_info);
00343   return;
00344 }
00345 
00346 /* Write a resource in binary resource format */
00347 static void
00348 write_res_bin (res, type, name, resinfo)
00349      const struct res_resource *res;
00350      const struct res_id *type;
00351      const struct res_id *name;
00352      const struct res_res_info *resinfo;
00353 {
00354   unsigned long datasize = 0;
00355   const struct bindata *bin_rep, *data;
00356 
00357   bin_rep = res_to_bin (res, 0);
00358   for (data = bin_rep; data != NULL; data = data->next)
00359     datasize += data->length;
00360 
00361   write_res_header (datasize, type, name, resinfo);
00362 
00363   for (data = bin_rep; data != NULL; data = data->next)
00364     write_res_data (data->data, data->length, 1);
00365 }
00366 
00367 /* Get number of bytes needed to store an id in binary format */
00368 static unsigned long
00369 get_id_size (id)
00370      const struct res_id *id;
00371 {
00372   if (id->named)
00373     return sizeof (unichar) * (id->u.n.length + 1);
00374   else
00375     return sizeof (unichar) * 2;
00376 }
00377 
00378 /* Write a resource header */
00379 static void
00380 write_res_header (datasize, type, name, resinfo)
00381      unsigned long datasize;
00382      const struct res_id *type;
00383      const struct res_id *name;
00384      const struct res_res_info *resinfo;
00385 {
00386   struct res_hdr reshdr;
00387   reshdr.data_size = datasize;
00388   reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
00389 
00390   reshdr.header_size = (reshdr.header_size + 3) & ~3;
00391 
00392   res_align_file ();
00393   write_res_data (&reshdr, sizeof (reshdr), 1);
00394   write_res_id (type);
00395   write_res_id (name);
00396 
00397   res_align_file ();
00398 
00399   write_res_info (resinfo);
00400   res_align_file ();
00401 }
00402 
00403 
00404 /* Write data to file, abort on failure */
00405 static void
00406 write_res_data (data, size, count)
00407      const void *data;
00408      size_t size;
00409      int count;
00410 {
00411   if ((size_t) fwrite (data, size, count, fres) != (size_t) count)
00412     fatal ("%s: could not write to file", filename);
00413 }
00414 
00415 /* Read data from file, abort on failure */
00416 static void
00417 read_res_data (data, size, count)
00418      void *data;
00419      size_t size;
00420      int count;
00421 {
00422   if (fread (data, size, count, fres) != (size_t) count)
00423     fatal ("%s: unexpected end of file", filename);
00424 }
00425 
00426 /* Write a resource id */
00427 static void
00428 write_res_id (id)
00429      const struct res_id *id;
00430 {
00431   if (id->named)
00432     {
00433       unsigned long len = id->u.n.length;
00434       unichar null_term = 0;
00435       write_res_data (id->u.n.name, len * sizeof (unichar), 1);
00436       write_res_data (&null_term, sizeof (null_term), 1);
00437     }
00438   else
00439     {
00440       unsigned short i = 0xFFFF;
00441       write_res_data (&i, sizeof (i), 1);
00442       i = id->u.id;
00443       write_res_data (&i, sizeof (i), 1);
00444     }
00445 }
00446 
00447 /* Write resource info */
00448 static void
00449 write_res_info (info)
00450      const struct res_res_info *info;
00451 {
00452   write_res_data (&info->version, sizeof (info->version), 1);
00453   write_res_data (&info->memflags, sizeof (info->memflags), 1);
00454   write_res_data (&info->language, sizeof (info->language), 1);
00455   write_res_data (&info->version, sizeof (info->version), 1);
00456   write_res_data (&info->characteristics, sizeof (info->characteristics), 1);
00457 }
00458 
00459 /* read a resource identifier */
00460 void
00461 read_res_id (id)
00462      struct res_id *id;
00463 {
00464   unsigned short ord;
00465   unichar *id_s = NULL;
00466   int len;
00467 
00468   read_res_data (&ord, sizeof (ord), 1);
00469   if (ord == 0xFFFF)        /* an ordinal id */
00470     {
00471       read_res_data (&ord, sizeof (ord), 1);
00472       id->named = 0;
00473       id->u.id = ord;
00474     }
00475   else
00476     /* named id */
00477     {
00478       if (fseek (fres, -sizeof (ord), SEEK_CUR) != 0)
00479        fatal ("%s: %s: could not seek in file", program_name, filename);
00480       id_s = read_unistring (&len);
00481       id->named = 1;
00482       id->u.n.length = len;
00483       id->u.n.name = id_s;
00484     }
00485 }
00486 
00487 /* Read a null terminated UNICODE string */
00488 static unichar *
00489 read_unistring (len)
00490      int *len;
00491 {
00492   unichar *s;
00493   unichar c;
00494   unichar *p;
00495   int l;
00496 
00497   *len = 0;
00498   l = 0;
00499 
00500   /* there are hardly any names longer than 256 characters */
00501   p = s = (unichar *) xmalloc (sizeof (unichar) * 256);
00502   do
00503     {
00504       read_res_data (&c, sizeof (c), 1);
00505       *p++ = c;
00506       if (c != 0)
00507        l++;
00508     }
00509   while (c != 0);
00510   *len = l;
00511   return s;
00512 }
00513 
00514 /* align file on DWORD boundary */
00515 static void
00516 res_align_file (void)
00517 {
00518   int pos = ftell (fres);
00519   int skip = ((pos + 3) & ~3) - pos;
00520   if (fseek (fres, skip, SEEK_CUR) != 0)
00521     fatal ("%s: %s: unable to align file", program_name, filename);
00522 }
00523 
00524 /* Check if file is a win32 binary resource file, if so
00525    skip past the null resource. Returns 0 if successful, -1 on
00526    error.
00527  */
00528 static void
00529 skip_null_resource (void)
00530 {
00531   struct res_hdr reshdr =
00532   {0, 0};
00533   read_res_data (&reshdr, sizeof (reshdr), 1);
00534   if ((reshdr.data_size != 0) || (reshdr.header_size != 0x20))
00535     goto skip_err;
00536 
00537   /* Subtract size of HeaderSize and DataSize */
00538   if (fseek (fres, reshdr.header_size - 8, SEEK_CUR) != 0)
00539     goto skip_err;
00540 
00541   return;
00542 
00543 skip_err:
00544   fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
00545           filename);
00546   xexit (1);
00547 }
00548 
00549 /* Add a resource to resource directory */
00550 void
00551 res_add_resource (r, type, id, language, dupok)
00552      struct res_resource *r;
00553      const struct res_id *type;
00554      const struct res_id *id;
00555      int language;
00556      int dupok;
00557 {
00558   struct res_id a[3];
00559 
00560   a[0] = *type;
00561   a[1] = *id;
00562   a[2].named = 0;
00563   a[2].u.id = language;
00564   res_append_resource (&resources, r, 3, a, dupok);
00565 }
00566 
00567 /* Append a resource to resource directory.
00568    This is just copied from define_resource
00569    and modified to add an existing resource.
00570  */
00571 void
00572 res_append_resource (resources, resource, cids, ids, dupok)
00573      struct res_directory **resources;
00574      struct res_resource *resource;
00575      int cids;
00576      const struct res_id *ids;
00577      int dupok;
00578 {
00579   struct res_entry *re = NULL;
00580   int i;
00581 
00582   assert (cids > 0);
00583   for (i = 0; i < cids; i++)
00584     {
00585       struct res_entry **pp;
00586 
00587       if (*resources == NULL)
00588        {
00589          static unsigned long timeval;
00590 
00591          /* Use the same timestamp for every resource created in a
00592             single run.  */
00593          if (timeval == 0)
00594            timeval = time (NULL);
00595 
00596          *resources = ((struct res_directory *)
00597                      res_alloc (sizeof **resources));
00598          (*resources)->characteristics = 0;
00599          (*resources)->time = timeval;
00600          (*resources)->major = 0;
00601          (*resources)->minor = 0;
00602          (*resources)->entries = NULL;
00603        }
00604 
00605       for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
00606        if (res_id_cmp ((*pp)->id, ids[i]) == 0)
00607          break;
00608 
00609       if (*pp != NULL)
00610        re = *pp;
00611       else
00612        {
00613          re = (struct res_entry *) res_alloc (sizeof *re);
00614          re->next = NULL;
00615          re->id = ids[i];
00616          if ((i + 1) < cids)
00617            {
00618              re->subdir = 1;
00619              re->u.dir = NULL;
00620            }
00621          else
00622            {
00623              re->subdir = 0;
00624              re->u.res = NULL;
00625            }
00626 
00627          *pp = re;
00628        }
00629 
00630       if ((i + 1) < cids)
00631        {
00632          if (!re->subdir)
00633            {
00634              fprintf (stderr, "%s: ", program_name);
00635              res_ids_print (stderr, i, ids);
00636              fprintf (stderr, ": expected to be a directory\n");
00637              xexit (1);
00638            }
00639 
00640          resources = &re->u.dir;
00641        }
00642     }
00643 
00644   if (re->subdir)
00645     {
00646       fprintf (stderr, "%s: ", program_name);
00647       res_ids_print (stderr, cids, ids);
00648       fprintf (stderr, ": expected to be a leaf\n");
00649       xexit (1);
00650     }
00651 
00652   if (re->u.res != NULL)
00653     {
00654       if (dupok)
00655        return;
00656 
00657       fprintf (stderr, "%s: warning: ", program_name);
00658       res_ids_print (stderr, cids, ids);
00659       fprintf (stderr, ": duplicate value\n");
00660     }
00661 
00662   re->u.res = resource;
00663 }