From: Ian Lance Taylor Date: Sun, 22 Jun 1997 21:35:35 +0000 (+0000) Subject: First stab at Windows resource compiler: X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=1d371d35ee4269aea572319c41143a2c0b75bda1;p=binutils-gdb.git First stab at Windows resource compiler: * windres.h: New file. * windres.c: New file. * resrc.c: New file. * rcparse.y: New file. * rclex.l: New file. * configure.in: Define and substitute BUILD_WINDRES. * configure: Rebuild. * Makefile.in: Rebuild dependencies. (WINDRES_PROG): New variable. (PROGS): Add @BUILD_WINDRES@. (HFILES): Add dlltool.h and windres.h. (CFILES): Add windres.c and resrc.c. (GENERATED_CFILES): Add rcparse.c and rclex.c. (WINDRES_OBJS): New variable. $(WINDRES_PROG): New target. (rcparse.c, rcparse.h, rclex.c): New targets. Snapshot. windres can parse and print rc files. --- diff --git a/binutils/.Sanitize b/binutils/.Sanitize index f253b5053da..5bae7957141 100644 --- a/binutils/.Sanitize +++ b/binutils/.Sanitize @@ -82,6 +82,9 @@ objdump.c prdbg.c ranlib.1 ranlib.sh +rclex.l +rcparse.y +resrc.c rdcoff.c rddbg.c sanity.sh @@ -98,6 +101,8 @@ syslex.l sysroff.info testsuite version.c +windres.h +windres.c wrstabs.c Things-to-lose: diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 1ab90556c0b..2a685dcc519 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,23 @@ +Sun Jun 22 17:29:41 1997 Ian Lance Taylor + + First stab at Windows resource compiler: + * windres.h: New file. + * windres.c: New file. + * resrc.c: New file. + * rcparse.y: New file. + * rclex.l: New file. + * configure.in: Define and substitute BUILD_WINDRES. + * configure: Rebuild. + * Makefile.in: Rebuild dependencies. + (WINDRES_PROG): New variable. + (PROGS): Add @BUILD_WINDRES@. + (HFILES): Add dlltool.h and windres.h. + (CFILES): Add windres.c and resrc.c. + (GENERATED_CFILES): Add rcparse.c and rclex.c. + (WINDRES_OBJS): New variable. + $(WINDRES_PROG): New target. + (rcparse.c, rcparse.h, rclex.c): New targets. + Thu Jun 12 12:27:51 1997 Ian Lance Taylor * dlltool.c (export_type): Add data field. diff --git a/binutils/Makefile.in b/binutils/Makefile.in index 985e2261d0a..65d25f79fa4 100644 --- a/binutils/Makefile.in +++ b/binutils/Makefile.in @@ -103,12 +103,13 @@ ADDR2LINE_PROG=addr2line NLMCONV_PROG=nlmconv DLLTOOL_PROG=dlltool +WINDRES_PROG=windres SRCONV_PROG=srconv sysdump coffdump MANPAGES= ar nm objdump ranlib size strings strip objcopy addr2line nlmconv -PROGS = $(SIZE_PROG) $(OBJDUMP_PROG) $(NM_PROG) $(AR_PROG) $(STRINGS_PROG) $(STRIP_PROG) $(RANLIB_PROG) $(DEMANGLER_PROG) $(OBJCOPY_PROG) $(ADDR2LINE_PROG) @BUILD_NLMCONV@ @BUILD_SRCONV@ @BUILD_DLLTOOL@ +PROGS = $(SIZE_PROG) $(OBJDUMP_PROG) $(NM_PROG) $(AR_PROG) $(STRINGS_PROG) $(STRIP_PROG) $(RANLIB_PROG) $(DEMANGLER_PROG) $(OBJCOPY_PROG) $(ADDR2LINE_PROG) @BUILD_NLMCONV@ @BUILD_SRCONV@ @BUILD_DLLTOOL@ @BUILD_WINDRES@ STAGESTUFF = $(PROGS) *.o # Files that can be generated, but should be in the distribution. # Don't build $(DEMANGLER_PROG).1, since its name may vary with the @@ -127,7 +128,8 @@ DEP = mkdep ALL_CFLAGS = -D_GNU_SOURCE $(INCLUDES) @HDEFINES@ $(CFLAGS) -HFILES = arsup.h bucomm.h budbg.h coffgrok.h debug.h nlmconv.h +HFILES = arsup.h bucomm.h budbg.h coffgrok.h debug.h nlmconv.h dlltool.h \ + windres.h GENERATED_HFILES = arparse.h sysroff.h sysinfo.h defparse.h @@ -135,11 +137,12 @@ CFILES = addr2line.c ar.c arsup.c bucomm.c coffdump.c coffgrok.c debug.c \ dlltool.c filemode.c ieee.c is-ranlib.c is-strip.c maybe-ranlib.c \ maybe-strip.c nlmconv.c nm.c not-ranlib.c not-strip.c \ objcopy.c objdump.c prdbg.c rdcoff.c rddbg.c size.c srconv.c \ - stabs.c strings.c sysdump.c version.c wrstabs.c + stabs.c strings.c sysdump.c version.c wrstabs.c \ + windres.c resrc.c GENERATED_CFILES = \ underscore.c arparse.c arlex.c sysroff.c sysinfo.c syslex.c \ - defparse.c deflex.c nlmheader.c + defparse.c deflex.c nlmheader.c rcparse.c rclex.c .c.o: $(CC) -c $(ALL_CFLAGS) $< @@ -235,7 +238,7 @@ check: site.exp TCL_LIBRARY=$${srcroot}/../tcl/library ; \ export TCL_LIBRARY ; else true; fi ; \ $(RUNTEST) --tool binutils --srcdir $(srcdir)/testsuite \ - $(RUNTESTFLAGS) CC="$(CC_FOR_TARGET)" CFLAGS="$(CFLAGS)" + $(RUNTESTFLAGS) CC_FOR_TARGET="$(CC_FOR_TARGET)" CFLAGS_FOR_TARGET="$(CFLAGS)" installcheck: /bin/sh $(srcdir)/sanity.sh $(bindir) @@ -406,6 +409,26 @@ nlmconv.o: nlmconv.c $(INCDIR)/coff/sym.h $(INCDIR)/coff/ecoff.h $(NLMCONV_PROG): nlmconv.o nlmheader.o $(ADDL_DEPS) $(HLDENV) $(CC) $(HLDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ nlmconv.o nlmheader.o $(ADDL_LIBS) $(EXTRALIBS) +WINDRES_OBJS = windres.o resrc.o rcparse.o rclex.o + +$(WINDRES_PROG): $(WINDRES_OBJS) $(ADDL_DEPS) + $(HLDENV) $(CC) $(HLDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(WINDRES_OBJS) $(ADDL_LIBS) $(EXTRALIBS) + +# Depend upon defparse.c to avoid building both defparse.c and +# rcparse.c simultaneously. +rcparse.c: rcparse.y defparse.c + $(BISON) $(BISONFLAGS) $(srcdir)/rcparse.y + mv -f y.tab.c rcparse.c + mv -f y.tab.h rcparse.h + +rcparse.h: rcparse.c + +# Depend upon deflex.c to avoid building both deflex.c and rclex.c +# simultaneously. +rclex.c: rclex.l deflex.c + $(LEX) $(LEX_OPTIONS) $(srcdir)/rclex.l + mv lex.yy.c rclex.c + # Targets to rebuild dependencies in this Makefile. # Have to get rid of .dep1 here so that "$?" later includes all of $(CFILES). .dep: dep.sed $(CFILES) $(HFILES) $(GENERATED_CFILES) $(GENERATED_HFILES) config.h @@ -533,7 +556,7 @@ distclean: maintainer-clean realclean: clean distclean @echo "This command is intended for maintainers to use;" @echo "it deletes files that may require special tools to rebuild." - -rm -f $(DISTSTUFF) *.info* TAGS + -rm -f $(DISTSTUFF) binutils.info* TAGS etags tags: TAGS @@ -680,7 +703,7 @@ debug.o: debug.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ debug.h dlltool.o: dlltool.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ $(INCDIR)/libiberty.h bucomm.h config.h $(INCDIR)/fopen-same.h \ - $(INCDIR)/getopt.h $(INCDIR)/demangle.h + $(INCDIR)/getopt.h $(INCDIR)/demangle.h dlltool.h filemode.o: filemode.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ bucomm.h config.h $(INCDIR)/fopen-same.h ieee.o: ieee.c ../bfd/bfd.h $(INCDIR)/ansidecl.h $(INCDIR)/ieee.h \ @@ -737,6 +760,12 @@ wrstabs.o: wrstabs.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ bucomm.h config.h $(INCDIR)/fopen-same.h $(INCDIR)/libiberty.h \ debug.h budbg.h $(INCDIR)/aout/aout64.h $(INCDIR)/aout/stab_gnu.h \ $(INCDIR)/aout/stab.def +windres.o: windres.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ + $(INCDIR)/getopt.h bucomm.h config.h $(INCDIR)/fopen-same.h \ + $(INCDIR)/libiberty.h windres.h +resrc.o: resrc.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ + bucomm.h config.h $(INCDIR)/fopen-same.h $(INCDIR)/libiberty.h \ + windres.h underscore.o: underscore.c arparse.o: arparse.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ bucomm.h config.h $(INCDIR)/fopen-same.h arsup.h @@ -744,10 +773,18 @@ arlex.o: arlex.c $(INCDIR)/libiberty.h arparse.h sysroff.o: sysroff.c sysinfo.o: sysinfo.c syslex.o: syslex.c sysinfo.h -defparse.o: defparse.c -deflex.o: deflex.c defparse.h +defparse.o: defparse.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ + bucomm.h config.h $(INCDIR)/fopen-same.h dlltool.h +deflex.o: deflex.c $(INCDIR)/libiberty.h $(INCDIR)/ansidecl.h \ + defparse.h dlltool.h nlmheader.o: nlmheader.c ../bfd/bfd.h bucomm.h config.h \ $(INCDIR)/fopen-same.h $(INCDIR)/nlm/common.h $(INCDIR)/nlm/internal.h \ nlmconv.h +rcparse.o: rcparse.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ + bucomm.h config.h $(INCDIR)/fopen-same.h $(INCDIR)/libiberty.h \ + windres.h +rclex.o: rclex.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ + bucomm.h config.h $(INCDIR)/fopen-same.h $(INCDIR)/libiberty.h \ + windres.h rcparse.h # IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/binutils/configure b/binutils/configure index 066b290e8ec..57ba36e59a1 100755 --- a/binutils/configure +++ b/binutils/configure @@ -1006,8 +1006,13 @@ esac if test "${commonbfdlib}" = "true"; then # when a shared libbfd is built with --enable-commonbfdlib, - # all of libopcodes is available in libbfd.so - OPCODES= + # all of libopcodes is available in libbfd.so. Unfortunately, on + # HP/UX, when using gcc -g, the linker does a static link, so we + # need to continue linking against opcodes on that platform. + case "${host}" in + *-*-hpux*) ;; + *) OPCODES= ;; + esac fi @@ -1024,7 +1029,7 @@ fi echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 -echo "configure:1028: checking how to run the C preprocessor" >&5 +echo "configure:1033: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= @@ -1039,13 +1044,13 @@ else # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1049: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1054: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : @@ -1056,13 +1061,13 @@ else rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1066: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1071: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : @@ -1088,17 +1093,17 @@ for ac_hdr in string.h strings.h stdlib.h unistd.h fcntl.h sys/file.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:1092: checking for $ac_hdr" >&5 +echo "configure:1097: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1102: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1107: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1125,12 +1130,12 @@ fi done echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&6 -echo "configure:1129: checking for sys/wait.h that is POSIX.1 compatible" >&5 +echo "configure:1134: checking for sys/wait.h that is POSIX.1 compatible" >&5 if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -1146,7 +1151,7 @@ wait (&s); s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; ; return 0; } EOF -if { (eval echo configure:1150: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1155: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_header_sys_wait_h=yes else @@ -1169,19 +1174,19 @@ fi # The Ultrix 4.2 mips builtin alloca declared by alloca.h only works # for constant arguments. Useless! echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6 -echo "configure:1173: checking for working alloca.h" >&5 +echo "configure:1178: checking for working alloca.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { char *p = alloca(2 * sizeof(int)); ; return 0; } EOF -if { (eval echo configure:1185: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1190: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* ac_cv_header_alloca_h=yes else @@ -1202,12 +1207,12 @@ EOF fi echo $ac_n "checking for alloca""... $ac_c" 1>&6 -echo "configure:1206: checking for alloca" >&5 +echo "configure:1211: checking for alloca" >&5 if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1239: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* ac_cv_func_alloca_works=yes else @@ -1262,12 +1267,12 @@ EOF echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6 -echo "configure:1266: checking whether alloca needs Cray hooks" >&5 +echo "configure:1271: checking whether alloca needs Cray hooks" >&5 if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&6 if test $ac_cv_os_cray = yes; then for ac_func in _getb67 GETB67 getb67; do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:1296: checking for $ac_func" >&5 +echo "configure:1301: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1329: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -1347,7 +1352,7 @@ done fi echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6 -echo "configure:1351: checking stack direction for C alloca" >&5 +echo "configure:1356: checking stack direction for C alloca" >&5 if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1355,7 +1360,7 @@ else ac_cv_c_stack_direction=0 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:1383: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ac_cv_c_stack_direction=1 else @@ -1398,12 +1403,12 @@ fi for ac_func in sbrk utimes do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:1402: checking for $ac_func" >&5 +echo "configure:1407: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1435: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -1452,12 +1457,12 @@ done if test "x$cross_compiling" = "xno"; then echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 -echo "configure:1456: checking for ANSI C header files" >&5 +echo "configure:1461: checking for ANSI C header files" >&5 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -1465,7 +1470,7 @@ else #include EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1469: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1474: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1482,7 +1487,7 @@ rm -f conftest* if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat > conftest.$ac_ext < EOF @@ -1500,7 +1505,7 @@ fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat > conftest.$ac_ext < EOF @@ -1521,7 +1526,7 @@ if test "$cross_compiling" = yes; then : else cat > conftest.$ac_ext < #define ISLOWER(c) ('a' <= (c) && (c) <= 'z') @@ -1532,7 +1537,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } EOF -if { (eval echo configure:1536: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:1541: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then : else @@ -1556,12 +1561,12 @@ EOF fi echo $ac_n "checking for pid_t""... $ac_c" 1>&6 -echo "configure:1560: checking for pid_t" >&5 +echo "configure:1565: checking for pid_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -1590,17 +1595,17 @@ fi ac_safe=`echo "vfork.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for vfork.h""... $ac_c" 1>&6 -echo "configure:1594: checking for vfork.h" >&5 +echo "configure:1599: checking for vfork.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1604: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1609: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1625,18 +1630,18 @@ else fi echo $ac_n "checking for working vfork""... $ac_c" 1>&6 -echo "configure:1629: checking for working vfork" >&5 +echo "configure:1634: checking for working vfork" >&5 if eval "test \"`echo '$''{'ac_cv_func_vfork_works'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then echo $ac_n "checking for vfork""... $ac_c" 1>&6 -echo "configure:1635: checking for vfork" >&5 +echo "configure:1640: checking for vfork" >&5 if eval "test \"`echo '$''{'ac_cv_func_vfork'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1668: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_vfork=yes" else @@ -1680,7 +1685,7 @@ fi else cat > conftest.$ac_ext < @@ -1775,7 +1780,7 @@ main() { } } EOF -if { (eval echo configure:1779: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:1784: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ac_cv_func_vfork_works=yes else @@ -1799,12 +1804,12 @@ fi else echo $ac_n "checking for vfork""... $ac_c" 1>&6 -echo "configure:1803: checking for vfork" >&5 +echo "configure:1808: checking for vfork" >&5 if eval "test \"`echo '$''{'ac_cv_func_vfork'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1836: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_vfork=yes" else @@ -1853,19 +1858,19 @@ fi fi echo $ac_n "checking for time_t in time.h""... $ac_c" 1>&6 -echo "configure:1857: checking for time_t in time.h" >&5 +echo "configure:1862: checking for time_t in time.h" >&5 if eval "test \"`echo '$''{'bu_cv_decl_time_t_time_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { time_t i; ; return 0; } EOF -if { (eval echo configure:1869: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1874: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* bu_cv_decl_time_t_time_h=yes else @@ -1886,19 +1891,19 @@ EOF fi echo $ac_n "checking for time_t in sys/types.h""... $ac_c" 1>&6 -echo "configure:1890: checking for time_t in sys/types.h" >&5 +echo "configure:1895: checking for time_t in sys/types.h" >&5 if eval "test \"`echo '$''{'bu_cv_decl_time_t_types_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { time_t i; ; return 0; } EOF -if { (eval echo configure:1902: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1907: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* bu_cv_decl_time_t_types_h=yes else @@ -1921,12 +1926,12 @@ fi # Under Next 3.2 apparently does not define struct utimbuf # by default. echo $ac_n "checking for utime.h""... $ac_c" 1>&6 -echo "configure:1925: checking for utime.h" >&5 +echo "configure:1930: checking for utime.h" >&5 if eval "test \"`echo '$''{'bu_cv_header_utime_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #ifdef HAVE_TIME_H @@ -1937,7 +1942,7 @@ int main() { struct utimbuf s; ; return 0; } EOF -if { (eval echo configure:1941: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1946: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* bu_cv_header_utime_h=yes else @@ -1958,12 +1963,12 @@ EOF fi echo $ac_n "checking whether fprintf must be declared""... $ac_c" 1>&6 -echo "configure:1962: checking whether fprintf must be declared" >&5 +echo "configure:1967: checking whether fprintf must be declared" >&5 if eval "test \"`echo '$''{'bfd_cv_decl_needed_fprintf'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < @@ -1984,7 +1989,7 @@ int main() { char *(*pfn) = (char *(*)) fprintf ; return 0; } EOF -if { (eval echo configure:1988: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1993: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* bfd_cv_decl_needed_fprintf=no else @@ -2006,12 +2011,12 @@ EOF fi echo $ac_n "checking whether strstr must be declared""... $ac_c" 1>&6 -echo "configure:2010: checking whether strstr must be declared" >&5 +echo "configure:2015: checking whether strstr must be declared" >&5 if eval "test \"`echo '$''{'bfd_cv_decl_needed_strstr'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < @@ -2032,7 +2037,7 @@ int main() { char *(*pfn) = (char *(*)) strstr ; return 0; } EOF -if { (eval echo configure:2036: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2041: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* bfd_cv_decl_needed_strstr=no else @@ -2054,12 +2059,12 @@ EOF fi echo $ac_n "checking whether sbrk must be declared""... $ac_c" 1>&6 -echo "configure:2058: checking whether sbrk must be declared" >&5 +echo "configure:2063: checking whether sbrk must be declared" >&5 if eval "test \"`echo '$''{'bfd_cv_decl_needed_sbrk'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < @@ -2080,7 +2085,7 @@ int main() { char *(*pfn) = (char *(*)) sbrk ; return 0; } EOF -if { (eval echo configure:2084: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2089: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* bfd_cv_decl_needed_sbrk=no else @@ -2102,12 +2107,12 @@ EOF fi echo $ac_n "checking whether getenv must be declared""... $ac_c" 1>&6 -echo "configure:2106: checking whether getenv must be declared" >&5 +echo "configure:2111: checking whether getenv must be declared" >&5 if eval "test \"`echo '$''{'bfd_cv_decl_needed_getenv'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < @@ -2128,7 +2133,7 @@ int main() { char *(*pfn) = (char *(*)) getenv ; return 0; } EOF -if { (eval echo configure:2132: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2137: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* bfd_cv_decl_needed_getenv=no else @@ -2165,7 +2170,7 @@ esac if test -n "$enable_targets"; then for targ in `echo $enable_targets | sed 's/,/ /g'` do - result=`$ac_config_sub $targ 2>/dev/null` + result=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $targ 2>/dev/null` if test -n "$result"; then canon_targets="$canon_targets $result" else @@ -2181,6 +2186,7 @@ NLMCONV_DEFS= BUILD_SRCONV= BUILD_DLLTOOL= DLLTOOL_DEFS= +BUILD_WINDRES= for targ in $target $canon_targets do @@ -2215,14 +2221,17 @@ do arm-*pe*) BUILD_DLLTOOL='$(DLLTOOL_PROG)' DLLTOOL_DEFS="$DLLTOOL_DEFS -DDLLTOOL_ARM" + BUILD_WINDRES='$(WINDRES_PROG)' ;; i[3-6]86-*pe* | i[3-6]86-*-cygwin32) BUILD_DLLTOOL='$(DLLTOOL_PROG)' DLLTOOL_DEFS="$DLLTOOL_DEFS -DDLLTOOL_I386" + BUILD_WINDRES='$(WINDRES_PROG)' ;; powerpc*-*-*pe* | powerpc*-*-cygwin32) BUILD_DLLTOOL='$(DLLTOOL_PROG)' DLLTOOL_DEFS="$DLLTOOL_DEFS -DDLLTOOL_PPC" + BUILD_WINDRES='$(WINDRES_PROG)' ;; esac fi @@ -2234,6 +2243,7 @@ done + targ=$target . $srcdir/../bfd/config.bfd if test "x$targ_underscore" = "xyes"; then @@ -2408,6 +2418,7 @@ s%@BUILD_NLMCONV@%$BUILD_NLMCONV%g s%@BUILD_SRCONV@%$BUILD_SRCONV%g s%@BUILD_DLLTOOL@%$BUILD_DLLTOOL%g s%@DLLTOOL_DEFS@%$DLLTOOL_DEFS%g +s%@BUILD_WINDRES@%$BUILD_WINDRES%g s%@UNDERSCORE@%$UNDERSCORE%g CEOF diff --git a/binutils/configure.in b/binutils/configure.in index f5ce9d6f73c..67fdb06d5b5 100644 --- a/binutils/configure.in +++ b/binutils/configure.in @@ -98,8 +98,13 @@ esac if test "${commonbfdlib}" = "true"; then # when a shared libbfd is built with --enable-commonbfdlib, - # all of libopcodes is available in libbfd.so - OPCODES= + # all of libopcodes is available in libbfd.so. Unfortunately, on + # HP/UX, when using gcc -g, the linker does a static link, so we + # need to continue linking against opcodes on that platform. + case "${host}" in + *-*-hpux*) ;; + *) OPCODES= ;; + esac fi AC_SUBST(BFDLIB) @@ -166,7 +171,7 @@ BFD_BINARY_FOPEN if test -n "$enable_targets"; then for targ in `echo $enable_targets | sed 's/,/ /g'` do - result=`$ac_config_sub $targ 2>/dev/null` + result=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $targ 2>/dev/null` if test -n "$result"; then canon_targets="$canon_targets $result" else @@ -182,6 +187,7 @@ NLMCONV_DEFS= BUILD_SRCONV= BUILD_DLLTOOL= DLLTOOL_DEFS= +BUILD_WINDRES= for targ in $target $canon_targets do @@ -218,16 +224,19 @@ changequote([,])dnl arm-*pe*) BUILD_DLLTOOL='$(DLLTOOL_PROG)' DLLTOOL_DEFS="$DLLTOOL_DEFS -DDLLTOOL_ARM" + BUILD_WINDRES='$(WINDRES_PROG)' ;; changequote(,)dnl i[3-6]86-*pe* | i[3-6]86-*-cygwin32) changequote([,])dnl BUILD_DLLTOOL='$(DLLTOOL_PROG)' DLLTOOL_DEFS="$DLLTOOL_DEFS -DDLLTOOL_I386" + BUILD_WINDRES='$(WINDRES_PROG)' ;; powerpc*-*-*pe* | powerpc*-*-cygwin32) BUILD_DLLTOOL='$(DLLTOOL_PROG)' DLLTOOL_DEFS="$DLLTOOL_DEFS -DDLLTOOL_PPC" + BUILD_WINDRES='$(WINDRES_PROG)' ;; esac fi @@ -238,6 +247,7 @@ AC_SUBST(BUILD_NLMCONV) AC_SUBST(BUILD_SRCONV) AC_SUBST(BUILD_DLLTOOL) AC_SUBST(DLLTOOL_DEFS) +AC_SUBST(BUILD_WINDRES) targ=$target . $srcdir/../bfd/config.bfd diff --git a/binutils/rclex.l b/binutils/rclex.l new file mode 100644 index 00000000000..eb36bf6533e --- /dev/null +++ b/binutils/rclex.l @@ -0,0 +1,320 @@ +%{ /* rclex.l -- lexer for Windows rc files parser */ +/* Copyright 1997 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support. + + This file is part of GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* This is a lex input file which generates a lexer used by the + Windows rc file parser. It basically just recognized a bunch of + keywords. */ + +#include "bfd.h" +#include "bucomm.h" +#include "libiberty.h" +#include "windres.h" +#include "rcparse.h" + +#include +#include + +static void cpp_line PARAMS ((const char *)); +static char *handle_quotes PARAMS ((const char *)); + +%} + +%% + +"BEGIN" { return BEG; } +"END" { return END; } +"ACCELERATORS" { return ACCELERATORS; } +"VIRTKEY" { return VIRTKEY; } +"ASCII" { return ASCII; } +"NOINVERT" { return NOINVERT; } +"SHIFT" { return SHIFT; } +"CONTROL" { return CONTROL; } +"ALT" { return ALT; } +"BITMAP" { return BITMAP; } +"CURSOR" { return CURSOR; } +"DIALOG" { return DIALOG; } +"DIALOGEX" { return DIALOGEX; } +"EXSTYLE" { return EXSTYLE; } +"CAPTION" { return CAPTION; } +"CLASS" { return CLASS; } +"STYLE" { return STYLE; } +"AUTO3STATE" { return AUTO3STATE; } +"AUTOCHECKBOX" { return AUTOCHECKBOX; } +"AUTORADIOBUTTON" { return AUTORADIOBUTTON; } +"CHECKBOX" { return CHECKBOX; } +"COMBOBOX" { return COMBOBOX; } +"CTEXT" { return CTEXT; } +"DEFPUSHBUTTON" { return DEFPUSHBUTTON; } +"EDITTEXT" { return EDITTEXT; } +"GROUPBOX" { return GROUPBOX; } +"LISTBOX" { return LISTBOX; } +"LTEXT" { return LTEXT; } +"PUSHBOX" { return PUSHBOX; } +"PUSHBUTTON" { return PUSHBUTTON; } +"RADIOBUTTON" { return RADIOBUTTON; } +"RTEXT" { return RTEXT; } +"SCROLLBAR" { return SCROLLBAR; } +"STATE3" { return STATE3; } +"USERBUTTON" { return USERBUTTON; } +"BEDIT" { return BEDIT; } +"HEDIT" { return HEDIT; } +"IEDIT" { return IEDIT; } +"FONT" { return FONT; } +"ICON" { return ICON; } +"LANGUAGE" { return LANGUAGE; } +"CHARACTERISTICS" { return CHARACTERISTICS; } +"VERSION" { return VERSION; } +"MENU" { return MENU; } +"MENUEX" { return MENUEX; } +"MENUITEM" { return MENUITEM; } +"SEPARATOR" { return SEPARATOR; } +"POPUP" { return POPUP; } +"CHECKED" { return CHECKED; } +"GRAYED" { return GRAYED; } +"HELP" { return HELP; } +"INACTIVE" { return INACTIVE; } +"MENUBARBREAK" { return MENUBARBREAK; } +"MENUBREAK" { return MENUBREAK; } +"MESSAGETABLE" { return MESSAGETABLE; } +"RCDATA" { return RCDATA; } +"STRINGTABLE" { return STRINGTABLE; } +"VERSIONINFO" { return VERSIONINFO; } +"FILEVERSION" { return FILEVERSION; } +"PRODUCTVERSION" { return PRODUCTVERSION; } +"FILEFLAGSMASK" { return FILEFLAGSMASK; } +"FILEFLAGS" { return FILEFLAGS; } +"FILEOS" { return FILEOS; } +"FILETYPE" { return FILETYPE; } +"FILESUBTYPE" { return FILESUBTYPE; } +"VALUE" { return VALUE; } +"MOVEABLE" { return MOVEABLE; } +"FIXED" { return FIXED; } +"PURE" { return PURE; } +"IMPURE" { return IMPURE; } +"PRELOAD" { return PRELOAD; } +"LOADONCALL" { return LOADONCALL; } +"DISCARDABLE" { return DISCARDABLE; } +"NOT" { return NOT; } + +"BLOCK"[ \t\n]*"\""[^\#\n]*"\"" { + char *s, *send; + + /* This is a hack to let us parse version + information easily. */ + + s = strchr (yytext, '"'); + ++s; + send = strchr (s, '"'); + if (strncmp (s, "StringFileInfo", + sizeof "StringFileInfo" - 1) == 0 + && s + sizeof "StringFileInfo" - 1 == send) + return BLOCKSTRINGFILEINFO; + else if (strncmp (s, "VarFileInfo", + sizeof "VarFileInfo" - 1) == 0 + && s + sizeof "VarFileInfo" - 1 == send) + return BLOCKVARFILEINFO; + else + { + yylval.s = (char *) xmalloc (send - s + 1); + strncpy (yylval.s, s, send - s); + yylval.s[send - s] = '\0'; + return BLOCK; + } + } + +"#"[^\n]* { + cpp_line (yytext); + } + +[0-9][x0-9A-Fa-f]*L { + yylval.i.val = strtoul (yytext, 0, 0); + yylval.i.dword = 1; + return NUMBER; + } + +[0-9][x0-9A-Fa-f]* { + yylval.i.val = strtoul (yytext, 0, 0); + yylval.i.dword = 0; + return NUMBER; + } + +("\""[^\"\n]*"\""[ \t]*)+ { + yylval.s = handle_quotes (yytext); + return QUOTEDSTRING; + } + +[A-Za-z][^ \t\r\n]* { + yylval.s = xstrdup (yytext); + return STRING; + } + +[\n] { ++rc_lineno; } +[ \t\r]+ { /* ignore whitespace */ } +. { return *yytext; } + +%% +#ifndef yywrap +/* This is needed for some versions of lex. */ +int yywrap () +{ + return 1; +} +#endif + +/* Handle a C preprocessor line. */ + +static void +cpp_line (s) + const char *s; +{ + int line; + char *send, *fn; + + ++s; + while (isspace (*s)) + ++s; + + line = strtol (s, &send, 0); + if (*send != '\0' && ! isspace (*send)) + return; + + /* Subtract 1 because we are about to count the newline. */ + rc_lineno = line - 1; + + s = send; + while (isspace (*s)) + ++s; + + if (*s != '"') + return; + + ++s; + send = strchr (s, '"'); + if (send == NULL) + return; + + fn = (char *) xmalloc (send - s + 1); + strncpy (fn, s, send - s); + fn[send - s] = '\0'; + + free (rc_filename); + rc_filename = fn; +} + +/* Handle a quoted string. The quotes are stripped. A pair of quotes + in a string are turned into a single quote. Adjacent strings are + merged separated by whitespace are merged, as in C. */ + +static char * +handle_quotes (input) + const char *input; +{ + char *ret, *s; + const char *t; + int ch; + + ret = (char *) xmalloc (strlen (input) + 1); + + s = ret; + t = input; + if (*t == '"') + ++t; + while (*t != '\0') + { + if (*t == '\\') + { + ++t; + switch (*t) + { + case '\0': + rcparse_warning ("backslash at end of string"); + break; + + case '\"': + rcparse_warning ("use \"\" to put \" in a string"); + break; + + case '\\': + *s++ = *t++; + break; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + ch = *t - '0'; + ++t; + if (*t >= '0' && *t <= '7') + { + ch = (ch << 3) | (*t - '0'); + ++t; + if (*t >= '0' && *t <= '7') + { + ch = (ch << 3) | (*t - '0'); + ++t; + } + } + *s++ = ch; + break; + + case 'x': + ++t; + ch = 0; + while (1) + { + if (*t >= '0' && *t <= '9') + ch = (ch << 4) | (*t - '0'); + else if (*t >= 'a' && *t <= 'f') + ch = (ch << 4) | (*t - 'a'); + else if (*t >= 'A' && *t <= 'F') + ch = (ch << 4) | (*t - 'A'); + else + break; + ++t; + } + *s++ = ch; + break; + } + } + else if (*t != '"') + *s++ = *t++; + else if (t[1] == '\0') + break; + else if (t[1] == '"') + { + *s++ = '"'; + t += 2; + } + else + { + ++t; + assert (isspace (*t)); + while (isspace (*t)) + ++t; + if (*t == '\0') + break; + assert (*t == '"'); + ++t; + } + } + + *s = '\0'; + + return ret; +} diff --git a/binutils/rcparse.y b/binutils/rcparse.y new file mode 100644 index 00000000000..56c6db86a63 --- /dev/null +++ b/binutils/rcparse.y @@ -0,0 +1,1513 @@ +%{ /* rcparse.y -- parser for Windows rc files + Copyright 1997 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support. + + This file is part of GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* This is a parser for Windows rc files. It is based on the parser + by Gunther Ebert . */ + +#include "bfd.h" +#include "bucomm.h" +#include "libiberty.h" +#include "windres.h" + +/* The current language. The default is U.S. English. */ + +static unsigned short language = 0x409; + +/* The resource information during a sub statement. */ + +static struct res_res_info sub_res_info; + +/* Dialog information. This is built by the nonterminals styles and + controls. */ + +static struct dialog dialog; + +/* This is used when building a style. It is modified by the + nonterminal styleexpr. */ + +static unsigned long style; + +/* These are used when building a control. They are set before using + control_params. */ + +static unsigned long base_style; +static unsigned long default_style; +static unsigned long class; + +%} + +%union +{ + struct accelerator acc; + struct accelerator *pacc; + struct dialog_control *dialog_control; + struct menuitem *menuitem; + struct rcdata_data *rcdata; + struct stringtable_data *stringtable; + struct fixed_versioninfo *fixver; + struct ver_info *verinfo; + struct ver_stringinfo *verstring; + struct ver_varinfo *vervar; + struct res_id id; + struct res_res_info res_info; + struct + { + unsigned short on; + unsigned short off; + } memflags; + struct + { + unsigned long val; + /* Nonzero if this number was explicitly specified as long. */ + int dword; + } i; + unsigned long il; + unsigned short is; + char *s; +}; + +%token BEG END +%token ACCELERATORS VIRTKEY ASCII NOINVERT SHIFT CONTROL ALT +%token BITMAP +%token CURSOR +%token DIALOG DIALOGEX EXSTYLE CAPTION CLASS STYLE +%token AUTO3STATE AUTOCHECKBOX AUTORADIOBUTTON CHECKBOX COMBOBOX CTEXT +%token DEFPUSHBUTTON EDITTEXT GROUPBOX LISTBOX LTEXT PUSHBOX PUSHBUTTON +%token RADIOBUTTON RTEXT SCROLLBAR STATE3 USERBUTTON +%token BEDIT HEDIT IEDIT +%token FONT +%token ICON +%token LANGUAGE CHARACTERISTICS VERSION +%token MENU MENUEX MENUITEM SEPARATOR POPUP CHECKED GRAYED HELP INACTIVE +%token MENUBARBREAK MENUBREAK +%token MESSAGETABLE +%token RCDATA +%token STRINGTABLE +%token VERSIONINFO FILEVERSION PRODUCTVERSION FILEFLAGSMASK FILEFLAGS +%token FILEOS FILETYPE FILESUBTYPE BLOCKSTRINGFILEINFO BLOCKVARFILEINFO +%token VALUE +%token BLOCK +%token MOVEABLE FIXED PURE IMPURE PRELOAD LOADONCALL DISCARDABLE +%token NOT +%token QUOTEDSTRING STRING +%token NUMBER + +%type acc_entries +%type acc_entry acc_event +%type control control_params +%type menuitems menuitem menuexitems menuexitem +%type optrcdata_data rcdata_data opt_control_data +%type fixedverinfo +%type verblocks +%type vervals +%type vertrans +%type suboptions memflags_move_discard memflags_move +%type memflag +%type id +%type exstyle parennumber +%type numexpr posnumexpr cnumexpr optcnumexpr cposnumexpr +%type acc_options acc_option menuitem_flags menuitem_flag +%type optstringc file_name +%type sizednumexpr sizedposnumexpr + +%left '|' +%left '^' +%left '&' +%left '+' '-' +%left '*' '/' '%' +%right '~' NEG + +%% + +input: + /* empty */ + | input accelerator + | input bitmap + | input cursor + | input dialog + | input font + | input icon + | input language + | input menu + | input menuex + | input messagetable + | input rcdata + | input stringtable + | input user + | input versioninfo + ; + +/* Accelerator resources. */ + +accelerator: + id ACCELERATORS suboptions BEG acc_entries END + { + define_accelerator ($1, &$3, $5); + } + ; + +acc_entries: + /* empty */ + { + $$ = NULL; + } + | acc_entries acc_entry + { + struct accelerator *a; + + a = (struct accelerator *) xmalloc (sizeof *a); + *a = $2; + if ($1 == NULL) + $$ = a; + else + { + struct accelerator **pp; + + for (pp = &$1->next; *pp != NULL; pp = &(*pp)->next) + ; + *pp = a; + $$ = $1; + } + } + ; + +acc_entry: + acc_event cposnumexpr + { + $$ = $1; + $$.id = $2; + } + | acc_event cposnumexpr ',' acc_options + { + $$ = $1; + $$.id = $2; + $$.flags |= $4; + } + ; + +acc_event: + QUOTEDSTRING + { + char *s = $1; + + $$.id = 0; + if (*s != '^') + $$.flags = 0; + else + { + $$.flags = ACC_CONTROL; + ++s; + } + $$.key = *s; + if (s[1] != '\0') + rcparse_warning ("accelerator should only be one character"); + free (s); + } + | posnumexpr + { + $$.flags = 0; + $$.id = 0; + $$.key = $1; + } + ; + +acc_options: + acc_option + { + $$ = $1; + } + | acc_options ',' acc_option + { + $$ = $1 | $3; + } + ; + +acc_option: + VIRTKEY + { + $$ = ACC_VIRTKEY; + } + | ASCII + { + /* This is just the absence of VIRTKEY. */ + $$ = 0; + } + | NOINVERT + { + $$ = ACC_NOINVERT; + } + | SHIFT + { + $$ = ACC_SHIFT; + } + | CONTROL + { + $$ = ACC_CONTROL; + } + | ALT + { + $$ = ACC_ALT; + } + ; + +/* Bitmap resources. */ + +bitmap: + id BITMAP memflags_move file_name + { + define_bitmap ($1, &$3, $4); + free ($4); + } + ; + +/* Cursor resources. */ + +cursor: + id CURSOR memflags_move_discard file_name + { + define_cursor ($1, &$3, $4); + free ($4); + } + ; + +/* Dialog resources. */ + +dialog: + id DIALOG memflags_move exstyle posnumexpr cnumexpr cnumexpr + cnumexpr + { + memset (&dialog, 0, sizeof dialog); + dialog.x = $5; + dialog.y = $6; + dialog.width = $7; + dialog.height = $8; + dialog.style = WS_POPUP | WS_BORDER | WS_SYSMENU; + dialog.exstyle = $4; + dialog.font = NULL; + dialog.ex = NULL; + dialog.controls = NULL; + sub_res_info = $3; + } + styles BEG controls END + { + define_dialog ($1, &sub_res_info, &dialog); + } + | id DIALOGEX memflags_move exstyle posnumexpr cnumexpr cnumexpr + cnumexpr + { + memset (&dialog, 0, sizeof dialog); + dialog.x = $5; + dialog.y = $6; + dialog.width = $7; + dialog.height = $8; + dialog.style = WS_POPUP | WS_BORDER | WS_SYSMENU; + dialog.exstyle = $4; + dialog.font = NULL; + dialog.ex = ((struct dialog_ex *) + xmalloc (sizeof (struct dialog_ex))); + memset (dialog.ex, 0, sizeof (struct dialog_ex)); + dialog.controls = NULL; + sub_res_info = $3; + } + styles BEG controls END + { + define_dialog ($1, &sub_res_info, &dialog); + } + | id DIALOGEX memflags_move exstyle posnumexpr cnumexpr cnumexpr + cnumexpr cnumexpr + { + memset (&dialog, 0, sizeof dialog); + dialog.x = $5; + dialog.y = $6; + dialog.width = $7; + dialog.height = $8; + dialog.style = WS_POPUP | WS_BORDER | WS_SYSMENU; + dialog.exstyle = $4; + dialog.font = NULL; + dialog.ex = ((struct dialog_ex *) + xmalloc (sizeof (struct dialog_ex))); + memset (dialog.ex, 0, sizeof (struct dialog_ex)); + dialog.ex->help = $9; + dialog.controls = NULL; + sub_res_info = $3; + } + styles BEG controls END + { + define_dialog ($1, &sub_res_info, &dialog); + } + ; + +exstyle: + /* empty */ + { + $$ = 0; + } + | EXSTYLE '=' numexpr + { + $$ = $3; + } + ; + +styles: + /* empty */ + | styles CAPTION QUOTEDSTRING + { + dialog.caption = $3; + } + | styles CLASS id + { + dialog.class = $3; + } + | styles STYLE + { style = dialog.style } + styleexpr + { + dialog.style = style; + } + | styles EXSTYLE numexpr + { + dialog.exstyle = $3; + } + | styles FONT numexpr ',' QUOTEDSTRING + { + dialog.pointsize = $3; + dialog.font = $5; + } + | styles FONT numexpr ',' QUOTEDSTRING cnumexpr cnumexpr + { + dialog.pointsize = $3; + dialog.font = $5; + if (dialog.ex == NULL) + rcparse_warning ("extended FONT requires DIALOGEX"); + else + { + dialog.ex->weight = $6; + dialog.ex->italic = $7; + } + } + | styles MENU id + { + dialog.menu = $3; + } + | styles CHARACTERISTICS numexpr + { + sub_res_info.characteristics = $3; + } + | styles LANGUAGE numexpr cnumexpr + { + sub_res_info.language = $3 | ($4 << 8); + } + | styles VERSION numexpr + { + sub_res_info.version = $3; + } + ; + +controls: + /* empty */ + | controls control + { + struct dialog_control **pp; + + for (pp = &dialog.controls; *pp != NULL; pp = &(*pp)->next) + ; + *pp = $2; + } + ; + +control: + AUTO3STATE + { + default_style = BS_AUTO3STATE | WS_TABSTOP; + base_style = BS_AUTO3STATE; + class = CTL_BUTTON; + } + control_params + { + $$ = $3; + } + | AUTOCHECKBOX + { + default_style = BS_AUTOCHECKBOX | WS_TABSTOP; + base_style = BS_AUTOCHECKBOX; + class = CTL_BUTTON; + } + control_params + { + $$ = $3; + } + | AUTORADIOBUTTON + { + default_style = BS_AUTORADIOBUTTON | WS_TABSTOP; + base_style = BS_AUTORADIOBUTTON; + class = CTL_BUTTON; + } + control_params + { + $$ = $3; + } + | BEDIT + { + default_style = ES_LEFT | WS_BORDER | WS_TABSTOP; + base_style = ES_LEFT | WS_BORDER | WS_TABSTOP; + class = CTL_EDIT; + } + control_params + { + $$ = $3; + if (dialog.ex == NULL) + rcparse_warning ("IEDIT requires DIALOGEX"); + res_string_to_id (&$$->class, "BEDIT"); + } + | CHECKBOX + { + default_style = BS_CHECKBOX | WS_TABSTOP; + base_style = BS_CHECKBOX | WS_TABSTOP; + class = CTL_BUTTON; + } + control_params + { + $$ = $3; + } + | COMBOBOX + { + default_style = CBS_SIMPLE | WS_TABSTOP; + base_style = 0; + class = CTL_COMBOBOX; + } + control_params + { + $$ = $3; + } + | CONTROL optstringc numexpr cnumexpr control_styleexpr cnumexpr + cnumexpr cnumexpr cnumexpr optcnumexpr opt_control_data + { + $$ = define_control ($2, $3, $6, $7, $8, $9, $4, style, $10); + if ($11 != NULL) + { + if (dialog.ex == NULL) + rcparse_warning ("control data requires DIALOGEX"); + $$->data = $11; + } + } + | CONTROL optstringc numexpr cnumexpr control_styleexpr cnumexpr + cnumexpr cnumexpr cnumexpr cnumexpr cnumexpr opt_control_data + { + $$ = define_control ($2, $3, $6, $7, $8, $9, $4, style, $10); + if (dialog.ex == NULL) + rcparse_warning ("help ID requires DIALOGEX"); + $$->help = $11; + $$->data = $12; + } + | CTEXT + { + default_style = SS_CENTER | WS_GROUP; + base_style = SS_CENTER; + class = CTL_STATIC; + } + control_params + { + $$ = $3; + } + | DEFPUSHBUTTON + { + default_style = BS_DEFPUSHBUTTON | WS_TABSTOP; + base_style = BS_DEFPUSHBUTTON | WS_TABSTOP; + class = CTL_BUTTON; + } + control_params + { + $$ = $3; + } + | EDITTEXT + { + default_style = ES_LEFT | WS_BORDER | WS_TABSTOP; + base_style = ES_LEFT | WS_BORDER | WS_TABSTOP; + class = CTL_EDIT; + } + control_params + { + $$ = $3; + } + | GROUPBOX + { + default_style = BS_GROUPBOX; + base_style = BS_GROUPBOX; + class = CTL_BUTTON; + } + control_params + { + $$ = $3; + } + | HEDIT + { + default_style = ES_LEFT | WS_BORDER | WS_TABSTOP; + base_style = ES_LEFT | WS_BORDER | WS_TABSTOP; + class = CTL_EDIT; + } + control_params + { + $$ = $3; + if (dialog.ex == NULL) + rcparse_warning ("IEDIT requires DIALOGEX"); + res_string_to_id (&$$->class, "HEDIT"); + } + | ICON optstringc numexpr cnumexpr cnumexpr opt_control_data + { + $$ = define_control ($2, $3, $4, $5, 0, 0, CTL_STATIC, + SS_ICON | WS_CHILD | WS_VISIBLE, 0); + if ($6 != NULL) + { + if (dialog.ex == NULL) + rcparse_warning ("control data requires DIALOGEX"); + $$->data = $6; + } + } + | ICON optstringc numexpr cnumexpr cnumexpr cnumexpr cnumexpr + icon_styleexpr optcnumexpr opt_control_data + { + $$ = define_control ($2, $3, $4, $5, $6, $7, CTL_STATIC, + style, $9); + if ($10 != NULL) + { + if (dialog.ex == NULL) + rcparse_warning ("control data requires DIALOGEX"); + $$->data = $10; + } + } + | ICON optstringc numexpr cnumexpr cnumexpr cnumexpr cnumexpr + icon_styleexpr cnumexpr cnumexpr opt_control_data + { + $$ = define_control ($2, $3, $4, $5, $6, $7, CTL_STATIC, + style, $9); + if (dialog.ex == NULL) + rcparse_warning ("help ID requires DIALOGEX"); + $$->help = $10; + $$->data = $11; + } + | IEDIT + { + default_style = ES_LEFT | WS_BORDER | WS_TABSTOP; + base_style = ES_LEFT | WS_BORDER | WS_TABSTOP; + class = CTL_EDIT; + } + control_params + { + $$ = $3; + if (dialog.ex == NULL) + rcparse_warning ("IEDIT requires DIALOGEX"); + res_string_to_id (&$$->class, "IEDIT"); + } + | LISTBOX + { + default_style = LBS_NOTIFY | WS_BORDER; + base_style = LBS_NOTIFY | WS_BORDER; + class = CTL_LISTBOX; + } + control_params + { + $$ = $3; + } + | LTEXT + { + default_style = SS_LEFT | WS_GROUP; + base_style = SS_LEFT; + class = CTL_STATIC; + } + control_params + { + $$ = $3; + } + | PUSHBOX + { + default_style = BS_PUSHBOX | WS_TABSTOP; + base_style = BS_PUSHBOX; + class = CTL_BUTTON; + } + control_params + { + $$ = $3; + } + | PUSHBUTTON + { + default_style = BS_PUSHBUTTON | WS_TABSTOP; + base_style = BS_PUSHBUTTON | WS_TABSTOP; + class = CTL_BUTTON; + } + control_params + { + $$ = $3; + } + | RADIOBUTTON + { + default_style = BS_RADIOBUTTON | WS_TABSTOP; + base_style = BS_RADIOBUTTON; + class = CTL_BUTTON; + } + control_params + { + $$ = $3; + } + | RTEXT + { + default_style = SS_RIGHT | WS_GROUP; + base_style = SS_RIGHT; + class = CTL_STATIC; + } + control_params + { + $$ = $3; + } + | SCROLLBAR + { + default_style = SBS_HORZ; + base_style = 0; + class = CTL_SCROLLBAR; + } + control_params + { + $$ = $3; + } + | STATE3 + { + default_style = BS_3STATE | WS_TABSTOP; + base_style = BS_3STATE; + class = CTL_BUTTON; + } + control_params + { + $$ = $3; + } + | USERBUTTON QUOTEDSTRING ',' numexpr ',' numexpr ',' numexpr ',' + numexpr ',' numexpr ',' + { style = WS_CHILD | WS_VISIBLE } + styleexpr optcnumexpr + { + $$ = define_control ($2, $4, $6, $8, $10, $12, CTL_BUTTON, + style, $16); + } + ; + +/* Parameters for a control. The static variables DEFAULT_STYLE, + BASE_STYLE, and CLASS must be initialized before this nonterminal + is used. DEFAULT_STYLE is the style to use if no style expression + is specified. BASE_STYLE is the base style to use if a style + expression is specified; the style expression modifies the base + style. CLASS is the class of the control. */ + +control_params: + optstringc numexpr cnumexpr cnumexpr cnumexpr cnumexpr + opt_control_data + { + $$ = define_control ($1, $2, $3, $4, $5, $6, class, + default_style | WS_CHILD | WS_VISIBLE, 0); + if ($7 != NULL) + { + if (dialog.ex == NULL) + rcparse_warning ("control data requires DIALOGEX"); + $$->data = $7; + } + } + | optstringc numexpr cnumexpr cnumexpr cnumexpr cnumexpr + control_params_styleexpr optcnumexpr opt_control_data + { + $$ = define_control ($1, $2, $3, $4, $5, $6, class, style, $8); + if ($9 != NULL) + { + if (dialog.ex == NULL) + rcparse_warning ("control data requires DIALOGEX"); + $$->data = $9; + } + } + | optstringc numexpr cnumexpr cnumexpr cnumexpr cnumexpr + control_params_styleexpr cnumexpr cnumexpr opt_control_data + { + $$ = define_control ($1, $2, $3, $4, $5, $6, class, style, $8); + if (dialog.ex == NULL) + rcparse_warning ("help ID requires DIALOGEX"); + $$->help = $9; + $$->data = $10; + } + ; + +optstringc: + /* empty */ + { + $$ = NULL; + } + | QUOTEDSTRING ',' + { + $$ = $1; + } + ; + +opt_control_data: + /* empty */ + { + $$ = NULL; + } + | BEG optrcdata_data END + { + $$ = $2; + } + ; + +/* These only exist to parse a reduction out of a common case. */ + +control_styleexpr: + ',' + { style = WS_CHILD | WS_VISIBLE; } + styleexpr + ; + +icon_styleexpr: + ',' + { style = SS_ICON | WS_CHILD | WS_VISIBLE; } + styleexpr + ; + +control_params_styleexpr: + ',' + { style = base_style | WS_CHILD | WS_VISIBLE; } + styleexpr + ; + +/* Font resources. */ + +font: + id FONT memflags_move_discard file_name + { + define_font ($1, &$3, $4); + free ($4); + } + ; + +/* Icon resources. */ + +icon: + id ICON memflags_move_discard file_name + { + define_icon ($1, &$3, $4); + free ($4); + } + ; + +/* Language command. This changes the static variable language, which + affects all subsequent resources. */ + +language: + LANGUAGE numexpr cnumexpr + { + language = $2 | ($3 << 8); + } + ; + +/* Menu resources. */ + +menu: + id MENU suboptions BEG menuitems END + { + define_menu ($1, &$3, $5); + } + ; + +menuitems: + /* empty */ + { + $$ = NULL; + } + | menuitems menuitem + { + if ($1 == NULL) + $$ = $2; + else + { + struct menuitem **pp; + + for (pp = &$1->next; *pp != NULL; pp = &(*pp)->next) + ; + *pp = $2; + $$ = $1; + } + } + ; + +menuitem: + MENUITEM QUOTEDSTRING cnumexpr menuitem_flags + { + $$ = define_menuitem ($2, $3, $4, 0, 0, NULL); + } + | MENUITEM SEPARATOR + { + $$ = define_menuitem (NULL, 0, 0, 0, 0, NULL); + } + | POPUP QUOTEDSTRING menuitem_flags BEG menuitems END + { + $$ = define_menuitem ($2, 0, $3, 0, 0, $5); + } + ; + +menuitem_flags: + /* empty */ + { + $$ = 0; + } + | menuitem_flags ',' menuitem_flag + { + $$ = $1 | $3; + } + | menuitem_flags menuitem_flag + { + $$ = $1 | $2; + } + ; + +menuitem_flag: + CHECKED + { + $$ = MENUITEM_CHECKED; + } + | GRAYED + { + $$ = MENUITEM_GRAYED; + } + | HELP + { + $$ = MENUITEM_HELP; + } + | INACTIVE + { + $$ = MENUITEM_INACTIVE; + } + | MENUBARBREAK + { + $$ = MENUITEM_MENUBARBREAK; + } + | MENUBREAK + { + $$ = MENUITEM_MENUBREAK; + } + ; + +/* Menuex resources. */ + +menuex: + id MENUEX suboptions BEG menuexitems END + { + define_menu ($1, &$3, $5); + } + ; + +menuexitems: + /* empty */ + { + $$ = NULL; + } + | menuexitems menuexitem + { + if ($1 == NULL) + $$ = $2; + else + { + struct menuitem **pp; + + for (pp = &$1->next; *pp != NULL; pp = &(*pp)->next) + ; + *pp = $2; + $$ = $1; + } + } + ; + +menuexitem: + MENUITEM QUOTEDSTRING + { + $$ = define_menuitem ($2, 0, 0, 0, 0, NULL); + } + | MENUITEM QUOTEDSTRING cnumexpr + { + $$ = define_menuitem ($2, $3, 0, 0, 0, NULL); + } + | MENUITEM QUOTEDSTRING cnumexpr cnumexpr optcnumexpr + { + $$ = define_menuitem ($2, $3, $4, $5, 0, NULL); + } + | POPUP QUOTEDSTRING BEG menuexitems END + { + $$ = define_menuitem ($2, 0, 0, 0, 0, $4); + } + | POPUP QUOTEDSTRING cnumexpr BEG menuexitems END + { + $$ = define_menuitem ($2, $3, 0, 0, 0, $5); + } + | POPUP QUOTEDSTRING cnumexpr cnumexpr BEG menuexitems END + { + $$ = define_menuitem ($2, $3, $4, 0, 0, $6); + } + | POPUP QUOTEDSTRING cnumexpr cnumexpr cnumexpr optcnumexpr + BEG menuexitems END + { + $$ = define_menuitem ($2, $3, $4, $5, $6, $8); + } + ; + +/* Messagetable resources. */ + +messagetable: + id MESSAGETABLE memflags_move file_name + { + define_messagetable ($1, &$3, $4); + free ($4); + } + ; + +/* Rcdata resources. */ + +rcdata: + id RCDATA suboptions BEG optrcdata_data END + { + define_rcdata ($1, &$3, $5); + } + ; + +optrcdata_data: + /* empty */ + { + $$ = NULL; + } + | rcdata_data + { + $$ = $1; + } + ; + +rcdata_data: + QUOTEDSTRING + { + $$ = append_rcdata_string (NULL, $1); + } + | sizednumexpr + { + $$ = append_rcdata_number (NULL, $1.val, $1.dword); + } + | rcdata_data ',' QUOTEDSTRING + { + $$ = append_rcdata_string ($1, $3); + } + | rcdata_data ',' sizednumexpr + { + $$ = append_rcdata_number ($1, $3.val, $3.dword); + } + ; + +/* Stringtable resources. */ + +stringtable: + STRINGTABLE suboptions BEG + { sub_res_info = $2; } + string_data END + ; + +string_data: + /* empty */ + | string_data numexpr QUOTEDSTRING + { + define_stringtable (&sub_res_info, $2, $3); + } + | string_data numexpr ',' QUOTEDSTRING + { + define_stringtable (&sub_res_info, $2, $4); + } + ; + +/* User defined resources. We accept general suboptions in the + file_name case to keep the parser happy. */ + +user: + id id suboptions BEG rcdata_data END + { + define_user_data ($1, $2, &$3, $5); + } + | id id suboptions file_name + { + define_user_file ($1, $2, &$3, $4); + free ($4); + } + ; + +/* Versioninfo resources. */ + +versioninfo: + id VERSIONINFO fixedverinfo BEG verblocks END + { + define_versioninfo ($1, language, $3, $5); + } + ; + +fixedverinfo: + /* empty */ + { + $$ = ((struct fixed_versioninfo *) + xmalloc (sizeof (struct fixed_versioninfo))); + memset ($$, 0, sizeof (struct fixed_versioninfo)); + } + | fixedverinfo FILEVERSION numexpr cnumexpr cnumexpr cnumexpr + { + $1->file_version_ms = ($3 << 16) | $4; + $1->file_version_ls = ($5 << 16) | $6; + $$ = $1; + } + | fixedverinfo PRODUCTVERSION numexpr cnumexpr cnumexpr cnumexpr + { + $1->product_version_ms = ($3 << 16) | $4; + $1->product_version_ls = ($5 << 16) | $6; + $$ = $1; + } + | fixedverinfo FILEFLAGSMASK numexpr + { + $1->file_flags_mask = $3; + $$ = $1; + } + | fixedverinfo FILEFLAGS numexpr + { + $1->file_flags = $3; + $$ = $1; + } + | fixedverinfo FILEOS numexpr + { + $1->file_os = $3; + $$ = $1; + } + | fixedverinfo FILETYPE numexpr + { + $1->file_type = $3; + $$ = $1; + } + | fixedverinfo FILESUBTYPE numexpr + { + $1->file_subtype = $3; + $$ = $1; + } + ; + +/* To handle verblocks successfully, the lexer handles BLOCK + specially. A BLOCK "StringFileInfo" is returned as + BLOCKSTRINGFILEINFO. A BLOCK "VarFileInfo" is returned as + BLOCKVARFILEINFO. A BLOCK with some other string returns BLOCK + with the string as the value. */ + +verblocks: + /* empty */ + { + $$ = NULL; + } + | verblocks BLOCKSTRINGFILEINFO BEG BLOCK BEG vervals END END + { + $$ = append_ver_stringfileinfo ($1, $4, $6); + } + | verblocks BLOCKVARFILEINFO BEG VALUE QUOTEDSTRING vertrans END + { + $$ = append_ver_varfileinfo ($1, $5, $6); + } + ; + +vervals: + /* empty */ + { + $$ = NULL; + } + | vervals VALUE QUOTEDSTRING ',' QUOTEDSTRING + { + $$ = append_verval ($1, $3, $5); + } + ; + +vertrans: + /* empty */ + { + $$ = NULL; + } + | vertrans cnumexpr cnumexpr + { + $$ = append_vertrans ($1, $2, $3); + } + ; + +/* A resource ID. */ + +id: + posnumexpr + { + $$.named = 0; + $$.u.id = $1; + } + | STRING + { + res_string_to_id (&$$, $1); + free ($1); + } + ; + +/* Generic suboptions. These may appear before the BEGIN in any + multiline statement. */ + +suboptions: + /* empty */ + { + memset (&$$, 0, sizeof (struct res_res_info)); + $$.language = language; + /* FIXME: Is this the right default? */ + $$.memflags = MEMFLAG_MOVEABLE; + } + | suboptions memflag + { + $$ = $1; + $$.memflags |= $2.on; + $$.memflags &=~ $2.off; + } + | suboptions CHARACTERISTICS numexpr + { + $$ = $1; + $$.characteristics = $3; + } + | suboptions LANGUAGE numexpr cnumexpr + { + $$ = $1; + $$.language = $3 | ($4 << 8); + } + | suboptions VERSION numexpr + { + $$ = $1; + $$.version = $3; + } + ; + +/* Memory flags which default to MOVEABLE and DISCARDABLE. */ + +memflags_move_discard: + /* empty */ + { + memset (&$$, 0, sizeof (struct res_res_info)); + $$.language = language; + $$.memflags = MEMFLAG_MOVEABLE | MEMFLAG_DISCARDABLE; + } + | memflags_move_discard memflag + { + $$ = $1; + $$.memflags |= $2.on; + $$.memflags &=~ $2.off; + } + ; + +/* Memory flags which default to MOVEABLE. */ + +memflags_move: + /* empty */ + { + memset (&$$, 0, sizeof (struct res_res_info)); + $$.language = language; + $$.memflags = MEMFLAG_MOVEABLE; + } + | memflags_move_discard memflag + { + $$ = $1; + $$.memflags |= $2.on; + $$.memflags &=~ $2.off; + } + ; + +/* Memory flags. This returns a struct with two integers, because we + sometimes want to set bits and we sometimes want to clear them. */ + +memflag: + MOVEABLE + { + $$.on = MEMFLAG_MOVEABLE; + $$.off = 0; + } + | FIXED + { + $$.on = 0; + $$.off = MEMFLAG_MOVEABLE; + } + | PURE + { + $$.on = MEMFLAG_PURE; + $$.off = 0; + } + | IMPURE + { + $$.on = 0; + $$.off = MEMFLAG_PURE; + } + | PRELOAD + { + $$.on = MEMFLAG_PRELOAD; + $$.off = 0; + } + | LOADONCALL + { + $$.on = 0; + $$.off = MEMFLAG_PRELOAD; + } + | DISCARDABLE + { + $$.on = MEMFLAG_DISCARDABLE; + $$.off = 0; + } + ; + +/* A file name. */ + +file_name: + QUOTEDSTRING + { + $$ = $1; + } + | STRING + { + $$ = $1; + } + ; + +/* A style expression. This changes the static variable STYLE. We do + it this way because rc appears to permit a style to be set to + something like + WS_GROUP | NOT WS_TABSTOP + to mean that a default of WS_TABSTOP should be removed. Anything + which wants to accept a style must first set STYLE to the default + value. The styleexpr nonterminal will change STYLE as specified by + the user. Note that we do not accept arbitrary expressions here, + just numbers separated by '|'. */ + +styleexpr: + parennumber + { + style |= $1; + } + | NOT parennumber + { + style &=~ $2; + } + | styleexpr '|' parennumber + { + style |= $3; + } + | styleexpr '|' NOT parennumber + { + style &=~ $4; + } + ; + +parennumber: + NUMBER + { + $$ = $1.val; + } + | '(' numexpr ')' + { + $$ = $2; + } + ; + +/* An optional expression with a leading comma. */ + +optcnumexpr: + /* empty */ + { + $$ = 0; + } + | cnumexpr + { + $$ = $1; + } + ; + +/* An expression with a leading comma. */ + +cnumexpr: + ',' numexpr + { + $$ = $2; + } + ; + +/* A possibly negated numeric expression. */ + +numexpr: + sizednumexpr + { + $$ = $1.val; + } + ; + +/* A possibly negated expression with a size. */ + +sizednumexpr: + NUMBER + { + $$ = $1; + } + | '(' sizednumexpr ')' + { + $$ = $2; + } + | '~' sizednumexpr %prec '~' + { + $$.val = ~ $2.val; + $$.dword = $2.dword; + } + | '-' sizednumexpr %prec NEG + { + $$.val = - $2.val; + $$.dword = $2.dword; + } + | sizednumexpr '*' sizednumexpr + { + $$.val = $1.val * $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizednumexpr '/' sizednumexpr + { + $$.val = $1.val / $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizednumexpr '%' sizednumexpr + { + $$.val = $1.val % $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizednumexpr '+' sizednumexpr + { + $$.val = $1.val + $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizednumexpr '-' sizednumexpr + { + $$.val = $1.val - $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizednumexpr '&' sizednumexpr + { + $$.val = $1.val & $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizednumexpr '^' sizednumexpr + { + $$.val = $1.val ^ $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizednumexpr '|' sizednumexpr + { + $$.val = $1.val | $3.val; + $$.dword = $1.dword || $3.dword; + } + ; + +/* An expression with a leading comma which does not use unary + negation. */ + +cposnumexpr: + ',' posnumexpr + { + $$ = $2; + } + ; + +/* An expression which does not use unary negation. */ + +posnumexpr: + sizedposnumexpr + { + $$ = $1.val; + } + ; + +/* An expression which does not use unary negation. We separate unary + negation to avoid parsing conflicts when two numeric expressions + appear consecutively. */ + +sizedposnumexpr: + NUMBER + { + $$ = $1; + } + | '(' sizednumexpr ')' + { + $$ = $2; + } + | '~' sizednumexpr %prec '~' + { + $$.val = ~ $2.val; + $$.dword = $2.dword; + } + | sizedposnumexpr '*' sizednumexpr + { + $$.val = $1.val * $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizedposnumexpr '/' sizednumexpr + { + $$.val = $1.val / $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizedposnumexpr '%' sizednumexpr + { + $$.val = $1.val % $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizedposnumexpr '+' sizednumexpr + { + $$.val = $1.val + $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizedposnumexpr '-' sizednumexpr + { + $$.val = $1.val - $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizedposnumexpr '&' sizednumexpr + { + $$.val = $1.val & $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizedposnumexpr '^' sizednumexpr + { + $$.val = $1.val ^ $3.val; + $$.dword = $1.dword || $3.dword; + } + | sizedposnumexpr '|' sizednumexpr + { + $$.val = $1.val | $3.val; + $$.dword = $1.dword || $3.dword; + } + ; + +%% + +/* Set the language from the command line. */ + +void +rcparse_set_language (lang) + int lang; +{ + language = lang; +} diff --git a/binutils/resrc.c b/binutils/resrc.c new file mode 100644 index 00000000000..64c6c787ea6 --- /dev/null +++ b/binutils/resrc.c @@ -0,0 +1,2220 @@ +/* resrc.c -- read and write Windows rc files. + Copyright 1997 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support. + + This file is part of GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* This file contains function that read and write Windows rc files. + These are text files that represent resources. */ + +#include "bfd.h" +#include "bucomm.h" +#include "libiberty.h" +#include "windres.h" + +#include +#include +#include + +/* The default preprocessor. */ + +#define DEFAULT_PREPROCESSOR "gcc -E -xc-header -DRC_INVOKED" + +/* We read the directory entries in a cursor or icon file into + instances of this structure. */ + +struct icondir +{ + /* Width of image. */ + unsigned char width; + /* Height of image. */ + unsigned char height; + /* Number of colors in image. */ + unsigned char colorcount; + union + { + struct + { + /* Color planes. */ + unsigned short planes; + /* Bits per pixel. */ + unsigned short bits; + } icon; + struct + { + /* X coordinate of hotspot. */ + unsigned short xhotspot; + /* Y coordinate of hotspot. */ + unsigned short yhotspot; + } cursor; + } u; + /* Bytes in image. */ + unsigned long bytes; + /* File offset of image. */ + unsigned long offset; +}; + +/* The name of the rc file we are reading. */ + +char *rc_filename; + +/* The line number in the rc file. */ + +int rc_lineno; + +/* The pipe we are reading from, so that we can close it if we exit. */ + +static FILE *cpp_pipe; + +/* As we read the rc file, we attach information to this structure. */ + +static struct res_directory *resources; + +/* The number of cursor resources we have written out. */ + +static int cursors; + +/* The number of font resources we have written out. */ + +static int fonts; + +/* Font directory information. */ + +struct fontdir *fontdirs; + +/* Resource info to use for fontdirs. */ + +struct res_res_info fontdirs_resinfo; + +/* The number of icon resources we have written out. */ + +static int icons; + +/* Local functions. */ + +static void close_pipe PARAMS ((void)); +static void unexpected_eof PARAMS ((const char *)); +static int get_word PARAMS ((FILE *, const char *)); +static unsigned long get_long PARAMS ((FILE *, const char *)); +static void get_data + PARAMS ((FILE *, unsigned char *, unsigned long, const char *)); +static void define_fontdirs PARAMS ((void)); + +/* Read an rc file. */ + +struct res_directory * +read_rc_file (filename, preprocessor, preprocargs, language) + const char *filename; + const char *preprocessor; + const char *preprocargs; + int language; +{ + char *cmd; + + if (preprocessor == NULL) + preprocessor = DEFAULT_PREPROCESSOR; + + if (preprocargs == NULL) + preprocargs = ""; + if (filename == NULL) + filename = "-"; + + cmd = xmalloc (strlen (preprocessor) + + strlen (preprocargs) + + strlen (filename) + + 10); + sprintf (cmd, "%s %s %s", preprocessor, preprocargs, filename); + + cpp_pipe = popen (cmd, FOPEN_RT); + if (cpp_pipe == NULL) + fatal ("can't popen `%s': %s", cmd, strerror (errno)); + + xatexit (close_pipe); + + rc_filename = xstrdup (filename); + rc_lineno = 1; + if (language != -1) + rcparse_set_language (language); + yyin = cpp_pipe; + yyparse (); + + if (pclose (cpp_pipe) != 0) + fprintf (stderr, "%s: warning: preprocessor failed\n", program_name); + cpp_pipe = NULL; + + if (fontdirs != NULL) + define_fontdirs (); + + free (rc_filename); + rc_filename = NULL; + + return resources; +} + +/* Close the pipe if it is open. This is called via xatexit. */ + +void +close_pipe () +{ + if (cpp_pipe != NULL) + pclose (cpp_pipe); +} + +/* Report an error while reading an rc file. */ + +void +yyerror (msg) + const char *msg; +{ + fatal ("%s:%d: %s", rc_filename, rc_lineno, msg); +} + +/* Issue a warning while reading an rc file. */ + +void +rcparse_warning (msg) + const char *msg; +{ + fprintf (stderr, "%s:%d: %s\n", rc_filename, rc_lineno, msg); +} + +/* Die if we get an unexpected end of file. */ + +static void +unexpected_eof (msg) + const char *msg; +{ + fatal ("%s: unexpected EOF", msg); +} + +/* Read a 16 bit word from a file. The data is assumed to be little + endian. */ + +static int +get_word (e, msg) + FILE *e; + const char *msg; +{ + int b1, b2; + + b1 = getc (e); + b2 = getc (e); + if (feof (e)) + unexpected_eof (msg); + return ((b2 & 0xff) << 8) | (b1 & 0xff); +} + +/* Read a 32 bit word from a file. The data is assumed to be little + endian. */ + +static unsigned long +get_long (e, msg) + FILE *e; + const char *msg; +{ + int b1, b2, b3, b4; + + b1 = getc (e); + b2 = getc (e); + b3 = getc (e); + b4 = getc (e); + if (feof (e)) + unexpected_eof (msg); + return (((((((b4 & 0xff) << 8) + | (b3 & 0xff)) << 8) + | (b2 & 0xff)) << 8) + | (b1 & 0xff)); +} + +/* Read data from a file. This is a wrapper to do error checking. */ + +static void +get_data (e, p, c, msg) + FILE *e; + unsigned char *p; + unsigned long c; + const char *msg; +{ + unsigned long got; + + got = fread (p, 1, c, e); + if (got == c) + return; + + fatal ("%s: read of %lu returned %lu", msg, c, got); +} + +/* Define an accelerator resource. */ + +void +define_accelerator (id, resinfo, data) + struct res_id id; + const struct res_res_info *resinfo; + struct accelerator *data; +{ + struct res_resource *r; + + r = define_standard_resource (&resources, RT_ACCELERATORS, id, + resinfo->language, 0); + r->type = RES_TYPE_ACCELERATOR; + r->u.acc = data; + r->res_info = *resinfo; +} + +/* Define a bitmap resource. Bitmap data is stored in a file. The + first 14 bytes of the file are a standard header, which is not + included in the resource data. */ + +#define BITMAP_SKIP (14) + +void +define_bitmap (id, resinfo, filename) + struct res_id id; + const struct res_res_info *resinfo; + const char *filename; +{ + FILE *e; + char *real_filename; + struct stat s; + unsigned char *data; + int i; + struct res_resource *r; + + e = open_file_search (filename, FOPEN_RB, "bitmap file", &real_filename); + + if (stat (real_filename, &s) < 0) + fatal ("stat failed on bitmap file `%s': %s", real_filename, + strerror (errno)); + + data = (unsigned char *) xmalloc (s.st_size - BITMAP_SKIP); + + for (i = 0; i < BITMAP_SKIP; i++) + getc (e); + + get_data (e, data, s.st_size - BITMAP_SKIP, real_filename); + + fclose (e); + free (real_filename); + + r = define_standard_resource (&resources, RT_BITMAP, id, + resinfo->language, 0); + + r->type = RES_TYPE_BITMAP; + r->u.data.length = s.st_size - BITMAP_SKIP; + r->u.data.data = data; + r->res_info = *resinfo; +} + +/* Define a cursor resource. A cursor file may contain a set of + bitmaps, each representing the same cursor at various different + resolutions. They each get written out with a different ID. The + real cursor resource is then a group resource which can be used to + select one of the actual cursors. */ + +void +define_cursor (id, resinfo, filename) + struct res_id id; + const struct res_res_info *resinfo; + const char *filename; +{ + FILE *e; + char *real_filename; + int type, count, i; + struct icondir *icondirs; + int first_cursor; + struct res_resource *r; + struct group_cursor *first, **pp; + + e = open_file_search (filename, FOPEN_RB, "cursor file", &real_filename); + + /* A cursor file is basically an icon file. The start of the file + is a three word structure. The first word is ignored. The + second word is the type of data. The third word is the number of + entries. */ + + get_word (e, real_filename); + type = get_word (e, real_filename); + count = get_word (e, real_filename); + if (type != 2) + fatal ("cursor file `%s' does not contain cursor data", real_filename); + + /* Read in the icon directory entries. */ + + icondirs = (struct icondir *) xmalloc (count * sizeof *icondirs); + + for (i = 0; i < count; i++) + { + icondirs[i].width = getc (e); + icondirs[i].height = getc (e); + icondirs[i].colorcount = getc (e); + getc (e); + icondirs[i].u.cursor.xhotspot = get_word (e, real_filename); + icondirs[i].u.cursor.yhotspot = get_word (e, real_filename); + icondirs[i].bytes = get_long (e, real_filename); + icondirs[i].offset = get_long (e, real_filename); + + if (feof (e)) + unexpected_eof (real_filename); + } + + /* Define each cursor as a unique resource. */ + + first_cursor = cursors; + + for (i = 0; i < count; i++) + { + unsigned char *data; + struct res_id name; + struct cursor *c; + + if (fseek (e, icondirs[i].offset, SEEK_SET) != 0) + fatal ("%s: fseek to %lu failed: %s", real_filename, + icondirs[i].offset, strerror (errno)); + + data = (unsigned char *) xmalloc (icondirs[i].bytes); + + get_data (e, data, icondirs[i].bytes, real_filename); + + c = (struct cursor *) xmalloc (sizeof *c); + c->xhotspot = icondirs[i].u.cursor.xhotspot; + c->yhotspot = icondirs[i].u.cursor.yhotspot; + c->length = icondirs[i].bytes; + c->data = data; + + ++cursors; + + name.named = 0; + name.u.id = cursors; + + r = define_standard_resource (&resources, RT_CURSOR, name, + resinfo->language, 0); + r->type = RES_TYPE_CURSOR; + r->u.cursor = c; + r->res_info = *resinfo; + } + + fclose (e); + free (real_filename); + + /* Define a cursor group resource. */ + + first = NULL; + pp = &first; + for (i = 0; i < count; i++) + { + struct group_cursor *cg; + + /* These manipulations of icondirs into cg are copied from rcl. */ + + cg = (struct group_cursor *) xmalloc (sizeof *cg); + cg->next = NULL; + cg->width = icondirs[i].width; + cg->height = 2 * icondirs[i].height; + cg->planes = 1; + cg->bits = 4; + cg->bytes = icondirs[i].bytes + 8; + cg->index = first_cursor + i + 1; + + *pp = cg; + pp = &(*pp)->next; + } + + r = define_standard_resource (&resources, RT_GROUP_CURSOR, id, + resinfo->language, 0); + r->type = RES_TYPE_GROUP_CURSOR; + r->u.group_cursor = first; + r->res_info = *resinfo; +} + +/* Define a dialog resource. */ + +void +define_dialog (id, resinfo, dialog) + struct res_id id; + const struct res_res_info *resinfo; + const struct dialog *dialog; +{ + struct dialog *copy; + struct res_resource *r; + + copy = (struct dialog *) xmalloc (sizeof *copy); + *copy = *dialog; + + r = define_standard_resource (&resources, RT_DIALOG, id, + resinfo->language, 0); + r->type = RES_TYPE_DIALOG; + r->u.dialog = copy; + r->res_info = *resinfo; +} + +/* Define a dialog control. This does not define a resource, but + merely allocates and fills in a structure. */ + +struct dialog_control * +define_control (text, id, x, y, width, height, class, style, exstyle) + char *text; + unsigned long id; + unsigned long x; + unsigned long y; + unsigned long width; + unsigned long height; + unsigned long class; + unsigned long style; + unsigned long exstyle; +{ + struct dialog_control *n; + + n = (struct dialog_control *) xmalloc (sizeof *n); + n->next = NULL; + n->id = id; + n->style = style; + n->exstyle = exstyle; + n->x = x; + n->y = y; + n->width = width; + n->height = height; + n->class.named = 0; + n->class.u.id = class; + if (text != NULL) + res_string_to_id (&n->text, text); + else + { + n->text.named = 0; + n->text.u.id = 0; + } + free (text); + n->data = NULL; + n->help = 0; + + return n; +} + +/* Define a font resource. */ + +void +define_font (id, resinfo, filename) + struct res_id id; + const struct res_res_info *resinfo; + const char *filename; +{ + FILE *e; + char *real_filename; + struct stat s; + unsigned char *data; + struct res_resource *r; + struct fontdir *fd; + long offset; + const char *device, *face; + struct fontdir **pp; + + e = open_file_search (filename, FOPEN_RB, "font file", &real_filename); + + if (stat (real_filename, &s) < 0) + fatal ("stat failed on bitmap file `%s': %s", real_filename, + strerror (errno)); + + data = (unsigned char *) xmalloc (s.st_size); + + get_data (e, data, s.st_size, real_filename); + + fclose (e); + free (real_filename); + + r = define_standard_resource (&resources, RT_FONT, id, + resinfo->language, 0); + + r->type = RES_TYPE_FONT; + r->u.data.length = s.st_size; + r->u.data.data = data; + r->res_info = *resinfo; + + /* For each font resource, we must add an entry in the FONTDIR + resource. The FONTDIR resource includes some strings in the font + file. To find them, we have to do some magic on the data we have + read. */ + + offset = ((((((data[47] << 8) + | data[46]) << 8) + | data[45]) << 8) + | data[44]); + if (offset > 0 && offset < s.st_size) + device = (char *) data + offset; + else + device = ""; + + offset = ((((((data[51] << 8) + | data[50]) << 8) + | data[49]) << 8) + | data[48]); + if (offset > 0 && offset < s.st_size) + face = (char *) data + offset; + else + face = ""; + + ++fonts; + + fd = (struct fontdir *) xmalloc (sizeof *fd); + fd->next = NULL; + fd->index = fonts; + fd->length = 58 + strlen (device) + strlen (face); + fd->data = (unsigned char *) xmalloc (fd->length); + + memcpy (fd->data, data, 56); + strcpy ((char *) fd->data + 56, device); + strcpy ((char *) fd->data + 57 + strlen (device), face); + + for (pp = &fontdirs; *pp != NULL; pp = &(*pp)->next) + ; + *pp = fd; + + /* For the single fontdirs resource, we always use the resource + information of the last font. I don't know what else to do. */ + fontdirs_resinfo = *resinfo; +} + +/* Define the fontdirs resource. This is called after the entire rc + file has been parsed, if any font resources were seen. */ + +static void +define_fontdirs () +{ + struct res_resource *r; + struct res_id id; + + id.named = 0; + id.u.id = 1; + + r = define_standard_resource (&resources, RT_FONTDIR, id, 0x409, 0); + + r->type = RES_TYPE_FONTDIR; + r->u.fontdir = fontdirs; + r->res_info = fontdirs_resinfo; +} + +/* Define an icon resource. An icon file may contain a set of + bitmaps, each representing the same icon at various different + resolutions. They each get written out with a different ID. The + real icon resource is then a group resource which can be used to + select one of the actual icon bitmaps. */ + +void +define_icon (id, resinfo, filename) + struct res_id id; + const struct res_res_info *resinfo; + const char *filename; +{ + FILE *e; + char *real_filename; + int type, count, i; + struct icondir *icondirs; + int first_icon; + struct res_resource *r; + struct group_icon *first, **pp; + + e = open_file_search (filename, FOPEN_RB, "icon file", &real_filename); + + /* The start of an icon file is a three word structure. The first + word is ignored. The second word is the type of data. The third + word is the number of entries. */ + + get_word (e, real_filename); + type = get_word (e, real_filename); + count = get_word (e, real_filename); + if (type != 1) + fatal ("icon file `%s' does not contain icon data", real_filename); + + /* Read in the icon directory entries. */ + + icondirs = (struct icondir *) xmalloc (count * sizeof *icondirs); + + for (i = 0; i < count; i++) + { + icondirs[i].width = getc (e); + icondirs[i].height = getc (e); + icondirs[i].colorcount = getc (e); + getc (e); + icondirs[i].u.icon.planes = get_word (e, real_filename); + icondirs[i].u.icon.bits = get_word (e, real_filename); + icondirs[i].bytes = get_long (e, real_filename); + icondirs[i].offset = get_long (e, real_filename); + + if (feof (e)) + unexpected_eof (real_filename); + } + + /* Define each icon as a unique resource. */ + + first_icon = icons; + + for (i = 0; i < count; i++) + { + unsigned char *data; + struct res_id name; + + if (fseek (e, icondirs[i].offset, SEEK_SET) != 0) + fatal ("%s: fseek to %lu failed: %s", real_filename, + icondirs[i].offset, strerror (errno)); + + data = (unsigned char *) xmalloc (icondirs[i].bytes); + + get_data (e, data, icondirs[i].bytes, real_filename); + + ++icons; + + name.named = 0; + name.u.id = icons; + + r = define_standard_resource (&resources, RT_ICON, name, + resinfo->language, 0); + r->type = RES_TYPE_ICON; + r->u.data.length = icondirs[i].bytes; + r->u.data.data = data; + r->res_info = *resinfo; + } + + fclose (e); + free (real_filename); + + /* Define an icon group resource. */ + + first = NULL; + pp = &first; + for (i = 0; i < count; i++) + { + struct group_icon *cg; + + /* FIXME: rcl sets planes and bits based on colors, rather than + just copying the values from the file. */ + + cg = (struct group_icon *) xmalloc (sizeof *cg); + cg->next = NULL; + cg->width = icondirs[i].width; + cg->height = icondirs[i].height; + cg->colors = icondirs[i].colorcount; + cg->planes = icondirs[i].u.icon.planes; + cg->bits = icondirs[i].u.icon.bits; + cg->bytes = icondirs[i].bytes; + cg->index = first_icon + i + 1; + + *pp = cg; + pp = &(*pp)->next; + } + + r = define_standard_resource (&resources, RT_GROUP_ICON, id, + resinfo->language, 0); + r->type = RES_TYPE_GROUP_ICON; + r->u.group_icon = first; + r->res_info = *resinfo; +} + +/* Define a menu resource. */ + +void +define_menu (id, resinfo, menuitems) + struct res_id id; + const struct res_res_info *resinfo; + struct menuitem *menuitems; +{ + struct res_resource *r; + + r = define_standard_resource (&resources, RT_MENU, id, resinfo->language, 0); + r->type = RES_TYPE_MENU; + r->u.menu = menuitems; + r->res_info = *resinfo; +} + +/* Define a menu item. This does not define a resource, but merely + allocates and fills in a structure. */ + +struct menuitem * +define_menuitem (text, menuid, type, state, help, menuitems) + char *text; + int menuid; + unsigned long type; + unsigned long state; + unsigned long help; + struct menuitem *menuitems; +{ + struct menuitem *mi; + + mi = (struct menuitem *) xmalloc (sizeof *mi); + mi->next = NULL; + mi->type = type; + mi->state = state; + mi->id = menuid; + mi->text = text; + mi->help = help; + mi->popup = menuitems; + return mi; +} + +/* Define a messagetable resource. */ + +void +define_messagetable (id, resinfo, filename) + struct res_id id; + const struct res_res_info *resinfo; + const char *filename; +{ + FILE *e; + char *real_filename; + struct stat s; + unsigned char *data; + struct res_resource *r; + + e = open_file_search (filename, FOPEN_RB, "messagetable file", + &real_filename); + + if (stat (real_filename, &s) < 0) + fatal ("stat failed on bitmap file `%s': %s", real_filename, + strerror (errno)); + + data = (unsigned char *) xmalloc (s.st_size); + + get_data (e, data, s.st_size, real_filename); + + fclose (e); + free (real_filename); + + r = define_standard_resource (&resources, RT_MESSAGETABLE, id, + resinfo->language, 0); + + r->type = RES_TYPE_MESSAGETABLE; + r->u.data.length = s.st_size; + r->u.data.data = data; + r->res_info = *resinfo; +} + +/* Define an rcdata resource. */ + +void +define_rcdata (id, resinfo, data) + struct res_id id; + const struct res_res_info *resinfo; + struct rcdata_data *data; +{ + struct res_resource *r; + + r = define_standard_resource (&resources, RT_RCDATA, id, + resinfo->language, 0); + r->type = RES_TYPE_RCDATA; + r->u.rcdata = data; + r->res_info = *resinfo; +} + +/* Add an rcdata_item to an rcdata resource. */ + +struct rcdata_data * +append_rcdata_item (data, item) + struct rcdata_data *data; + struct rcdata_item *item; +{ + if (data == NULL) + { + data = (struct rcdata_data *) xmalloc (sizeof *data); + data->first = item; + data->last = item; + } + else + { + data->last->next = item; + data->last = item; + } + + return data; +} + +/* Add a string to an rcdata resource. */ + +struct rcdata_data * +append_rcdata_string (data, string) + struct rcdata_data *data; + char *string; +{ + struct rcdata_item *ri; + + ri = (struct rcdata_item *) xmalloc (sizeof *ri); + ri->next = NULL; + ri->type = RCDATA_STRING; + ri->u.string = string; + + return append_rcdata_item (data, ri); +} + +/* Add a number to an rcdata resource. */ + +struct rcdata_data * +append_rcdata_number (data, val, dword) + struct rcdata_data *data; + unsigned long val; + int dword; +{ + struct rcdata_item *ri; + + ri = (struct rcdata_item *) xmalloc (sizeof *ri); + ri->next = NULL; + ri->type = dword ? RCDATA_DWORD : RCDATA_WORD; + ri->u.word = val; + + return append_rcdata_item (data, ri); +} + +/* Define a stringtable resource. This is called for each string + which appears in a STRINGTABLE statement. */ + +void +define_stringtable (resinfo, stringid, string) + const struct res_res_info *resinfo; + unsigned long stringid; + char *string; +{ + struct res_id id; + struct res_resource *r; + + id.named = 0; + id.u.id = stringid >> 4; + r = define_standard_resource (&resources, RT_STRING, id, + resinfo->language, 1); + + if (r->type == RES_TYPE_UNINITIALIZED) + { + int i; + + r->type = RES_TYPE_STRINGTABLE; + r->u.stringtable = ((struct stringtable *) + xmalloc (sizeof (struct stringtable))); + for (i = 0; i < 16; i++) + { + r->u.stringtable->strings[i].length = 0; + r->u.stringtable->strings[i].string = NULL; + } + + r->res_info = *resinfo; + } + + unicode_from_ascii (&r->u.stringtable->strings[stringid & 0xf].length, + &r->u.stringtable->strings[stringid & 0xf].string, + string); + free (string); +} + +/* Define a user data resource where the data is in the rc file. */ + +void +define_user_data (id, type, resinfo, data) + struct res_id id; + struct res_id type; + const struct res_res_info *resinfo; + struct rcdata_data *data; +{ + struct res_id ids[3]; + struct res_resource *r; + + ids[0] = type; + ids[1] = id; + ids[2].named = 0; + ids[2].u.id = resinfo->language; + + r = define_resource (&resources, 3, ids, 0); + r->type = RES_TYPE_USERDATA; + r->u.userdata = data; + r->res_info = *resinfo; +} + +/* Define a user data resource where the data is in a file. */ + +void +define_user_file (id, type, resinfo, filename) + struct res_id id; + struct res_id type; + const struct res_res_info *resinfo; + const char *filename; +{ + FILE *e; + char *real_filename; + struct stat s; + unsigned char *data; + struct res_id ids[3]; + struct res_resource *r; + + e = open_file_search (filename, FOPEN_RB, "font file", &real_filename); + + if (stat (real_filename, &s) < 0) + fatal ("stat failed on bitmap file `%s': %s", real_filename, + strerror (errno)); + + data = (unsigned char *) xmalloc (s.st_size); + + get_data (e, data, s.st_size, real_filename); + + fclose (e); + free (real_filename); + + ids[0] = type; + ids[1] = id; + ids[2].named = 0; + ids[2].u.id = resinfo->language; + + r = define_resource (&resources, 3, ids, 0); + r->type = RES_TYPE_USERDATA; + r->u.userdata = ((struct rcdata_data *) + xmalloc (sizeof (struct rcdata_data))); + r->u.userdata->first = ((struct rcdata_item *) + xmalloc (sizeof (struct rcdata_item))); + r->u.userdata->last = r->u.userdata->first; + r->u.userdata->first->next = NULL; + r->u.userdata->first->type = RCDATA_BUFFER; + r->u.userdata->first->u.buffer.length = s.st_size; + r->u.userdata->first->u.buffer.data = data; + r->res_info = *resinfo; +} + +/* Define a versioninfo resource. */ + +void +define_versioninfo (id, language, fixedverinfo, verinfo) + struct res_id id; + int language; + struct fixed_versioninfo *fixedverinfo; + struct ver_info *verinfo; +{ + struct res_resource *r; + + r = define_standard_resource (&resources, RT_VERSION, id, language, 0); + r->type = RES_TYPE_VERSIONINFO; + r->u.versioninfo = ((struct versioninfo *) + xmalloc (sizeof (struct versioninfo))); + r->u.versioninfo->fixed = fixedverinfo; + r->u.versioninfo->var = verinfo; + r->res_info.language = language; +} + +/* Add string version info to a list of version information. */ + +struct ver_info * +append_ver_stringfileinfo (verinfo, language, strings) + struct ver_info *verinfo; + char *language; + struct ver_stringinfo *strings; +{ + struct ver_info *vi, **pp; + + vi = (struct ver_info *) xmalloc (sizeof *vi); + vi->next = NULL; + vi->type = VERINFO_STRING; + unicode_from_ascii ((unsigned short *) NULL, &vi->u.string.language, + language); + free (language); + vi->u.string.strings = strings; + + for (pp = &verinfo; *pp != NULL; pp = &(*pp)->next) + ; + *pp = vi; + + return verinfo; +} + +/* Add variable version info to a list of version information. */ + +struct ver_info * +append_ver_varfileinfo (verinfo, key, var) + struct ver_info *verinfo; + char *key; + struct ver_varinfo *var; +{ + struct ver_info *vi, **pp; + + vi = (struct ver_info *) xmalloc (sizeof *vi); + vi->next = NULL; + vi->type = VERINFO_VAR; + unicode_from_ascii ((unsigned short *) NULL, &vi->u.var.key, key); + free (key); + vi->u.var.var = var; + + for (pp = &verinfo; *pp != NULL; pp = &(*pp)->next) + ; + *pp = vi; + + return verinfo; +} + +/* Append version string information to a list. */ + +struct ver_stringinfo * +append_verval (strings, key, value) + struct ver_stringinfo *strings; + char *key; + char *value; +{ + struct ver_stringinfo *vs, **pp; + + vs = (struct ver_stringinfo *) xmalloc (sizeof *vs); + vs->next = NULL; + unicode_from_ascii ((unsigned short *) NULL, &vs->key, key); + free (key); + unicode_from_ascii ((unsigned short *) NULL, &vs->value, value); + free (value); + + for (pp = &strings; *pp != NULL; pp = &(*pp)->next) + ; + *pp = vs; + + return strings; +} + +/* Append version variable information to a list. */ + +struct ver_varinfo * +append_vertrans (var, language, charset) + struct ver_varinfo *var; + unsigned long language; + unsigned long charset; +{ + struct ver_varinfo *vv, **pp; + + vv = (struct ver_varinfo *) xmalloc (sizeof *vv); + vv->next = NULL; + vv->language = language; + vv->charset = charset; + + for (pp = &var; *pp != NULL; pp = &(*pp)->next) + ; + *pp = vv; + + return var; +} + +/* Local functions used to write out an rc file. */ + +static void indent PARAMS ((FILE *, int)); +static void write_rc_directory + PARAMS ((FILE *, const struct res_directory *, const struct res_id *, + const struct res_id *, int *, int)); +static void write_rc_subdir + PARAMS ((FILE *, const struct res_entry *, const struct res_id *, + const struct res_id *, int *, int)); +static void write_rc_resource + PARAMS ((FILE *, const struct res_id *, const struct res_id *, + const struct res_resource *, int *)); +static void write_rc_accelerators + PARAMS ((FILE *, const struct accelerator *)); +static void write_rc_cursor PARAMS ((FILE *, const struct cursor *)); +static void write_rc_group_cursor + PARAMS ((FILE *, const struct group_cursor *)); +static void write_rc_dialog PARAMS ((FILE *, const struct dialog *)); +static void write_rc_dialog_control + PARAMS ((FILE *, const struct dialog_control *)); +static void write_rc_fontdir PARAMS ((FILE *, const struct fontdir *)); +static void write_rc_group_icon PARAMS ((FILE *, const struct group_icon *)); +static void write_rc_menu PARAMS ((FILE *, const struct menuitem *, int, int)); +static void write_rc_rcdata PARAMS ((FILE *, const struct rcdata_data *, int)); +static void write_rc_stringtable + PARAMS ((FILE *, const struct res_id *, const struct stringtable *)); +static void write_rc_versioninfo PARAMS ((FILE *, const struct versioninfo *)); +static void write_rc_filedata + PARAMS ((FILE *, unsigned long, const unsigned char *)); + +/* Indent a given number of spaces. */ + +static void +indent (e, c) + FILE *e; + int c; +{ + int i; + + for (i = 0; i < c; i++) + putc (' ', e); +} + +/* Dump the resources we have read in the format of an rc file. + + Actually, we don't use the format of an rc file, because it's way + too much of a pain--for example, we'd have to write icon resources + into a file and refer to that file. We just generate a readable + format that kind of looks like an rc file, and is useful for + understanding the contents of a resource file. Someday we may want + to generate an rc file which the rc compiler can read; if that day + comes, this code will have to be fixed up. */ + +void +write_rc_file (filename, resources) + const char *filename; + const struct res_directory *resources; +{ + FILE *e; + int language; + + if (filename == NULL) + e = stdout; + else + { + e = fopen (filename, FOPEN_WT); + if (e == NULL) + fatal ("can't open `%s' for output: %s", filename, strerror (errno)); + } + + language = -1; + write_rc_directory (e, resources, (const struct res_id *) NULL, + (const struct res_id *) NULL, &language, 1); +} + +/* Write out a directory. E is the file to write to. RD is the + directory. TYPE is a pointer to the level 1 ID which serves as the + resource type. NAME is a pointer to the level 2 ID which serves as + an individual resource name. LANGUAGE is a pointer to the current + language. LEVEL is the level in the tree. */ + +static void +write_rc_directory (e, rd, type, name, language, level) + FILE *e; + const struct res_directory *rd; + const struct res_id *type; + const struct res_id *name; + int *language; + int level; +{ + const struct res_entry *re; + + /* Print out some COFF information that rc files can't represent. */ + + if (rd->time != 0) + fprintf (e, "// Time stamp: %lu\n", rd->time); + if (rd->characteristics != 0) + fprintf (e, "// Characteristics: %lu\n", rd->characteristics); + if (rd->major != 0 || rd->minor != 0) + fprintf (e, "// Version: %d %d\n", rd->major, rd->minor); + + for (re = rd->entries; re != NULL; re = re->next) + { + switch (level) + { + case 1: + /* If we're at level 1, the key of this resource is the + type. This normally duplicates the information we have + stored with the resource itself, but we need to remember + the type if this is a user define resource type. */ + type = &re->id; + break; + + case 2: + /* If we're at level 2, the key of this resource is the name + we are going to use in the rc printout. */ + name = &re->id; + break; + + case 3: + /* If we're at level 3, then this key represents a language. + Use it to update the current language. */ + if (! re->id.named + && re->id.u.id != *language + && (re->id.u.id & 0xffff) == re->id.u.id) + { + fprintf (e, "LANGUAGE %lu, %lu\n", + re->id.u.id & 0xff, (re->id.u.id >> 8) & 0xff); + *language = re->id.u.id; + } + break; + + default: + break; + } + + if (re->subdir) + write_rc_subdir (e, re, type, name, language, level); + else + { + if (level == 3) + { + /* This is the normal case: the three levels are + TYPE/NAME/LANGUAGE. NAME will have been set at level + 2, and represents the name to use. We probably just + set LANGUAGE, and it will probably match what the + resource itself records if anything. */ + write_rc_resource (e, type, name, re->u.res, language); + } + else + { + fprintf (e, "// Resource at unexpected level %d\n", level); + write_rc_resource (e, type, (struct res_id *) NULL, re->u.res, + language); + } + } + } +} + +/* Write out a subdirectory entry. E is the file to write to. RE is + the subdirectory entry. TYPE and NAME are pointers to higher level + IDs, or NULL. LANGUAGE is a pointer to the current language. + LEVEL is the level in the tree. */ + +static void +write_rc_subdir (e, re, type, name, language, level) + FILE *e; + const struct res_entry *re; + const struct res_id *type; + const struct res_id *name; + int *language; + int level; +{ + fprintf (e, "\n"); + switch (level) + { + case 1: + fprintf (e, "// Type: "); + if (re->id.named) + res_id_print (e, re->id, 1); + else + { + const char *s; + + switch (re->id.u.id) + { + case RT_CURSOR: s = "cursor"; break; + case RT_BITMAP: s = "bitmap"; break; + case RT_ICON: s = "icon"; break; + case RT_MENU: s = "menu"; break; + case RT_DIALOG: s = "dialog"; break; + case RT_STRING: s = "stringtable"; break; + case RT_FONTDIR: s = "fontdir"; break; + case RT_FONT: s = "font"; break; + case RT_ACCELERATORS: s = "accelerators"; break; + case RT_RCDATA: s = "rcdata"; break; + case RT_MESSAGETABLE: s = "messagetable"; break; + case RT_GROUP_CURSOR: s = "group cursor"; break; + case RT_GROUP_ICON: s = "group icon"; break; + case RT_VERSION: s = "version"; break; + case RT_DLGINCLUDE: s = "dlginclude"; break; + case RT_PLUGPLAY: s = "plugplay"; break; + case RT_VXD: s = "vxd"; break; + case RT_ANICURSOR: s = "anicursor"; break; + case RT_ANIICON: s = "aniicon"; break; + default: s = NULL; break; + } + + if (s != NULL) + fprintf (e, "%s", s); + else + res_id_print (e, re->id, 1); + } + fprintf (e, "\n"); + break; + + case 2: + fprintf (e, "// Name: "); + res_id_print (e, re->id, 1); + fprintf (e, "\n"); + break; + + case 3: + fprintf (e, "// Language: "); + res_id_print (e, re->id, 1); + fprintf (e, "\n"); + break; + + default: + fprintf (e, "// Level %d: ", level); + res_id_print (e, re->id, 1); + fprintf (e, "\n"); + } + + write_rc_directory (e, re->u.dir, type, name, language, level + 1); +} + +/* Write out a single resource. E is the file to write to. TYPE is a + pointer to the type of the resource. NAME is a pointer to the name + of the resource; it will be NULL if there is a level mismatch. RES + is the resource data. LANGUAGE is a pointer to the current + language. */ + +static void +write_rc_resource (e, type, name, res, language) + FILE *e; + const struct res_id *type; + const struct res_id *name; + const struct res_resource *res; + int *language; +{ + const char *s; + int rt; + int menuex = 0; + + fprintf (e, "\n"); + + switch (res->type) + { + default: + abort (); + + case RES_TYPE_ACCELERATOR: + s = "ACCELERATOR"; + rt = RT_ACCELERATORS; + break; + + case RES_TYPE_BITMAP: + s = "BITMAP"; + rt = RT_BITMAP; + break; + + case RES_TYPE_CURSOR: + s = "CURSOR"; + rt = RT_CURSOR; + break; + + case RES_TYPE_GROUP_CURSOR: + s = "GROUP_CURSOR"; + rt = RT_GROUP_CURSOR; + break; + + case RES_TYPE_DIALOG: + if (extended_dialog (res->u.dialog)) + s = "DIALOGEX"; + else + s = "DIALOG"; + rt = RT_DIALOG; + break; + + case RES_TYPE_FONT: + s = "FONT"; + rt = RT_FONT; + break; + + case RES_TYPE_FONTDIR: + s = "FONTDIR"; + rt = RT_FONTDIR; + break; + + case RES_TYPE_ICON: + s = "ICON"; + rt = RT_ICON; + break; + + case RES_TYPE_GROUP_ICON: + s = "GROUP_ICON"; + rt = RT_GROUP_ICON; + break; + + case RES_TYPE_MENU: + if (extended_menu (res->u.menu)) + { + s = "MENUEX"; + menuex = 1; + } + else + { + s = "MENU"; + menuex = 0; + } + rt = RT_MENU; + break; + + case RES_TYPE_MESSAGETABLE: + s = "MESSAGETABLE"; + rt = RT_MESSAGETABLE; + break; + + case RES_TYPE_RCDATA: + s = "RCDATA"; + rt = RT_RCDATA; + break; + + case RES_TYPE_STRINGTABLE: + s = "STRINGTABLE"; + rt = RT_STRING; + break; + + case RES_TYPE_USERDATA: + s = NULL; + rt = 0; + break; + + case RES_TYPE_VERSIONINFO: + s = "VERSIONINFO"; + rt = RT_VERSION; + break; + } + + if (rt != 0 + && type != NULL + && (type->named || type->u.id != rt)) + { + fprintf (e, "// Unexpected resource type mismatch: "); + res_id_print (e, *type, 1); + fprintf (e, " != %d", rt); + } + + if (res->coff_info.codepage != 0) + fprintf (e, "// Code page: %lu\n", res->coff_info.codepage); + if (res->coff_info.reserved != 0) + fprintf (e, "// COFF reserved value: %lu\n", res->coff_info.reserved); + + if (name != NULL) + res_id_print (e, *name, 0); + else + fprintf (e, "??Unknown-Name??"); + + fprintf (e, " "); + if (s != NULL) + fprintf (e, "%s", s); + else if (type != NULL) + res_id_print (e, *type, 0); + else + fprintf (e, "??Unknown-Type??"); + + if (res->res_info.memflags != 0) + { + if ((res->res_info.memflags & MEMFLAG_MOVEABLE) != 0) + fprintf (e, " MOVEABLE"); + if ((res->res_info.memflags & MEMFLAG_PURE) != 0) + fprintf (e, " PURE"); + if ((res->res_info.memflags & MEMFLAG_PRELOAD) != 0) + fprintf (e, " PRELOAD"); + if ((res->res_info.memflags & MEMFLAG_DISCARDABLE) != 0) + fprintf (e, " DISCARDABLE"); + } + + if (res->type == RES_TYPE_DIALOG) + { + fprintf (e, " %d, %d, %d, %d", res->u.dialog->x, res->u.dialog->y, + res->u.dialog->width, res->u.dialog->height); + if (res->u.dialog->ex != NULL + && res->u.dialog->ex->help != 0) + fprintf (e, ", %lu", res->u.dialog->ex->help); + } + + fprintf (e, "\n"); + + if ((res->res_info.language != 0 && res->res_info.language != *language) + || res->res_info.characteristics != 0 + || res->res_info.version != 0) + { + int modifiers; + + switch (res->type) + { + case RES_TYPE_ACCELERATOR: + case RES_TYPE_DIALOG: + case RES_TYPE_MENU: + case RES_TYPE_RCDATA: + case RES_TYPE_STRINGTABLE: + modifiers = 1; + break; + + default: + modifiers = 0; + break; + } + + if (res->res_info.language != 0 && res->res_info.language != *language) + fprintf (e, "%sLANGUAGE %d, %d\n", + modifiers ? "// " : "", + res->res_info.language & 0xff, + (res->res_info.language >> 8) & 0xff); + if (res->res_info.characteristics != 0) + fprintf (e, "%sCHARACTERISTICS %lu\n", + modifiers ? "// " : "", + res->res_info.characteristics); + if (res->res_info.version != 0) + fprintf (e, "%sVERSION %lu\n", + modifiers ? "// " : "", + res->res_info.version); + } + + switch (res->type) + { + default: + abort (); + + case RES_TYPE_ACCELERATOR: + write_rc_accelerators (e, res->u.acc); + break; + + case RES_TYPE_CURSOR: + write_rc_cursor (e, res->u.cursor); + break; + + case RES_TYPE_GROUP_CURSOR: + write_rc_group_cursor (e, res->u.group_cursor); + break; + + case RES_TYPE_DIALOG: + write_rc_dialog (e, res->u.dialog); + break; + + case RES_TYPE_FONTDIR: + write_rc_fontdir (e, res->u.fontdir); + break; + + case RES_TYPE_GROUP_ICON: + write_rc_group_icon (e, res->u.group_icon); + break; + + case RES_TYPE_MENU: + write_rc_menu (e, res->u.menu, menuex, 0); + break; + + case RES_TYPE_RCDATA: + write_rc_rcdata (e, res->u.rcdata, 0); + break; + + case RES_TYPE_STRINGTABLE: + write_rc_stringtable (e, name, res->u.stringtable); + break; + + case RES_TYPE_USERDATA: + write_rc_rcdata (e, res->u.userdata, 0); + break; + + case RES_TYPE_VERSIONINFO: + write_rc_versioninfo (e, res->u.versioninfo); + break; + + case RES_TYPE_BITMAP: + case RES_TYPE_FONT: + case RES_TYPE_ICON: + case RES_TYPE_MESSAGETABLE: + write_rc_filedata (e, res->u.data.length, res->u.data.data); + break; + } +} + +/* Write out accelerator information. */ + +static void +write_rc_accelerators (e, accelerators) + FILE *e; + const struct accelerator *accelerators; +{ + const struct accelerator *acc; + + fprintf (e, "BEGIN\n"); + for (acc = accelerators; acc != NULL; acc = acc->next) + { + int printable; + + fprintf (e, " "); + + if ((acc->key & 0x7f) == acc->key + && isprint ((unsigned char) acc->key) + && (acc->flags & ACC_VIRTKEY) == 0) + { + fprintf (e, "\"%c\"", acc->key); + printable = 1; + } + else + { + fprintf (e, "%d", acc->key); + printable = 0; + } + + fprintf (e, ", %d", acc->id); + + if (! printable) + { + if ((acc->flags & ACC_VIRTKEY) != 0) + fprintf (e, ", VIRTKEY"); + else + fprintf (e, ", ASCII"); + } + + if ((acc->flags & ACC_SHIFT) != 0) + fprintf (e, ", SHIFT"); + if ((acc->flags & ACC_CONTROL) != 0) + fprintf (e, ", CONTROL"); + if ((acc->flags & ACC_ALT) != 0) + fprintf (e, ", ALT"); + + fprintf (e, "\n"); + } + + fprintf (e, "END\n"); +} + +/* Write out cursor information. This would normally be in a separate + file, which the rc file would include. */ + +static void +write_rc_cursor (e, cursor) + FILE *e; + const struct cursor *cursor; +{ + fprintf (e, "// Hotspot: x: %d; y: %d\n", cursor->xhotspot, + cursor->yhotspot); + write_rc_filedata (e, cursor->length, cursor->data); +} + +/* Write out group cursor data. This would normally be built from the + cursor data. */ + +static void +write_rc_group_cursor (e, group_cursor) + FILE *e; + const struct group_cursor *group_cursor; +{ + const struct group_cursor *gc; + + for (gc = group_cursor; gc != NULL; gc = gc->next) + { + fprintf (e, "// width: %d; height %d; planes %d; bits %d\n", + gc->width, gc->height, gc->planes, gc->bits); + fprintf (e, "// data bytes: %lu; index: %d\n", + gc->bytes, gc->index); + } +} + +/* Write dialog data. */ + +static void +write_rc_dialog (e, dialog) + FILE *e; + const struct dialog *dialog; +{ + const struct dialog_control *control; + + if (dialog->style != 0) + fprintf (e, "STYLE 0x%lx\n", dialog->style); + if (dialog->exstyle != 0) + fprintf (e, "EXSTYLE 0x%lx\n", dialog->exstyle); + if (dialog->class.named || dialog->class.u.id != 0) + { + fprintf (e, "CLASS "); + res_id_print (e, dialog->class, 0); + fprintf (e, "\n"); + } + if (dialog->caption != NULL) + fprintf (e, "CAPTION \"%s\"\n", dialog->caption); + if (dialog->menu.named || dialog->menu.u.id != 0) + { + fprintf (e, "MENU "); + res_id_print (e, dialog->menu, 0); + fprintf (e, "\n"); + } + if (dialog->font != NULL) + { + fprintf (e, "FONT %d, \"%s\"", dialog->pointsize, dialog->font); + if (dialog->ex != NULL + && (dialog->ex->weight != 0 || dialog->ex->italic != 0)) + fprintf (e, ", %d, %d", dialog->ex->weight, dialog->ex->italic); + fprintf (e, "\n"); + } + + fprintf (e, "BEGIN\n"); + + for (control = dialog->controls; control != NULL; control = control->next) + write_rc_dialog_control (e, control); + + fprintf (e, "END\n"); +} + +/* For each predefined control keyword, this table provides the class + and the style. */ + +struct control_info +{ + const char *name; + unsigned short class; + unsigned long style; +}; + +static const struct control_info control_info[] = +{ + { "AUTO3STATE", CTL_BUTTON, BS_AUTO3STATE }, + { "AUTOCHECKBOX", CTL_BUTTON, BS_AUTOCHECKBOX }, + { "AUTORADIOBUTTON", CTL_BUTTON, BS_AUTORADIOBUTTON }, + { "CHECKBOX", CTL_BUTTON, BS_CHECKBOX }, + { "COMBOBOX", CTL_COMBOBOX, (unsigned long) -1 }, + { "CTEXT", CTL_STATIC, SS_CENTER }, + { "DEFPUSHBUTTON", CTL_BUTTON, BS_DEFPUSHBUTTON }, + { "EDITTEXT", CTL_EDIT, (unsigned long) -1 }, + { "GROUPBOX", CTL_BUTTON, BS_GROUPBOX }, + { "ICON", CTL_STATIC, SS_ICON }, + { "LISTBOX", CTL_LISTBOX, (unsigned long) -1 }, + { "LTEXT", CTL_STATIC, SS_LEFT }, + { "PUSHBOX", CTL_BUTTON, BS_PUSHBOX }, + { "PUSHBUTTON", CTL_BUTTON, BS_PUSHBUTTON }, + { "RADIOBUTTON", CTL_BUTTON, BS_RADIOBUTTON }, + { "RTEXT", CTL_STATIC, SS_RIGHT }, + { "SCROLLBAR", CTL_SCROLLBAR, (unsigned long) -1 }, + { "STATE3", CTL_BUTTON, BS_3STATE }, + /* It's important that USERBUTTON come after all the other button + types, so that it won't be matched too early. */ + { "USERBUTTON", CTL_BUTTON, (unsigned long) -1 }, + { NULL, 0, 0 } +}; + +/* Write a dialog control. */ + +static void +write_rc_dialog_control (e, control) + FILE *e; + const struct dialog_control *control; +{ + const struct control_info *ci; + + fprintf (e, " "); + + if (control->class.named) + ci = NULL; + else + { + for (ci = control_info; ci->name != NULL; ++ci) + if (ci->class == control->class.u.id + && (ci->style == (unsigned long) -1 + || ci->style == (control->style & 0xff))) + break; + } + + if (ci->name != NULL) + fprintf (e, "%s", ci->name); + else + fprintf (e, "CONTROL"); + + if (control->text.named || control->text.u.id != 0) + { + fprintf (e, " "); + res_id_print (e, control->text, 1); + fprintf (e, ","); + } + + fprintf (e, " %d, ", control->id); + + if (ci->name == NULL) + { + res_id_print (e, control->class, 0); + fprintf (e, ", 0x%lx, ", control->style); + } + + fprintf (e, "%d, %d", control->x, control->y); + + if (control->style != SS_ICON + || control->exstyle != 0 + || control->width != 0 + || control->height != 0 + || control->help != 0) + { + fprintf (e, ", %d, %d", control->width, control->height); + + /* FIXME: We don't need to print the style if it is the default. + More importantly, in certain cases we actually need to turn + off parts of the forced style, by using NOT. */ + fprintf (e, ", 0x%lx", control->style); + + if (control->exstyle != 0 || control->help != 0) + fprintf (e, ", 0x%lx, %lu", control->exstyle, control->help); + } + + fprintf (e, "\n"); + + if (control->data != NULL) + write_rc_rcdata (e, control->data, 2); +} + +/* Write out font directory data. This would normally be built from + the font data. */ + +static void +write_rc_fontdir (e, fontdir) + FILE *e; + const struct fontdir *fontdir; +{ + const struct fontdir *fc; + + for (fc = fontdir; fc != NULL; fc = fc->next) + { + fprintf (e, "// Font index: %d\n", fc->index); + write_rc_filedata (e, fc->length, fc->data); + } +} + +/* Write out group icon data. This would normally be built from the + icon data. */ + +static void +write_rc_group_icon (e, group_icon) + FILE *e; + const struct group_icon *group_icon; +{ + const struct group_icon *gi; + + for (gi = group_icon; gi != NULL; gi = gi->next) + { + fprintf (e, "// width: %d; height %d; colors: %d; planes %d; bits %d\n", + gi->width, gi->height, gi->colors, gi->planes, gi->bits); + fprintf (e, "// data bytes: %lu; index: %d\n", + gi->bytes, gi->index); + } +} + +/* Write out a menu resource. */ + +static void +write_rc_menu (e, menuitems, menuex, ind) + FILE *e; + const struct menuitem *menuitems; + int menuex; + int ind; +{ + const struct menuitem *mi; + + indent (e, ind); + fprintf (e, "BEGIN\n"); + + for (mi = menuitems; mi != NULL; mi = mi->next) + { + indent (e, ind + 2); + + if (mi->popup == NULL) + fprintf (e, "MENUITEM"); + else + fprintf (e, "POPUP"); + + if (! menuex + && mi->popup == NULL + && mi->text == NULL + && mi->type == 0 + && mi->id == 0) + { + fprintf (e, " SEPARATOR\n"); + continue; + } + + if (mi->text == NULL) + fprintf (e, " \"\""); + else + fprintf (e, " \"%s\"", mi->text); + + if (! menuex) + { + if (mi->popup == NULL) + fprintf (e, ", %d", mi->id); + + if ((mi->type & MENUITEM_CHECKED) != 0) + fprintf (e, ", CHECKED"); + if ((mi->type & MENUITEM_GRAYED) != 0) + fprintf (e, ", GRAYED"); + if ((mi->type & MENUITEM_HELP) != 0) + fprintf (e, ", HELP"); + if ((mi->type & MENUITEM_INACTIVE) != 0) + fprintf (e, ", INACTIVE"); + if ((mi->type & MENUITEM_MENUBARBREAK) != 0) + fprintf (e, ", MENUBARBREAK"); + if ((mi->type & MENUITEM_MENUBREAK) != 0) + fprintf (e, ", MENUBREAK"); + } + else + { + if (mi->id != 0 || mi->type != 0 || mi->state != 0 || mi->help != 0) + { + fprintf (e, ", %d", mi->id); + if (mi->type != 0 || mi->state != 0 || mi->help != 0) + { + fprintf (e, ", %lu", mi->type); + if (mi->state != 0 || mi->help != 0) + { + fprintf (e, ", %lu", mi->state); + if (mi->help != 0) + fprintf (e, ", %lu", mi->help); + } + } + } + } + + fprintf (e, "\n"); + + if (mi->popup != NULL) + write_rc_menu (e, mi->popup, menuex, ind + 2); + } + + indent (e, ind); + fprintf (e, "END\n"); +} + +/* Write out an rcdata resource. This is also used for other types of + resources that need to print arbitrary data. */ + +static void +write_rc_rcdata (e, rcdata, ind) + FILE *e; + const struct rcdata_data *rcdata; + int ind; +{ + const struct rcdata_item *ri; + + indent (e, ind); + fprintf (e, "BEGIN\n"); + + for (ri = rcdata->first; ri != NULL; ri = ri->next) + { + if (ri->type == RCDATA_BUFFER && ri->u.buffer.length == 0) + continue; + + indent (e, ind + 2); + + switch (ri->type) + { + default: + abort (); + + case RCDATA_WORD: + fprintf (e, "%d", ri->u.word); + break; + + case RCDATA_DWORD: + fprintf (e, "%luL", ri->u.dword); + break; + + case RCDATA_STRING: + fprintf (e, "\"%s\"", ri->u.string); + break; + + case RCDATA_WSTRING: + fprintf (e, "L\""); + unicode_print (e, ri->u.wstring, -1); + fprintf (e, "\""); + break; + + case RCDATA_BUFFER: + { + unsigned long i; + int first; + + /* Assume little endian data. */ + + first = 1; + for (i = 0; i + 3 < ri->u.buffer.length; i += 4) + { + unsigned long l; + + l = ((((((ri->u.buffer.data[i + 3] << 8) + | ri->u.buffer.data[i + 2]) << 8) + | ri->u.buffer.data[i + 1]) << 8) + | ri->u.buffer.data[i]); + if (first) + first = 0; + else + { + fprintf (e, ",\n"); + indent (e, ind); + } + fprintf (e, "%luL", l); + } + + if (i + 1 < ri->u.buffer.length) + { + int i; + + i = (ri->u.buffer.data[i + 1] << 8) | ri->u.buffer.data[i]; + if (first) + first = 0; + else + { + fprintf (e, ",\n"); + indent (e, ind); + } + fprintf (e, "%d", i); + i += 2; + } + + if (i < ri->u.buffer.length) + { + if (first) + first = 0; + else + { + fprintf (e, ",\n"); + indent (e, ind); + } + if ((ri->u.buffer.data[i] & 0x7f) == ri->u.buffer.data[i] + && isprint (ri->u.buffer.data[i])) + fprintf (e, "\"%c\"", ri->u.buffer.data[i]); + else + fprintf (e, "\"\%03o\"", ri->u.buffer.data[i]); + } + + break; + } + } + + if (ri->next != NULL) + fprintf (e, ","); + fprintf (e, "\n"); + } + + indent (e, ind); + fprintf (e, "END\n"); +} + +/* Write out a stringtable resource. */ + +static void +write_rc_stringtable (e, name, stringtable) + FILE *e; + const struct res_id *name; + const struct stringtable *stringtable; +{ + unsigned long offset; + int i; + + if (name != NULL && ! name->named) + offset = name->u.id << 4; + else + { + fprintf (e, "// %s string table name\n", + name == NULL ? "Missing" : "Invalid"); + offset = 0; + } + + fprintf (e, "BEGIN\n"); + + for (i = 0; i < 16; i++) + { + if (stringtable->strings[i].length != 0) + { + fprintf (e, " %lu, \"", offset + i); + unicode_print (e, stringtable->strings[i].string, + stringtable->strings[i].length); + fprintf (e, "\"\n"); + } + } + + fprintf (e, "END\n"); +} + +/* Write out a versioninfo resource. */ + +static void +write_rc_versioninfo (e, versioninfo) + FILE *e; + const struct versioninfo *versioninfo; +{ + const struct fixed_versioninfo *f; + const struct ver_info *vi; + + f = versioninfo->fixed; + if (f->file_version_ms != 0 || f->file_version_ls != 0) + fprintf (e, " FILEVERSION %lu, %lu, %lu, %lu\n", + (f->file_version_ms >> 16) & 0xffff, + f->file_version_ms & 0xffff, + (f->file_version_ls >> 16) & 0xffff, + f->file_version_ls & 0xffff); + if (f->product_version_ms != 0 || f->product_version_ls != 0) + fprintf (e, " PRODUCTVERSION %lu, %lu, %lu, %lu\n", + (f->product_version_ms >> 16) & 0xffff, + f->product_version_ms & 0xffff, + (f->product_version_ls >> 16) & 0xffff, + f->product_version_ls & 0xffff); + if (f->file_flags_mask != 0) + fprintf (e, " FILEFLAGSMASK 0x%lx\n", f->file_flags_mask); + if (f->file_flags != 0) + fprintf (e, " FILEFLAGS 0x%lx\n", f->file_flags); + if (f->file_os != 0) + fprintf (e, " FILEOS 0x%lx\n", f->file_os); + if (f->file_type != 0) + fprintf (e, " FILETYPE 0x%lx\n", f->file_type); + if (f->file_subtype != 0) + fprintf (e, " FILESUBTYPE 0x%lx\n", f->file_subtype); + if (f->file_date_ms != 0 || f->file_date_ls != 0) + fprintf (e, "// Date: %lu, %lu\n", f->file_date_ms, f->file_date_ls); + + fprintf (e, "BEGIN\n"); + + for (vi = versioninfo->var; vi != NULL; vi = vi->next) + { + switch (vi->type) + { + case VERINFO_STRING: + { + const struct ver_stringinfo *vs; + + fprintf (e, " BLOCK \"StringFileInfo\"\n"); + fprintf (e, " BEGIN\n"); + fprintf (e, " BLOCK \""); + unicode_print (e, vi->u.string.language, -1); + fprintf (e, "\"\n"); + fprintf (e, " BEGIN\n"); + + for (vs = vi->u.string.strings; vs != NULL; vs = vs->next) + { + fprintf (e, " VALUE \""); + unicode_print (e, vs->key, -1); + fprintf (e, "\", \""); + unicode_print (e, vs->value, -1); + fprintf (e, "\"\n"); + } + + fprintf (e, " END\n"); + fprintf (e, " END\n"); + break; + } + + case VERINFO_VAR: + { + const struct ver_varinfo *vv; + + fprintf (e, " BLOCK \"VarFileInfo\"\n"); + fprintf (e, " BEGIN\n"); + fprintf (e, " VALUE \""); + unicode_print (e, vi->u.var.key, -1); + fprintf (e, "\""); + + for (vv = vi->u.var.var; vv != NULL; vv = vv->next) + fprintf (e, ", 0x%x, %d", (unsigned int) vv->language, + vv->charset); + + fprintf (e, "\n END\n"); + + break; + } + } + } + + fprintf (e, "END\n"); +} + +/* Write out data which would normally be read from a file. */ + +static void +write_rc_filedata (e, length, data) + FILE *e; + unsigned long length; + const unsigned char *data; +{ + unsigned long i; + + for (i = 0; i + 15 < length; i += 16) + { + fprintf (e, "// %4lx: ", i); + fprintf (e, "%02x %02x %02x %02x %02x %02x %02x %02x ", + data[i + 0], data[i + 1], data[i + 2], data[i + 3], + data[i + 4], data[i + 5], data[i + 6], data[i + 7]); + fprintf (e, "%02x %02x %02x %02x %02x %02x %02x %02x\n", + data[i + 8], data[i + 9], data[i + 10], data[i + 11], + data[i + 12], data[i + 13], data[i + 14], data[i + 15]); + } + + if (i < length) + { + fprintf (e, "// %4lx:", i); + while (i < length) + { + fprintf (e, " %02x", data[i]); + ++i; + } + fprintf (e, "\n"); + } +} diff --git a/binutils/windres.c b/binutils/windres.c new file mode 100644 index 00000000000..d08fb97a82d --- /dev/null +++ b/binutils/windres.c @@ -0,0 +1,912 @@ +/* windres.c -- a program to manipulate Windows resources + Copyright 1997 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support. + + This file is part of GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* This program can read and write Windows resources in various + formats. In particular, it can act like the rc resource compiler + program, and it can act like the cvtres res to COFF conversion + program. + + It is based on information taken from the following sources: + + * Microsoft documentation. + + * The rcl program, written by Gunther Ebert + . + + * The res2coff program, written by Pedro A. Aranda . + + */ + +#include "bfd.h" +#include "getopt.h" +#include "bucomm.h" +#include "libiberty.h" +#include "windres.h" + +#include +#include + +/* An enumeration of format types. */ + +enum res_format +{ + /* Unknown format. */ + RES_FORMAT_UNKNOWN, + /* Textual RC file. */ + RES_FORMAT_RC, + /* Binary RES file. */ + RES_FORMAT_RES, + /* COFF file. */ + RES_FORMAT_COFF +}; + +/* A structure used to map between format types and strings. */ + +struct format_map +{ + const char *name; + enum res_format format; +}; + +/* A mapping between names and format types. */ + +static const struct format_map format_names[] = +{ + { "rc", RES_FORMAT_RC }, + { "res", RES_FORMAT_RES }, + { "coff", RES_FORMAT_COFF }, + { NULL, RES_FORMAT_UNKNOWN } +}; + +/* A mapping from file extensions to format types. */ + +static const struct format_map format_fileexts[] = +{ + { "rc", RES_FORMAT_RC }, + { "res", RES_FORMAT_RES }, + { "exe", RES_FORMAT_COFF }, + { "obj", RES_FORMAT_COFF }, + { "o", RES_FORMAT_COFF }, + { NULL, RES_FORMAT_UNKNOWN } +}; + +/* A list of include directories. */ + +struct include_dir +{ + struct include_dir *next; + char *dir; +}; + +static struct include_dir *include_dirs; + +/* Long options. */ + +/* 150 isn't special; it's just an arbitrary non-ASCII char value. */ + +#define OPTION_DEFINE 150 +#define OPTION_HELP (OPTION_DEFINE + 1) +#define OPTION_INCLUDE_DIR (OPTION_HELP + 1) +#define OPTION_LANGUAGE (OPTION_INCLUDE_DIR + 1) +#define OPTION_PREPROCESSOR (OPTION_LANGUAGE + 1) +#define OPTION_VERSION (OPTION_PREPROCESSOR + 1) +#define OPTION_YYDEBUG (OPTION_VERSION + 1) + +static const struct option long_options[] = +{ + {"define", required_argument, 0, OPTION_DEFINE}, + {"help", no_argument, 0, OPTION_HELP}, + {"include-dir", required_argument, 0, OPTION_INCLUDE_DIR}, + {"input-format", required_argument, 0, 'I'}, + {"language", required_argument, 0, OPTION_LANGUAGE}, + {"output-format", required_argument, 0, 'O'}, + {"preprocessor", required_argument, 0, OPTION_PREPROCESSOR}, + {"target", required_argument, 0, 'F'}, + {"version", no_argument, 0, OPTION_VERSION}, + {"yydebug", no_argument, 0, OPTION_YYDEBUG}, + {0, no_argument, 0, 0} +}; + +/* Static functions. */ + +static enum res_format format_from_name PARAMS ((const char *)); +static enum res_format format_from_filename PARAMS ((const char *, int)); +static void usage PARAMS ((FILE *, int)); + +/* Open a file using the include directory search list. */ + +FILE * +open_file_search (filename, mode, errmsg, real_filename) + const char *filename; + const char *mode; + const char *errmsg; + char **real_filename; +{ + FILE *e; + struct include_dir *d; + + e = fopen (filename, mode); + if (e != NULL) + { + *real_filename = xstrdup (filename); + return e; + } + + if (errno == ENOENT) + { + for (d = include_dirs; d != NULL; d = d->next) + { + char *n; + + n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2); + sprintf (n, "%s/%s", d->dir, filename); + e = fopen (n, mode); + if (e != NULL) + { + *real_filename = n; + return e; + } + + if (errno != ENOENT) + break; + } + } + + fatal ("can't open %s `%s': %s", errmsg, filename, strerror (errno)); + + /* Return a value to avoid a compiler warning. */ + return NULL; +} + +/* Unicode support. */ + +/* Convert an ASCII string to a unicode string. We just copy it, + expanding chars to shorts, rather than doing something intelligent. */ + +void +unicode_from_ascii (length, unicode, ascii) + unsigned short *length; + unsigned short **unicode; + const char *ascii; +{ + int len; + const char *s; + unsigned short *w; + + len = strlen (ascii); + + if (length != NULL) + { + if (len > 0xffff) + fatal ("string too long (%d chars > 0xffff)", len); + *length = len; + } + + *unicode = (unsigned short *) xmalloc ((len + 1) * sizeof (unsigned short)); + + for (s = ascii, w = *unicode; *s != '\0'; s++, w++) + *w = *s & 0xff; + *w = 0; +} + +/* Print the unicode string UNICODE to the file E. LENGTH is the + number of characters to print, or -1 if we should print until the + end of the string. */ + +void +unicode_print (e, unicode, length) + FILE *e; + const unsigned short *unicode; + int length; +{ + while (1) + { + unsigned short ch; + + if (length == 0) + return; + if (length > 0) + --length; + + ch = *unicode; + + if (ch == 0) + return; + + ++unicode; + + if ((ch & 0x7f) == ch && isprint (ch)) + putc (ch, e); + else if ((ch & 0xff) == ch) + fprintf (e, "\\%03o", (unsigned int) ch); + else + fprintf (e, "\\x%x", (unsigned int) ch); + } +} + +/* Compare two resource ID's. We consider name entries to come before + numeric entries, because that is how they appear in the COFF .rsrc + section. */ + +int +res_id_cmp (a, b) + struct res_id a; + struct res_id b; +{ + if (! a.named) + { + if (b.named) + return 1; + if (a.u.id > b.u.id) + return 1; + else if (a.u.id < b.u.id) + return -1; + else + return 0; + } + else + { + unsigned short *as, *ase, *bs, *bse; + + if (! b.named) + return -1; + + as = a.u.n.name; + ase = as + a.u.n.length; + bs = b.u.n.name; + bse = bs + b.u.n.length; + + while (as < ase) + { + int i; + + if (bs >= bse) + return 1; + i = (int) *as - (int) *bs; + if (i != 0) + return i; + ++as; + ++bs; + } + + if (bs < bse) + return -1; + + return 0; + } +} + +/* Print a resource ID. */ + +void +res_id_print (stream, id, quote) + FILE *stream; + struct res_id id; + int quote; +{ + if (! id.named) + fprintf (stream, "%lu", id.u.id); + else + { + unsigned short *s, *se; + + if (quote) + putc ('"', stream); + s = id.u.n.name; + se = s + id.u.n.length; + while (s < se) + { + if (*s == '"') + fprintf (stream, "\\\""); + else if ((*s & 0xff) == *s && isprint (*s)) + putc (*s, stream); + else + fprintf (stream, "\\%03o", *s); + ++s; + } + if (quote) + putc ('"', stream); + } +} + +/* Print a list of resource ID's. */ + +void +res_ids_print (stream, cids, ids) + FILE *stream; + int cids; + const struct res_id *ids; +{ + int i; + + for (i = 0; i < cids; i++) + { + res_id_print (stream, ids[i], 1); + if (i + 1 < cids) + fprintf (stream, ": "); + } +} + +/* Convert an ASCII string to a resource ID. */ + +void +res_string_to_id (res_id, string) + struct res_id *res_id; + const char *string; +{ + res_id->named = 1; + unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string); +} + +/* Define a resource. The arguments are the resource tree, RESOURCES, + and the location at which to put it in the tree, CIDS and IDS. + This returns a newly allocated res_resource structure, which the + caller is expected to initialize. If DUPOK is non-zero, then if a + resource with this ID exists, it is returned. Otherwise, a warning + is issued, and a new resource is created replacing the existing + one. */ + +struct res_resource * +define_resource (resources, cids, ids, dupok) + struct res_directory **resources; + int cids; + const struct res_id *ids; + int dupok; +{ + struct res_entry *re = NULL; + int i; + + assert (cids > 0); + for (i = 0; i < cids; i++) + { + struct res_entry **pp; + + if (*resources == NULL) + { + *resources = (struct res_directory *) xmalloc (sizeof **resources); + (*resources)->characteristics = 0; + (*resources)->time = 0; + (*resources)->major = 0; + (*resources)->minor = 0; + (*resources)->entries = NULL; + } + + for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next) + if (res_id_cmp ((*pp)->id, ids[i]) == 0) + break; + + if (*pp != NULL) + re = *pp; + else + { + re = (struct res_entry *) xmalloc (sizeof *re); + re->next = NULL; + re->id = ids[i]; + if ((i + 1) < cids) + { + re->subdir = 1; + re->u.dir = NULL; + } + else + { + re->subdir = 0; + re->u.res = NULL; + } + + *pp = re; + } + + if ((i + 1) < cids) + { + if (! re->subdir) + { + fprintf (stderr, "%s: ", program_name); + res_ids_print (stderr, i, ids); + fprintf (stderr, ": expected to be a directory\n"); + xexit (1); + } + + resources = &re->u.dir; + } + } + + if (re->subdir) + { + fprintf (stderr, "%s: ", program_name); + res_ids_print (stderr, cids, ids); + fprintf (stderr, ": expected to be a leaf\n"); + xexit (1); + } + + if (re->u.res != NULL) + { + if (dupok) + return re->u.res; + + fprintf (stderr, "%s: warning: ", program_name); + res_ids_print (stderr, cids, ids); + fprintf (stderr, ": duplicate value\n"); + } + + re->u.res = (struct res_resource *) xmalloc (sizeof (struct res_resource)); + + re->u.res->type = RES_TYPE_UNINITIALIZED; + memset (&re->u.res->res_info, 0, sizeof (struct res_res_info)); + memset (&re->u.res->coff_info, 0, sizeof (struct res_coff_info)); + + return re->u.res; +} + +/* Define a standard resource. This is a version of define_resource + that just takes type, name, and language arguments. */ + +struct res_resource * +define_standard_resource (resources, type, name, language, dupok) + struct res_directory **resources; + int type; + struct res_id name; + int language; + int dupok; +{ + struct res_id a[3]; + + a[0].named = 0; + a[0].u.id = type; + a[1] = name; + a[2].named = 0; + a[2].u.id = language; + return define_resource (resources, 3, a, dupok); +} + +/* Return whether the dialog resource DIALOG is a DIALOG or a + DIALOGEX. */ + +int +extended_dialog (dialog) + const struct dialog *dialog; +{ + const struct dialog_control *c; + + if (dialog->ex != NULL) + return 1; + + for (c = dialog->controls; c != NULL; c = c->next) + if (c->data != NULL || c->help != 0) + return 1; + + return 0; +} + +/* Return whether MENUITEMS are a MENU or a MENUEX. */ + +int +extended_menu (menuitems) + const struct menuitem *menuitems; +{ + const struct menuitem *mi; + + for (mi = menuitems; mi != NULL; mi = mi->next) + { + if (mi->help != 0 || mi->state != 0) + return 1; + if (mi->popup != NULL && mi->id != 0) + return 1; + if ((mi->type + & ~ (MENUITEM_CHECKED + | MENUITEM_GRAYED + | MENUITEM_HELP + | MENUITEM_INACTIVE + | MENUITEM_MENUBARBREAK + | MENUITEM_MENUBREAK)) + != 0) + return 1; + if (mi->popup != NULL) + { + if (extended_menu (mi->popup)) + return 1; + } + } + + return 0; +} + +/* Convert a string to a format type, or exit if it can't be done. */ + +static enum res_format +format_from_name (name) + const char *name; +{ + const struct format_map *m; + + for (m = format_names; m->name != NULL; m++) + if (strcasecmp (m->name, name) == 0) + break; + + if (m->name == NULL) + { + fprintf (stderr, "%s: unknown format type `%s'\n", program_name, name); + fprintf (stderr, "%s: supported formats:", program_name); + for (m = format_names; m->name != NULL; m++) + fprintf (stderr, " %s", m->name); + fprintf (stderr, "\n"); + xexit (1); + } + + return m->format; +} + +/* Work out a format type given a file name. If INPUT is non-zero, + it's OK to look at the file itself. */ + +static enum res_format +format_from_filename (filename, input) + const char *filename; + int input; +{ + const char *ext; + FILE *e; + unsigned char b1, b2, b3, b4, b5; + int magic; + + /* If we have an extension, see if we recognize it as implying a + particular format. */ + ext = strrchr (filename, '.'); + if (ext != NULL) + { + const struct format_map *m; + + ++ext; + for (m = format_fileexts; m->name != NULL; m++) + if (strcasecmp (m->name, ext) == 0) + return m->format; + } + + /* If we don't recognize the name of an output file, assume it's a + COFF file. */ + + if (! input) + return RES_FORMAT_COFF; + + /* Read the first few bytes of the file to see if we can guess what + it is. */ + + e = fopen (filename, FOPEN_RB); + if (e == NULL) + fatal ("%s: %s", filename, strerror (errno)); + + b1 = getc (e); + b2 = getc (e); + b3 = getc (e); + b4 = getc (e); + b5 = getc (e); + + fclose (e); + + /* A PE executable starts with 0x4d 0x5a 0x90 0x00. */ + if (b1 == 0x4d && b2 == 0x5a && b3 == 0x90 && b4 == 0) + return RES_FORMAT_COFF; + + /* A COFF .o file starts with a COFF magic number. */ + magic = (b2 << 8) | b1; + switch (magic) + { + case 0x14c: /* i386 */ + case 0x166: /* MIPS */ + case 0x184: /* Alpha */ + case 0x268: /* 68k */ + case 0x1f0: /* PowerPC */ + case 0x290: /* PA */ + return RES_FORMAT_COFF; + } + + /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */ + if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20) + return RES_FORMAT_RES; + + /* If every character is printable or space, assume it's an RC file. */ + if ((isprint (b1) || isspace (b1)) + && (isprint (b2) || isspace (b2)) + && (isprint (b3) || isspace (b3)) + && (isprint (b4) || isspace (b4)) + && (isprint (b5) || isspace (b5))) + return RES_FORMAT_RC; + + /* Otherwise, we give up. */ + fatal ("can not determine type of file `%s'; use the -I option", + filename); + + /* Return something to silence the compiler warning. */ + return RES_FORMAT_UNKNOWN; +} + +/* Print a usage message and exit. */ + +static void +usage (stream, status) + FILE *stream; + int status; +{ + fprintf (stream, "Usage: %s [options] [input-file] [output-file]\n", + program_name); + fprintf (stream, "\ +Options:\n\ + -i FILE, --input FILE Name input file\n\ + -o FILE, --output FILE Name output file\n\ + -I FORMAT, --input-format FORMAT\n\ + Specify input format\n\ + -O FORMAT, --output-format FORMAT\n\ + Specify output format\n\ + -F TARGET, --target TARGET Specify COFF target\n\ + --preprocessor PROGRAM Program to use to preprocess rc file\n\ + --include-dir DIR Include directory when preprocessing rc file\n\ + --define SYM[=VAL] Define SYM when preprocessing rc file\n\ + --language VAL Set language when reading rc file\n\ +#ifdef YYDEBUG + --yydebug Turn on parser debugging\n\ +#endif + --help Print this help message\n\ + --version Print version information\n"); + fprintf (stream, "\ +FORMAT is one of rc, res, or coff, and is deduced from the file name\n\ +extension if not specified. A single file name is an input file.\n\ +No input-file is stdin, default rc. No output-file is stdout, default rc.\n"); + list_supported_targets (program_name, stream); + if (status == 0) + fprintf (stream, "Report bugs to bug-gnu-utils@prep.ai.mit.edu\n"); + exit (status); +} + +/* The main function. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + char *input_filename; + char *output_filename; + enum res_format input_format; + enum res_format output_format; + char *target; + char *preprocessor; + char *preprocargs; + int language; + struct res_directory *resources; + + program_name = argv[0]; + xmalloc_set_program_name (program_name); + + bfd_init (); + set_default_bfd_target (); + + input_filename = NULL; + output_filename = NULL; + input_format = RES_FORMAT_UNKNOWN; + output_format = RES_FORMAT_UNKNOWN; + target = NULL; + preprocessor = NULL; + preprocargs = NULL; + language = -1; + + while ((c = getopt_long (argc, argv, "i:o:I:O:F:", long_options, + (int *) 0)) != EOF) + { + switch (c) + { + case 'i': + input_filename = optarg; + break; + + case 'o': + output_filename = optarg; + break; + + case 'I': + input_format = format_from_name (optarg); + break; + + case 'O': + output_format = format_from_name (optarg); + break; + + case 'F': + target = optarg; + break; + + case OPTION_PREPROCESSOR: + preprocessor = optarg; + break; + + case OPTION_DEFINE: + if (preprocargs == NULL) + { + preprocargs = xmalloc (strlen (optarg) + 3); + sprintf (preprocargs, "-D%s", optarg); + } + else + { + char *n; + + n = xmalloc (strlen (preprocargs) + strlen (optarg) + 4); + sprintf (n, "%s -D%s", preprocargs, optarg); + free (preprocargs); + preprocargs = n; + } + break; + + case OPTION_INCLUDE_DIR: + if (preprocargs == NULL) + { + preprocargs = xmalloc (strlen (optarg) + 3); + sprintf (preprocargs, "-I%s", optarg); + } + else + { + char *n; + + n = xmalloc (strlen (preprocargs) + strlen (optarg) + 4); + sprintf (n, "%s -I%s", preprocargs, optarg); + free (preprocargs); + preprocargs = n; + } + + { + struct include_dir *n, **pp; + + n = (struct include_dir *) xmalloc (sizeof *n); + n->next = NULL; + n->dir = optarg; + + for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next) + ; + *pp = n; + } + + break; + + case OPTION_LANGUAGE: + language = strtol (optarg, (char **) NULL, 16); + break; + +#ifdef YYDEBUG + case OPTION_YYDEBUG: + yydebug = 1; + break; +#endif + + case OPTION_HELP: + usage (stdout, 0); + break; + + case OPTION_VERSION: + print_version ("windres"); + break; + + default: + usage (stderr, 1); + break; + } + } + + if (input_filename == NULL && optind < argc) + { + input_filename = argv[optind]; + ++optind; + } + + if (output_filename == NULL && optind < argc) + { + output_filename = argv[optind]; + ++optind; + } + + if (argc != optind) + usage (stderr, 1); + + if (input_format == RES_FORMAT_UNKNOWN) + { + if (input_filename == NULL) + input_format = RES_FORMAT_RC; + else + input_format = format_from_filename (input_filename, 1); + } + + if (output_format == RES_FORMAT_UNKNOWN) + { + if (output_filename == NULL) + output_format = RES_FORMAT_RC; + else + output_format = format_from_filename (output_filename, 0); + } + + /* Read the input file. */ + + switch (input_format) + { + default: + abort (); + case RES_FORMAT_RC: + resources = read_rc_file (input_filename, preprocessor, preprocargs, + language); + break; + case RES_FORMAT_RES: + resources = read_res_file (input_filename); + break; + case RES_FORMAT_COFF: + resources = read_coff_rsrc (input_filename, target); + break; + } + + /* Write the output file. */ + + switch (output_format) + { + default: + abort (); + case RES_FORMAT_RC: + write_rc_file (output_filename, resources); + break; + case RES_FORMAT_RES: + write_res_file (output_filename, resources); + break; + case RES_FORMAT_COFF: + write_coff_file (output_filename, target, resources); + break; + } + + xexit (0); + return 0; +} + +struct res_directory * +read_res_file (filename) + const char *filename; +{ + fatal ("read_res_file unimplemented"); + return NULL; +} + +struct res_directory * +read_coff_rsrc (filename, target) + const char *filename; + const char *target; +{ + fatal ("read_coff_rsrc unimplemented"); + return NULL; +} + +void +write_res_file (filename, resources) + const char *filename; + const struct res_directory *resources; +{ + fatal ("write_res_file unimplemented"); +} + +void +write_coff_file (filename, target, resources) + const char *filename; + const char *target; + const struct res_directory *resources; +{ + fatal ("write_coff_file unimplemented"); +} diff --git a/binutils/windres.h b/binutils/windres.h new file mode 100644 index 00000000000..dbb9c5c2afa --- /dev/null +++ b/binutils/windres.h @@ -0,0 +1,820 @@ +/* windres.h -- header file for windres program. + Copyright 1997 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support. + + This file is part of GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#include + +/* This is the header file for the windres program. It defines + structures and declares functions used within the program. */ + +/* We represent resources internally as a tree, similar to the tree + used in the .rsrc section of a COFF file. The root is a + res_directory structure. */ + +struct res_directory +{ + /* Resource flags. According to the MS docs, this is currently + always zero. */ + unsigned long characteristics; + /* Time/date stamp. */ + unsigned long time; + /* Major version number. */ + unsigned short major; + /* Minor version number. */ + unsigned short minor; + /* Directory entries. */ + struct res_entry *entries; +}; + +/* A resource ID is stored in a res_id structure. */ + +struct res_id +{ + /* Non-zero if this entry has a name rather than an ID. */ + unsigned int named : 1; + union + { + /* If the named field is non-zero, this is the name. */ + struct + { + /* Length of the name. */ + unsigned short length; + /* Pointer to the name, which is a Unicode string. */ + unsigned short *name; + } n; + /* If the named field is zero, this is the ID. */ + unsigned long id; + } u; +}; + +/* Each entry in the tree is a res_entry structure. We mix + directories and resources because in a COFF file all entries in a + directory are sorted together, whether the entries are + subdirectories or resources. */ + +struct res_entry +{ + /* Next entry. */ + struct res_entry *next; + /* Resource ID. */ + struct res_id id; + /* Non-zero if this entry is a subdirectory rather than a leaf. */ + unsigned int subdir : 1; + union + { + /* If the subdir field is non-zero, this is a pointer to the + subdirectory. */ + struct res_directory *dir; + /* If the subdir field is zero, this is a pointer to the resource + data. */ + struct res_resource *res; + } u; +}; + +/* Types of resources. */ + +enum res_type +{ + RES_TYPE_UNINITIALIZED, + RES_TYPE_ACCELERATOR, + RES_TYPE_BITMAP, + RES_TYPE_CURSOR, + RES_TYPE_GROUP_CURSOR, + RES_TYPE_DIALOG, + RES_TYPE_FONT, + RES_TYPE_FONTDIR, + RES_TYPE_ICON, + RES_TYPE_GROUP_ICON, + RES_TYPE_MENU, + RES_TYPE_MESSAGETABLE, + RES_TYPE_RCDATA, + RES_TYPE_STRINGTABLE, + RES_TYPE_USERDATA, + RES_TYPE_VERSIONINFO +}; + +/* A res file and a COFF file store information differently. The + res_info structures holds data which in a res file is stored with + each resource, but in a COFF file is stored elsewhere. */ + +struct res_res_info +{ + /* Language. In a COFF file, the third level of the directory is + keyed by the language, so the language of a resource is defined + by its location in the resource tree. */ + unsigned short language; + /* Characteristics of the resource. Entirely user defined. In a + COFF file, the res_directory structure has a characteristics + field, but I don't know if it's related to the one in the res + file. */ + unsigned long characteristics; + /* Version of the resource. Entirely user defined. In a COFF file, + the res_directory structure has a characteristics field, but I + don't know if it's related to the one in the res file. */ + unsigned long version; + /* Memory flags. This is a combination of the MEMFLAG values + defined below. Most of these values are historical, and are not + meaningful for win32. I don't think there is any way to store + this information in a COFF file. */ + unsigned short memflags; +}; + +/* Each resource in a COFF file has some information which can does + not appear in a res file. */ + +struct res_coff_info +{ + /* The code page used for the data. I don't really know what this + should be. */ + unsigned long codepage; + /* A resource entry in a COFF file has a reserved field, which we + record here when reading a COFF file. When writing a COFF file, + we set this field to zero. */ + unsigned long reserved; +}; + +/* Resource data is stored in a res_resource structure. */ + +struct res_resource +{ + /* The type of resource. */ + enum res_type type; + /* The data for the resource. */ + union + { + struct + { + unsigned long length; + unsigned char *data; + } data; + struct accelerator *acc; + struct cursor *cursor; + struct group_cursor *group_cursor; + struct dialog *dialog; + struct fontdir *fontdir; + struct group_icon *group_icon; + struct menuitem *menu; + struct rcdata_data *rcdata; + struct stringtable *stringtable; + struct rcdata_data *userdata; + struct versioninfo *versioninfo; + } u; + /* Information from a res file. */ + struct res_res_info res_info; + /* Information from a COFF file. */ + struct res_coff_info coff_info; +}; + +/* Memory flags in the memflags field of a struct res_resource. */ + +#define MEMFLAG_MOVEABLE 0x10 +#define MEMFLAG_PURE 0x20 +#define MEMFLAG_PRELOAD 0x40 +#define MEMFLAG_DISCARDABLE 0x1000 + +/* Standard resource type codes. These are used in the ID field of a + res_entry structure. */ + +#define RT_CURSOR 1 +#define RT_BITMAP 2 +#define RT_ICON 3 +#define RT_MENU 4 +#define RT_DIALOG 5 +#define RT_STRING 6 +#define RT_FONTDIR 7 +#define RT_FONT 8 +#define RT_ACCELERATORS 9 +#define RT_RCDATA 10 +#define RT_MESSAGETABLE 11 +#define RT_GROUP_CURSOR 12 +#define RT_GROUP_ICON 14 +#define RT_VERSION 16 +#define RT_DLGINCLUDE 17 +#define RT_PLUGPLAY 18 +#define RT_VXD 19 +#define RT_ANICURSOR 21 +#define RT_ANIICON 22 + +/* An accelerator resource is a linked list of these structures. */ + +struct accelerator +{ + /* Next accelerator. */ + struct accelerator *next; + /* Flags. A combination of the ACC values defined below. */ + unsigned short flags; + /* Key value. */ + unsigned short key; + /* Resource ID. */ + unsigned short id; +}; + +/* Accelerator flags in the flags field of a struct accelerator. + These are the same values that appear in a res file. I hope. */ + +#define ACC_VIRTKEY 0x01 +#define ACC_NOINVERT 0x02 +#define ACC_SHIFT 0x04 +#define ACC_CONTROL 0x08 +#define ACC_ALT 0x10 +#define ACC_LAST 0x80 + +/* A cursor resource. */ + +struct cursor +{ + /* X coordinate of hotspot. */ + short xhotspot; + /* Y coordinate of hotspot. */ + short yhotspot; + /* Length of bitmap data. */ + unsigned long length; + /* Data. */ + unsigned char *data; +}; + +/* A group_cursor resource is a list of group_cursor structures. */ + +struct group_cursor +{ + /* Next cursor in group. */ + struct group_cursor *next; + /* Width. */ + unsigned short width; + /* Height. */ + unsigned short height; + /* Planes. */ + unsigned short planes; + /* Bits per pixel. */ + unsigned short bits; + /* Number of bytes in cursor resource. */ + unsigned long bytes; + /* Index of cursor resource. */ + unsigned short index; +}; + +/* A dialog resource. */ + +struct dialog +{ + /* Basic window style. */ + unsigned long style; + /* Extended window style. */ + unsigned long exstyle; + /* X coordinate. */ + unsigned short x; + /* Y coordinate. */ + unsigned short y; + /* Width. */ + unsigned short width; + /* Height. */ + unsigned short height; + /* Menu name. */ + struct res_id menu; + /* Class name. */ + struct res_id class; + /* Caption. */ + char *caption; + /* Font point size. */ + unsigned short pointsize; + /* Font name. */ + char *font; + /* Extended information for a dialogex. */ + struct dialog_ex *ex; + /* Controls. */ + struct dialog_control *controls; +}; + +/* An extended dialog has additional information. */ + +struct dialog_ex +{ + /* Help ID. */ + unsigned long help; + /* Font weight. */ + unsigned short weight; + /* Whether the font is italic. */ + unsigned short italic; +}; + +/* Window style flags, from the winsup Defines.h header file. These + can appear in the style field of a struct dialog or a struct + dialog_control. */ + +#define CW_USEDEFAULT (0x80000000) +#define WS_BORDER (0x800000L) +#define WS_CAPTION (0xc00000L) +#define WS_CHILD (0x40000000L) +#define WS_CHILDWINDOW (0x40000000L) +#define WS_CLIPCHILDREN (0x2000000L) +#define WS_CLIPSIBLINGS (0x4000000L) +#define WS_DISABLED (0x8000000L) +#define WS_DLGFRAME (0x400000L) +#define WS_GROUP (0x20000L) +#define WS_HSCROLL (0x100000L) +#define WS_ICONIC (0x20000000L) +#define WS_MAXIMIZE (0x1000000L) +#define WS_MAXIMIZEBOX (0x10000L) +#define WS_MINIMIZE (0x20000000L) +#define WS_MINIMIZEBOX (0x20000L) +#define WS_OVERLAPPED (0L) +#define WS_OVERLAPPEDWINDOW (0xcf0000L) +#define WS_POPUP (0x80000000L) +#define WS_POPUPWINDOW (0x80880000L) +#define WS_SIZEBOX (0x40000L) +#define WS_SYSMENU (0x80000L) +#define WS_TABSTOP (0x10000L) +#define WS_THICKFRAME (0x40000L) +#define WS_TILED (0L) +#define WS_TILEDWINDOW (0xcf0000L) +#define WS_VISIBLE (0x10000000L) +#define WS_VSCROLL (0x200000L) +#define MDIS_ALLCHILDSTYLES (0x1) +#define BS_3STATE (0x5L) +#define BS_AUTO3STATE (0x6L) +#define BS_AUTOCHECKBOX (0x3L) +#define BS_AUTORADIOBUTTON (0x9L) +#define BS_BITMAP (0x80L) +#define BS_BOTTOM (0x800L) +#define BS_CENTER (0x300L) +#define BS_CHECKBOX (0x2L) +#define BS_DEFPUSHBUTTON (0x1L) +#define BS_GROUPBOX (0x7L) +#define BS_ICON (0x40L) +#define BS_LEFT (0x100L) +#define BS_LEFTTEXT (0x20L) +#define BS_MULTILINE (0x2000L) +#define BS_NOTIFY (0x4000L) +#define BS_OWNERDRAW (0xbL) +#define BS_PUSHBOX (0xcL) /* FIXME! What should this be? */ +#define BS_PUSHBUTTON (0L) +#define BS_PUSHLIKE (0x1000L) +#define BS_RADIOBUTTON (0x4L) +#define BS_RIGHT (0x200L) +#define BS_RIGHTBUTTON (0x20L) +#define BS_TEXT (0L) +#define BS_TOP (0x400L) +#define BS_USERBUTTON (0x8L) +#define BS_VCENTER (0xc00L) +#define CBS_AUTOHSCROLL (0x40L) +#define CBS_DISABLENOSCROLL (0x800L) +#define CBS_DROPDOWN (0x2L) +#define CBS_DROPDOWNLIST (0x3L) +#define CBS_HASSTRINGS (0x200L) +#define CBS_LOWERCASE (0x4000L) +#define CBS_NOINTEGRALHEIGHT (0x400L) +#define CBS_OEMCONVERT (0x80L) +#define CBS_OWNERDRAWFIXED (0x10L) +#define CBS_OWNERDRAWVARIABLE (0x20L) +#define CBS_SIMPLE (0x1L) +#define CBS_SORT (0x100L) +#define CBS_UPPERCASE (0x2000L) +#define ES_AUTOHSCROLL (0x80L) +#define ES_AUTOVSCROLL (0x40L) +#define ES_CENTER (0x1L) +#define ES_LEFT (0L) +#define ES_LOWERCASE (0x10L) +#define ES_MULTILINE (0x4L) +#define ES_NOHIDESEL (0x100L) +#define ES_NUMBER (0x2000L) +#define ES_OEMCONVERT (0x400L) +#define ES_PASSWORD (0x20L) +#define ES_READONLY (0x800L) +#define ES_RIGHT (0x2L) +#define ES_UPPERCASE (0x8L) +#define ES_WANTRETURN (0x1000L) +#define LBS_DISABLENOSCROLL (0x1000L) +#define LBS_EXTENDEDSEL (0x800L) +#define LBS_HASSTRINGS (0x40L) +#define LBS_MULTICOLUMN (0x200L) +#define LBS_MULTIPLESEL (0x8L) +#define LBS_NODATA (0x2000L) +#define LBS_NOINTEGRALHEIGHT (0x100L) +#define LBS_NOREDRAW (0x4L) +#define LBS_NOSEL (0x4000L) +#define LBS_NOTIFY (0x1L) +#define LBS_OWNERDRAWFIXED (0x10L) +#define LBS_OWNERDRAWVARIABLE (0x20L) +#define LBS_SORT (0x2L) +#define LBS_STANDARD (0xa00003L) +#define LBS_USETABSTOPS (0x80L) +#define LBS_WANTKEYBOARDINPUT (0x400L) +#define SBS_BOTTOMALIGN (0x4L) +#define SBS_HORZ (0L) +#define SBS_LEFTALIGN (0x2L) +#define SBS_RIGHTALIGN (0x4L) +#define SBS_SIZEBOX (0x8L) +#define SBS_SIZEBOXBOTTOMRIGHTALIGN (0x4L) +#define SBS_SIZEBOXTOPLEFTALIGN (0x2L) +#define SBS_SIZEGRIP (0x10L) +#define SBS_TOPALIGN (0x2L) +#define SBS_VERT (0x1L) +#define SS_BITMAP (0xeL) +#define SS_BLACKFRAME (0x7L) +#define SS_BLACKRECT (0x4L) +#define SS_CENTER (0x1L) +#define SS_CENTERIMAGE (0x200L) +#define SS_ENHMETAFILE (0xfL) +#define SS_ETCHEDFRAME (0x12L) +#define SS_ETCHEDHORZ (0x10L) +#define SS_ETCHEDVERT (0x11L) +#define SS_GRAYFRAME (0x8L) +#define SS_GRAYRECT (0x5L) +#define SS_ICON (0x3L) +#define SS_LEFT (0L) +#define SS_LEFTNOWORDWRAP (0xcL) +#define SS_NOPREFIX (0x80L) +#define SS_NOTIFY (0x100L) +#define SS_OWNERDRAW (0xdL) +#define SS_REALSIZEIMAGE (0x800L) +#define SS_RIGHT (0x2L) +#define SS_RIGHTJUST (0x400L) +#define SS_SIMPLE (0xbL) +#define SS_SUNKEN (0x1000L) +#define SS_USERITEM (0xaL) +#define SS_WHITEFRAME (0x9L) +#define SS_WHITERECT (0x6L) +#define DS_3DLOOK (0x4L) +#define DS_ABSALIGN (0x1L) +#define DS_CENTER (0x800L) +#define DS_CENTERMOUSE (0x1000L) +#define DS_CONTEXTHELP (0x2000L) +#define DS_CONTROL (0x400L) +#define DS_FIXEDSYS (0x8L) +#define DS_LOCALEDIT (0x20L) +#define DS_MODALFRAME (0x80L) +#define DS_NOFAILCREATE (0x10L) +#define DS_NOIDLEMSG (0x100L) +#define DS_SETFONT (0x40L) +#define DS_SETFOREGROUND (0x200L) +#define DS_SYSMODAL (0x2L) + +/* A dialog control. */ + +struct dialog_control +{ + /* Next control. */ + struct dialog_control *next; + /* ID. */ + unsigned short id; + /* Style. */ + unsigned long style; + /* Extended style. */ + unsigned long exstyle; + /* X coordinate. */ + unsigned short x; + /* Y coordinate. */ + unsigned short y; + /* Width. */ + unsigned short width; + /* Height. */ + unsigned short height; + /* Class name. */ + struct res_id class; + /* Associated text. */ + struct res_id text; + /* Extra data for the window procedure. */ + struct rcdata_data *data; + /* Help ID. Only used in an extended dialog. */ + unsigned long help; +}; + +/* Control classes. These can be used as the ID field in a struct + dialog_control. */ + +#define CTL_BUTTON 0x80 +#define CTL_EDIT 0x81 +#define CTL_STATIC 0x82 +#define CTL_LISTBOX 0x83 +#define CTL_SCROLLBAR 0x84 +#define CTL_COMBOBOX 0x85 + +/* A fontdir resource is a list of fontdir structures. */ + +struct fontdir +{ + struct fontdir *next; + /* Index of font entry. */ + short index; + /* Length of font information. */ + unsigned long length; + /* Font information. */ + unsigned char *data; +}; + +/* A group_icon resource is a list of group_icon structures. */ + +struct group_icon +{ + /* Next icon in group. */ + struct group_icon *next; + /* Width. */ + unsigned char width; + /* Height. */ + unsigned char height; + /* Color count. */ + unsigned char colors; + /* Planes. */ + unsigned short planes; + /* Bits per pixel. */ + unsigned short bits; + /* Number of bytes in cursor resource. */ + unsigned long bytes; + /* Index of cursor resource. */ + unsigned short index; +}; + +/* A menu resource is a list of menuitem structures. */ + +struct menuitem +{ + /* Next menuitem. */ + struct menuitem *next; + /* Type. In a normal menu, rather than a menuex, this is the flags + field. */ + unsigned long type; + /* State. This is only used in a menuex. */ + unsigned long state; + /* Id. */ + unsigned short id; + /* Text. */ + char *text; + /* Popup menu items for a popup. */ + struct menuitem *popup; + /* Help ID. This is only used in a menuex. */ + unsigned long help; +}; + +/* Menu item flags. These can appear in the flags field of a struct + menuitem. */ + +#define MENUITEM_GRAYED 0x001 +#define MENUITEM_INACTIVE 0x002 +#define MENUITEM_BITMAP 0x004 +#define MENUITEM_OWNERDRAW 0x100 +#define MENUITEM_CHECKED 0x008 +#define MENUITEM_POPUP 0x010 +#define MENUITEM_MENUBARBREAK 0x020 +#define MENUITEM_MENUBREAK 0x040 +#define MENUITEM_HELP 0x4000 + +/* An rcdata resource is a pointer to an rcdata_data structure. */ + +struct rcdata_data +{ + /* First data item. */ + struct rcdata_item *first; + /* Last data item. */ + struct rcdata_item *last; +}; + +/* For an rcdata resource we keep a list of rcdata_item structures. */ + +struct rcdata_item +{ + /* Next data item. */ + struct rcdata_item *next; + /* Type of data. */ + enum + { + RCDATA_WORD, + RCDATA_DWORD, + RCDATA_STRING, + RCDATA_WSTRING, + RCDATA_BUFFER + } type; + union + { + unsigned int word; + unsigned long dword; + char *string; + unsigned short *wstring; + struct + { + unsigned long length; + unsigned char *data; + } buffer; + } u; +}; + +/* A stringtable resource is a pointer to a stringtable structure. */ + +struct stringtable +{ + /* Each stringtable resource is a list of 16 unicode strings. */ + struct + { + /* Length of string. */ + unsigned short length; + /* String data if length > 0. */ + unsigned short *string; + } strings[16]; +}; + +/* A versioninfo resource points to a versioninfo structure. */ + +struct versioninfo +{ + /* Fixed version information. */ + struct fixed_versioninfo *fixed; + /* Variable version information. */ + struct ver_info *var; +}; + +/* The fixed portion of a versioninfo resource. */ + +struct fixed_versioninfo +{ + /* The file version, which is two 32 bit integers. */ + unsigned long file_version_ms; + unsigned long file_version_ls; + /* The product version, which is two 32 bit integers. */ + unsigned long product_version_ms; + unsigned long product_version_ls; + /* The file flags mask. */ + unsigned long file_flags_mask; + /* The file flags. */ + unsigned long file_flags; + /* The OS type. */ + unsigned long file_os; + /* The file type. */ + unsigned long file_type; + /* The file subtype. */ + unsigned long file_subtype; + /* The date, which in Windows is two 32 bit integers. */ + unsigned long file_date_ms; + unsigned long file_date_ls; +}; + +/* A list of variable version information. */ + +struct ver_info +{ + /* Next item. */ + struct ver_info *next; + /* Type of data. */ + enum { VERINFO_STRING, VERINFO_VAR } type; + union + { + /* StringFileInfo data. */ + struct + { + /* Language. */ + unsigned short *language; + /* Strings. */ + struct ver_stringinfo *strings; + } string; + /* VarFileInfo data. */ + struct + { + /* Key. */ + unsigned short *key; + /* Values. */ + struct ver_varinfo *var; + } var; + } u; +}; + +/* A list of string version information. */ + +struct ver_stringinfo +{ + /* Next string. */ + struct ver_stringinfo *next; + /* Key. */ + unsigned short *key; + /* Value. */ + unsigned short *value; +}; + +/* A list of variable version information. */ + +struct ver_varinfo +{ + /* Next item. */ + struct ver_varinfo *next; + /* Language ID. */ + unsigned short language; + /* Character set ID. */ + unsigned short charset; +}; + +/* Function declarations. */ + +extern struct res_directory *read_rc_file + PARAMS ((const char *, const char *, const char *, int)); +extern struct res_directory *read_res_file PARAMS ((const char *)); +extern struct res_directory *read_coff_rsrc + PARAMS ((const char *, const char *)); +extern void write_rc_file + PARAMS ((const char *, const struct res_directory *)); +extern void write_res_file + PARAMS ((const char *, const struct res_directory *)); +extern void write_coff_file + PARAMS ((const char *, const char *, const struct res_directory *)); + +extern FILE *open_file_search + PARAMS ((const char *, const char *, const char *, char **)); + +/* Resource ID handling. */ + +extern int res_id_cmp PARAMS ((struct res_id, struct res_id)); +extern void res_id_print PARAMS ((FILE *, struct res_id, int)); +extern void res_ids_print PARAMS ((FILE *, int, const struct res_id *)); +extern void res_string_to_id PARAMS ((struct res_id *, const char *)); + +/* Unicode support. */ + +extern void unicode_from_ascii + PARAMS ((unsigned short *, unsigned short **, const char *)); +extern void unicode_print PARAMS ((FILE *, const unsigned short *, int)); + +/* Manipulation of the resource tree. */ + +extern struct res_resource *define_resource + PARAMS ((struct res_directory **, int, const struct res_id *, int)); +extern struct res_resource *define_standard_resource + PARAMS ((struct res_directory **, int, struct res_id, int, int)); + +extern int extended_dialog PARAMS ((const struct dialog *)); +extern int extended_menu PARAMS ((const struct menuitem *)); + +/* Communication between the rc file support and the parser and lexer. */ + +extern int yydebug; +extern FILE *yyin; +extern char *rc_filename; +extern int rc_lineno; +extern int yyparse PARAMS ((void)); +extern int yylex PARAMS ((void)); +extern void yyerror PARAMS ((const char *)); +extern void rcparse_warning PARAMS ((const char *)); +extern void rcparse_set_language PARAMS ((int)); + +extern void define_accelerator + PARAMS ((struct res_id, const struct res_res_info *, struct accelerator *)); +extern void define_bitmap + PARAMS ((struct res_id, const struct res_res_info *, const char *)); +extern void define_cursor + PARAMS ((struct res_id, const struct res_res_info *, const char *)); +extern void define_dialog + PARAMS ((struct res_id, const struct res_res_info *, const struct dialog *)); +extern struct dialog_control *define_control + PARAMS ((char *, unsigned long, unsigned long, unsigned long, + unsigned long, unsigned long, unsigned long, unsigned long, + unsigned long)); +extern void define_font + PARAMS ((struct res_id, const struct res_res_info *, const char *)); +extern void define_icon + PARAMS ((struct res_id, const struct res_res_info *, const char *)); +extern void define_menu + PARAMS ((struct res_id, const struct res_res_info *, struct menuitem *)); +extern struct menuitem *define_menuitem + PARAMS ((char *, int, unsigned long, unsigned long, unsigned long, + struct menuitem *)); +extern void define_messagetable + PARAMS ((struct res_id, const struct res_res_info *, const char *)); +extern void define_rcdata + PARAMS ((struct res_id, const struct res_res_info *, struct rcdata_data *)); +extern struct rcdata_data *append_rcdata_item + PARAMS ((struct rcdata_data *, struct rcdata_item *)); +extern struct rcdata_data *append_rcdata_string + PARAMS ((struct rcdata_data *, char *)); +extern struct rcdata_data *append_rcdata_number + PARAMS ((struct rcdata_data *, unsigned long, int)); +extern void define_stringtable + PARAMS ((const struct res_res_info *, unsigned long, char *)); +extern void define_user_data + PARAMS ((struct res_id, struct res_id, const struct res_res_info *, + struct rcdata_data *)); +extern void define_user_file + PARAMS ((struct res_id, struct res_id, const struct res_res_info *, + const char *)); +extern void define_versioninfo + PARAMS ((struct res_id, int, struct fixed_versioninfo *, + struct ver_info *)); +extern struct ver_info *append_ver_stringfileinfo + PARAMS ((struct ver_info *, char *, struct ver_stringinfo *)); +extern struct ver_info *append_ver_varfileinfo + PARAMS ((struct ver_info *, char *, struct ver_varinfo *)); +extern struct ver_stringinfo *append_verval + PARAMS ((struct ver_stringinfo *, char *, char *)); +extern struct ver_varinfo *append_vertrans + PARAMS ((struct ver_varinfo *, unsigned long, unsigned long));