Coverage Report

Created: 2023-06-29 07:03

/src/binutils-gdb/binutils/fuzz_windres.h
Line
Count
Source (jump to first uncovered line)
1
/* windres.c -- a program to manipulate Windows resources
2
   Copyright (C) 1997-2023 Free Software Foundation, Inc.
3
   Written by Ian Lance Taylor, Cygnus Support.
4
   Rewritten 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 3 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 write Windows resources in various
24
   formats.  In particular, it can act like the rc resource compiler
25
   program, and it can act like the cvtres res to COFF conversion
26
   program.
27
28
   It is based on information taken from the following sources:
29
30
   * Microsoft documentation.
31
32
   * The rcl program, written by Gunther Ebert
33
     <gunther.ebert@ixos-leipzig.de>.
34
35
   * The res2coff program, written by Pedro A. Aranda <paag@tid.es>.  */
36
37
#include "sysdep.h"
38
#include <assert.h>
39
#include "bfd.h"
40
#include "getopt.h"
41
#include "bucomm.h"
42
#include "libiberty.h"
43
#include "safe-ctype.h"
44
#include "obstack.h"
45
#include "windres.h"
46
47
/* Used by resrc.c at least.  */
48
49
int verbose = 0;
50
51
bool target_is_bigendian = 0;
52
const char *def_target_arch;
53
54
static void set_endianness (bfd *, const char *);
55
56
/* An enumeration of format types.  */
57
58
enum res_format
59
{
60
  /* Unknown format.  */
61
  RES_FORMAT_UNKNOWN,
62
  /* Textual RC file.  */
63
  RES_FORMAT_RC,
64
  /* Binary RES file.  */
65
  RES_FORMAT_RES,
66
  /* COFF file.  */
67
  RES_FORMAT_COFF
68
};
69
70
/* A structure used to map between format types and strings.  */
71
72
struct format_map
73
{
74
  const char *name;
75
  enum res_format format;
76
};
77
78
/* A mapping between names and format types.  */
79
80
static const struct format_map format_names[] =
81
{
82
  { "rc", RES_FORMAT_RC },
83
  { "res", RES_FORMAT_RES },
84
  { "coff", RES_FORMAT_COFF },
85
  { NULL, RES_FORMAT_UNKNOWN }
86
};
87
88
/* A mapping from file extensions to format types.  */
89
90
static const struct format_map format_fileexts[] =
91
{
92
  { "rc", RES_FORMAT_RC },
93
  { "res", RES_FORMAT_RES },
94
  { "exe", RES_FORMAT_COFF },
95
  { "obj", RES_FORMAT_COFF },
96
  { "o", RES_FORMAT_COFF },
97
  { NULL, RES_FORMAT_UNKNOWN }
98
};
99
100
/* A list of include directories.  */
101
102
struct include_dir
103
{
104
  struct include_dir *next;
105
  char *dir;
106
};
107
108
static struct include_dir *include_dirs;
109
110
/* Static functions.  */
111
112
static void res_init (void);
113
static int extended_menuitems (const rc_menuitem *);
114
static enum res_format format_from_name (const char *, int);
115
static enum res_format format_from_filename (const char *, int);
116
static void usage (FILE *, int);
117
static int cmp_res_entry (const void *, const void *);
118
static rc_res_directory *sort_resources (rc_res_directory *);
119
static void reswr_init (void);
120
static const char * quot (const char *);
121

122
static rc_uint_type target_get_8 (const void *, rc_uint_type);
123
static void target_put_8 (void *, rc_uint_type);
124
static rc_uint_type target_get_16 (const void *, rc_uint_type);
125
static void target_put_16 (void *, rc_uint_type);
126
static rc_uint_type target_get_32 (const void *, rc_uint_type);
127
static void target_put_32 (void *, rc_uint_type);
128
129

130
/* When we are building a resource tree, we allocate everything onto
131
   an obstack, so that we can free it all at once if we want.  */
132
133
#define obstack_chunk_alloc xmalloc
134
#define obstack_chunk_free free
135
136
/* The resource building obstack.  */
137
138
static struct obstack res_obstack;
139
140
/* Initialize the resource building obstack.  */
141
142
static void
143
res_init (void)
144
0
{
145
0
  obstack_init (&res_obstack);
146
0
}
147
148
/* Allocate space on the resource building obstack.  */
149
150
void *
151
res_alloc (rc_uint_type bytes)
152
0
{
153
0
  return obstack_alloc (&res_obstack, (size_t) bytes);
154
0
}
155
156
/* We also use an obstack to save memory used while writing out a set
157
   of resources.  */
158
159
static struct obstack reswr_obstack;
160
161
/* Initialize the resource writing obstack.  */
162
163
static void
164
reswr_init (void)
165
0
{
166
0
  obstack_init (&reswr_obstack);
167
0
}
168
169
/* Allocate space on the resource writing obstack.  */
170
171
void *
172
reswr_alloc (rc_uint_type bytes)
173
0
{
174
0
  return obstack_alloc (&reswr_obstack, (size_t) bytes);
175
0
}
176

177
/* Open a file using the include directory search list.  */
178
179
FILE *
180
open_file_search (const char *filename, const char *mode, const char *errmsg,
181
      char **real_filename)
182
0
{
183
0
  FILE *e;
184
0
  struct include_dir *d;
185
186
0
  e = fopen (filename, mode);
187
0
  if (e != NULL)
188
0
    {
189
0
      *real_filename = xstrdup (filename);
190
0
      return e;
191
0
    }
192
193
0
  if (errno == ENOENT)
194
0
    {
195
0
      for (d = include_dirs; d != NULL; d = d->next)
196
0
  {
197
0
    char *n;
198
199
0
    n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2);
200
0
    sprintf (n, "%s/%s", d->dir, filename);
201
0
    e = fopen (n, mode);
202
0
    if (e != NULL)
203
0
      {
204
0
        *real_filename = n;
205
0
        return e;
206
0
      }
207
0
    free (n);
208
209
0
    if (errno != ENOENT)
210
0
      break;
211
0
  }
212
0
    }
213
214
0
  fatal (_("can't open %s `%s': %s"), errmsg, filename, strerror (errno));
215
216
  /* Return a value to avoid a compiler warning.  */
