From 30855917677c82b141a1f13ccbaae4414c4b0e4d Mon Sep 17 00:00:00 2001 From: Bertho Stultiens Date: Tue, 13 Jun 2000 04:34:41 +0000 Subject: [PATCH] Initial release of the message compiler. --- Make.rules.in | 6 + configure | 2 + configure.in | 1 + tools/Makefile.in | 3 +- tools/wmc/.cvsignore | 4 + tools/wmc/CHANGES | 5 + tools/wmc/Makefile.in | 48 +++ tools/wmc/lang.c | 151 +++++++++ tools/wmc/lang.h | 26 ++ tools/wmc/language.c | 107 ++++++ tools/wmc/mcl.c | 733 ++++++++++++++++++++++++++++++++++++++++++ tools/wmc/mcy.y | 659 +++++++++++++++++++++++++++++++++++++ tools/wmc/utils.c | 241 ++++++++++++++ tools/wmc/utils.h | 36 +++ tools/wmc/wmc.c | 268 +++++++++++++++ tools/wmc/wmc.h | 74 +++++ tools/wmc/wmc.man | 103 ++++++ tools/wmc/wmctypes.h | 109 +++++++ tools/wmc/write.c | 502 +++++++++++++++++++++++++++++ tools/wmc/write.h | 14 + unicode/wctomb.c | 2 +- 21 files changed, 3092 insertions(+), 2 deletions(-) create mode 100644 tools/wmc/.cvsignore create mode 100644 tools/wmc/CHANGES create mode 100644 tools/wmc/Makefile.in create mode 100644 tools/wmc/lang.c create mode 100644 tools/wmc/lang.h create mode 100644 tools/wmc/language.c create mode 100644 tools/wmc/mcl.c create mode 100644 tools/wmc/mcy.y create mode 100644 tools/wmc/utils.c create mode 100644 tools/wmc/utils.h create mode 100644 tools/wmc/wmc.c create mode 100644 tools/wmc/wmc.h create mode 100644 tools/wmc/wmc.man create mode 100644 tools/wmc/wmctypes.h create mode 100644 tools/wmc/write.c create mode 100644 tools/wmc/write.h diff --git a/Make.rules.in b/Make.rules.in index f7fe8acfed..4d6c2211ef 100644 --- a/Make.rules.in +++ b/Make.rules.in @@ -58,6 +58,7 @@ BUILD = $(TOPOBJDIR)/tools/build@PROGEXT@ MAKEDEP = $(TOPOBJDIR)/tools/makedep@PROGEXT@ WRC = $(TOPOBJDIR)/tools/wrc/wrc@PROGEXT@ WRCFLAGS = -c -s -p $* +WMC = $(TOPOBJDIR)/tools/wmc/wmc@PROGEXT@ DLLDIR = $(TOPOBJDIR)/dlls @SET_MAKE@ @@ -200,6 +201,11 @@ all: Makefile $(WRC) check_wrc: cd $(TOPOBJDIR)/tools/wrc && $(MAKE) wrc@PROGEXT@ +# Rule to rebuild the message compiler + +$(WMC) check_wmc: + cd $(TOPOBJDIR)/tools/wmc && $(MAKE) wmc@PROGEXT@ + # Rule to rebuild the 'makedep' program $(MAKEDEP) check_makedep: diff --git a/configure b/configure index 3f29442b7a..b666d7b473 100755 --- a/configure +++ b/configure @@ -6300,6 +6300,7 @@ server/Makefile tools/Makefile tools/cvdump/Makefile tools/wrc/Makefile +tools/wmc/Makefile tsx11/Makefile unicode/Makefile win32/Makefile @@ -6534,6 +6535,7 @@ server/Makefile tools/Makefile tools/cvdump/Makefile tools/wrc/Makefile +tools/wmc/Makefile tsx11/Makefile unicode/Makefile win32/Makefile diff --git a/configure.in b/configure.in index c307d97f68..b48bd1561e 100644 --- a/configure.in +++ b/configure.in @@ -1094,6 +1094,7 @@ server/Makefile tools/Makefile tools/cvdump/Makefile tools/wrc/Makefile +tools/wmc/Makefile tsx11/Makefile unicode/Makefile win32/Makefile diff --git a/tools/Makefile.in b/tools/Makefile.in index b985f3e667..c5ae78c02d 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -11,6 +11,7 @@ C_SRCS = build.c makedep.c fnt2bdf.c bin2res.c SUBDIRS = \ cvdump \ + wmc \ wrc EXTRASUBDIRS = \ @@ -19,7 +20,7 @@ EXTRASUBDIRS = \ winapi_check/win32 \ wineconf.libs -all: $(PROGRAMS) wrc +all: $(PROGRAMS) wmc wrc @MAKE_RULES@ diff --git a/tools/wmc/.cvsignore b/tools/wmc/.cvsignore new file mode 100644 index 0000000000..92dc8ee4d0 --- /dev/null +++ b/tools/wmc/.cvsignore @@ -0,0 +1,4 @@ +Makefile +wmc +y.tab.c +y.tab.h diff --git a/tools/wmc/CHANGES b/tools/wmc/CHANGES new file mode 100644 index 0000000000..bec49ca452 --- /dev/null +++ b/tools/wmc/CHANGES @@ -0,0 +1,5 @@ +--------------------------------------------------------------------------- +Version 1.0.0 (12-Jun-2000) + +Bertho Stultiens +- Initial release diff --git a/tools/wmc/Makefile.in b/tools/wmc/Makefile.in new file mode 100644 index 0000000000..13fee710d7 --- /dev/null +++ b/tools/wmc/Makefile.in @@ -0,0 +1,48 @@ +DEFS = -D__WINE__ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +YACCOPT = #-v + +PROGRAMS = wmc@PROGEXT@ +MODULE = none + +C_SRCS = \ + lang.c \ + mcl.c \ + utils.c \ + wmc.c \ + write.c + +EXTRA_SRCS = mcy.y +EXTRA_OBJS = y.tab.o + +all: check_unicode $(PROGRAMS) + +depend: y.tab.h + +@MAKE_RULES@ + +wmc@PROGEXT@: $(OBJS) $(TOPOBJDIR)/unicode/unicode.o + $(CC) $(CFLAGS) -o wmc@PROGEXT@ $(OBJS) $(TOPOBJDIR)/unicode/unicode.o $(LEXLIB) + +$(TOPOBJDIR)/unicode/unicode.o check_unicode: + cd $(TOPSRCDIR)/unicode && $(MAKE) + +y.tab.c y.tab.h: mcy.y + $(YACC) $(YACCOPT) -d -t $(SRCDIR)/mcy.y + +clean:: + $(RM) y.tab.c y.tab.h y.output + +install:: $(PROGRAMS) + [ -d $(bindir) ] || $(MKDIR) $(bindir) + [ -d $(mandir)/man$(prog_manext) ] || $(MKDIR) $(mandir)/man$(prog_manext) + $(INSTALL_DATA) wmc.man $(mandir)/man$(prog_manext)/wmc.$(prog_manext) + $(INSTALL_PROGRAM) wmc $(bindir)/wmc + +uninstall:: + $(RM) $(bindir)/wmc $(mandir)/man$(prog_manext)/wmc.$(prog_manext) + +### Dependencies: diff --git a/tools/wmc/lang.c b/tools/wmc/lang.c new file mode 100644 index 0000000000..d5fe72e94d --- /dev/null +++ b/tools/wmc/lang.c @@ -0,0 +1,151 @@ +/* + * Wine Message Compiler language and codepage support + * + * Copyright 2000 Bertho A. Stultiens (BS) + * + */ +#include +#include +#include +#include + +#include "wmc.h" +#include "lang.h" + + +/* + * Languages supported + * + * MUST be sorting ascending on language ID + */ +static const language_t languages[] = { + + {0x0402, 866, 1251, "Bulgarian", "Bulgaria"}, + {0x0403, 850, 1252, "Catalan", "Spain"}, + {0x0405, 852, 1250, "Czech", "Czech Republic"}, + {0x0406, 850, 1252, "Danish", "Denmark"}, + {0x0407, 850, 1252, "German", "Germany"}, + {0x0408, 737, 1253, "Greek", "Greece"}, + {0x0409, 437, 1252, "English", "United States"}, + {0x040A, 850, 1252, "Spanish - Traditional Sort", "Spain"}, + {0x040B, 850, 1252, "Finnish", "Finland"}, + {0x040C, 850, 1252, "French", "France"}, + {0x040E, 852, 1250, "Hungarian", "Hungary"}, + {0x040F, 850, 1252, "Icelandic", "Iceland"}, + {0x0410, 850, 1252, "Italian", "Italy"}, + {0x0411, 932, 932, "Japanese", "Japan"}, + {0x0412, 949, 949, "Korean", "Korea (south)"}, + {0x0413, 850, 1252, "Dutch", "Netherlands"}, + {0x0414, 850, 1252, "Norwegian (Bokmål)", "Norway"}, + {0x0415, 852, 1250, "Polish", "Poland"}, + {0x0416, 850, 1252, "Portuguese", "Brazil"}, + {0x0418, 852, 1250, "Romanian", "Romania"}, + {0x0419, 866, 1251, "Russian", "Russia"}, + {0x041A, 852, 1250, "Croatian", "Croatia"}, + {0x041B, 852, 1250, "Slovak", "Slovakia"}, + {0x041C, 852, 1250, "Albanian", "Albania"}, + {0x041D, 850, 1252, "Swedish", "Sweden"}, + {0x041F, 857, 1254, "Turkish", "Turkey"}, + {0x0421, 850, 1252, "Indonesian", "Indonesia"}, + {0x0422, 866, 1251, "Ukrainian", "Ukraine"}, + {0x0423, 866, 1251, "Belarusian", "Belarus"}, + {0x0424, 852, 1250, "Slovene", "Slovenia"}, + {0x0425, 775, 1257, "Estonian", "Estonia"}, + {0x0426, 775, 1257, "Latvian", "Latvia"}, + {0x0427, 775, 1257, "Lithuanian", "Lithuania"}, +/* {0x042A, ?, ?, "Vietnamese", "Vietnam"},*/ + {0x042D, 850, 1252, "Basque", "Spain"}, + {0x042F, 866, 1251, "Macedonian", "Former Yugoslav Republic of Macedonia"}, + {0x0436, 850, 1252, "Afrikaans", "South Africa"}, +/* {0x0438, 852, 1252, "Faroese", "Faroe Islands"}, FIXME: Not sure about codepages */ + {0x043C, 437, 1252, "Irish", "Ireland"}, +/* {0x048F, ?, ?, "Esperanto", ""},*/ +/* {0x0804, ?, ?, "Chinese (People's replublic of China)", People's republic of China"},*/ + {0x0807, 850, 1252, "German", "Switzerland"}, + {0x0809, 850, 1252, "English", "United Kingdom"}, + {0x080A, 850, 1252, "Spanish", "Mexico"}, + {0x080C, 850, 1252, "French", "Belgium"}, + {0x0810, 850, 1252, "Italian", "Switzerland"}, + {0x0813, 850, 1252, "Dutch", "Belgium"}, + {0x0814, 850, 1252, "Norwegian (Nynorsk)", "Norway"}, + {0x0816, 850, 1252, "Portuguese", "Portugal"}, +/* {0x081A, ?, ?, "Serbian (latin)", "Yugoslavia"},*/ + {0x081D, 850, 1252, "Swedish (Finland)", "Finland"}, + {0x0C07, 850, 1252, "German", "Austria"}, + {0x0C09, 850, 1252, "English", "Australia"}, + {0x0C0A, 850, 1252, "Spanish - International Sort", "Spain"}, + {0x0C0C, 850, 1252, "French", "Canada"}, + {0x0C1A, 855, 1251, "Serbian (Cyrillic)", "Serbia"}, + {0x1007, 850, 1252, "German", "Luxembourg"}, + {0x1009, 850, 1252, "English", "Canada"}, + {0x100A, 850, 1252, "Spanish", "Guatemala"}, + {0x100C, 850, 1252, "French", "Switzerland"}, + {0x1407, 850, 1252, "German", "Liechtenstein"}, + {0x1409, 850, 1252, "English", "New Zealand"}, + {0x140A, 850, 1252, "Spanish", "Costa Rica"}, + {0x140C, 850, 1252, "French", "Luxembourg"}, + {0x1809, 850, 1252, "English", "Ireland"}, + {0x180A, 850, 1252, "Spanish", "Panama"}, + {0x1C09, 437, 1252, "English", "South Africa"}, + {0x1C0A, 850, 1252, "Spanish", "Dominican Republic"}, + {0x2009, 850, 1252, "English", "Jamaica"}, + {0x200A, 850, 1252, "Spanish", "Venezuela"}, + {0x2409, 850, 1252, "English", "Caribbean"}, + {0x240A, 850, 1252, "Spanish", "Colombia"}, + {0x2809, 850, 1252, "English", "Belize"}, + {0x280A, 850, 1252, "Spanish", "Peru"}, + {0x2C09, 437, 1252, "English", "Trinidad & Tobago"}, + {0x2C0A, 850, 1252, "Spanish", "Argentina"}, + {0x300A, 850, 1252, "Spanish", "Ecuador"}, + {0x340A, 850, 1252, "Spanish", "Chile"}, + {0x380A, 850, 1252, "Spanish", "Uruguay"}, + {0x3C0A, 850, 1252, "Spanish", "Paraguay"}, + {0x400A, 850, 1252, "Spanish", "Bolivia"}, + {0x440A, 850, 1252, "Spanish", "El Salvador"}, + {0x480A, 850, 1252, "Spanish", "Honduras"}, + {0x4C0A, 850, 1252, "Spanish", "Nicaragua"}, + {0x500A, 850, 1252, "Spanish", "Puerto Rico"} +}; + +#define NLAN (sizeof(languages)/sizeof(languages[0])) + +void show_languages(void) +{ + int i; + printf(" Code | DOS-cp | WIN-cp | Language | Country\n"); + printf("-------+--------+--------+--------------+---------\n"); + for(i = 0; i < NLAN; i++) + printf("0x%04x | %5d | %5d | %-12s | %s\n", + languages[i].id, + languages[i].doscp, + languages[i].wincp, + languages[i].name, + languages[i].country); +} + +static int langcmp(const void *p1, const void *p2) +{ + return *(unsigned *)p1 - ((language_t *)p2)->id; +} + +const language_t *find_language(unsigned id) +{ + return (const language_t *)bsearch(&id, languages, NLAN, sizeof(languages[0]), langcmp); +} + +void show_codepages(void) +{ + unsigned i; + const union cptable *cpp; + printf("Codepages:\n"); + for(i = 0; (cpp = cp_enum_table(i)); i++) + { + printf("%-5d %s\n", cpp->info.codepage, cpp->info.name); + } +} + +const union cptable *find_codepage(int id) +{ + return cp_get_table(id); +} + diff --git a/tools/wmc/lang.h b/tools/wmc/lang.h new file mode 100644 index 0000000000..678a35e92f --- /dev/null +++ b/tools/wmc/lang.h @@ -0,0 +1,26 @@ +/* + * Wine Message Compiler language and codepage support + * + * Copyright 2000 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WMC_LANG_H +#define __WMC_LANG_H + +#include "wine/unicode.h" + +typedef struct language { + unsigned id; + unsigned doscp; + unsigned wincp; + char *name; + char *country; +} language_t; + +void show_languages(void); +const language_t *find_language(unsigned id); +void show_codepages(void); +const union cptable *find_codepage(int id); + +#endif diff --git a/tools/wmc/language.c b/tools/wmc/language.c new file mode 100644 index 0000000000..d878a0d5e4 --- /dev/null +++ b/tools/wmc/language.c @@ -0,0 +1,107 @@ +{"South Africa", 0x0436, 1252}, +{"Saudi Arabia", 0x0401, 1256}, +{"Lebanon", 0x0401, 1256}, +{"Egypt", 0x0401, 1256}, +{"Algeria", 0x0401, 1256}, +{"Iraq", 0x0401, 1256}, +{"Kuwait", 0x0401, 1256}, +{"Marocco", 0x0401, 1256}, +{"Oman", 0x0401, 1256}, +{"Quatar", 0x0401, 1256}, +{"Syria", 0x0401, 1256}, +{"Tunisia", 0x0401, 1256}, +{"United Arab Emirates",0x0401, 1256}, +{"Belaruss", 0x0423, 1251}, +{"Bulgaria", 0x0402 +{"France", 0x040c, 1252}, +{"Spain", 0x0403, 1252}, +{"China (Taiwan)", 0x0404 +{"United Kingdom", 0x0409, 1252}, +{"Wales", 0x0409, 1252}, /* FIXME */ +{"Czech Republic", 0x0405, 1250}, +{"Denmark", 0x0406, 1252}, +{"Austria", 0x0407, 1252}, +{"Liechtenstein", 0x0407, 1252}, +{"Luxemburg", 0x0407, 1252}, +{"Switzerland", 0x0807, 1252}, +{"Germany", 0x0407, 1252}, +{"Australia", 0x0c09, 1252}, +{"Caribbean", 0x2409, 1252}, +{"Canada", 0x1009, 1252}, +{"United Kingdom", 0x0809, 1252}, +{"Ireland", 0x1809, 1252}, +{"Jamaica", 0x2009, 1252}, +{"Belize", 0x2809, 1252}, +{"South Africa", 0x1c09, 1252}, +{"Trinidad & Tobago", 0x2c09, 1252}, +{"United States", 0x0409, 1252}, +{"New Zealand", 0x1409, 1252}, +{"Panama", 0x040a, 1252}, +{"Bolivia", 0x040a, 1252}, +{"Costa Rica", 0x140a, 1252}, +{"Dominican Republic", 0x040a, 1252}, +{"El Salvador", 0x040a, 1252}, +{"Ecuador", 0x040a, 1252}, +{"Guatemala", 0x040a, 1252}, +{"Honduras", 0x040a, 1252}, +{"Nicaragua", 0x040a, 1252}, +{"Chile", 0x040a, 1252}, +{"Mexico", 0x040a, 1252}, +{"Spain", 0x040a, 1252}, +{"Colombia", 0x040a, 1252}, +{"Spain", 0x040a, 1252}, +{"Peru", 0x040a, 1252}, +{"Argentina", 0x040a, 1252}, +{"Estonia", 0x0425, 1252}, +{"Puerto Rico", 0x040a, 1252}, +{"Venezuela", 0x040a, 1252}, +{"Uruguay", 0x380a, 1252}, +{"Paraguay", 0x040a, 1252}, +{"Spain (Basque)", 0x04d2, 1252}, +{"Finland", 0x040b, 1252}, +{"Faroe Islands", 0x0438, 1252}, +{"France", 0x040c, 1252}, +{"Belgium", 0x040c, 1252}, +{"Canada", 0x040c, 1252}, +{"Luxemburg", 0x040c, 1252}, +{"Switzerland", 0x040c, 1252}, +{"Ireland", 0x0000, 1252}, +{"United Kingdom", 0x0409, 1252}, +{"Isle of Man", 0x0409, 1252}, +{"Greece", 0x0408, 1253}, +{"Croatia", 0x041a, 1250}, +{"Hungary", 0x040e, 1250}, +{"Indonesia", 0x0421, 1252}, +{"Iceland", 0x040f, 1252}, +{"Italy", 0x0410, 1252}, +{"Switzerand", 0x0410, 1252}, +{"Japan", 0x0411, 0}, +{"Korea", 0x0000, 0}, +{"Korea (South)", 0x0412, 0}, +{"Lithuania", 0x0427, 1257}, +{"Latvia", 0x0426, 1257}, +{"Belgium", 0x0413, 1252}, +{"Netherlands", 0x0413, 1252}, +{"Suriname", 0x0413, 1252}, +{"Norway", 0x0814, 1252}, +{"Norway", 0x0414, 1252}, +{"Poland", 0x0415, 1250}, +{"Brazil", 0x0416, 1252}, +{"Portugal", 0x0416, 1252}, +{"Romania", 0x0418, 1250}, +{"Russia", 0x0000, 1251}, +{"Slovakia", 0x041b, 1250}, +{"Slovenia", 0x0424, 1250}, +{"Albania", 0x041c, 0}, +{"Yugoslavia", 0x0c1a, 0}, +{"Yugoslavia", 0x081a, 1250}, +{"Sweden", 0x041d, 1252}, +{"Finland", 0x081d, 1252}, +{"Thailand", 0x041e, 0}, +{"Turkey", 0x041f, 1254}, +{"Ukrainia", 0x0422, 1251}, +{"Vietnam", 0x042a, 0}, +{"Belgium", 0x0490, 1252}, +{"Hong Kong", 0x0404, 0}, +{"People's republic of China", 0x0804, 0}, +{"Singapore", 0x0404, 0} diff --git a/tools/wmc/mcl.c b/tools/wmc/mcl.c new file mode 100644 index 0000000000..55fd27be21 --- /dev/null +++ b/tools/wmc/mcl.c @@ -0,0 +1,733 @@ +/* + * Wine Message Compiler lexical scanner + * + * Copyright 2000 Bertho A. Stultiens (BS) + * + */ +#include +#include +#include +#include + +#include "config.h" + +#include "utils.h" +#include "wmc.h" +#include "lang.h" + +#include "y.tab.h" + +/* + * Keywords are case insenitive. All normal input is treated as + * being in codepage iso-8859-1 for ascii input files (unicode + * page 0) and as equivalent unicode if unicode input is selected. + * All normal input, which is not part of a message text, is + * enforced to be unicode page 0. Otherwise an error will be + * generated. The normal file data should only be ASCII because + * that is the basic definition of the grammar. + * + * Byteorder or unicode input is determined automatically by + * reading the first 8 bytes and checking them against unicode + * page 0 byteorder (hibyte must be 0). + * -- FIXME -- + * Alternatively, the input is checked against a special byte + * sequence to identify the file. + * -- FIXME -- + * + * + * Keywords: + * Codepages + * Facility + * FacilityNames + * LanguageNames + * MessageId + * MessageIdTypedef + * Severity + * SeverityNames + * SymbolicName + * + * Default added identifiers for classes: + * SeverityNames: + * Success = 0x0 + * Informational = 0x1 + * Warning = 0x2 + * Error = 0x3 + * FacilityNames: + * System = 0x0FF + * Application = 0xFFF + * + * The 'Codepages' keyword is a wmc extension. + */ + +static WCHAR ustr_application[] = { 'A', 'p', 'p', 'l', 'i', 'c', 'a', 't', 'i', 'o', 'n', 0 }; +static WCHAR ustr_codepages[] = { 'C', 'o', 'd', 'e', 'p', 'a', 'g', 'e', 's', 0 }; +static WCHAR ustr_english[] = { 'E', 'n', 'g', 'l', 'i', 's', 'h', 0 }; +static WCHAR ustr_error[] = { 'E', 'r', 'r', 'o', 'r', 0 }; +static WCHAR ustr_facility[] = { 'F', 'a', 'c', 'i', 'l', 'i', 't', 'y', 0 }; +static WCHAR ustr_facilitynames[] = { 'F', 'a', 'c', 'i', 'l', 'i', 't', 'y', 'N', 'a', 'm', 'e', 's', 0 }; +static WCHAR ustr_informational[] = { 'I', 'n', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'o', 'n', 'a', 'l', 0 }; +static WCHAR ustr_language[] = { 'L', 'a', 'n', 'g', 'u', 'a', 'g', 'e', 0}; +static WCHAR ustr_languagenames[] = { 'L', 'a', 'n', 'g', 'u', 'a', 'g', 'e', 'N', 'a', 'm', 'e', 's', 0}; +static WCHAR ustr_messageid[] = { 'M', 'e', 's', 's', 'a', 'g', 'e', 'I', 'd', 0 }; +static WCHAR ustr_messageidtypedef[] = { 'M', 'e', 's', 's', 'a', 'g', 'e', 'I', 'd', 'T', 'y', 'p', 'e', 'd', 'e', 'f', 0 }; +static WCHAR ustr_outputbase[] = { 'O', 'u', 't', 'p', 'u', 't', 'B', 'a', 's', 'e', 0 }; +static WCHAR ustr_severity[] = { 'S', 'e', 'v', 'e', 'r', 'i', 't', 'y', 0 }; +static WCHAR ustr_severitynames[] = { 'S', 'e', 'v', 'e', 'r', 'i', 't', 'y', 'N', 'a', 'm', 'e', 's', 0 }; +static WCHAR ustr_success[] = { 'S', 'u', 'c', 'c', 'e', 's', 's', 0 }; +static WCHAR ustr_symbolicname[] = { 'S', 'y', 'm', 'b', 'o', 'l', 'i', 'c', 'N', 'a', 'm', 'e', 0 }; +static WCHAR ustr_system[] = { 'S', 'y', 's', 't', 'e', 'm', 0 }; +static WCHAR ustr_warning[] = { 'W', 'a', 'r', 'n', 'i', 'n', 'g', 0 }; +static WCHAR ustr_msg00001[] = { 'm', 's', 'g', '0', '0', '0', '0', '1', 0 }; +/* + * This table is to beat any form of "expression building" to check for + * correct filename characters. It is also used for ident checks. + * FIXME: use it more consistently. + */ + +#define CH_SHORTNAME 0x01 +#define CH_LONGNAME 0x02 +#define CH_IDENT 0x04 +#define CH_NUMBER 0x08 +/*#define CH_WILDCARD 0x10*/ +/*#define CH_DOT 0x20*/ +#define CH_PUNCT 0x40 +#define CH_INVALID 0x80 + +static const char char_table[256] = { + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, /* 0x00 - 0x07 */ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, /* 0x08 - 0x0F */ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, /* 0x10 - 0x17 */ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, /* 0x18 - 0x1F */ + 0x80, 0x03, 0x80, 0x03, 0x03, 0x03, 0x03, 0x03, /* 0x20 - 0x27 " !"#$%&'" */ + 0x43, 0x43, 0x10, 0x80, 0x03, 0x03, 0x22, 0x80, /* 0x28 - 0x2F "()*+,-./" */ + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, /* 0x30 - 0x37 "01234567" */ + 0x0b, 0x0b, 0xc0, 0x80, 0x80, 0x80, 0x80, 0x10, /* 0x38 - 0x3F "89:;<=>?" */ + 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x40 - 0x47 "@ABCDEFG" */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x48 - 0x4F "HIJKLMNO" */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x50 - 0x57 "PQRSTUVW" */ + 0x07, 0x07, 0x07, 0x80, 0x80, 0x80, 0x80, 0x07, /* 0x58 - 0x5F "XYZ[\]^_" */ + 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x60 - 0x67 "`abcdefg" */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x68 - 0x6F "hijklmno" */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x70 - 0x77 "pqrstuvw" */ + 0x07, 0x07, 0x07, 0x03, 0x80, 0x03, 0x03, 0x80, /* 0x78 - 0x7F "xyz{|}~ " */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0x80 - 0x87 */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0x88 - 0x8F */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0x90 - 0x97 */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0x98 - 0x9F */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0xA0 - 0xA7 */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0xA8 - 0xAF */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0xB0 - 0xB7 */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0xB8 - 0xBF */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0xC0 - 0xC7 */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0xC8 - 0xCF */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0xD0 - 0xD7 */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0xD8 - 0xDF */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0xE0 - 0xE7 */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0xE8 - 0xEF */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 0xF0 - 0xF7 */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x80, /* 0xF8 - 0xFF */ +}; + +static int isisochar(int ch) +{ + return !(ch & (~0xff)); +} + +static int codepage; +static const union cptable *codepage_def; + +void set_codepage(int cp) +{ + codepage = cp; + codepage_def = find_codepage(codepage); + if(!codepage_def) + xyyerror("Codepage %d not found; cannot process", codepage); +} + +/* + * Input functions + */ +static int nungetstack = 0; +static int allocungetstack = 0; +static char *ungetstack = NULL; +static int ninputbuffer = 0; +static WCHAR *inputbuffer = NULL; +static char *xlatebuffer = NULL; + +#define INPUTBUFFER_SIZE 2048 /* Must be larger than 4 and approx. large enough to hold a line */ + +/* + * Fill the input buffer with *one* line of input. + * The line is '\n' terminated so that scanning + * messages with translation works as expected + * (otherwise we cannot pre-translate because the + * language is first known one line before the + * actual message). + */ +static int fill_inputbuffer(void) +{ + int n; + static char err_fatalread[] = "Fatal: reading input failed"; + static int endian = -1; + + if(!inputbuffer) + { + inputbuffer = xmalloc(INPUTBUFFER_SIZE); + xlatebuffer = xmalloc(INPUTBUFFER_SIZE); + } + +try_again: + if(!unicodein) + { + char *cptr; + cptr = fgets(xlatebuffer, INPUTBUFFER_SIZE, yyin); + if(!cptr && ferror(yyin)) + xyyerror(err_fatalread); + else if(!cptr) + return 0; + assert(codepage_def != NULL); + n = cp_mbstowcs(codepage_def, 0, xlatebuffer, strlen(xlatebuffer)+1, inputbuffer, INPUTBUFFER_SIZE); + if(n < 0) + internal_error(__FILE__, __LINE__, "Could not translate to unicode (%d)", n); + if(n <= 1) + goto try_again; /* Should not hapen */ + n--; /* Strip added conversion '\0' from input length */ + /* + * FIXME: + * Detect UTF-8 in the first time we read some bytes by + * checking the special sequence "FE..." or something like + * that. I need to check www.unicode.org for details. + */ + } + else + { + if(endian == -1) + { + n = fread(inputbuffer, 1, 8, yyin); + if(n != 8) + { + if(!n && ferror(yyin)) + xyyerror(err_fatalread); + else + xyyerror("Fatal: file to short to determine byteorder (should never happen)"); + } + if(isisochar(inputbuffer[0]) && + isisochar(inputbuffer[1]) && + isisochar(inputbuffer[2]) && + isisochar(inputbuffer[3])) + { +#ifdef WORDS_BIGENDIAN + endian = WMC_BO_BIG; +#else + endian = WMC_BO_LITTLE; +#endif + } + else if(isisochar(BYTESWAP_WORD(inputbuffer[0])) && + isisochar(BYTESWAP_WORD(inputbuffer[1])) && + isisochar(BYTESWAP_WORD(inputbuffer[2])) && + isisochar(BYTESWAP_WORD(inputbuffer[3]))) + { +#ifdef WORDS_BIGENDIAN + endian = WMC_BO_LITTLE; +#else + endian = WMC_BO_BIG; +#endif + } + else + xyyerror("Fatal: cannot determine file's byteorder"); + /* FIXME: + * Determine the file-endian with the leader-bytes + * "FF FE..."; can't remember the exact sequence. + */ + n /= 2; +#ifdef WORDS_BIGENDIAN + if(endian == WMC_BO_LITTLE) +#else + if(endian == WMC_BO_BIG) +#endif + { + inputbuffer[0] = BYTESWAP_WORD(inputbuffer[0]); + inputbuffer[1] = BYTESWAP_WORD(inputbuffer[1]); + inputbuffer[2] = BYTESWAP_WORD(inputbuffer[2]); + inputbuffer[3] = BYTESWAP_WORD(inputbuffer[3]); + } + + } + else + { + int i; + n = 0; + for(i = 0; i < INPUTBUFFER_SIZE; i++) + { + int t; + t = fread(&inputbuffer[i], 2, 1, yyin); + if(!t && ferror(yyin)) + xyyerror(err_fatalread); + else if(!t && n) + break; + n++; +#ifdef WORDS_BIGENDIAN + if(endian == WMC_BO_LITTLE) +#else + if(endian == WMC_BO_BIG) +#endif + { + if((inputbuffer[i] = BYTESWAP_WORD(inputbuffer[i])) == '\n') + break; + } + else + { + if(inputbuffer[i] == '\n') + break; + } + } + } + + } + + if(!n) + { + yywarning("Re-read line (input was or converted to zilch)"); + goto try_again; /* Should not happen, but could be due to stdin reading and a signal */ + } + + ninputbuffer += n; + return 1; +} + +static int get_unichar(void) +{ + static WCHAR *b = NULL; + char_number++; + + if(nungetstack) + return ungetstack[--nungetstack]; + + if(!ninputbuffer) + { + if(!fill_inputbuffer()) + return EOF; + b = inputbuffer; + } + + ninputbuffer--; + return (int)(*b++ & 0xffff); +} + +static void unget_unichar(int ch) +{ + if(ch == EOF) + return; + + char_number--; + + if(nungetstack == allocungetstack) + { + allocungetstack += 32; + ungetstack = xrealloc(ungetstack, allocungetstack * sizeof(*ungetstack)); + } + + ungetstack[nungetstack++] = (WCHAR)ch; +} + + +/* + * Normal character stack. + * Used for number scanning. + */ +static int ncharstack = 0; +static int alloccharstack = 0; +static char *charstack = NULL; + +static void empty_char_stack(void) +{ + ncharstack = 0; +} + +static void push_char(int ch) +{ + if(ncharstack == alloccharstack) + { + alloccharstack += 32; + charstack = xrealloc(charstack, alloccharstack * sizeof(*charstack)); + } + charstack[ncharstack++] = (char)ch; +} + +static int tos_char_stack(void) +{ + if(!ncharstack) + return 0; + else + return (int)(charstack[ncharstack-1] & 0xff); +} + +static char *get_char_stack(void) +{ + return charstack; +} + +/* + * Unicode character stack. + * Used for general scanner. + */ +static int nunicharstack = 0; +static int allocunicharstack = 0; +static WCHAR *unicharstack = NULL; + +static void empty_unichar_stack(void) +{ + nunicharstack = 0; +} + +static void push_unichar(int ch) +{ + if(nunicharstack == allocunicharstack) + { + allocunicharstack += 128; + unicharstack = xrealloc(unicharstack, allocunicharstack * sizeof(*unicharstack)); + } + unicharstack[nunicharstack++] = (WCHAR)ch; +} + +#if 0 +static int tos_unichar_stack(void) +{ + if(!nunicharstack) + return 0; + else + return (int)(unicharstack[nunicharstack-1] & 0xffff); +} +#endif + +static WCHAR *get_unichar_stack(void) +{ + return unicharstack; +} + +/* + * Number scanner + * + * state | ch | next state + * ------+-----------------+-------------------------- + * 0 | [0] | 1 + * 0 | [1-9] | 4 + * 0 | . | error (should never occur) + * 1 | [xX] | 2 + * 1 | [0-7] | 3 + * 1 | [89a-wyzA-WYZ_] | error invalid digit + * 1 | . | return 0 + * 2 | [0-9a-fA-F] | 2 + * 2 | [g-zG-Z_] | error invalid hex digit + * 2 | . | return (hex-number) if TOS != [xX] else error + * 3 | [0-7] | 3 + * 3 | [89a-zA-Z_] | error invalid octal digit + * 3 | . | return (octal-number) + * 4 | [0-9] | 4 + * 4 | [a-zA-Z_] | error invalid decimal digit + * 4 | . | return (decimal-number) + * + * All non-identifier characters [^a-zA-Z_0-9] terminate the scan + * and return the value. This is not entirely correct, but close + * enough (should check punctuators as trailing context, but the + * char_table is not adapted to that and it is questionable whether + * it is worth the trouble). + * All non-iso-8859-1 characters are an error. + */ +static int scan_number(int ch) +{ + int state = 0; + int base = 10; + empty_char_stack(); + + while(1) + { + if(!isisochar(ch)) + xyyerror("Invalid digit"); + + switch(state) + { + case 0: + if(isdigit(ch)) + { + push_char(ch); + if(ch == '0') + state = 1; + else + state = 4; + } + else + internal_error(__FILE__, __LINE__, "Non-digit in first number-scanner state"); + break; + case 1: + if(ch == 'x' || ch == 'X') + { + push_char(ch); + state = 2; + } + else if(ch >= '0' && ch <= '7') + { + push_char(ch); + state = 3; + } + else if(isalpha(ch) || ch == '_') + xyyerror("Invalid number digit"); + else + { + unget_unichar(ch); + yylval.num = 0; + return tNUMBER; + } + break; + case 2: + if(isxdigit(ch)) + push_char(ch); + else if(isalpha(ch) || ch == '_' || !isxdigit(tos_char_stack())) + xyyerror("Invalid hex digit"); + else + { + base = 16; + goto finish; + } + break; + case 3: + if(ch >= '0' && ch <= '7') + push_char(ch); + else if(isalnum(ch) || ch == '_') + xyyerror("Invalid octal digit"); + else + { + base = 8; + goto finish; + } + break; + case 4: + if(isdigit(ch)) + push_char(ch); + else if(isalnum(ch) || ch == '_') + xyyerror("Invalid decimal digit"); + else + { + base = 10; + goto finish; + } + break; + default: + internal_error(__FILE__, __LINE__, "Invalid state in number-scanner"); + } + ch = get_unichar(); + } +finish: + unget_unichar(ch); + push_char(0); + yylval.num = strtoul(get_char_stack(), NULL, base); + return tNUMBER; +} + +static void newline(void) +{ + line_number++; + char_number = 1; +} + +static int unisort(const void *p1, const void *p2) +{ + return unistricmp(((token_t *)p1)->name, ((token_t *)p2)->name); +} + +static token_t *tokentable = NULL; +static int ntokentable = 0; + +token_t *lookup_token(const WCHAR *s) +{ + token_t tok; + + tok.name = s; + return (token_t *)bsearch(&tok, tokentable, ntokentable, sizeof(*tokentable), unisort); +} + +void add_token(tok_e type, const WCHAR *name, int tok, int cp, const WCHAR *alias, int fix) +{ + ntokentable++; + tokentable = xrealloc(tokentable, ntokentable * sizeof(*tokentable)); + tokentable[ntokentable-1].type = type; + tokentable[ntokentable-1].name = name; + tokentable[ntokentable-1].token = tok; + tokentable[ntokentable-1].codepage = cp; + tokentable[ntokentable-1].alias = alias; + tokentable[ntokentable-1].fixed = fix; + qsort(tokentable, ntokentable, sizeof(*tokentable), unisort); +} + +void get_tokentable(token_t **tab, int *len) +{ + assert(tab != NULL); + assert(len != NULL); + *tab = tokentable; + *len = ntokentable; +} + +/* + * The scanner + * + */ +int yylex(void) +{ + static WCHAR ustr_dot1[] = { '.', '\n', 0 }; + static WCHAR ustr_dot2[] = { '.', '\r', '\n', 0 }; + static int isinit = 0; + int ch; + + if(!isinit) + { + isinit++; + set_codepage(WMC_DEFAULT_CODEPAGE); + add_token(tok_keyword, ustr_codepages, tCODEPAGE, 0, NULL, 0); + add_token(tok_keyword, ustr_facility, tFACILITY, 0, NULL, 1); + add_token(tok_keyword, ustr_facilitynames, tFACNAMES, 0, NULL, 1); + add_token(tok_keyword, ustr_language, tLANGUAGE, 0, NULL, 1); + add_token(tok_keyword, ustr_languagenames, tLANNAMES, 0, NULL, 1); + add_token(tok_keyword, ustr_messageid, tMSGID, 0, NULL, 1); + add_token(tok_keyword, ustr_messageidtypedef, tTYPEDEF, 0, NULL, 1); + add_token(tok_keyword, ustr_outputbase, tBASE, 0, NULL, 1); + add_token(tok_keyword, ustr_severity, tSEVERITY, 0, NULL, 1); + add_token(tok_keyword, ustr_severitynames, tSEVNAMES, 0, NULL, 1); + add_token(tok_keyword, ustr_symbolicname, tSYMNAME, 0, NULL, 1); + add_token(tok_severity, ustr_error, 0x03, 0, NULL, 0); + add_token(tok_severity, ustr_warning, 0x02, 0, NULL, 0); + add_token(tok_severity, ustr_informational, 0x01, 0, NULL, 0); + add_token(tok_severity, ustr_success, 0x00, 0, NULL, 0); + add_token(tok_facility, ustr_application, 0xFFF, 0, NULL, 0); + add_token(tok_facility, ustr_system, 0x0FF, 0, NULL, 0); + add_token(tok_language, ustr_english, 0x409, 437, ustr_msg00001, 0); + } + + empty_unichar_stack(); + + while(1) + { + if(want_line) + { + while((ch = get_unichar()) != '\n') + { + if(ch == EOF) + xyyerror("Unexpected EOF"); + push_unichar(ch); + } + newline(); + push_unichar(ch); + push_unichar(0); + if(!unistrcmp(ustr_dot1, get_unichar_stack()) || !unistrcmp(ustr_dot2, get_unichar_stack())) + { + want_line = 0; + /* Reset the codepage to our default after each message */ + set_codepage(WMC_DEFAULT_CODEPAGE); + return tMSGEND; + } + yylval.str = xunistrdup(get_unichar_stack()); + return tLINE; + } + + ch = get_unichar(); + + if(ch == EOF) + return EOF; + + if(ch == '\n') + { + newline(); + if(want_nl) + { + want_nl = 0; + return tNL; + } + continue; + } + + if(isisochar(ch)) + { + if(want_file) + { + int n = 0; + while(n < 8 && isisochar(ch)) + { + int t = char_table[ch]; + if((t & CH_PUNCT) || !(t & CH_SHORTNAME)) + break; + + push_unichar(ch); + n++; + ch = get_unichar(); + } + unget_unichar(ch); + push_unichar(0); + want_file = 0; + yylval.str = xunistrdup(get_unichar_stack()); + return tFILE; + } + + if(char_table[ch] & CH_IDENT) + { + token_t *tok; + while(isisochar(ch) && (char_table[ch] & (CH_IDENT|CH_NUMBER))) + { + push_unichar(ch); + ch = get_unichar(); + } + unget_unichar(ch); + push_unichar(0); + if(!(tok = lookup_token(get_unichar_stack()))) + { + yylval.str = xunistrdup(get_unichar_stack()); + return tIDENT; + } + switch(tok->type) + { + case tok_keyword: + return tok->token; + + case tok_language: + codepage = tok->codepage; + /* Fall through */ + case tok_severity: + case tok_facility: + yylval.tok = tok; + return tTOKEN; + + default: + internal_error(__FILE__, __LINE__, "Invalid token type encountered"); + } + } + + if(isspace(ch)) /* Ignore space */ + continue; + + if(isdigit(ch)) + return scan_number(ch); + } + + switch(ch) + { + case ':': + case '=': + case '+': + case '(': + case ')': + return ch; + case ';': + while(ch != '\n' && ch != EOF) + { + push_unichar(ch); + ch = get_unichar(); + } + newline(); + push_unichar(ch); /* Include the newline */ + push_unichar(0); + yylval.str = xunistrdup(get_unichar_stack()); + return tCOMMENT; + default: + xyyerror("Invalid character '%c' (0x%04x)", isisochar(ch) && isprint(ch) ? ch : '.', ch); + } + } +} + diff --git a/tools/wmc/mcy.y b/tools/wmc/mcy.y new file mode 100644 index 0000000000..904357b49d --- /dev/null +++ b/tools/wmc/mcy.y @@ -0,0 +1,659 @@ +/* + * Wine Message Compiler parser + * + * Copyright 2000 Bertho A. Stultiens (BS) + * + * The basic grammar of the file is yet another example of, humpf, + * design. There is is mix of context-insensitive and -sentitive + * stuff, which makes it rather complicated. + * The header definitions are all context-insensitive because they have + * delimited arguments, whereas the message headers are (semi-) context- + * sensitive and the messages themselves are, well, RFC82[12] delimited. + * This mixture seems to originate from the time that ms and ibm were + * good friends and developing os/2 according to the "compatibility" + * switch and reading some comments here and there. + * + * I'll ignore most of the complications and concentrate on the concept + * which allows me to use yacc. Basically, everything is context- + * insensitive now, with the exception of the message-text itself and + * the preceding language declaration. + * + */ + +%{ + +#include +#include +#include + +#include "config.h" + +#include "utils.h" +#include "wmc.h" +#include "lang.h" + +static const char err_syntax[] = "Syntax error"; +static const char err_number[] = "Number expected"; +static const char err_ident[] = "Identifier expected"; +static const char err_assign[] = "'=' expected"; +static const char err_popen[] = "'(' expected"; +static const char err_pclose[] = "')' expected"; +static const char err_colon[] = "':' expected"; +static const char err_msg[] = "Message expected"; + +/* Scanner switches */ +int want_nl = 0; /* Request next newlinw */ +int want_line = 0; /* Request next complete line */ +int want_file = 0; /* Request next ident as filename */ + +node_t *nodehead = NULL; /* The list of all parsed elements */ +static node_t *nodetail = NULL; +lan_blk_t *lanblockhead; /* List of parsed elements transposed */ + +static int base = 16; /* Current printout base to use (8, 10 or 16) */ +static WCHAR *cast = NULL; /* Current typecast to use */ + +static int last_id = 0; /* The last message ID parsed */ +static int last_sev = 0; /* Last severity code parsed */ +static int last_fac = 0; /* Last facility code parsed */ +static WCHAR *last_sym = NULL;/* Last alias symbol parsed */ +static int have_sev; /* Set if severity parsed for current message */ +static int have_fac; /* Set if facility parsed for current message */ +static int have_sym; /* Set is symbol parsed for current message */ + +static cp_xlat_t *cpxlattab = NULL; /* Codepage translation table */ +static int ncpxlattab = 0; + +/* Prototypes */ +static WCHAR *merge(WCHAR *s1, WCHAR *s2); +static lanmsg_t *new_lanmsg(lan_cp_t *lcp, WCHAR *msg); +static msg_t *add_lanmsg(msg_t *msg, lanmsg_t *lanmsg); +static msg_t *complete_msg(msg_t *msg, int id); +static void add_node(node_e type, void *p); +static void do_add_token(tok_e type, token_t *tok, const char *code); +static void test_id(int id); +static int check_languages(node_t *head); +static lan_blk_t *block_messages(node_t *head); +static void add_cpxlat(int lan, int cpin, int cpout); +cp_xlat_t *find_cpxlat(int lan); + +%} + + +%union { + WCHAR *str; + unsigned num; + token_t *tok; + lanmsg_t *lmp; + msg_t *msg; + lan_cp_t lcp; +} + + +%token tSEVNAMES tFACNAMES tLANNAMES tBASE tCODEPAGE +%token tTYPEDEF tNL tSYMNAME tMSGEND +%token tSEVERITY tFACILITY tLANGUAGE tMSGID +%token tIDENT tLINE tFILE tCOMMENT +%token tNUMBER +%token tTOKEN + +%type alias lines +%type optcp id msgid clan +%type token +%type body +%type bodies msg +%type lang + +%% +file : items { + if(!check_languages(nodehead)) + xyyerror("No messages defined"); + lanblockhead = block_messages(nodehead); + } + ; + +items : decl + | items decl + ; + +decl : global + | msg { add_node(nd_msg, $1); } + | tCOMMENT { add_node(nd_comment, $1); } + | error { xyyerror(err_syntax); /* `Catch all' error */ } + ; + +global : tSEVNAMES '=' '(' smaps ')' + | tSEVNAMES '=' '(' smaps error { xyyerror(err_pclose); } + | tSEVNAMES '=' error { xyyerror(err_popen); } + | tSEVNAMES error { xyyerror(err_assign); } + | tFACNAMES '=' '(' fmaps ')' + | tFACNAMES '=' '(' fmaps error { xyyerror(err_pclose); } + | tFACNAMES '=' error { xyyerror(err_popen); } + | tFACNAMES error { xyyerror(err_assign); } + | tLANNAMES '=' '(' lmaps ')' + | tLANNAMES '=' '(' lmaps error { xyyerror(err_pclose); } + | tLANNAMES '=' error { xyyerror(err_popen); } + | tLANNAMES error { xyyerror(err_assign); } + | tCODEPAGE '=' '(' cmaps ')' + | tCODEPAGE '=' '(' cmaps error { xyyerror(err_pclose); } + | tCODEPAGE '=' error { xyyerror(err_popen); } + | tCODEPAGE error { xyyerror(err_assign); } + | tTYPEDEF '=' tIDENT { cast = $3; } + | tTYPEDEF '=' error { xyyerror(err_number); } + | tTYPEDEF error { xyyerror(err_assign); } + | tBASE '=' tNUMBER { + switch(base) + { + case 8: + case 10: + case 16: + base = $3; + break; + default: + xyyerror("Numberbase must be 8, 10 or 16"); + } + } + | tBASE '=' error { xyyerror(err_number); } + | tBASE error { xyyerror(err_assign); } + ; + +/*---------------------------------------------------------------------- + * SeverityNames mapping + */ +smaps : smap + | smaps smap + | error { xyyerror(err_ident); } + ; + +smap : token '=' tNUMBER alias { + $1->token = $3; + $1->alias = $4; + if($3 & (~0x3)) + xyyerror("Severity value out of range (0x%08x > 0x3)", $3); + do_add_token(tok_severity, $1, "severity"); + } + | token '=' error { xyyerror(err_number); } + | token error { xyyerror(err_assign); } + ; + +/*---------------------------------------------------------------------- + * FacilityNames mapping + */ +fmaps : fmap + | fmaps fmap + | error { xyyerror(err_ident); } + ; + +fmap : token '=' tNUMBER alias { + $1->token = $3; + $1->alias = $4; + if($3 & (~0xfff)) + xyyerror("Facility value out of range (0x%08x > 0xfff)", $3); + do_add_token(tok_facility, $1, "facility"); + } + | token '=' error { xyyerror(err_number); } + | token error { xyyerror(err_assign); } + ; + +alias : /* Empty */ { $$ = NULL; } + | ':' tIDENT { $$ = $2; } + | ':' error { xyyerror(err_ident); } + ; + +/*---------------------------------------------------------------------- + * LanguageNames mapping + */ +lmaps : lmap + | lmaps lmap + | error { xyyerror(err_ident); } + ; + +lmap : token '=' tNUMBER setfile ':' tFILE optcp { + $1->token = $3; + $1->alias = $6; + $1->codepage = $7; + do_add_token(tok_language, $1, "language"); + if(!find_language($1->token) && !find_cpxlat($1->token)) + yywarning("Language 0x%x not built-in, using codepage %d; use explicit codepage to override", $1->token, WMC_DEFAULT_CODEPAGE); + } + | token '=' tNUMBER setfile ':' error { xyyerror("Filename expected"); } + | token '=' tNUMBER error { xyyerror(err_colon); } + | token '=' error { xyyerror(err_number); } + | token error { xyyerror(err_assign); } + ; + +optcp : /* Empty */ { $$ = 0; } + | ':' tNUMBER { $$ = $2; } + | ':' error { xyyerror("Codepage-number expected"); } + ; + +/*---------------------------------------------------------------------- + * Codepages mapping + */ +cmaps : cmap + | cmaps cmap + | error { xyyerror(err_ident); } + ; + +cmap : clan '=' tNUMBER ':' tNUMBER { + static const char err_nocp[] = "Codepage %d not builtin; cannot convert"; + if(find_cpxlat($1)) + xyyerror("Codepage translation already defined for language 0x%x", $1); + if($3 && !find_codepage($3)) + xyyerror(err_nocp, $3); + if($5 && !find_codepage($5)) + xyyerror(err_nocp, $5); + add_cpxlat($1, $3, $5); + } + | clan '=' tNUMBER ':' error { xyyerror(err_number); } + | clan '=' tNUMBER error { xyyerror(err_colon); } + | clan '=' error { xyyerror(err_number); } + | clan error { xyyerror(err_assign); } + ; + +clan : tNUMBER { $$ = $1; } + | tTOKEN { + if($1->type != tok_language) + xyyerror("Language name or code expected"); + $$ = $1->token; + } + ; + +/*---------------------------------------------------------------------- + * Message-definition parsing + */ +msg : msgid sevfacsym { test_id($1); } bodies { $$ = complete_msg($4, $1); } + ; + +msgid : tMSGID '=' id { + if($3 & (~0xffff)) + xyyerror("Message ID value out of range (0x%08x > 0xffff)", $3); + $$ = $3; + } + | tMSGID error { xyyerror(err_assign); } + ; + +id : /* Empty */ { $$ = ++last_id; } + | tNUMBER { $$ = last_id = $1; } + | '+' tNUMBER { $$ = last_id += $2; } + | '+' error { xyyerror(err_number); } + ; + +sevfacsym: /* Empty */ { have_sev = have_fac = have_sym = 0; } + | sevfacsym sev { if(have_sev) xyyerror("Severity already defined"); have_sev = 1; } + | sevfacsym fac { if(have_fac) xyyerror("Facility already defined"); have_fac = 1; } + | sevfacsym sym { if(have_sym) xyyerror("Symbolname already defined"); have_sym = 1; } + ; + +sym : tSYMNAME '=' tIDENT { last_sym = $3; } + | tSYMNAME '=' error { xyyerror(err_ident); } + | tSYMNAME error { xyyerror(err_assign); } + ; + +sev : tSEVERITY '=' token { + token_t *tok = lookup_token($3->name); + if(!tok) + xyyerror("Undefined severityname"); + if(tok->type != tok_severity) + xyyerror("Identifier is not of class 'severity'"); + last_sev = tok->token; + } + | tSEVERITY '=' error { xyyerror(err_ident); } + | tSEVERITY error { xyyerror(err_assign); } + ; + +fac : tFACILITY '=' token { + token_t *tok = lookup_token($3->name); + if(!tok) + xyyerror("Undefined facilityname"); + if(tok->type != tok_facility) + xyyerror("Identifier is not of class 'facility'"); + last_fac = tok->token; + } + | tFACILITY '=' error { xyyerror(err_ident); } + | tFACILITY error { xyyerror(err_assign); } + ; + +/*---------------------------------------------------------------------- + * Message-text parsing + */ +bodies : body { $$ = add_lanmsg(NULL, $1); } + | bodies body { $$ = add_lanmsg($1, $2); } + | error { xyyerror("'Language=...' (start of message text-definition) expected"); } + ; + +body : lang setline lines tMSGEND { $$ = new_lanmsg(&$1, $3); } + ; + + /* + * The newline is to be able to set the codepage + * to the language based codepage for the next + * message to be parsed. + */ +lang : tLANGUAGE setnl '=' token tNL { + token_t *tok = lookup_token($4->name); + cp_xlat_t *cpx; + if(!tok) + xyyerror("Undefined language"); + if(tok->type != tok_language) + xyyerror("Identifier is not of class 'language'"); + if((cpx = find_cpxlat(tok->token))) + { + set_codepage($$.codepage = cpx->cpin); + } + else if(!tok->codepage) + { + const language_t *lan = find_language(tok->token); + if(!lan) + { + /* Just set default; warning was given while parsing languagenames */ + set_codepage($$.codepage = WMC_DEFAULT_CODEPAGE); + } + else + { + /* The default seems to be to use the DOS codepage... */ + set_codepage($$.codepage = lan->doscp); + } + } + else + set_codepage($$.codepage = tok->codepage); + $$.language = tok->token; + } + | tLANGUAGE setnl '=' token error { xyyerror("Missing newline"); } + | tLANGUAGE setnl '=' error { xyyerror(err_ident); } + | tLANGUAGE error { xyyerror(err_assign); } + ; + +lines : tLINE { $$ = $1; } + | lines tLINE { $$ = merge($1, $2); } + | error { xyyerror(err_msg); } + | lines error { xyyerror(err_msg); } + ; + +/*---------------------------------------------------------------------- + * Helper rules + */ +token : tIDENT { $$ = xmalloc(sizeof(token_t)); $$->name = $1; } + | tTOKEN { $$ = $1; } + ; + +setnl : /* Empty */ { want_nl = 1; } + ; + +setline : /* Empty */ { want_line = 1; } + ; + +setfile : /* Empty */ { want_file = 1; } + ; + +%% + +static WCHAR *merge(WCHAR *s1, WCHAR *s2) +{ + int l1 = unistrlen(s1); + int l2 = unistrlen(s2); + s1 = xrealloc(s1, (l1 + l2 + 1) * sizeof(*s1)); + unistrcpy(s1+l1, s2); + free(s2); + return s1; +} + +static void do_add_token(tok_e type, token_t *tok, const char *code) +{ + token_t *tp = lookup_token(tok->name); + if(tp) + { + if(tok->type != type) + yywarning("Type change in token"); + if(tp != tok) + xyyerror("Overlapping token not the same"); + /* else its already defined and changed */ + if(tok->fixed) + xyyerror("Redefinition of %s", code); + tok->fixed = 1; + } + else + { + add_token(type, tok->name, tok->token, tok->codepage, tok->alias, 1); + free(tok); + } +} + +static lanmsg_t *new_lanmsg(lan_cp_t *lcp, WCHAR *msg) +{ + lanmsg_t *lmp = (lanmsg_t *)xmalloc(sizeof(lanmsg_t)); + lmp->lan = lcp->language; + lmp->cp = lcp->codepage; + lmp->msg = msg; + lmp->len = unistrlen(msg) + 1; /* Include termination */ + if(lmp->len > 4096) + yywarning("Message exceptionally long; might be a missing termination"); + return lmp; +} + +static msg_t *add_lanmsg(msg_t *msg, lanmsg_t *lanmsg) +{ + int i; + if(!msg) + msg = xmalloc(sizeof(msg_t)); + msg->msgs = xrealloc(msg->msgs, (msg->nmsgs+1) * sizeof(*(msg->msgs))); + msg->msgs[msg->nmsgs] = lanmsg; + msg->nmsgs++; + for(i = 0; i < msg->nmsgs-1; i++) + { + if(msg->msgs[i]->lan == lanmsg->lan) + xyyerror("Message for language 0x%x already defined", lanmsg->lan); + } + return msg; +} + +static int sort_lanmsg(const void *p1, const void *p2) +{ + return (*(lanmsg_t **)p1)->lan - (*(lanmsg_t **)p2)->lan; +} + +static msg_t *complete_msg(msg_t *mp, int id) +{ + assert(mp != NULL); + mp->id = id; + if(have_sym) + mp->sym = last_sym; + else + xyyerror("No symbolic name defined for message id %d", id); + mp->sev = last_sev; + mp->fac = last_fac; + qsort(mp->msgs, mp->nmsgs, sizeof(*(mp->msgs)), sort_lanmsg); + mp->realid = id | (last_sev << 30) | (last_fac << 16); + if(custombit) + mp->realid |= 1 << 29; + mp->base = base; + mp->cast = cast; + return mp; +} + +static void add_node(node_e type, void *p) +{ + node_t *ndp = (node_t *)xmalloc(sizeof(node_t)); + ndp->type = type; + ndp->u.all = p; + + if(nodetail) + { + ndp->prev = nodetail; + nodetail->next = ndp; + nodetail = ndp; + } + else + { + nodehead = nodetail = ndp; + } +} + +static void test_id(int id) +{ + node_t *ndp; + for(ndp = nodehead; ndp; ndp = ndp->next) + { + if(ndp->type != nd_msg) + continue; + if(ndp->u.msg->id == id && ndp->u.msg->sev == last_sev && ndp->u.msg->fac == last_fac) + xyyerror("MessageId %d with facility 0x%x and severity 0x%x already defined", id, last_fac, last_sev); + } +} + +static int check_languages(node_t *head) +{ + static char err_missing[] = "Missing definition for language 0x%x; MessageID %d, facility 0x%x, severity 0x%x"; + node_t *ndp; + int nm = 0; + msg_t *msg = NULL; + + for(ndp = head; ndp; ndp = ndp->next) + { + if(ndp->type != nd_msg) + continue; + if(!nm) + { + msg = ndp->u.msg; + } + else + { + int i; + msg_t *m1; + msg_t *m2; + if(ndp->u.msg->nmsgs > msg->nmsgs) + { + m1 = ndp->u.msg; + m2 = msg; + } + else + { + m1 = msg; + m2 = ndp->u.msg; + } + + for(i = 0; i < m1->nmsgs; i++) + { + if(i > m2->nmsgs) + error(err_missing, m1->msgs[i]->lan, m2->id, m2->fac, m2->sev); + else if(m1->msgs[i]->lan < m2->msgs[i]->lan) + error(err_missing, m1->msgs[i]->lan, m2->id, m2->fac, m2->sev); + else if(m1->msgs[i]->lan > m2->msgs[i]->lan) + error(err_missing, m2->msgs[i]->lan, m1->id, m1->fac, m1->sev); + } + } + nm++; + } + return nm; +} + +#define MSGRID(x) ((*(msg_t **)(x))->realid) +static int sort_msg(const void *p1, const void *p2) +{ + return MSGRID(p1) > MSGRID(p2) ? 1 : (MSGRID(p1) == MSGRID(p2) ? 0 : -1); + /* return (*(msg_t **)p1)->realid - (*(msg_t **)p1)->realid; */ +} + +/* + * block_messages() basically transposes the messages + * from ID/language based list to a language/ID + * based list. + */ +static lan_blk_t *block_messages(node_t *head) +{ + lan_blk_t *lbp; + lan_blk_t *lblktail = NULL; + lan_blk_t *lblkhead = NULL; + msg_t **msgtab = NULL; + node_t *ndp; + int nmsg = 0; + int i; + int nl; + int factor = unicodeout ? 2 : 1; + + for(ndp = head; ndp; ndp = ndp->next) + { + if(ndp->type != nd_msg) + continue; + msgtab = xrealloc(msgtab, (nmsg+1) * sizeof(*msgtab)); + msgtab[nmsg++] = ndp->u.msg; + } + + assert(nmsg != 0); + qsort(msgtab, nmsg, sizeof(*msgtab), sort_msg); + + for(nl = 0; nl < msgtab[0]->nmsgs; nl++) /* This should be equal for all after check_languages() */ + { + lbp = xmalloc(sizeof(lan_blk_t)); + + if(!lblktail) + { + lblkhead = lblktail = lbp; + } + else + { + lblktail->next = lbp; + lbp->prev = lblktail; + lblktail = lbp; + } + lbp->nblk = 1; + lbp->blks = xmalloc(sizeof(*lbp->blks)); + lbp->blks[0].idlo = msgtab[0]->realid; + lbp->blks[0].idhi = msgtab[0]->realid; + /* The plus 4 is the entry header; (+3)&~3 is DWORD alignment */ + lbp->blks[0].size = ((factor * msgtab[0]->msgs[nl]->len + 3) & ~3) + 4; + lbp->blks[0].msgs = xmalloc(sizeof(*lbp->blks[0].msgs)); + lbp->blks[0].nmsg = 1; + lbp->blks[0].msgs[0] = msgtab[0]->msgs[nl]; + lbp->lan = msgtab[0]->msgs[nl]->lan; + + for(i = 1; i < nmsg; i++) + { + block_t *blk = &(lbp->blks[lbp->nblk-1]); + if(msgtab[i]->realid == blk->idhi+1) + { + blk->size += ((factor * msgtab[i]->msgs[nl]->len + 3) & ~3) + 4; + blk->idhi++; + blk->msgs = xrealloc(blk->msgs, (blk->nmsg+1) * sizeof(*blk->msgs)); + blk->msgs[blk->nmsg++] = msgtab[i]->msgs[nl]; + } + else + { + lbp->nblk++; + lbp->blks = xrealloc(lbp->blks, lbp->nblk * sizeof(*lbp->blks)); + blk = &(lbp->blks[lbp->nblk-1]); + blk->idlo = msgtab[i]->realid; + blk->idhi = msgtab[i]->realid; + blk->size = ((factor * msgtab[i]->msgs[nl]->len + 3) & ~3) + 4; + blk->msgs = xmalloc(sizeof(*blk->msgs)); + blk->nmsg = 1; + blk->msgs[0] = msgtab[i]->msgs[nl]; + } + } + } + free(msgtab); + return lblkhead; +} + +static int sc_xlat(const void *p1, const void *p2) +{ + return ((cp_xlat_t *)p1)->lan - ((cp_xlat_t *)p2)->lan; +} + +static void add_cpxlat(int lan, int cpin, int cpout) +{ + cpxlattab = xrealloc(cpxlattab, (ncpxlattab+1) * sizeof(*cpxlattab)); + cpxlattab[ncpxlattab].lan = lan; + cpxlattab[ncpxlattab].cpin = cpin; + cpxlattab[ncpxlattab].cpout = cpout; + ncpxlattab++; + qsort(cpxlattab, ncpxlattab, sizeof(*cpxlattab), sc_xlat); +} + +cp_xlat_t *find_cpxlat(int lan) +{ + cp_xlat_t t; + t.lan = lan; + return (cp_xlat_t *)bsearch(&t, cpxlattab, ncpxlattab, sizeof(*cpxlattab), sc_xlat); +} + diff --git a/tools/wmc/utils.c b/tools/wmc/utils.c new file mode 100644 index 0000000000..5d0dee53e2 --- /dev/null +++ b/tools/wmc/utils.c @@ -0,0 +1,241 @@ +/* + * Utility routines + * + * Copyright 1998,2000 Bertho A. Stultiens + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "wmctypes.h" +#include "utils.h" +#include "wmc.h" + +#define SUPPRESS_YACC_ERROR_MESSAGE + +static void generic_msg(const char *s, const char *t, va_list ap) +{ + fprintf(stderr, "%s %s: %d, %d: ", t, input_name ? input_name : "stdin", line_number, char_number); + vfprintf(stderr, s, ap); + fprintf(stderr, "\n"); +} + +/* + * The yyerror routine should not exit because we use the error-token + * to determine the syntactic error in the source. However, YACC + * uses the same routine to print an error just before the error + * token is reduced. + * The extra routine 'xyyerror' is used to exit after giving a real + * message. + */ +int yyerror(const char *s, ...) +{ +#ifndef SUPPRESS_YACC_ERROR_MESSAGE + va_list ap; + va_start(ap, s); + generic_msg(s, "Yacc error", ap); + va_end(ap); +#endif + return 1; +} + +int xyyerror(const char *s, ...) +{ + va_list ap; + va_start(ap, s); + generic_msg(s, "Error", ap); + va_end(ap); + exit(1); + return 1; +} + +int yywarning(const char *s, ...) +{ + va_list ap; + va_start(ap, s); + generic_msg(s, "Warning", ap); + va_end(ap); + return 0; +} + +void internal_error(const char *file, int line, const char *s, ...) +{ + va_list ap; + va_start(ap, s); + fprintf(stderr, "Internal error (please report) %s %d: ", file, line); + vfprintf(stderr, s, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(3); +} + +void error(const char *s, ...) +{ + va_list ap; + va_start(ap, s); + fprintf(stderr, "Error: "); + vfprintf(stderr, s, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(2); +} + +void warning(const char *s, ...) +{ + va_list ap; + va_start(ap, s); + fprintf(stderr, "Warning: "); + vfprintf(stderr, s, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +char *dup_basename(const char *name, const char *ext) +{ + int namelen; + int extlen = strlen(ext); + char *base; + char *slash; + + if(!name) + name = "wmc.tab"; + + slash = strrchr(name, '/'); + if (slash) + name = slash + 1; + + namelen = strlen(name); + + /* +4 for later extension and +1 for '\0' */ + base = (char *)xmalloc(namelen +4 +1); + strcpy(base, name); + if(!strcasecmp(name + namelen-extlen, ext)) + { + base[namelen - extlen] = '\0'; + } + return base; +} + +void *xmalloc(size_t size) +{ + void *res; + + assert(size > 0); + assert(size < 102400); + res = malloc(size); + if(res == NULL) + { + error("Virtual memory exhausted.\n"); + } + /* + * We set it to 0. + * This is *paramount* because we depend on it + * just about everywhere in the rest of the code. + */ + memset(res, 0, size); + return res; +} + + +void *xrealloc(void *p, size_t size) +{ + void *res; + + assert(size > 0); + assert(size < 102400); + res = realloc(p, size); + if(res == NULL) + { + error("Virtual memory exhausted.\n"); + } + return res; +} + +char *xstrdup(const char *str) +{ + char *s; + + assert(str != NULL); + s = (char *)xmalloc(strlen(str)+1); + return strcpy(s, str); +} + +int unistrlen(const WCHAR *s) +{ + int n; + for(n = 0; *s; n++, s++) + ; + return n; +} + +WCHAR *unistrcpy(WCHAR *dst, const WCHAR *src) +{ + WCHAR *t = dst; + while(*src) + *t++ = *src++; + *t = 0; + return dst; +} + +WCHAR *xunistrdup(const WCHAR * str) +{ + WCHAR *s; + + assert(str != NULL); + s = (WCHAR *)xmalloc((unistrlen(str)+1) * sizeof(WCHAR)); + return unistrcpy(s, str); +} + +int unistricmp(const WCHAR *s1, const WCHAR *s2) +{ + int i; + int once = 0; + static char warn[] = "Don't know the uppercase equivalent of non acsii characters;" + "comparison might yield wrong results"; + while(*s1 && *s2) + { + if((*s1 & 0xffff) > 0x7f || (*s2 & 0xffff) > 0x7f) + { + if(!once) + { + once++; + yywarning(warn); + } + i = *s1++ - *s2++; + } + else + i = toupper(*s1++) - toupper(*s2++); + if(i) + return i; + } + + if((*s1 & 0xffff) > 0x7f || (*s2 & 0xffff) > 0x7f) + { + if(!once) + yywarning(warn); + return *s1 - *s2; + } + else + return toupper(*s1) - toupper(*s2); +} + +int unistrcmp(const WCHAR *s1, const WCHAR *s2) +{ + int i; + while(*s1 && *s2) + { + i = *s1++ - *s2++; + if(i) + return i; + } + + return *s1 - *s2; +} + diff --git a/tools/wmc/utils.h b/tools/wmc/utils.h new file mode 100644 index 0000000000..156e5c420a --- /dev/null +++ b/tools/wmc/utils.h @@ -0,0 +1,36 @@ +/* + * Utility routines' prototypes etc. + * + * Copyright 1998,2000 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WMC_UTILS_H +#define __WMC_UTILS_H + +#ifndef __WMC_WMCTYPES_H +#include "wmctypes.h" +#endif + +#include /* size_t */ + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *str); + +int yyerror(const char *s, ...) __attribute__((format (printf, 1, 2))); +int xyyerror(const char *s, ...) __attribute__((format (printf, 1, 2))); +int yywarning(const char *s, ...) __attribute__((format (printf, 1, 2))); +void internal_error(const char *file, int line, const char *s, ...) __attribute__((format (printf, 3, 4))); +void error(const char *s, ...) __attribute__((format (printf, 1, 2))); +void warning(const char *s, ...) __attribute__((format (printf, 1, 2))); + +char *dup_basename(const char *name, const char *ext); + +WCHAR *xunistrdup(const WCHAR * str); +WCHAR *unistrcpy(WCHAR *dst, const WCHAR *src); +int unistrlen(const WCHAR *s); +int unistricmp(const WCHAR *s1, const WCHAR *s2); +int unistrcmp(const WCHAR *s1, const WCHAR *s2); + +#endif diff --git a/tools/wmc/wmc.c b/tools/wmc/wmc.c new file mode 100644 index 0000000000..2750209d47 --- /dev/null +++ b/tools/wmc/wmc.c @@ -0,0 +1,268 @@ +/* + * Wine Message Compiler main program + * + * Copyright 2000 Bertho A. Stultiens (BS) + * + */ +#include +#include +#include +#include + +#include "config.h" + +#include "wmc.h" +#include "utils.h" +#include "lang.h" +#include "write.h" + +static char usage[] = + "Usage: wmc [options...] [inputfile.mc]\n" + " -B x Set output byte-order x={n[ative], l[ittle], b[ig]}\n" + " (default is n[ative] which equals " +#ifdef WORDS_BIGENDIAN + "big" +#else + "little" +#endif + "-endian)\n" + " -c Set 'custom-bit' in values\n" + " -d Use decimal values in output\n" + " -D Set debug flag\n" + " -h This message\n" + " -H file Write headerfile to file (default is inputfile.h)\n" + " -i Inline messagetable(s)\n" + " -o file Output to file (default is inputfile.rc)\n" + " -u Inputfile is in unicode\n" + " -U Output unicode messagetable(s)\n" + " -v Show supported codepages and languages\n" + " -V Print version end exit\n" + " -W Enable pedantic warnings\n" + "Input is taken from stdin if no inputfile is specified.\n" + "Byteorder of unicode input is based upon the first couple of\n" + "bytes read, which should be 0x0000..0x00ff.\n" + ; + +static char version_string[] = + "Wine Message Compiler Version " WMC_FULLVERSION "\n" + "Copyright 2000 Bertho A. Stultiens\n" + ; + +/* + * The output byte-order of resources (set with -B) + */ +int byteorder = WMC_BO_NATIVE; + +/* + * Custom bit (bit 29) in output values must be set (-c option) + */ +int custombit = 0; + +/* + * Output decimal values (-d option) + */ +int decimal = 0; + +/* + * Enable pedantic warnings; check arg references (-W option) + */ +int pedantic = 0; + +/* + * Unicode input (-u option) + */ +int unicodein = 0; + +/* + * Unicode output (-U option) + */ +int unicodeout = 0; + +/* + * Inline the messagetables (don't write *.bin files; -i option) + */ +int rcinline = 0; + +/* + * Debugging flag (-D option) + */ +int dodebug = 0; + +char *output_name = NULL; /* The name given by the -o option */ +char *input_name = NULL; /* The name given on the command-line */ +char *header_name = NULL; /* The name given by the -H option */ + +int line_number = 1; /* The current line */ +int char_number = 1; /* The current char pos within the line */ + +char *cmdline; /* The entire commandline */ +time_t now; /* The time of start of wmc */ + +int getopt (int argc, char *const *argv, const char *optstring); +static void segvhandler(int sig); + +int main(int argc,char *argv[]) +{ + extern char* optarg; + extern int optind; + int optc; + int lose = 0; + int ret; + int i; + int cmdlen; + + signal(SIGSEGV, segvhandler); + + now = time(NULL); + + /* First rebuild the commandline to put in destination */ + /* Could be done through env[], but not all OS-es support it */ + cmdlen = 4; /* for "wmc " */ + for(i = 1; i < argc; i++) + cmdlen += strlen(argv[i]) + 1; + cmdline = (char *)xmalloc(cmdlen); + strcpy(cmdline, "wmc "); + for(i = 1; i < argc; i++) + { + strcat(cmdline, argv[i]); + if(i < argc-1) + strcat(cmdline, " "); + } + + while((optc = getopt(argc, argv, "B:cdDhH:io:p:uUvVW")) != EOF) + { + switch(optc) + { + case 'B': + switch(optarg[0]) + { + case 'n': + case 'N': + byteorder = WMC_BO_NATIVE; + break; + case 'l': + case 'L': + byteorder = WMC_BO_LITTLE; + break; + case 'b': + case 'B': + byteorder = WMC_BO_BIG; + break; + default: + fprintf(stderr, "Byteordering must be n[ative], l[ittle] or b[ig]\n"); + lose++; + } + break; + case 'c': + custombit = 1; + break; + case 'd': + decimal = 1; + break; + case 'D': + dodebug = 1; + break; + case 'h': + printf("%s", usage); + exit(0); + /* No return */ + case 'H': + header_name = xstrdup(optarg); + break; + case 'i': + rcinline = 1; + break; + case 'o': + output_name = xstrdup(optarg); + break; + case 'u': + unicodein = 1; + break; + case 'U': + unicodeout = 1; + break; + case 'v': + show_languages(); + show_codepages(); + exit(0); + /* No return */ + case 'V': + printf(version_string); + exit(0); + /* No return */ + case 'W': + pedantic = 1; + break; + default: + lose++; + break; + } + } + + if(lose) + { + fprintf(stderr, "%s", usage); + return 1; + } + + yydebug = dodebug; + if(dodebug) + { + setbuf(stdout, 0); + setbuf(stderr, 0); + } + + /* Check for input file on command-line */ + if(optind < argc) + { + input_name = argv[optind]; + } + + /* Generate appropriate outfile names */ + if(!output_name) + { + output_name = dup_basename(input_name, ".mc"); + strcat(output_name, ".rc"); + } + + if(!header_name) + { + header_name = dup_basename(input_name, ".mc"); + strcat(header_name, ".h"); + } + + if(input_name) + { + if(!(yyin = fopen(input_name, "rb"))) + error("Could not open %s for input\n", input_name); + } + else + yyin = stdin; + + ret = yyparse(); + + if(input_name) + fclose(yyin); + + if(ret) + { + /* Error during parse */ + exit(1); + } + + write_h_file(header_name); + write_rc_file(output_name); + if(!rcinline) + write_bin_files(); + + return 0; +} + +static void segvhandler(int sig) +{ + fprintf(stderr, "\n%s:%d: Oops, segment violation\n", input_name, line_number); + fflush(stdout); + fflush(stderr); + abort(); +} + diff --git a/tools/wmc/wmc.h b/tools/wmc/wmc.h new file mode 100644 index 0000000000..5446bee8b5 --- /dev/null +++ b/tools/wmc/wmc.h @@ -0,0 +1,74 @@ +/* + * Main definitions and externals + * + * Copyright 2000 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WMC_WMC_H +#define __WMC_WMC_H + +#ifndef __WMC_WMCTYPES_H +#include "wmctypes.h" +#endif + +#include /* For time_t */ + +#define WMC_MAJOR_VERSION 1 +#define WMC_MINOR_VERSION 0 +#define WMC_MICRO_VERSION 0 +#define WMC_RELEASEDATE "(12-Jun-2000)" + +#define WMC_STRINGIZE(a) #a +#define WMC_VERSIONIZE(a,b,c) WMC_STRINGIZE(a) "." WMC_STRINGIZE(b) "." WMC_STRINGIZE(c) +#define WMC_VERSION WMC_VERSIONIZE(WMC_MAJOR_VERSION, WMC_MINOR_VERSION, WMC_MICRO_VERSION) +#define WMC_FULLVERSION WMC_VERSION " " WMC_RELEASEDATE + +/* + * The default codepage setting is only to + * read and convert input which is non-message + * text. It doesn't really matter that much because + * all codepages map 0x00-0x7f to 0x0000-0x007f from + * char to unicode and all non-message text should + * be plain ASCII. + * However, we do implement iso-8859-1 for 1-to-1 + * mapping for all other chars, so this is very close + * to what we really want. + */ +#define WMC_DEFAULT_CODEPAGE 28591 + +extern int pedantic; +extern int leave_case; +extern int byteorder; +extern int decimal; +extern int custombit; +extern int unicodein; +extern int unicodeout; +extern int rcinline; + +extern char *output_name; +extern char *input_name; +extern char *header_name; +extern char *cmdline; +extern time_t now; + +extern int line_number; +extern int char_number; + +int yyparse(void); +extern int yydebug; +extern int want_nl; +extern int want_line; +extern int want_file; +extern node_t *nodehead; +extern lan_blk_t *lanblockhead; + +int yylex(void); +FILE *yyin; +void set_codepage(int cp); + +void add_token(tok_e type, const WCHAR *name, int tok, int cp, const WCHAR *alias, int fix); +token_t *lookup_token(const WCHAR *s); +void get_tokentable(token_t **tab, int *len); + +#endif diff --git a/tools/wmc/wmc.man b/tools/wmc/wmc.man new file mode 100644 index 0000000000..ec5cdcb199 --- /dev/null +++ b/tools/wmc/wmc.man @@ -0,0 +1,103 @@ +.TH WMC 1 "June 12, 2000" "Version 1.0.0" "Wine Message Compiler" +.SH NAME +wrc \- Wine Message Compiler +.SH SYNOPSIS +.BI "wmc " "[options] " "[inputfile]" +.SH DESCRIPTION +.B wmc +compiles messages from +.B inputfile +into FormatMessage[AW] compatible format encapsulated in a resourcescript +format. +.B wmc +outputs the data either in a standard \fB.bin\fR formatted binary +file, or can generated inline resource data. +.PP +.B wmc +takes only one \fBinputfile\fR as argument (see \fBBUGS\fR). The +\fBinputfile\fR normally has extension \fB.mc\fR. The messages are read from +standard input if no inputfile is given. If the outputfile is not specified +with \fI-o\fR, then \fBwmc\fR will write the output to \fBinputfile.{rc,h}\fR. +The outputfile is named \fBwmc.tab.{rc,h}\fR if no inputfile was given. +.SH OPTIONS +.TP +.I \-B x +Set output byte-order x={n[ative], l[ittle], b[ig]}. Default is n[ative]. +.TP +.I \-c +Set 'custom-bit' in message-code values. +.TP +.I \-d +NON-FUNCTIONAL; Use decimal values in output +.TP +.I \-D +Set debug flag. This results is a parser trace and a lot of extra messages. +.TP +.I \-h +Print an informative usage message. +.TP +.I \-H file +Write headerfile to \fIfile\fR. Default is \fIinputfile.h\fR. +.TP +.I \-i +Inline messagetable(s). This option skips the generation of all \fI.bin\fR files +and writes all output into the \fI.rc\fR file. This encoding is parsable with +wrc(1). +.TP +.I \-o file +Output to \fIfile\fR. Default is \fIinputfile.rc\fR. +.TP +.I \-u +Assume that the inputfile is in unicode. +.TP +.I \-U +Write resource output in unicode formatted messagetable(s). +.TP +.I \-v +Show all supported codepages and languages. +.TP +.I \-V +Print version end exit. +.TP +.I \-W +Enable pedantic warnings. +.SH EXTENSIONS +The original syntax is extended to support codepages more smoothly. Normally, +codepages are based on the DOS\-codepage from the language setting. The +original syntax only allows the destination codepage to be set. However, this +is not enough for non\-DOS systems which do not use unicode source-files. +.PP +A new keyword \fICodepages\fR is introduced to set both input and output +codepages to anything one wants for each language. The syntax is similar to +the other constructs: +.PP +Codepages '=' '(' language '=' cpin ':' cpout ... ')' +.PP +The \fIlanguage\fR is the numerical language\-ID or the alias set with +LanguageNames. The input\-codepage \fIcpin\fR and output\-codepage +\fIcpout\fR are the numerical codepage\-IDs. There can be multiple mapping +within the definition and the definition may occur more than once. +.SH AUTHORS +.B wmc +was written by Bertho A. Stultiens. +.SH BUGS +The message compiler should be able to have multiple inputfiles and combine +them into one outputfile. This would enable the splitting of languages into +separate files. +.PP +Unicode detection of the input is suboptimal, to say the least. It should +recognize byte\-order\-marks (BOM) and decide what to do. +.PP +Decimal output is completely lacking. Don't know whether it should be +implemented because it is a, well, non-informative format change. It is +recognized on the commandline for some form of compatibility. +.SH AVAILABILITY +.B wmc +is part of the wine distribution, which is available through +WineHQ, the +.B wine +development headquarters, at +.I http://www.winehq.com/. +.SH "SEE ALSO" +.BR wine (1), +.BR wrc (1) diff --git a/tools/wmc/wmctypes.h b/tools/wmc/wmctypes.h new file mode 100644 index 0000000000..8a2839ff00 --- /dev/null +++ b/tools/wmc/wmctypes.h @@ -0,0 +1,109 @@ +/* + * Main definitions and externals + * + * Copyright 2000 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WMC_WMCTYPES_H +#define __WMC_WMCTYPES_H + +#ifndef __WINE_WINDEF_H +#include "windef.h" +#endif + +/* Byteordering defines */ +#define WMC_BO_NATIVE 0x00 +#define WMC_BO_LITTLE 0x01 +#define WMC_BO_BIG 0x02 + +#define WMC_LOBYTE(w) ((WORD)(w) & 0xff) +#define WMC_HIBYTE(w) (((WORD)(w) >> 8) & 0xff) +#define WMC_LOWORD(d) ((DWORD)(d) & 0xffff) +#define WMC_HIWORD(d) (((DWORD)(d) >> 16) & 0xffff) +#define BYTESWAP_WORD(w) ((WORD)(((WORD)WMC_LOBYTE(w) << 8) + (WORD)WMC_HIBYTE(w))) +#define BYTESWAP_DWORD(d) ((DWORD)(((DWORD)BYTESWAP_WORD(WMC_LOWORD(d)) << 16) + ((DWORD)BYTESWAP_WORD(WMC_HIWORD(d))))) + +/* + * Tokenizer types + */ +typedef enum tok_enum { + tok_null = 0, + tok_keyword, + tok_severity, + tok_facility, + tok_language +} tok_e; + +typedef struct token { + tok_e type; + const WCHAR *name; /* Parsed name of token */ + int token; /* Tokenvalue or language code */ + int codepage; + const WCHAR *alias; /* Alias or filename */ + int fixed; /* Cleared if token may change */ +} token_t; + +typedef struct lan_cp { + int language; + int codepage; +} lan_cp_t; + +typedef struct cp_xlat { + int lan; + int cpin; + int cpout; +} cp_xlat_t; + +typedef struct lanmsg { + int lan; /* Language code of message */ + int cp; /* Codepage of message */ + WCHAR *msg; /* Message text */ + int len; /* Message length including trailing '\0' */ +} lanmsg_t; + +typedef struct msg { + int id; /* Message ID */ + unsigned realid; /* Combined message ID */ + WCHAR *sym; /* Symbolic name */ + int sev; /* Severity code */ + int fac; /* Facility code */ + lanmsg_t **msgs; /* Array message texts */ + int nmsgs; /* Number of message texts in array */ + int base; /* Base of number to print */ + WCHAR *cast; /* Typecase to use */ +} msg_t; + +typedef enum { + nd_msg, + nd_comment +} node_e; + +typedef struct node { + struct node *next; + struct node *prev; + node_e type; + union { + void *all; + WCHAR *comment; + msg_t *msg; + } u; +} node_t; + +typedef struct block { + unsigned idlo; /* Lowest ID in this set */ + unsigned idhi; /* Highest ID in this set */ + int size; /* Size of this set */ + lanmsg_t **msgs; /* Array of messages in this set */ + int nmsg; /* Number of array entries */ +} block_t; + +typedef struct lan_blk { + struct lan_blk *next; /* Linkage for languages */ + struct lan_blk *prev; + int lan; /* The language of this block */ + block_t *blks; /* Array of blocks for this language */ + int nblk; /* Nr of blocks in array */ +} lan_blk_t; + +#endif diff --git a/tools/wmc/write.c b/tools/wmc/write.c new file mode 100644 index 0000000000..3519bc75d2 --- /dev/null +++ b/tools/wmc/write.c @@ -0,0 +1,502 @@ +/* + * Wine Message Compiler output generation + * + * Copyright 2000 Bertho A. Stultiens (BS) + * + */ + +#include +#include +#include +#include +#include + +#include "wmc.h" +#include "utils.h" +#include "lang.h" +#include "write.h" + +/* + * The binary resource layout is as follows: + * + * +===============+ + * Header | NBlocks | + * +===============+ + * Block 0 | Low ID | + * +---------------+ + * | High ID | + * +---------------+ + * | Offset |---+ + * +===============+ | + * Block 1 | Low ID | | + * +---------------+ | + * | High ID | | + * +---------------+ | + * | Offset |------+ + * +===============+ | | + * | | | | + * ... ... | | + * | | | | + * +===============+ <-+ | + * B0 LoID | Len | Flags | | + * +---+---+---+---+ | + * | b | l | a | b | | + * +---+---+---+---+ | + * | l | a | \0| \0| | + * +===============+ | + * | | | + * ... ... | + * | | | + * +===============+ | + * B0 HiID | Len | Flags | | + * +---+---+---+---+ | + * | M | o | r | e | | + * +---+---+---+---+ | + * | b | l | a | \0| | + * +===============+ <----+ + * B1 LoID | Len | Flags | + * +---+---+---+---+ + * | J | u | n | k | + * +---+---+---+---+ + * | \0| \0| \0| \0| + * +===============+ + * | | + * ... ... + * | | + * +===============+ + * + * All Fields are aligned on their natural boundaries. The length + * field (Len) covers both the length of the string and the header + * fields (Len and Flags). Strings are '\0' terminated. Flags is 0 + * for normal character strings and 1 for unicode strings. + */ + +static char str_header[] = + "/* This file is generated with wmc version " WMC_FULLVERSION ". Do not edit! */\n" + "/* Source : %s */\n" + "/* Cmdline: %s */\n" + "/* Date : %s */\n" + "\n" + ; + +static char *dup_u2c(int cp, const WCHAR *uc) +{ + int len = unistrlen(uc); + char *cptr = xmalloc(len+1); + const union cptable *cpdef = find_codepage(cp); + if(!cpdef) + internal_error(__FILE__, __LINE__, "Codepage %d not found (vanished?)", cp); + if((len = cp_wcstombs(cpdef, 0, uc, unistrlen(uc)+1, cptr, len+1, NULL, NULL)) < 0) + internal_error(__FILE__, __LINE__, "Buffer overflow? code %d.", len); + return cptr; +} + +static void killnl(char *s, int ddd) +{ + char *tmp; + tmp = strstr(s, "\r\n"); + if(tmp) + { + if(ddd && tmp - s > 3) + { + tmp[0] = tmp[1] = tmp[2] = '.'; + tmp[3] = '\0'; + } + else + *tmp = '\0'; + } + tmp = strchr(s, '\n'); + if(tmp) + { + if(ddd && tmp - s > 3) + { + tmp[0] = tmp[1] = tmp[2] = '.'; + tmp[3] = '\0'; + } + else + *tmp = '\0'; + } +} + +static int killcomment(char *s) +{ + char *tmp = s; + int b = 0; + while((tmp = strstr(tmp, "/*"))) + { + tmp[1] = 'x'; + b++; + } + tmp = s; + while((tmp = strstr(tmp, "*/"))) + { + tmp[0] = 'x'; + b++; + } + return b; +} + +void write_h_file(const char *fname) +{ + node_t *ndp; + char *cptr; + char *cast; + FILE *fp; + token_t *ttab; + int ntab; + int i; + int once = 0; + int idx_en = 0; + + fp = fopen(fname, "w"); + if(!fp) + { + perror(fname); + exit(1); + } + cptr = ctime(&now); + killnl(cptr, 0); + fprintf(fp, str_header, input_name ? input_name : "", cmdline, cptr); + fprintf(fp, "#ifndef __WMCGENERATED_%08lx_H\n", now); + fprintf(fp, "#define __WMCGENERATED_%08lx_H\n", now); + fprintf(fp, "\n"); + + /* Write severity and facility aliases */ + get_tokentable(&ttab, &ntab); + fprintf(fp, "/* Severity codes */\n"); + for(i = 0; i < ntab; i++) + { + if(ttab[i].type == tok_severity && ttab[i].alias) + { + cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ttab[i].alias); + fprintf(fp, "#define %s\t0x%x\n", cptr, ttab[i].token); + free(cptr); + } + } + fprintf(fp, "\n"); + + fprintf(fp, "/* Facility codes */\n"); + for(i = 0; i < ntab; i++) + { + if(ttab[i].type == tok_facility && ttab[i].alias) + { + cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ttab[i].alias); + fprintf(fp, "#define %s\t0x%x\n", cptr, ttab[i].token); + free(cptr); + } + } + fprintf(fp, "\n"); + + /* Write the message codes */ + fprintf(fp, "/* Message definitions */\n"); + for(ndp = nodehead; ndp; ndp = ndp->next) + { + switch(ndp->type) + { + case nd_comment: + cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ndp->u.comment+1); + killnl(cptr, 0); + killcomment(cptr); + if(*cptr) + fprintf(fp, "/* %s */\n", cptr); + else + fprintf(fp, "\n"); + free(cptr); + break; + case nd_msg: + if(!once) + { + /* + * Search for an english text. + * If not found, then use the first in the list + */ + once++; + for(i = 0; i < ndp->u.msg->nmsgs; i++) + { + if(ndp->u.msg->msgs[i]->lan == 0x409) + { + idx_en = i; + break; + } + } + fprintf(fp, "\n"); + } + fprintf(fp, "/* MessageId : 0x%08x */\n", ndp->u.msg->realid); + cptr = dup_u2c(ndp->u.msg->msgs[idx_en]->cp, ndp->u.msg->msgs[idx_en]->msg); + killnl(cptr, 0); + killcomment(cptr); + fprintf(fp, "/* Approx. msg: %s */\n", cptr); + free(cptr); + cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ndp->u.msg->sym); + if(ndp->u.msg->cast) + cast = dup_u2c(WMC_DEFAULT_CODEPAGE, ndp->u.msg->cast); + else + cast = NULL; + switch(ndp->u.msg->base) + { + case 8: + if(cast) + fprintf(fp, "#define %s\t((%s)0%oL)\n\n", cptr, cast, ndp->u.msg->realid); + else + fprintf(fp, "#define %s\t0%oL\n\n", cptr, ndp->u.msg->realid); + break; + case 10: + if(cast) + fprintf(fp, "#define %s\t((%s)%dL)\n\n", cptr, cast, ndp->u.msg->realid); + else + fprintf(fp, "#define %s\t%dL\n\n", cptr, ndp->u.msg->realid); + break; + case 16: + if(cast) + fprintf(fp, "#define %s\t((%s)0x%08xL)\n\n", cptr, cast, ndp->u.msg->realid); + else + fprintf(fp, "#define %s\t0x%08xL\n\n", cptr, ndp->u.msg->realid); + break; + default: + internal_error(__FILE__, __LINE__, "Invalid base for number print"); + } + free(cptr); + if(cast) + free(cast); + break; + default: + internal_error(__FILE__, __LINE__, "Invalid node type %d", ndp->type); + } + } + fprintf(fp, "\n#endif\n"); + fclose(fp); +} + +static void write_rcbin(FILE *fp) +{ + lan_blk_t *lbp; + token_t *ttab; + int ntab; + int i; + + get_tokentable(&ttab, &ntab); + + for(lbp = lanblockhead; lbp; lbp = lbp->next) + { + char *cptr = NULL; + fprintf(fp, "LANGUAGE 0x%x, 0x%x\n", lbp->lan & 0x3ff, lbp->lan >> 10); + for(i = 0; i < ntab; i++) + { + if(ttab[i].type == tok_language && ttab[i].token == lbp->lan) + { + if(ttab[i].alias) + cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ttab[i].alias); + break; + } + } + if(!cptr) + internal_error(__FILE__, __LINE__, "Filename vanished for language 0x%0x", lbp->lan); + fprintf(fp, "1 MESSAGETABLE \"%s.bin\"\n", cptr); + free(cptr); + } +} + +static char *make_string(WCHAR *uc, int len, int codepage) +{ + char *str = xmalloc(7*len + 1); + char *cptr = str; + int i; + int b; + + if(!codepage) + { + *cptr++ = ' '; + *cptr++ = 'L'; + *cptr++ = '"'; + for(i = b = 0; i < len; i++, uc++) + { + int n; + if(*uc < 0x100) + { + if(isprint(*uc)) + { + *cptr++ = *uc; + b++; + } + else + { + switch(*uc) + { + case '\a': *cptr++ = '\\'; *cptr++ = 'a'; b += 2; break; + case '\b': *cptr++ = '\\'; *cptr++ = 'b'; b += 2; break; + case '\f': *cptr++ = '\\'; *cptr++ = 'f'; b += 2; break; + case '\n': *cptr++ = '\\'; *cptr++ = 'n'; b += 2; break; + case '\r': *cptr++ = '\\'; *cptr++ = 'r'; b += 2; break; + case '\t': *cptr++ = '\\'; *cptr++ = 't'; b += 2; break; + case '\v': *cptr++ = '\\'; *cptr++ = 'v'; b += 2; break; + case '\\': *cptr++ = '\\'; *cptr++ = '\\'; b += 2; break; + case '"': *cptr++ = '\\'; *cptr++ = '"'; b += 2; break; + default: + n = sprintf(cptr, "\\x%04x", *uc & 0xffff); + cptr += n; + b += n; + } + } + } + else + { + n = sprintf(cptr, "\\x%04x", *uc & 0xffff); + cptr += n; + b += n; + } + if(i < len-1 && b >= 72) + { + *cptr++ = '"'; + *cptr++ = ','; + *cptr++ = '\n'; + *cptr++ = ' '; + *cptr++ = 'L'; + *cptr++ = '"'; + b = 0; + } + } + len = (len + 3) & ~3; + for(; i < len; i++) + { + *cptr++ = '\\'; + *cptr++ = 'x'; + *cptr++ = '0'; + *cptr++ = '0'; + *cptr++ = '0'; + *cptr++ = '0'; + } + *cptr++ = '"'; + *cptr = '\0'; + } + else + { + char *tmp = xmalloc(2*len+1); + char *cc = tmp; + const union cptable *cpdef = find_codepage(codepage); + + assert(cpdef != NULL); + if((i = cp_wcstombs(cpdef, 0, uc, unistrlen(uc)+1, tmp, 2*len+1, NULL, NULL)) < 0) + internal_error(__FILE__, __LINE__, "Buffer overflow? code %d.", i); + *cptr++ = ' '; + *cptr++ = '"'; + for(i = b = 0; i < len; i++, cc++) + { + int n; + if(isprint(*cc)) + { + *cptr++ = *cc; + b++; + } + else + { + switch(*cc) + { + case '\a': *cptr++ = '\\'; *cptr++ = 'a'; b += 2; break; + case '\b': *cptr++ = '\\'; *cptr++ = 'b'; b += 2; break; + case '\f': *cptr++ = '\\'; *cptr++ = 'f'; b += 2; break; + case '\n': *cptr++ = '\\'; *cptr++ = 'n'; b += 2; break; + case '\r': *cptr++ = '\\'; *cptr++ = 'r'; b += 2; break; + case '\t': *cptr++ = '\\'; *cptr++ = 't'; b += 2; break; + case '\v': *cptr++ = '\\'; *cptr++ = 'v'; b += 2; break; + case '\\': *cptr++ = '\\'; *cptr++ = '\\'; b += 2; break; + case '"': *cptr++ = '\\'; *cptr++ = '"'; b += 2; break; + default: + n = sprintf(cptr, "\\x%02x", *cc & 0xff); + cptr += n; + b += n; + } + } + if(i < len-1 && b >= 72) + { + *cptr++ = '"'; + *cptr++ = ','; + *cptr++ = '\n'; + *cptr++ = ' '; + *cptr++ = '"'; + b = 0; + } + } + len = (len + 3) & ~3; + for(; i < len; i++) + { + *cptr++ = '\\'; + *cptr++ = 'x'; + *cptr++ = '0'; + *cptr++ = '0'; + } + *cptr++ = '"'; + *cptr = '\0'; + free(tmp); + } + return str; +} + +static void write_rcinline(FILE *fp) +{ + lan_blk_t *lbp; + int i; + int j; + + for(lbp = lanblockhead; lbp; lbp = lbp->next) + { + unsigned offs = 4 * (lbp->nblk * 3 + 1); + fprintf(fp, "\n1 MESSAGETABLE\n"); + fprintf(fp, "LANGUAGE 0x%x, 0x%x\n", lbp->lan & 0x3ff, lbp->lan >> 10); + fprintf(fp, "{\n"); + fprintf(fp, " /* NBlocks */ 0x%08xL,\n", lbp->nblk); + for(i = 0; i < lbp->nblk; i++) + { + fprintf(fp, " /* Lo,Hi,Offs */ 0x%08xL, 0x%08xL, 0x%08xL,\n", + lbp->blks[i].idlo, + lbp->blks[i].idhi, + offs); + offs += lbp->blks[i].size; + } + for(i = 0; i < lbp->nblk; i++) + { + block_t *blk = &lbp->blks[i]; + for(j = 0; j < blk->nmsg; j++) + { + char *cptr; + int l = blk->msgs[j]->len; + char *comma = j == blk->nmsg-1 && i == lbp->nblk-1 ? "" : ","; + cptr = make_string(blk->msgs[j]->msg, l, unicodeout ? 0 : blk->msgs[j]->cp); + fprintf(fp, "\n /* Msg 0x%08x */ 0x%04x, 0x000%c,\n", + blk->idlo + j, + unicodeout ? (l*2+3)&~3 : (l+3)&~3, + unicodeout ? '1' : '0'); + fprintf(fp, "%s%s\n", cptr, comma); + free(cptr); + } + } + fprintf(fp, "}\n"); + } +} + +void write_rc_file(const char *fname) +{ + FILE *fp; + char *cptr; + + fp = fopen(fname, "w"); + if(!fp) + { + perror(fname); + exit(1); + } + cptr = ctime(&now); + killnl(cptr, 0); + fprintf(fp, str_header, input_name ? input_name : "", cmdline, cptr); + + if(rcinline) + write_rcinline(fp); + else + write_rcbin(fp); + fclose(fp); +} + +void write_bin_files(void) +{ + assert(rcinline == 0); +} + diff --git a/tools/wmc/write.h b/tools/wmc/write.h new file mode 100644 index 0000000000..18127174ff --- /dev/null +++ b/tools/wmc/write.h @@ -0,0 +1,14 @@ +/* + * Wine Message Compiler outpur generation + * + * Copyright 2000 Bertho A. Stultiens (BS) + * + */ +#ifndef __WMC_WRITE_H +#define __WMC_WRITE_H + +void write_h_file(const char *fname); +void write_rc_file(const char *fname); +void write_bin_files(void); + +#endif diff --git a/unicode/wctomb.c b/unicode/wctomb.c index b2fcbc9198..fd8b694c69 100644 --- a/unicode/wctomb.c +++ b/unicode/wctomb.c @@ -208,6 +208,6 @@ int cp_wcstombs( const union cptable *table, int flags, if (flags || defchar || used) return wcstombs_dbcs_slow( &table->dbcs, flags, src, srclen, dst, dstlen, defchar, used ); - return wcstombs_sbcs( &table->sbcs, src, srclen, dst, dstlen ); + return wcstombs_dbcs( &table->dbcs, src, srclen, dst, dstlen ); } }