Initial release of the message compiler.

This commit is contained in:
Bertho Stultiens 2000-06-13 04:34:41 +00:00 committed by Alexandre Julliard
parent 13d74c5f0a
commit 3085591767
21 changed files with 3092 additions and 2 deletions

View File

@ -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:

2
configure vendored
View File

@ -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

View File

@ -1094,6 +1094,7 @@ server/Makefile
tools/Makefile
tools/cvdump/Makefile
tools/wrc/Makefile
tools/wmc/Makefile
tsx11/Makefile
unicode/Makefile
win32/Makefile

View File

@ -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@

4
tools/wmc/.cvsignore Normal file
View File

@ -0,0 +1,4 @@
Makefile
wmc
y.tab.c
y.tab.h

5
tools/wmc/CHANGES Normal file
View File

@ -0,0 +1,5 @@
---------------------------------------------------------------------------
Version 1.0.0 (12-Jun-2000)
Bertho Stultiens <bertho@akhphd.au.dk>
- Initial release

48
tools/wmc/Makefile.in Normal file
View File

@ -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:

151
tools/wmc/lang.c Normal file
View File

@ -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);
}

26
tools/wmc/lang.h Normal file
View File

@ -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

107
tools/wmc/language.c Normal file
View File

@ -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}

733
tools/wmc/mcl.c Normal file
View File

@ -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);
}
}
}

659
tools/wmc/mcy.y Normal file
View File

@ -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);
}

241
tools/wmc/utils.c Normal file
View File

@ -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;
}

36
tools/wmc/utils.h Normal file
View File

@ -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

268
tools/wmc/wmc.c Normal file
View File

@ -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();
}

74
tools/wmc/wmc.h Normal file
View File

@ -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

103
tools/wmc/wmc.man Normal file
View File

@ -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)

109
tools/wmc/wmctypes.h Normal file
View File

@ -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

502
tools/wmc/write.c Normal file
View File

@ -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);
}

14
tools/wmc/write.h Normal file
View File

@ -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

View File

@ -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 );
}
}