217
0
  return NULL;
218
0
}
219

220
/* Compare two resource ID's.  We consider name entries to come before
221
   numeric entries, because that is how they appear in the COFF .rsrc
222
   section.  */
223
224
int
225
res_id_cmp (rc_res_id a, rc_res_id b)
226
0
{
227
0
  if (! a.named)
228
0
    {
229
0
      if (b.named)
230
0
  return 1;
231
0
      if (a.u.id > b.u.id)
232
0
  return 1;
233
0
      else if (a.u.id < b.u.id)
234
0
  return -1;
235
0
      else
236
0
  return 0;
237
0
    }
238
0
  else
239
0
    {
240
0
      unichar *as, *ase, *bs, *bse;
241
242
0
      if (! b.named)
243
0
  return -1;
244
245
0
      as = a.u.n.name;
246
0
      ase = as + a.u.n.length;
247
0
      bs = b.u.n.name;
248
0
      bse = bs + b.u.n.length;
249
250
0
      while (as < ase)
251
0
  {
252
0
    int i;
253
254
0
    if (bs >= bse)
255
0
      return 1;
256
0
    i = (int) *as - (int) *bs;
257
0
    if (i != 0)
258
0
      return i;
259
0
    ++as;
260
0
    ++bs;
261
0
  }
262
263
0
      if (bs < bse)
264
0
  return -1;
265
266
0
      return 0;
267
0
    }
268
0
}
269
270
/* Print a resource ID.  */
271
272
void
273
res_id_print (FILE *stream, rc_res_id id, int quote)
274
0
{
275
0
  if (! id.named)
276
0
    fprintf (stream, "%u", (int) id.u.id);
277
0
  else
278
0
    {
279
0
      if (quote)
280
0
  unicode_print_quoted (stream, id.u.n.name, id.u.n.length);
281
0
      else
282
0
      unicode_print (stream, id.u.n.name, id.u.n.length);
283
0
    }
284
0
}
285
286
/* Print a list of resource ID's.  */
287
288
void
289
res_ids_print (FILE *stream, int cids, const rc_res_id *ids)
290
0
{
291
0
  int i;
292
293
0
  for (i = 0; i < cids; i++)
294
0
    {
295
0
      res_id_print (stream, ids[i], 1);
296
0
      if (i + 1 < cids)
297
0
  fprintf (stream, ": ");
298
0
    }
299
0
}
300
301
/* Convert an ASCII string to a resource ID.  */
302
303
void
304
res_string_to_id (rc_res_id *res_id, const char *string)
305
0
{
306
0
  res_id->named = 1;
307
0
  unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
308
0
}
309
310
/* Convert an unicode string to a resource ID.  */
311
void
312
res_unistring_to_id (rc_res_id *res_id, const unichar *u)
313
0
{
314
0
  res_id->named = 1;
315
0
  res_id->u.n.length = unichar_len (u);
316
0
  res_id->u.n.name = unichar_dup_uppercase (u);
317
0
}
318
319
/* Define a resource.  The arguments are the resource tree, RESOURCES,
320
   and the location at which to put it in the tree, CIDS and IDS.
321
   This returns a newly allocated rc_res_resource structure, which the
322
   caller is expected to initialize.  If DUPOK is non-zero, then if a
323
   resource with this ID exists, it is returned.  Otherwise, a warning
324
   is issued, and a new resource is created replacing the existing
325
   one.  */
326
327
rc_res_resource *
328
define_resource (rc_res_directory **resources, int cids,
329
     const rc_res_id *ids, int dupok)
330
0
{
331
0
  rc_res_entry *re = NULL;
332
0
  int i;
333
334
0
  assert (cids > 0);
335
0
  for (i = 0; i < cids; i++)
336
0
    {
337
0
      rc_res_entry **pp;
338
339
0
      if (*resources == NULL)
340
0
  {
341
0
    *resources = ((rc_res_directory *)
342
0
      res_alloc (sizeof (rc_res_directory)));
343
0
    (*resources)->characteristics = 0;
344
    /* Using a real timestamp only serves to create non-deterministic
345
       results.  Use zero instead.  */
346
0
    (*resources)->time = 0;
347
0
    (*resources)->major = 0;
348
0
    (*resources)->minor = 0;
349
0
    (*resources)->entries = NULL;
350
0
  }
351
352
0
      for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
353
0
  if (res_id_cmp ((*pp)->id, ids[i]) == 0)
354
0
    break;
355
356
0
      if (*pp != NULL)
357
0
  re = *pp;
358
0
      else
359
0
  {
360
0
    re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
361
0
    re->next = NULL;
362
0
    re->id = ids[i];
363
0
    if ((i + 1) < cids)
364
0
      {
365
0
        re->subdir = 1;
366
0
        re->u.dir = NULL;
367
0
      }
368
0
    else
369
0
      {
370
0
        re->subdir = 0;
371
0
        re->u.res = NULL;
372
0
      }
373
374
0
    *pp = re;
375
0
  }
376
377
0
      if ((i + 1) < cids)
378
0
  {
379
0
    if (! re->subdir)
380
0
      {
381
0
        fprintf (stderr, "%s: ", program_name);
382
0
        res_ids_print (stderr, i, ids);
383
0
        fprintf (stderr, _(": expected to be a directory\n"));
384
0
        xexit (1);
385
0
      }
386
387
0
    resources = &re->u.dir;
388
0
  }
389
0
    }
390
391
0
  if (re->subdir)
392
0
    {
393
0
      fprintf (stderr, "%s: ", program_name);
394
0
      res_ids_print (stderr, cids, ids);
395
0
      fprintf (stderr, _(": expected to be a leaf\n"));
396
0
      xexit (1);
397
0
    }
398
399
0
  if (re->u.res != NULL)
400
0
    {
401
0
      if (dupok)
402
0
  return re->u.res;
403
404
0
      fprintf (stderr, _("%s: warning: "), program_name);
405
0
      res_ids_print (stderr, cids, ids);
406
0
      fprintf (stderr, _(": duplicate value\n"));
407
0
    }
408
409
0
  re->u.res = ((rc_res_resource *)
410
0
         res_alloc (sizeof (rc_res_resource)));
411
0
  memset (re->u.res, 0, sizeof (rc_res_resource));
412
413
0
  re->u.res->type = RES_TYPE_UNINITIALIZED;
414
0
  return re->u.res;
415
0
}
416
417
/* Define a standard resource.  This is a version of define_resource
418
   that just takes type, name, and language arguments.  */
