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