Added LGPL standard comment, and copyright notices where necessary.
[wine] / tools / wrc / wrc.c
1 /*
2  * Copyright  Martin von Loewis, 1994
3  * Copyrignt 1998 Bertho A. Stultiens (BS)
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * History:
20  * 30-Apr-2000 BS       - Integrated a new preprocessor (-E and -N)
21  * 20-Jun-1998 BS       - Added -L option to prevent case conversion
22  *                        of embedded filenames.
23  *
24  * 08-Jun-1998 BS       - Added -A option to generate autoregister code
25  *                        for winelib operation.
26  *
27  * 21-May-1998 BS       - Removed the CPP option. Its internal now.
28  *                      - Added implementations for defines and includes
29  *                        on the commandline.
30  *
31  * 30-Apr-1998 BS       - The options now contain nearly the entire alphabet.
32  *                        Seems to be a sign for too much freedom. I implemeted
33  *                        most of them as a user choice possibility for things
34  *                        that I do not know what to put there by default.
35  *                      - -l and -L options are now known as -t and -T.
36  *
37  * 23-Apr-1998 BS       - Finally gave up on backward compatibility on the
38  *                        commandline (after a blessing from the newsgroup).
39  *                        So, I changed the lot.
40  *
41  * 17-Apr-1998 BS       - Added many new command-line options but took care
42  *                        that it would not break old scripts (sigh).
43  *
44  * 16-Apr-1998 BS       - There is not much left of the original source...
45  *                        I had to rewrite most of it because the parser
46  *                        changed completely with all the types etc..
47  *
48  */
49
50 #include "config.h"
51
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <unistd.h>
55 #include <string.h>
56 #include <assert.h>
57 #include <ctype.h>
58 #include <signal.h>
59
60 #include "wrc.h"
61 #include "utils.h"
62 #include "writeres.h"
63 #include "readres.h"
64 #include "dumpres.h"
65 #include "genres.h"
66 #include "newstruc.h"
67 #include "preproc.h"
68 #include "parser.h"
69
70 static char usage[] =
71         "Usage: wrc [options...] [infile[.rc|.res]]\n"
72         "   -a n        Alignment of resource (win16 only, default is 4)\n"
73         "   -A          Auto register resources (only with gcc 2.7 and better)\n"
74         "   -b          Create an assembly array from a binary .res file\n"
75         "   -B x        Set output byte-order x={n[ative], l[ittle], b[ig]}\n"
76         "               (win32 only; default is n[ative] which equals "
77 #ifdef WORDS_BIGENDIAN
78         "big"
79 #else
80         "little"
81 #endif
82         "-endian)\n"
83         "   -c          Add 'const' prefix to C constants\n"
84         "   -C cp       Set the resource's codepage to cp (default is 0)\n"
85         "   -d n        Set debug level to 'n'\n"
86         "   -D id[=val] Define preprocessor identifier id=val\n"
87         "   -e          Disable recognition of win32 keywords in 16bit compile\n"
88         "   -E          Preprocess only\n"
89         "   -g          Add symbols to the global c namespace\n"
90         "   -h          Also generate a .h file\n"
91         "   -H file     Same as -h but written to file\n"
92         "   -I path     Set include search dir to path (multiple -I allowed)\n"
93         "   -l lan      Set default language to lan (default is neutral {0, 0})\n"
94         "   -L          Leave case of embedded filenames as is\n"
95         "   -m          Do not remap numerical resource IDs\n"
96         "   -n          Do not generate .s file\n"
97         "   -N          Do not preprocess input\n"
98         "   -o file     Output to file (default is infile.[res|s|h]\n"
99         "   -p prefix   Give a prefix for the generated names\n"
100         "   -r          Create binary .res file (compile only)\n"
101         "   -s          Add structure with win32/16 (PE/NE) resource directory\n"
102         "   -t          Generate indirect loadable resource tables\n"
103         "   -T          Generate only indirect loadable resources tables\n"
104         "   -V          Print version end exit\n"
105         "   -w 16|32    Select win16 or win32 output (default is win32)\n"
106         "   -W          Enable pedantic warnings\n"
107         "Input is taken from stdin if no sourcefile specified.\n"
108         "Debug level 'n' is a bitmask with following meaning:\n"
109         "    * 0x01 Tell which resource is parsed (verbose mode)\n"
110         "    * 0x02 Dump internal structures\n"
111         "    * 0x04 Create a parser trace (yydebug=1)\n"
112         "    * 0x08 Preprocessor messages\n"
113         "    * 0x10 Preprocessor lex messages\n"
114         "    * 0x20 Preprocessor yacc trace\n"
115         "The -o option only applies to the final destination file, which is\n"
116         "in case of normal compile a .s file. You must use the '-H header.h'\n"
117         "option to override the header-filename.\n"
118         "If no input filename is given and the output name is not overridden\n"
119         "with -o and/or -H, then the output is written to \"wrc.tab.[sh]\"\n"
120         ;
121
122 char version_string[] = "Wine Resource Compiler Version " WRC_FULLVERSION "\n"
123                         "Copyright 1998-2000 Bertho A. Stultiens\n"
124                         "          1994 Martin von Loewis\n";
125
126 /*
127  * Default prefix for resource names used in the C array.
128  * Option '-p name' sets it to 'name'
129  */
130 #ifdef NEED_UNDERSCORE_PREFIX
131 char *prefix = "__Resource";
132 #else
133 char *prefix = "_Resource";
134 #endif
135
136 /*
137  * Set if compiling in 32bit mode (default).
138  */
139 int win32 = 1;
140
141 /*
142  * Set when generated C variables should be prefixed with 'const'
143  */
144 int constant = 0;
145
146 /*
147  * Create a .res file from the source and exit (-r option).
148  */
149 int create_res = 0;
150
151 /*
152  * debuglevel == DEBUGLEVEL_NONE        Don't bother
153  * debuglevel & DEBUGLEVEL_CHAT         Say whats done
154  * debuglevel & DEBUGLEVEL_DUMP         Dump internal structures
155  * debuglevel & DEBUGLEVEL_TRACE        Create parser trace
156  * debuglevel & DEBUGLEVEL_PPMSG        Preprocessor messages
157  * debuglevel & DEBUGLEVEL_PPLEX        Preprocessor lex trace
158  * debuglevel & DEBUGLEVEL_PPTRACE      Preprocessor yacc trace
159  */
160 int debuglevel = DEBUGLEVEL_NONE;
161
162 /*
163  * Recognize win32 keywords if set (-w 32 enforces this),
164  * otherwise set with -e option.
165  */
166 int extensions = 1;
167
168 /*
169  * Set when creating C array from .res file (-b option).
170  */
171 int binary = 0;
172
173 /*
174  * Set when an additional C-header is to be created in compile (-h option).
175  */
176 int create_header = 0;
177
178 /*
179  * Set when the NE/PE resource directory should be dumped into
180  * the output file.
181  */
182 int create_dir = 0;
183
184 /*
185  * Set when all symbols should be added to the global namespace (-g option)
186  */
187 int global = 0;
188
189 /*
190  * Set when indirect loadable resource tables should be created (-t)
191  */
192 int indirect = 0;
193
194 /*
195  * Set when _only_ indirect loadable resource tables should be created (-T)
196  */
197 int indirect_only = 0;
198
199 /*
200  * NE segment resource aligment (-a option)
201  */
202 int alignment = 4;
203 int alignment_pwr;
204
205 /*
206  * Cleared when the assembly file must be suppressed (-n option)
207  */
208 int create_s = 1;
209
210 /*
211  * Language setting for resources (-l option)
212  */
213 language_t *currentlanguage = NULL;
214
215 /*
216  * The codepage to write in win32 PE resource segment (-C option)
217  */
218 DWORD codepage = 0;
219
220 /*
221  * Set when extra warnings should be generated (-W option)
222  */
223 int pedantic = 0;
224
225 /*
226  * Set when autoregister code must be added to the output (-A option)
227  */
228 int auto_register = 0;
229
230 /*
231  * Set when the case of embedded filenames should not be converted
232  * to lower case (-L option)
233  */
234 int leave_case = 0;
235
236 /*
237  * The output byte-order of resources (set with -B)
238  */
239 int byteorder = WRC_BO_NATIVE;
240
241 /*
242  * Set when _only_ to run the preprocessor (-E option)
243  */
244 int preprocess_only = 0;
245
246 /*
247  * Set when _not_ to run the preprocessor (-N option)
248  */
249 int no_preprocess = 0;
250
251 /*
252  * Cleared when _not_ to remap resource types (-m option)
253  */
254 int remap = 1;
255
256 char *output_name;              /* The name given by the -o option */
257 char *input_name;               /* The name given on the command-line */
258 char *header_name;              /* The name given by the -H option */
259 char *temp_name;                /* Temporary file for preprocess pipe */
260
261 int line_number = 1;            /* The current line */
262 int char_number = 1;            /* The current char pos within the line */
263
264 char *cmdline;                  /* The entire commandline */
265 time_t now;                     /* The time of start of wrc */
266
267 resource_t *resource_top;       /* The top of the parsed resources */
268
269 int getopt (int argc, char *const *argv, const char *optstring);
270 static void rm_tempfile(void);
271 static void segvhandler(int sig);
272
273 int main(int argc,char *argv[])
274 {
275         extern char* optarg;
276         extern int   optind;
277         int optc;
278         int lose = 0;
279         int ret;
280         int a;
281         int i;
282         int cmdlen;
283
284         signal(SIGSEGV, segvhandler);
285
286         now = time(NULL);
287
288         /* First rebuild the commandline to put in destination */
289         /* Could be done through env[], but not all OS-es support it */
290         cmdlen = 4; /* for "wrc " */
291         for(i = 1; i < argc; i++)
292                 cmdlen += strlen(argv[i]) + 1;
293         cmdline = (char *)xmalloc(cmdlen);
294         strcpy(cmdline, "wrc ");
295         for(i = 1; i < argc; i++)
296         {
297                 strcat(cmdline, argv[i]);
298                 if(i < argc-1)
299                         strcat(cmdline, " ");
300         }
301
302         while((optc = getopt(argc, argv, "a:AbB:cC:d:D:eEghH:I:l:LmnNo:p:rstTVw:W")) != EOF)
303         {
304                 switch(optc)
305                 {
306                 case 'a':
307                         alignment = atoi(optarg);
308                         break;
309                 case 'A':
310                         auto_register = 1;
311                         break;
312                 case 'b':
313                         binary = 1;
314                         break;
315                 case 'B':
316                         switch(optarg[0])
317                         {
318                         case 'n':
319                         case 'N':
320                                 byteorder = WRC_BO_NATIVE;
321                                 break;
322                         case 'l':
323                         case 'L':
324                                 byteorder = WRC_BO_LITTLE;
325                                 break;
326                         case 'b':
327                         case 'B':
328                                 byteorder = WRC_BO_BIG;
329                                 break;
330                         default:
331                                 fprintf(stderr, "Byte ordering must be n[ative], l[ittle] or b[ig]\n");
332                                 lose++;
333                         }
334                         break;
335                 case 'c':
336                         constant = 1;
337                         break;
338                 case 'C':
339                         codepage = strtol(optarg, NULL, 0);
340                         break;
341                 case 'd':
342                         debuglevel = strtol(optarg, NULL, 0);
343                         break;
344                 case 'D':
345                         add_cmdline_define(optarg);
346                         break;
347                 case 'e':
348                         extensions = 0;
349                         break;
350                 case 'E':
351                         preprocess_only = 1;
352                         break;
353                 case 'g':
354                         global = 1;
355                         break;
356                 case 'H':
357                         header_name = strdup(optarg);
358                         /* Fall through */
359                 case 'h':
360                         create_header = 1;
361                         break;
362                 case 'I':
363                         add_include_path(optarg);
364                         break;
365                 case 'l':
366                         {
367                                 int lan;
368                                 lan = strtol(optarg, NULL, 0);
369                                 currentlanguage = new_language(PRIMARYLANGID(lan), SUBLANGID(lan));
370                         }
371                         break;
372                 case 'L':
373                         leave_case = 1;
374                         break;
375                 case 'm':
376                         remap = 0;
377                         break;
378                 case 'n':
379                         create_s = 0;
380                         break;
381                 case 'N':
382                         no_preprocess = 1;
383                         break;
384                 case 'o':
385                         output_name = strdup(optarg);
386                         break;
387                 case 'p':
388 #ifdef NEED_UNDERSCORE_PREFIX
389                         prefix = (char *)xmalloc(strlen(optarg)+2);
390                         prefix[0] = '_';
391                         strcpy(prefix+1, optarg);
392 #else
393                         prefix = xstrdup(optarg);
394 #endif
395                         break;
396                 case 'r':
397                         create_res = 1;
398                         break;
399                 case 's':
400                         create_dir = 1;
401                         break;
402                 case 'T':
403                         indirect_only = 1;
404                         /* Fall through */
405                 case 't':
406                         indirect = 1;
407                         break;
408                 case 'V':
409                         printf(version_string);
410                         exit(0);
411                         break;
412                 case 'w':
413                         if(!strcmp(optarg, "16"))
414                                 win32 = 0;
415                         else if(!strcmp(optarg, "32"))
416                                 win32 = 1;
417                         else
418                                 lose++;
419                         break;
420                 case 'W':
421                         pedantic = 1;
422                         break;
423                 default:
424                         lose++;
425                         break;
426                 }
427         }
428
429         if(lose)
430         {
431                 fprintf(stderr, usage);
432                 return 1;
433         }
434
435         /* Check the command line options for invalid combinations */
436         if(win32)
437         {
438                 if(!extensions)
439                 {
440                         warning("Option -e ignored with 32bit compile\n");
441                         extensions = 1;
442                 }
443         }
444
445         if(create_res)
446         {
447                 if(constant)
448                 {
449                         warning("Option -c ignored with compile to .res\n");
450                         constant = 0;
451                 }
452
453                 if(create_header)
454                 {
455                         warning("Option -[h|H] ignored with compile to .res\n");
456                         create_header = 0;
457                 }
458
459                 if(indirect)
460                 {
461                         warning("Option -t ignored with compile to .res\n");
462                         indirect = 0;
463                 }
464
465                 if(indirect_only)
466                 {
467                         warning("Option -T ignored with compile to .res\n");
468                         indirect_only = 0;
469                 }
470
471                 if(global)
472                 {
473                         warning("Option -g ignored with compile to .res\n");
474                         global = 0;
475                 }
476
477                 if(create_dir)
478                 {
479                         error("Option -r and -s cannot be used together\n");
480                 }
481
482                 if(binary)
483                 {
484                         error("Option -r and -b cannot be used together\n");
485                 }
486         }
487
488         if(byteorder != WRC_BO_NATIVE)
489         {
490                 if(binary)
491                         error("Forced byteordering not supported for binary resources\n");
492         }
493
494         if(preprocess_only)
495         {
496                 if(constant)
497                 {
498                         warning("Option -c ignored with preprocess only\n");
499                         constant = 0;
500                 }
501
502                 if(create_header)
503                 {
504                         warning("Option -[h|H] ignored with preprocess only\n");
505                         create_header = 0;
506                 }
507
508                 if(indirect)
509                 {
510                         warning("Option -t ignored with preprocess only\n");
511                         indirect = 0;
512                 }
513
514                 if(indirect_only)
515                 {
516                         error("Option -E and -T cannot be used together\n");
517                 }
518
519                 if(global)
520                 {
521                         warning("Option -g ignored with preprocess only\n");
522                         global = 0;
523                 }
524
525                 if(create_dir)
526                 {
527                         warning("Option -s ignored with preprocess only\n");
528                         create_dir = 0;
529                 }
530
531                 if(binary)
532                 {
533                         error("Option -E and -b cannot be used together\n");
534                 }
535
536                 if(no_preprocess)
537                 {
538                         error("Option -E and -N cannot be used together\n");
539                 }
540         }
541
542 #if !defined(HAVE_WINE_CONSTRUCTOR)
543         if(auto_register)
544         {
545                 warning("Autoregister code non-operable (HAVE_WINE_CONSTRUCTOR not defined)");
546                 auto_register = 0;
547         }
548 #endif
549
550         /* Set alignment power */
551         a = alignment;
552         for(alignment_pwr = 0; alignment_pwr < 10 && a > 1; alignment_pwr++)
553         {
554                 a >>= 1;
555         }
556         if(a != 1)
557         {
558                 error("Alignment must be between 1 and 1024");
559         }
560         if((1 << alignment_pwr) != alignment)
561         {
562                 error("Alignment must be a power of 2");
563         }
564
565         /* Kill io buffering when some kind of debuglevel is enabled */
566         if(debuglevel)
567         {
568                 setbuf(stdout,0);
569                 setbuf(stderr,0);
570         }
571
572         yydebug = debuglevel & DEBUGLEVEL_TRACE ? 1 : 0;
573         yy_flex_debug = debuglevel & DEBUGLEVEL_TRACE ? 1 : 0;
574         ppdebug = debuglevel & DEBUGLEVEL_PPTRACE ? 1 : 0;
575         pp_flex_debug = debuglevel & DEBUGLEVEL_PPLEX ? 1 : 0;
576
577         /* Set the default defined stuff */
578         add_cmdline_define("__WRC__=" WRC_EXP_STRINGIZE(WRC_MAJOR_VERSION));
579         add_cmdline_define("__WRC_MINOR__=" WRC_EXP_STRINGIZE(WRC_MINOR_VERSION));
580         add_cmdline_define("__WRC_MICRO__=" WRC_EXP_STRINGIZE(WRC_MICRO_VERSION));
581         add_cmdline_define("__WRC_PATCH__=" WRC_EXP_STRINGIZE(WRC_MICRO_VERSION));
582
583         add_cmdline_define("RC_INVOKED=1");
584
585         if(win32)
586         {
587                 add_cmdline_define("__WIN32__=1");
588                 add_cmdline_define("__FLAT__=1");
589         }
590
591         add_special_define("__FILE__");
592         add_special_define("__LINE__");
593         add_special_define("__DATE__");
594         add_special_define("__TIME__");
595
596         /* Check if the user set a language, else set default */
597         if(!currentlanguage)
598                 currentlanguage = new_language(0, 0);
599
600         /* Check for input file on command-line */
601         if(optind < argc)
602         {
603                 input_name = argv[optind];
604         }
605
606         if(binary && !input_name)
607         {
608                 error("Binary mode requires .res file as input\n");
609         }
610
611         /* Generate appropriate outfile names */
612         if(!output_name && !preprocess_only)
613         {
614                 output_name = dup_basename(input_name, binary ? ".res" : ".rc");
615                 strcat(output_name, create_res ? ".res" : ".s");
616         }
617
618         if(!header_name && !create_res)
619         {
620                 header_name = dup_basename(input_name, binary ? ".res" : ".rc");
621                 strcat(header_name, ".h");
622         }
623
624         /* Run the preprocessor on the input */
625         if(!no_preprocess && !binary)
626         {
627                 char *real_name;
628                 /*
629                  * Preprocess the input to a temp-file, or stdout if
630                  * no output was given.
631                  */
632
633                 chat("Starting preprocess");
634
635                 if(preprocess_only && !output_name)
636                 {
637                         ppout = stdout;
638                 }
639                 else if(preprocess_only && output_name)
640                 {
641                         if(!(ppout = fopen(output_name, "wb")))
642                                 error("Could not open %s for writing\n", output_name);
643                 }
644                 else
645                 {
646                         if(!(temp_name = tmpnam(NULL)))
647                                 error("Could nor generate a temp-name\n");
648                         temp_name = xstrdup(temp_name);
649                         if(!(ppout = fopen(temp_name, "wb")))
650                                 error("Could not create a temp-file\n");
651
652                         atexit(rm_tempfile);
653                 }
654
655                 real_name = input_name; /* Because it gets overwritten */
656
657                 if(!input_name)
658                         ppin = stdin;
659                 else
660                 {
661                         if(!(ppin = fopen(input_name, "rb")))
662                                 error("Could not open %s\n", input_name);
663                 }
664
665                 fprintf(ppout, "# 1 \"%s\" 1\n", input_name ? input_name : "");
666
667                 ret = ppparse();
668
669                 input_name = real_name;
670
671                 if(input_name)
672                         fclose(ppin);
673
674                 fclose(ppout);
675
676                 input_name = temp_name;
677
678                 if(ret)
679                         exit(1);        /* Error during preprocess */
680
681                 if(preprocess_only)
682                         exit(0);
683         }
684
685         if(!binary)
686         {
687                 /* Go from .rc to .res or .s */
688                 chat("Starting parse");
689
690                 if(!(yyin = fopen(input_name, "rb")))
691                         error("Could not open %s for input\n", input_name);
692
693                 ret = yyparse();
694
695                 if(input_name)
696                         fclose(yyin);
697
698                 if(ret)
699                 {
700                         /* Error during parse */
701                         exit(1);
702                 }
703
704                 if(debuglevel & DEBUGLEVEL_DUMP)
705                         dump_resources(resource_top);
706
707                 /* Convert the internal lists to binary data */
708                 resources2res(resource_top);
709
710                 if(create_res)
711                 {
712                         chat("Writing .res-file");
713                         write_resfile(output_name, resource_top);
714                 }
715                 else
716                 {
717                         if(create_s)
718                         {
719                                 chat("Writing .s-file");
720                                 write_s_file(output_name, resource_top);
721                         }
722                         if(create_header)
723                         {
724                                 chat("Writing .h-file");
725                                 write_h_file(header_name, resource_top);
726                         }
727                 }
728
729         }
730         else
731         {
732                 /* Go from .res to .s */
733                 chat("Reading .res-file");
734                 resource_top = read_resfile(input_name);
735                 if(create_s)
736                 {
737                         chat("Writing .s-file");
738                         write_s_file(output_name, resource_top);
739                 }
740                 if(create_header)
741                 {
742                         chat("Writing .h-file");
743                         write_h_file(header_name, resource_top);
744                 }
745         }
746
747         return 0;
748 }
749
750
751 static void rm_tempfile(void)
752 {
753         if(temp_name)
754                 unlink(temp_name);
755 }
756
757 static void segvhandler(int sig)
758 {
759         fprintf(stderr, "\n%s:%d: Oops, segment violation\n", input_name, line_number);
760         fflush(stdout);
761         fflush(stderr);
762         abort();
763 }
764