419
420
rc_res_resource *
421
define_standard_resource (rc_res_directory **resources, int type,
422
        rc_res_id name, rc_uint_type language, int dupok)
423
0
{
424
0
  rc_res_id a[3];
425
426
0
  a[0].named = 0;
427
0
  a[0].u.id = type;
428
0
  a[1] = name;
429
0
  a[2].named = 0;
430
0
  a[2].u.id = language;
431
0
  return define_resource (resources, 3, a, dupok);
432
0
}
433
434
/* Comparison routine for resource sorting.  */
435
436
static int
437
cmp_res_entry (const void *p1, const void *p2)
438
0
{
439
0
  const rc_res_entry **re1, **re2;
440
441
0
  re1 = (const rc_res_entry **) p1;
442
0
  re2 = (const rc_res_entry **) p2;
443
0
  return res_id_cmp ((*re1)->id, (*re2)->id);
444
0
}
445
446
/* Sort the resources.  */
447
448
static rc_res_directory *
449
sort_resources (rc_res_directory *resdir)
450
0
{
451
0
  int c, i;
452
0
  rc_res_entry *re;
453
0
  rc_res_entry **a;
454
455
0
  if (resdir->entries == NULL)
456
0
    return resdir;
457
458
0
  c = 0;
459
0
  for (re = resdir->entries; re != NULL; re = re->next)
460
0
    ++c;
461
462
  /* This is a recursive routine, so using xmalloc is probably better
463
     than alloca.  */
464
0
  a = (rc_res_entry **) xmalloc (c * sizeof (rc_res_entry *));
465
466
0
  for (i = 0, re = resdir->entries; re != NULL; re = re->next, i++)
467
0
    a[i] = re;
468
469
0
  qsort (a, c, sizeof (rc_res_entry *), cmp_res_entry);
470
471
0
  resdir->entries = a[0];
472
0
  for (i = 0; i < c - 1; i++)
473
0
    a[i]->next = a[i + 1];
474
0
  a[i]->next = NULL;
475
476
0
  free (a);
477
478
  /* Now sort the subdirectories.  */
479
480
0
  for (re = resdir->entries; re != NULL; re = re->next)
481
0
    if (re->subdir)
482
0
      re->u.dir = sort_resources (re->u.dir);
483
484
0
  return resdir;
485
0
}
486

487
/* Return whether the dialog resource DIALOG is a DIALOG or a
488
   DIALOGEX.  */
489
490
int
491
extended_dialog (const rc_dialog *dialog)
492
0
{
493
0
  const rc_dialog_control *c;
494
495
0
  if (dialog->ex != NULL)
496
0
    return 1;
497
498
0
  for (c = dialog->controls; c != NULL; c = c->next)
499
0
    if (c->data != NULL || c->help != 0)
500
0
      return 1;
501
502
0
  return 0;
503
0
}
504
505
/* Return whether MENUITEMS are a MENU or a MENUEX.  */
506
507
int
508
extended_menu (const rc_menu *menu)
509
0
{
510
0
  return extended_menuitems (menu->items);
511
0
}
512
513
static int
514
extended_menuitems (const rc_menuitem *menuitems)
515
0
{
516
0
  const rc_menuitem *mi;
517
518
0
  for (mi = menuitems; mi != NULL; mi = mi->next)
519
0
    {
520
0
      if (mi->help != 0 || mi->state != 0)
521
0
  return 1;
522
0
      if (mi->popup != NULL && mi->id != 0)
523
0
  return 1;
524
0
      if ((mi->type
525
0
     & ~ (MENUITEM_CHECKED
526
0
    | MENUITEM_GRAYED
527
0
    | MENUITEM_HELP
528
0
    | MENUITEM_INACTIVE
529
0
    | MENUITEM_MENUBARBREAK
530
0
    | MENUITEM_BITMAP
531
0
    | MENUITEM_OWNERDRAW
532
0
    | MENUITEM_MENUBREAK))
533
0
    != 0)
534
0
  return 1;
535
0
      if (mi->popup != NULL)
536
0
  {
537
0
    if (extended_menuitems (mi->popup))
538
0
      return 1;
539
0
  }
540
0
    }
541
542
0
  return 0;
543
0
}
544

545
/* Convert a string to a format type, or exit if it can't be done.  */
546
547
static enum res_format
548
format_from_name (const char *name, int exit_on_error)
549
0
{
550
0
  const struct format_map *m;
551
552
0
  for (m = format_names; m->name != NULL; m++)
553
0
    if (strcasecmp (m->name, name) == 0)
554
0
      break;
555
556
0
  if (m->name == NULL && exit_on_error)
557
0
    {
558
0
      non_fatal (_("unknown format type `%s'"), name);
559
0
      fprintf (stderr, _("%s: supported formats:"), program_name);
560
0
      for (m = format_names; m->name != NULL; m++)
561
0
  fprintf (stderr, " %s", m->name);
562
0
      fprintf (stderr, "\n");
563
0
      xexit (1);
564
0
    }
565
566
0
  return m->format;
567
0
}
568
569
/* Work out a format type given a file name.  If INPUT is non-zero,
570
   it's OK to look at the file itself.  */
