diff --git a/Make.rules.in b/Make.rules.in
index f7fe8acfed5cacd19aba6e7394b542b2df1b0f2f..4d6c2211efbfd93e9021a71f7dc895a35ec2dd13 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 3f29442b7a2f8c1421b5e5c824f8ef7e4840d60b..b666d7b473487ef6c1569a37e80f328f8958557a 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 c307d97f6880435c2dde15b8cd631d74ae884920..b48bd1561ee11c6a6b414d777e04e457a664b79c 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 b985f3e667f729b57e842a36ac5bfbf3ed253e96..c5ae78c02d687f121523436250f7c2be4d719c7c 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 0000000000000000000000000000000000000000..92dc8ee4d0dbea777b6f6f5d209cfc580e59ebb3
--- /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 0000000000000000000000000000000000000000..bec49ca4521547ab5a732219161220bdd81b1efb
--- /dev/null
+++ b/tools/wmc/CHANGES
@@ -0,0 +1,5 @@
+---------------------------------------------------------------------------
+Version 1.0.0 (12-Jun-2000)
+
+Bertho Stultiens <bertho@akhphd.au.dk>
+- Initial release
diff --git a/tools/wmc/Makefile.in b/tools/wmc/Makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..13fee710d7714e62afedf1b1ef31077e76aa9872
--- /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 0000000000000000000000000000000000000000..d5fe72e94d8c73acc64d81ae1b7c555ad71f408f
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#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", "<none>"},*/
+/*	{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 0000000000000000000000000000000000000000..678a35e92fa2c7222367d21c68e01d60e3a014e7
--- /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 0000000000000000000000000000000000000000..d878a0d5e4764d3254dea0f3ae0294df1b1d3699
--- /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 0000000000000000000000000000000000000000..55fd27be213afa0856eae47803f39a1741712f4c
--- /dev/null
+++ b/tools/wmc/mcl.c
@@ -0,0 +1,733 @@
+/*
+ * Wine Message Compiler lexical scanner
+ *
+ * Copyright 2000 Bertho A. Stultiens (BS)
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+
+#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 0000000000000000000000000000000000000000..904357b49d4fadd414c973a39bc0d30ab3b65638
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#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 <str> tIDENT tLINE tFILE tCOMMENT
+%token <num> tNUMBER
+%token <tok> tTOKEN
+
+%type <str>	alias lines
+%type <num>	optcp id msgid clan
+%type <tok>	token
+%type <lmp>	body
+%type <msg>	bodies msg
+%type <lcp>	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 0000000000000000000000000000000000000000..5d0dee53e23ea14e87c53338b07920dbd82a1452
--- /dev/null
+++ b/tools/wmc/utils.c
@@ -0,0 +1,241 @@
+/*
+ * Utility routines
+ *
+ * Copyright 1998,2000 Bertho A. Stultiens
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#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 0000000000000000000000000000000000000000..156e5c420aac7119736a8a4548075b4556d690c8
--- /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 <stddef.h>	/* 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 0000000000000000000000000000000000000000..2750209d47e5ab36e23e537d812c3bbfe16c182f
--- /dev/null
+++ b/tools/wmc/wmc.c
@@ -0,0 +1,268 @@
+/*
+ * Wine Message Compiler main program
+ *
+ * Copyright 2000 Bertho A. Stultiens (BS)
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#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 0000000000000000000000000000000000000000..5446bee8b571c9cceb8481d00df6eb1e50b5fc73
--- /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 <time.h>	/* 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 0000000000000000000000000000000000000000..ec5cdcb1990b36c2331a7d0d06b20ac5e2033d50
--- /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 0000000000000000000000000000000000000000..8a2839ff00fb010a936f1010dc54047ed4212afd
--- /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 0000000000000000000000000000000000000000..3519bc75d25ca1c42e7fda8031edddbc0a39c493
--- /dev/null
+++ b/tools/wmc/write.c
@@ -0,0 +1,502 @@
+/*
+ * Wine Message Compiler output generation
+ *
+ * Copyright 2000 Bertho A. Stultiens (BS)
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#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 : "<stdin>", 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 : "<stdin>", 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 0000000000000000000000000000000000000000..18127174ff17f868f3985a47d0573d378ee4e3f3
--- /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 b2fcbc9198e611c4651c62e3fbb8f8154967305c..fd8b694c695f0e5de3c88293d0d27e843564c679 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 );
     }
 }