571
572
static enum res_format
573
format_from_filename (const char *filename, int input)
574
0
{
575
0
  const char *ext;
576
0
  FILE *e;
577
0
  bfd_byte b1, b2, b3, b4, b5;
578
0
  int magic;
579
580
  /* If we have an extension, see if we recognize it as implying a
581
     particular format.  */
582
0
  ext = strrchr (filename, '.');
583
0
  if (ext != NULL)
584
0
    {
585
0
      const struct format_map *m;
586
587
0
      ++ext;
588
0
      for (m = format_fileexts; m->name != NULL; m++)
589
0
  if (strcasecmp (m->name, ext) == 0)
590
0
    return m->format;
591
0
    }
592
593
  /* If we don't recognize the name of an output file, assume it's a
594
     COFF file.  */
595
0
  if (! input)
596
0
    return RES_FORMAT_COFF;
597
598
  /* Read the first few bytes of the file to see if we can guess what
599
     it is.  */
600
0
  e = fopen (filename, FOPEN_RB);
601
0
  if (e == NULL)
602
0
    fatal ("%s: %s", filename, strerror (errno));
603
604
0
  b1 = getc (e);
605
0
  b2 = getc (e);
606
0
  b3 = getc (e);
607
0
  b4 = getc (e);
608
0
  b5 = getc (e);
609
610
0
  fclose (e);
611
612
  /* A PE executable starts with 0x4d 0x5a.  */
613
0
  if (b1 == 0x4d && b2 == 0x5a)
614
0
    return RES_FORMAT_COFF;
615
616
  /* A COFF .o file starts with a COFF magic number.  */
617
0
  magic = (b2 << 8) | b1;
618
0
  switch (magic)
619
0
    {
620
0
    case 0x14c: /* i386 */
621
0
    case 0x166: /* MIPS */
622
0
    case 0x184: /* Alpha */
623
0
    case 0x268: /* 68k */
624
0
    case 0x1f0: /* PowerPC */
625
0
    case 0x290: /* PA */
626
0
      return RES_FORMAT_COFF;
627
0
    }
628
629
  /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0.  */
630
0
  if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20)
631
0
    return RES_FORMAT_RES;
632
633
  /* If every character is printable or space, assume it's an RC file.  */
634
0
  if ((ISPRINT (b1) || ISSPACE (b1))
635
0
      && (ISPRINT (b2) || ISSPACE (b2))
636
0
      && (ISPRINT (b3) || ISSPACE (b3))
637
0
      && (ISPRINT (b4) || ISSPACE (b4))
638
0
      && (ISPRINT (b5) || ISSPACE (b5)))
639
0
    return RES_FORMAT_RC;
640
641
  /* Otherwise, we give up.  */
642
0
  fatal (_("can not determine type of file `%s'; use the -J option"),
643
0
   filename);
644
645
  /* Return something to silence the compiler warning.  */
646
0
  return RES_FORMAT_UNKNOWN;
647
0
}
648
649
/* Print a usage message and exit.  */
650
651
static void
652
usage (FILE *stream, int status)
653
0
{
654
0
  fprintf (stream, _("Usage: %s [option(s)] [input-file] [output-file]\n"),
655
0
     program_name);
656
0
  fprintf (stream, _(" The options are:\n\
657
0
  -i --input=<file>            Name input file\n\
658
0
  -o --output=<file>           Name output file\n\
659
0
  -J --input-format=<format>   Specify input format\n\
660
0
  -O --output-format=<format>  Specify output format\n\
661
0
  -F --target=<target>         Specify COFF target\n\
662
0
     --preprocessor=<program>  Program to use to preprocess rc file\n\
663
0
     --preprocessor-arg=<arg>  Additional preprocessor argument\n\
664
0
  -I --include-dir=<dir>       Include directory when preprocessing rc file\n\
665
0
  -D --define <sym>[=<val>]    Define SYM when preprocessing rc file\n\
666
0
  -U --undefine <sym>          Undefine SYM when preprocessing rc file\n\
667
0
  -v --verbose                 Verbose - tells you what it's doing\n\
668
0
  -c --codepage=<codepage>     Specify default codepage\n\
669
0
  -l --language=<val>          Set language when reading rc file\n\
670
0
     --use-temp-file           Use a temporary file instead of popen to read\n\
671
0
                               the preprocessor output\n\
672
0
     --no-use-temp-file        Use popen (default)\n"));
673
#ifdef YYDEBUG
674
  fprintf (stream, _("\
675
     --yydebug                 Turn on parser debugging\n"));
676
#endif
677
0
  fprintf (stream, _("\
678
0
  -r                           Ignored for compatibility with rc\n\
679
0
  @<file>                      Read options from <file>\n\
680
0
  -h --help                    Print this help message\n\
681
0
  -V --version                 Print version information\n"));
682
0
  fprintf (stream, _("\
683
0
FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
684
0
extension if not specified.  A single file name is an input file.\n\
685
0
No input-file is stdin, default rc.  No output-file is stdout, default rc.\n"));
686
687
0
  list_supported_targets (program_name, stream);
688
689
0
  if (REPORT_BUGS_TO[0] && status == 0)
690
0
    fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
691
692
0
  exit (status);
693
0
}
694
695
/* Quote characters that will confuse the shell when we run the preprocessor.  */
696
697
static const char *
698
quot (const char *string)
699
0
{
700
0
  static char *buf = 0;
701
0
  static int buflen = 0;
702
0
  int slen = strlen (string);
703
0
  const char *src;
704
0
  char *dest;
705
706
0
  if ((buflen < slen * 2 + 3) || ! buf)
707
0
    {
708
0
      buflen = slen * 2 + 3;
709
0
      free (buf);
710
0
      buf = (char *) xmalloc (buflen);
711
0
    }
712
713
0
  for (src = string, dest = buf; *src; src++, dest++)
714
0
    {
715
0
      if (*src == '(' || *src == ')' || *src == ' ')
716
0
  *dest++ = '\\';
717
0
      *dest = *src;
718
0
    }
719
720
0
  *dest = 0;
721
0
  return buf;
722
0
}
723
724
/* Long options.  */
725
726
enum option_values
727
{
728
  /* 150 isn't special; it's just an arbitrary non-ASCII char value.  */
729
  OPTION_PREPROCESSOR = 150,
730
  OPTION_USE_TEMP_FILE,
731
  OPTION_NO_USE_TEMP_FILE,
732
  OPTION_YYDEBUG,
733
  OPTION_INCLUDE_DIR,
734
  OPTION_PREPROCESSOR_ARG
735
};
736
737
static const struct option long_options[] =
738
{
739
  {"input", required_argument, 0, 'i'},
740
  {"output", required_argument, 0, 'o'},
741
  {"input-format", required_argument, 0, 'J'},
742
  {"output-format", required_argument, 0, 'O'},
743
  {"target", required_argument, 0, 'F'},
744
  {"preprocessor", required_argument, 0, OPTION_PREPROCESSOR},
745
  {"preprocessor-arg", required_argument, 0, OPTION_PREPROCESSOR_ARG},
746
  {"include-dir", required_argument, 0, OPTION_INCLUDE_DIR},
747
  {"define", required_argument, 0, 'D'},
748
  {"undefine", required_argument, 0, 'U'},
749
  {"verbose", no_argument, 0, 'v'},
750
  {"codepage", required_argument, 0, 'c'},
751
  {"language", required_argument, 0, 'l'},
752
  {"use-temp-file", no_argument, 0, OPTION_USE_TEMP_FILE},
753
  {"no-use-temp-file", no_argument, 0, OPTION_NO_USE_TEMP_FILE},
754
  {"yydebug", no_argument, 0, OPTION_YYDEBUG},
755
  {"version", no_argument, 0, 'V'},
756
  {"help", no_argument, 0, 'h'},
757
  {0, no_argument, 0, 0}
758
};
759
760
void
761
windres_add_include_dir (const char *p)
762
0
{
763
0
  struct include_dir *n, **pp;
764
765
  /* Computing paths is often complicated and error prone.
766
     The easiest way to check for mistakes is at the time
767
     we add them to include_dirs.  */
768
0
  assert (p != NULL);
769
0
  assert (*p != '\0');
770
771
0
  n = xmalloc (sizeof *n);
772
0
  n->next = NULL;
773
0
  n->dir = (char * ) p;
774
775
0
  for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
776
0
    ;
777
0
  *pp = n;
778
0
}
779
780
/* This keeps gcc happy when using -Wmissing-prototypes -Wstrict-prototypes.  */
781
int main (int, char **);
782
783
/* The main function.  */
784
785
int
786
old_main32 (int argc, char **argv);
787
int old_main32 (int argc, char **argv)
788
0
{
789
0
  int c;
790
0
  char *input_filename;
791
0
  char *output_filename;
792
0
  enum res_format input_format;
793
0
  enum res_format input_format_tmp;
794
0
  enum res_format output_format;
795
0
  char *target;
796
0
  char *preprocessor;
797
0
  char *preprocargs;
798
0
  const char *quotedarg;
799
0
  int language;
800
0
  rc_res_directory *resources;
801
0
  int use_temp_file;
802
803
0
#ifdef HAVE_LC_MESSAGES
804
0
  setlocale (LC_MESSAGES, "");
805
0
#endif
806
0
  setlocale (LC_CTYPE, "");
807
0
  bindtextdomain (PACKAGE, LOCALEDIR);
808
0
  textdomain (PACKAGE);
809
810
0
  program_name = argv[0];
811
0
  xmalloc_set_program_name (program_name);
812
0
  bfd_set_error_program_name (program_name);
813
814
0
  expandargv (&argc, &argv);
815
816
0
  if (bfd_init () != BFD_INIT_MAGIC)
817
0
    fatal (_("fatal error: libbfd ABI mismatch"));
818
0
  set_default_bfd_target ();
819
820
0
  res_init ();
821
822
0
  input_filename = NULL;
823
0
  output_filename = NULL;
824
0
  input_format = RES_FORMAT_UNKNOWN;
825
0
  output_format = RES_FORMAT_UNKNOWN;
826
0
  target = NULL;
827
0
  preprocessor = NULL;
828
0
  preprocargs = NULL;
829
0
  language = 0x409;   /* LANG_ENGLISH, SUBLANG_ENGLISH_US.  */
830
0
  use_temp_file = 0;
831
832
0
  while ((c = getopt_long (argc, argv, "c:f:i:l:o:I:J:O:F:D:U:rhHvV", long_options,
833
0
         (int *) 0)) != EOF)
834
0
    {
835
0
      switch (c)
836
0
  {
837
0
  case 'c':
838
0
    {
839
0
      rc_uint_type ncp;
840
841
0
      if (optarg[0] == '0' && (optarg[1] == 'x' || optarg[1] == 'X'))
842
0
        ncp = (rc_uint_type) strtol (optarg + 2, NULL, 16);
843
0
      else
844
0
        ncp = (rc_uint_type) strtol (optarg, NULL, 10);
845
0
      if (ncp == CP_UTF16 || ! unicode_is_valid_codepage (ncp))
846
0
        fatal (_("invalid codepage specified.\n"));
847
0
      wind_default_codepage = wind_current_codepage = ncp;
848
0
    }
849
0
    break;
850
851
0
  case 'i':
852
0
    input_filename = optarg;
853
0
    break;
854
855
0
  case 'f':
856
    /* For compatibility with rc we accept "-fo <name>" as being the
857
       equivalent of "-o <name>".  We do not advertise this fact
858
       though, as we do not want users to use non-GNU like command
859
       line switches.  */
860
0
    if (*optarg != 'o')
861
0
      fatal (_("invalid option -f\n"));
862
0
    optarg++;
863
0
    if (* optarg == 0)
864
0
      {
865
0
        if (optind == argc)
866
0
    fatal (_("No filename following the -fo option.\n"));
867
0
        optarg = argv [optind++];
868
0
      }
869
    /* Fall through.  */
870
871
0
  case 'o':
872
0
    output_filename = optarg;
873
0
    break;
874
875
0
  case 'J':
876
0
    input_format = format_from_name (optarg, 1);
877
0
    break;
878
879
0
  case 'O':
880
0
    output_format = format_from_name (optarg, 1);
881
0
    break;
882
883
0
  case 'F':
884
0
    target = optarg;
885
0
    break;
886
887
0
  case OPTION_PREPROCESSOR:
888
0
    if (strchr (optarg, ' '))
889
0
      {
890
0
        if (asprintf (& preprocessor, "\"%s\"", optarg) == -1)
891
0
    preprocessor = optarg;
892
0
      }
893
0
    else
894
0
      preprocessor = optarg;     
895
0
    break;
896
897
0
  case OPTION_PREPROCESSOR_ARG:
898
0
    if (preprocargs == NULL)
899
0
      {
900
0
        quotedarg = quot (optarg);
901
0
        preprocargs = xstrdup (quotedarg);
902
0
      }
903
0
    else
904
0
      {
905
0
        char *n;
906
907
0
        quotedarg = quot (optarg);
908
0
        n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 2);
909
0
        sprintf (n, "%s %s", preprocargs, quotedarg);
910
0
        free (preprocargs);
911
0
        preprocargs = n;
912
0
      }
913
0
    break;
914
915
0
  case 'D':
916
0
  case 'U':
917
0
    if (preprocargs == NULL)
918
0
      {
919
0
        quotedarg = quot (optarg);
920
0
        preprocargs = xmalloc (strlen (quotedarg) + 3);
921
0
        sprintf (preprocargs, "-%c%s", c, quotedarg);
922
0
      }
923
0
    else
924
0
      {
925
0
        char *n;
926
927
0
        quotedarg = quot (optarg);
928
0
        n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
929
0
        sprintf (n, "%s -%c%s", preprocargs, c, quotedarg);
930
0
        free (preprocargs);
931
0
        preprocargs = n;
932
0
      }
933
0
    break;
934
935
0
  case 'r':
936
    /* Ignored for compatibility with rc.  */
937
0
    break;
938
939
0
  case 'v':
940
0
    verbose ++;
941
0
    break;
942
943
0
  case 'I':
944
    /* For backward compatibility, should be removed in the future.  */
945
0
    input_format_tmp = format_from_name (optarg, 0);
946
0
    if (input_format_tmp != RES_FORMAT_UNKNOWN)
947
0
      {
948
0
        struct stat statbuf;
949
0
        char modebuf[11];
950
951
0
        if (stat (optarg, & statbuf) == 0
952
      /* Coded this way to avoid importing knowledge of S_ISDIR into this file.  */
953
0
      && (mode_string (statbuf.st_mode, modebuf), modebuf[0] == 'd'))
954
    /* We have a -I option with a directory name that just happens
955
       to match a format name as well.  eg: -I res  Assume that the
956
       user knows what they are doing and do not complain.  */
957
0
    ;
958
0
        else
959
0
    {
960
0
      fprintf (stderr,
961
0
         _("Option -I is deprecated for setting the input format, please use -J instead.\n"));
962
0
      input_format = input_format_tmp;
963
0
      break;
964
0
    }
965
0
      }
966
    /* Fall through.  */
967
968
0
  case OPTION_INCLUDE_DIR:
969
0
    if (preprocargs == NULL)
970
0
      {
971
0
        quotedarg = quot (optarg);
972
0
        preprocargs = xmalloc (strlen (quotedarg) + 3);
973
0
        sprintf (preprocargs, "-I%s", quotedarg);
974
0
      }
975
0
    else
976
0
      {
977
0
        char *n;
978
979
0
        quotedarg = quot (optarg);
980
0
        n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
981
0
        sprintf (n, "%s -I%s", preprocargs, quotedarg);
982
0
        free (preprocargs);
983
0
        preprocargs = n;
984
0
      }
985
986
0
    windres_add_include_dir (optarg);
987
988
0
    break;
989
990
0
  case 'l':
991
0
    language = strtol (optarg, (char **) NULL, 16);
992
0
    break;
993
994
0
  case OPTION_USE_TEMP_FILE:
995
0
    use_temp_file = 1;
996
0
    break;
997
998
0
  case OPTION_NO_USE_TEMP_FILE:
999
0
    use_temp_file = 0;
1000
0
    break;
1001
1002
#ifdef YYDEBUG
1003
  case OPTION_YYDEBUG:
1004
    yydebug = 1;
1005
    break;
1006
#endif
1007
1008
0
  case 'h':
1009
0
  case 'H':
1010
0
    usage (stdout, 0);
1011
0
    break;
1012
1013
0
  case 'V':
1014
0
    print_version ("windres");
1015
0
    break;
1016
1017
0
  default:
1018
0
    usage (stderr, 1);
1019
0
    break;
1020
0
  }
1021
0
    }
1022
1023
0
  if (input_filename == NULL && optind < argc)
1024
0
    {
1025
0
      input_filename = argv[optind];
1026
0
      ++optind;
1027
0
    }
1028
1029
0
  if (output_filename == NULL && optind < argc)
1030
0
    {
1031
0
      output_filename = argv[optind];
1032
0
      ++optind;
1033
0
    }
1034
1035
0
  if (argc != optind)
1036
0
    usage (stderr, 1);
1037
1038
0
  if (input_format == RES_FORMAT_UNKNOWN)
1039
0
    {
1040
0
      if (input_filename == NULL)
1041
0
  input_format = RES_FORMAT_RC;
1042
0
      else
1043
0
  input_format = format_from_filename (input_filename, 1);
1044
0
    }
1045
1046
0
  if (output_format == RES_FORMAT_UNKNOWN)
1047
0
    {
1048
0
      if (output_filename == NULL)
1049
0
  output_format = RES_FORMAT_RC;
1050
0
      else
1051
0
  output_format = format_from_filename (output_filename, 0);
1052
0
    }
1053
1054
0
  set_endianness (NULL, target);
1055
1056
  /* Read the input file.  */
1057
0
  switch (input_format)
1058
0
    {
1059
0
    default:
1060
0
      abort ();
1061
0
    case RES_FORMAT_RC:
1062
0
      resources = read_rc_file (input_filename, preprocessor, preprocargs,
1063
0
        language, use_temp_file);
1064
0
      break;
1065
0
    case RES_FORMAT_RES:
1066
0
      resources = read_res_file (input_filename);
1067
0
      break;
1068
0
    case RES_FORMAT_COFF:
1069
0
      resources = read_coff_rsrc (input_filename, target);
1070
0
      break;
1071
0
    }
1072
1073
0
  if (resources == NULL)
1074
0
    fatal (_("no resources"));
1075
1076
  /* Sort the resources.  This is required for COFF, convenient for
1077
     rc, and unimportant for res.  */
1078
0
  resources = sort_resources (resources);
1079
1080
  /* Write the output file.  */
1081
0
  reswr_init ();
1082
1083
0
  switch (output_format)
1084
0
    {
1085
0
    default:
1086
0
      abort ();
1087
0
    case RES_FORMAT_RC:
1088
0
      write_rc_file (output_filename, resources);
1089
0
      break;
1090
0
    case RES_FORMAT_RES:
1091
0
      write_res_file (output_filename, resources);
1092
0
      break;
1093
0
    case RES_FORMAT_COFF:
1094
0
      write_coff_file (output_filename, target, resources);
1095
0
      break;
1096
0
    }
1097
1098
0
  xexit (0);
1099
0
  return 0;
1100
0
}
1101
1102
static void
1103
set_endianness (bfd *abfd, const char *target)
1104
0
{
1105
0
  const bfd_target *target_vec;
1106
1107
0
  def_target_arch = NULL;
1108
0
  target_vec = bfd_get_target_info (target, abfd, &target_is_bigendian, NULL,
1109
0
                                   &def_target_arch);
1110
0
  if (! target_vec)
1111
0
    fatal ("Can't detect target endianness and architecture.");
1112
0
  if (! def_target_arch)
1113
0
    fatal ("Can't detect architecture.");
1114
0
}
1115
1116
bfd *
1117
windres_open_as_binary (const char *filename, int rdmode)
1118
0
{
1119
0
  bfd *abfd;
1120
1121
0
  abfd = (rdmode ? bfd_openr (filename, "binary") : bfd_openw (filename, "binary"));
1122
0
  if (! abfd)
1123
0
    fatal ("can't open `%s' for %s", filename, (rdmode ? "input" : "output"));
1124
1125
0
  if (rdmode && ! bfd_check_format (abfd, bfd_object))
1126
0
    fatal ("can't open `%s' for input.", filename);
1127
1128
0
  return abfd;
1129
0
}
1130
1131
void
1132
set_windres_bfd_endianness (windres_bfd *wrbfd, int is_bigendian)
1133
0
{
1134
0
  assert (!! wrbfd);
1135
0
  switch (WR_KIND(wrbfd))
1136
0
  {
1137
0
  case WR_KIND_BFD_BIN_L:
1138
0
    if (is_bigendian)
1139
0
      WR_KIND(wrbfd) = WR_KIND_BFD_BIN_B;
1140
0
    break;
1141
0
  case WR_KIND_BFD_BIN_B:
1142
0
    if (! is_bigendian)
1143
0
      WR_KIND(wrbfd) = WR_KIND_BFD_BIN_L;
1144
0
    break;
1145
0
  default:
1146
    /* only binary bfd can be overriden. */
1147
0
    abort ();
1148
0
  }
1149
0
}
1150
1151
void
1152
set_windres_bfd (windres_bfd *wrbfd, bfd *abfd, asection *sec, rc_uint_type kind)
1153
30
{
1154
30
  assert (!! wrbfd);
1155
30
  switch (kind)
1156
30
  {
1157
0
  case WR_KIND_TARGET:
1158
0
    abfd = NULL;
1159
0
    sec = NULL;
1160
0
    break;
1161
30
  case WR_KIND_BFD:
1162
30
  case WR_KIND_BFD_BIN_L:
1163
30
  case WR_KIND_BFD_BIN_B:
1164
30
    assert (!! abfd);
1165
30
    assert (!!sec);
1166
30
    break;
1167
30
  default:
1168
0
    abort ();
1169
30
  }
1170
30
  WR_KIND(wrbfd) = kind;
1171
30
  WR_BFD(wrbfd) = abfd;
1172
30
  WR_SECTION(wrbfd) = sec;
1173
30
}
1174
1175
void
1176
set_windres_bfd_content (windres_bfd *wrbfd, const void *data, rc_uint_type off,
1177
       rc_uint_type length)
1178
0
{
1179
0
  if (WR_KIND(wrbfd) != WR_KIND_TARGET)
1180
0
    {
1181
0
      if (! bfd_set_section_contents (WR_BFD(wrbfd), WR_SECTION(wrbfd), data, off, length))
1182
0
  bfd_fatal ("bfd_set_section_contents");
1183
0
    }
1184
0
  else
1185
0
    abort ();
1186
0
}
1187
1188
void
1189
get_windres_bfd_content (windres_bfd *wrbfd, void *data, rc_uint_type off,
1190
       rc_uint_type length)
1191
0
{
1192
0
  if (WR_KIND(wrbfd) != WR_KIND_TARGET)
1193
0
    {
1194
0
      if (! bfd_get_section_contents (WR_BFD(wrbfd), WR_SECTION(wrbfd), data, off, length))
1195
0
  bfd_fatal ("bfd_get_section_contents");
1196
0
    }
1197
0
  else
1198
0
    abort ();
1199
0
}
1200
1201
void
1202
windres_put_8 (windres_bfd *wrbfd, void *p, rc_uint_type value)
1203
0
{
1204
0
  switch (WR_KIND(wrbfd))
1205
0
    {
1206
0
    case WR_KIND_TARGET:
1207
0
      target_put_8 (p, value);
1208
0
      break;
1209
0
    case WR_KIND_BFD:
1210
0
    case WR_KIND_BFD_BIN_L:
1211
0
    case WR_KIND_BFD_BIN_B:
1212
0
      bfd_put_8 (WR_BFD(wrbfd), value, p);
1213
0
      break;
1214
0
    default:
1215
0
      abort ();
1216
0
    }
1217
0
}
1218
1219
void
1220
windres_put_16 (windres_bfd *wrbfd, void *data, rc_uint_type value)
1221
0
{
1222
0
  switch (WR_KIND(wrbfd))
1223
0
    {
1224
0
    case WR_KIND_TARGET:
1225
0
      target_put_16 (data, value);
1226
0
      break;
1227
0
    case WR_KIND_BFD:
1228
0
    case WR_KIND_BFD_BIN_B:
1229
0
      bfd_put_16 (WR_BFD(wrbfd), value, data);
1230
0
      break;
1231
0
    case WR_KIND_BFD_BIN_L:
1232
0
      bfd_putl16 (value, data);
1233
0
      break;
1234
0
    default:
1235
0
      abort ();
1236
0
    }
1237
0
}
1238
1239
void
1240
windres_put_32 (windres_bfd *wrbfd, void *data, rc_uint_type value)
1241
0
{
1242
0
  switch (WR_KIND(wrbfd))
1243
0
    {
1244
0
    case WR_KIND_TARGET:
1245
0
      target_put_32 (data, value);
1246
0
      break;
1247
0
    case WR_KIND_BFD:
1248
0
    case WR_KIND_BFD_BIN_B:
1249
0
      bfd_put_32 (WR_BFD(wrbfd), value, data);
1250
0
      break;
1251
0
    case WR_KIND_BFD_BIN_L:
1252
0
      bfd_putl32 (value, data);
1253
0
      break;
1254
0
    default:
1255
0
      abort ();
1256
0
    }
1257
0
}
1258
1259
rc_uint_type
1260
windres_get_8 (windres_bfd *wrbfd, const void *data, rc_uint_type length)
1261
0
{
1262
0
  if (length < 1)
1263
0
    fatal ("windres_get_8: unexpected eob.");
1264
0
  switch (WR_KIND(wrbfd))
1265
0
    {
1266
0
    case WR_KIND_TARGET:
1267
0
      return target_get_8 (data, length);
1268
0
    case WR_KIND_BFD:
1269
0
    case WR_KIND_BFD_BIN_B:
1270
0
    case WR_KIND_BFD_BIN_L:
1271
0
      return bfd_get_8 (WR_BFD(wrbfd), data);
1272
0
    default:
1273
0
      abort ();
1274
0
    }
1275
0
  return 0;
1276
0
}
1277
1278
rc_uint_type
1279
windres_get_16 (windres_bfd *wrbfd, const void *data, rc_uint_type length)
1280
0
{
1281
0
  if (length < 2)
1282
0
    fatal ("windres_get_16: unexpected eob.");
1283
0
  switch (WR_KIND(wrbfd))
1284
0
    {
1285
0
    case WR_KIND_TARGET:
1286
0
      return target_get_16 (data, length);
1287
0
    case WR_KIND_BFD:
1288
0
    case WR_KIND_BFD_BIN_B:
1289
0
      return bfd_get_16 (WR_BFD(wrbfd), data);
1290
0
    case WR_KIND_BFD_BIN_L:
1291
0
      return bfd_getl16 (data);
1292
0
    default:
1293
0
      abort ();
1294
0
    }
1295
0
  return 0;
1296
0
}
1297
1298
rc_uint_type
1299
windres_get_32 (windres_bfd *wrbfd, const void *data, rc_uint_type length)
1300
0
{
1301
0
  if (length < 4)
1302
0
    fatal ("windres_get_32: unexpected eob.");
1303
0
  switch (WR_KIND(wrbfd))
1304
0
    {
1305
0
    case WR_KIND_TARGET:
1306
0
      return target_get_32 (data, length);
1307
0
    case WR_KIND_BFD:
1308
0
    case WR_KIND_BFD_BIN_B:
1309
0
      return bfd_get_32 (WR_BFD(wrbfd), data);
1310
0
    case WR_KIND_BFD_BIN_L:
1311
0
      return bfd_getl32 (data);
1312
0
    default:
1313
0
      abort ();
1314
0
    }
1315
0
  return 0;
1316
0
}
1317
1318
static rc_uint_type
1319
target_get_8 (const void *p, rc_uint_type length)
1320
0
{
1321
0
  rc_uint_type ret;
1322
1323
0
  if (length < 1)
1324
0
    fatal ("Resource too small for getting 8-bit value.");
1325
1326
0
  ret = (rc_uint_type) *((const bfd_byte *) p);
1327
0
  return ret & 0xff;
1328
0
}
1329
1330
static rc_uint_type
1331
target_get_16 (const void *p, rc_uint_type length)
1332
0
{
1333
0
  if (length < 2)
1334
0
    fatal ("Resource too small for getting 16-bit value.");
1335
1336
0
  if (target_is_bigendian)
1337
0
    return bfd_getb16 (p);
1338
0
  else
1339
0
    return bfd_getl16 (p);
1340
0
}
1341
1342
static rc_uint_type
1343
target_get_32 (const void *p, rc_uint_type length)
1344
0
{
1345
0
  if (length < 4)
1346
0
    fatal ("Resource too small for getting 32-bit value.");
1347
1348
0
  if (target_is_bigendian)
1349
0
    return bfd_getb32 (p);
1350
0
  else
1351
0
    return bfd_getl32 (p);
1352
0
}
1353
1354
static void
1355
target_put_8 (void *p, rc_uint_type value)
1356
0
{
1357
0
  assert (!! p);
1358
0
  *((bfd_byte *) p)=(bfd_byte) value;
1359
0
}
1360
1361
static void
1362
target_put_16 (void *p, rc_uint_type value)
1363
0
{
1364
0
  assert (!! p);
1365
1366
0
  if (target_is_bigendian)
1367
0
    bfd_putb16 (value, p);
1368
0
  else
1369
0
    bfd_putl16 (value, p);
1370
0
}
1371
1372
static void
1373
target_put_32 (void *p, rc_uint_type value)
1374
0
{
1375
0
  assert (!! p);
1376
1377
0
  if (target_is_bigendian)
1378
0
    bfd_putb32 (value, p);
1379
0
  else
1380
0
    bfd_putl32 (value, p);
1381
0
}
1382
1383
static int isInComment = 0;
1384
1385
int wr_printcomment (FILE *e, const char *fmt, ...)
1386
0
{
1387
0
  va_list arg;
1388
0
  int r = 0;
1389
1390
0
  if (isInComment)
1391
0
    r += fprintf (e, "\n   ");
1392
0
  else
1393
0
    fprintf (e, "/* ");
1394
0
  isInComment = 1;
1395
0
  if (fmt == NULL)
1396
0
    return r;
1397
0
  va_start (arg, fmt);
1398
0
  r += vfprintf (e, fmt, arg);
1399
0
  va_end (arg);
1400
0
  return r;
1401
0
}
1402
1403
int wr_print (FILE *e, const char *fmt, ...)
1404
0
{
1405
0
  va_list arg;
1406
0
  int r = 0;
1407
0
  if (isInComment)
1408
0
    r += fprintf (e, ".  */\n");
1409
0
  isInComment = 0;
1410
0
  if (! fmt)
1411
0
    return r;
1412
0
  va_start (arg, fmt);
1413
0
  r += vfprintf (e, fmt, arg);
1414
0
  va_end (arg);
1415
0
  return r;
1416
0
}