866 lines
21 KiB
C
866 lines
21 KiB
C
/*
|
|
* Copyright 2004-2020 Sandboxie Holdings, LLC
|
|
* Copyright 2020-2021 David Xanatos, xanasoft.com
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include "common/list.h"
|
|
#include "common/list.c"
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Structures and Types
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
struct MSG {
|
|
|
|
LIST_ELEM list_elem;
|
|
ULONG code;
|
|
ULONG ver;
|
|
WCHAR *text;
|
|
};
|
|
|
|
|
|
struct LANG {
|
|
|
|
LIST_ELEM list_elem;
|
|
WCHAR name[128];
|
|
ULONG code;
|
|
LIST msgs;
|
|
};
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Alloc
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
void *Alloc(ULONG Size)
|
|
{
|
|
void *Buffer = HeapAlloc(GetProcessHeap(), 0, Size);
|
|
if (! Buffer) {
|
|
fprintf(stderr, "memory allocation error\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (! HeapValidate(GetProcessHeap(), 0, NULL)) {
|
|
fprintf(stderr, "memory allocation error\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
return Buffer;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// AddTextEntry
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
ULONG AddTextEntry(
|
|
const UCHAR *path, WCHAR *BufPtr, LIST *msgs, ULONG LineNum)
|
|
{
|
|
ULONG code, ver, why;
|
|
WCHAR *ptr, *cr, *lf, *dot;
|
|
struct MSG *msg;
|
|
|
|
why = 0;
|
|
|
|
if (BufPtr[4] != L';') {
|
|
why = 1;
|
|
goto err;
|
|
}
|
|
|
|
code = _wtoi(BufPtr);
|
|
if (! code) {
|
|
why = 2;
|
|
goto err;
|
|
}
|
|
|
|
ptr = BufPtr + 5;
|
|
if (_wcsnicmp(ptr, L"evt;pop;", 8) == 0) {
|
|
code |= 0x103 << 16; // Facility=EventPopup
|
|
ptr += 8;
|
|
} else if (_wcsnicmp(ptr, L"evt;", 4) == 0) {
|
|
code |= 0x101 << 16; // Facility=Event
|
|
ptr += 4;
|
|
} else if (_wcsnicmp(ptr, L"pop;", 4) == 0) {
|
|
code |= 0x102 << 16; // Facility=Popup
|
|
ptr += 4;
|
|
} else if (_wcsnicmp(ptr, L"txt;", 4) == 0) {
|
|
code |= 0x000 << 16; // Facility=Text
|
|
ptr += 4;
|
|
} else if (_wcsnicmp(ptr, L"ins;", 4) == 0) {
|
|
code |= 0xFFF << 16; // NSIS text
|
|
ptr += 4;
|
|
} else {
|
|
why = 3;
|
|
goto err;
|
|
}
|
|
|
|
if ((code & 0xFFF0000) == 0x1010000 ||
|
|
(code & 0xFFF0000) == 0x1020000 ||
|
|
(code & 0xFFF0000) == 0x1030000) {
|
|
|
|
// parse Severity only for Event/Popup/EventPopup
|
|
|
|
if (_wcsnicmp(ptr, L"inf;", 4) == 0) {
|
|
code |= 1 << 30; // Severity=Informational
|
|
ptr += 4;
|
|
} else if (_wcsnicmp(ptr, L"wrn;", 4) == 0) {
|
|
code |= 2 << 30; // Severity=Warning
|
|
ptr += 4;
|
|
} else if (_wcsnicmp(ptr, L"err;", 4) == 0) {
|
|
code |= 3 << 30; // Severity=Error
|
|
ptr += 4;
|
|
} else {
|
|
why = 4;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
ver = _wtoi(ptr);
|
|
if (! ver) {
|
|
why = 5;
|
|
goto err;
|
|
}
|
|
|
|
cr = wcschr(ptr, L'\r');
|
|
lf = wcschr(ptr, L'\n');
|
|
if (cr && cr > lf)
|
|
ptr = cr + 1;
|
|
else if (lf)
|
|
ptr = lf + 1;
|
|
else {
|
|
why = 6;
|
|
goto err;
|
|
}
|
|
|
|
dot = ptr;
|
|
while (1) {
|
|
dot = wcschr(dot, L'.');
|
|
if (! dot) {
|
|
why = 7;
|
|
goto err;
|
|
}
|
|
++dot;
|
|
if ((*dot == L'\r' || *dot == L'\n') &&
|
|
(dot[-2] == L'\r' || dot[-2] == L'\n'))
|
|
{
|
|
*dot = L'\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
msg = Alloc(sizeof(struct MSG));
|
|
msg->code = code;
|
|
msg->ver = ver;
|
|
msg->text = Alloc((wcslen(ptr) + 1) * sizeof(WCHAR));
|
|
wcscpy(msg->text, ptr);
|
|
|
|
List_Insert_After(msgs, NULL, msg);
|
|
|
|
if ((code & 0xFFF0000) == 0xFFF0000) {
|
|
|
|
// eliminate terminating dot for NSIS text
|
|
|
|
ULONG len = wcslen(msg->text);
|
|
if (len && msg->text[len - 1] == L'.') {
|
|
|
|
while (len) {
|
|
--len;
|
|
if (! len)
|
|
break;
|
|
if (msg->text[len - 1] != L'\n' &&
|
|
msg->text[len - 1] != L'\r')
|
|
break;
|
|
}
|
|
|
|
msg->text[len] = L'\0';
|
|
}
|
|
}
|
|
|
|
// printf("Added code %08X/%02X text <%S>\n", msg->code, msg->ver, msg->text);
|
|
|
|
*dot = L'\n';
|
|
return dot - BufPtr;
|
|
|
|
err:
|
|
|
|
cr = wcschr(BufPtr, L'\r');
|
|
if (cr)
|
|
*cr = L'\0';
|
|
lf = wcschr(BufPtr, L'\n');
|
|
if (lf)
|
|
*lf = L'\0';
|
|
|
|
fprintf(stderr, "*==========\n* Syntax Error in %s\n*==========\n\n", path);
|
|
fprintf(stderr, "syntax error (%d) in file %s - line %d - %S\n", why, path, LineNum, BufPtr);
|
|
fprintf(stderr, "\nA B O R T I N G\n\n\n");
|
|
exit(EXIT_FAILURE);
|
|
return 0;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// ReadTextFile
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
void ReadTextFile(const UCHAR *path, LIST *msgs)
|
|
{
|
|
HANDLE hFile;
|
|
ULONG ByteSize, ReadSize;
|
|
UCHAR* Buffer;
|
|
WCHAR *BufPtr;
|
|
ULONG LineNum;
|
|
ULONG i;
|
|
BOOLEAN isUTF8 = TRUE;
|
|
|
|
//
|
|
// read entire contents of text file
|
|
//
|
|
|
|
hFile = CreateFileA(path, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
fprintf(stderr, "can\'t open input file - %s\n", path);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ByteSize = GetFileSize(hFile, NULL);
|
|
if (ByteSize)
|
|
Buffer = Alloc(ByteSize + 16);
|
|
else
|
|
Buffer = NULL;
|
|
|
|
if (! ReadFile(hFile, Buffer, ByteSize, &ReadSize, NULL))
|
|
ReadSize = 0;
|
|
if (ReadSize == 0 || ReadSize != ByteSize) {
|
|
fprintf(stderr, "can\'t read input file - %s\n", path);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
memset(&Buffer[ReadSize], 0, 16);
|
|
|
|
//
|
|
// build list of messages
|
|
//
|
|
|
|
List_Init(msgs);
|
|
|
|
LineNum = 1;
|
|
|
|
|
|
if (Buffer[0] == 0xFF && Buffer[1] == 0xFE) // Unicode BOM
|
|
{
|
|
BufPtr = &Buffer[2];
|
|
ReadSize -= 2;
|
|
isUTF8 = FALSE;
|
|
}
|
|
else if (Buffer[0] == 0xEF && Buffer[1] == 0xBB && Buffer[2] == 0xBF) // UTF8 BOM
|
|
{
|
|
BufPtr = &Buffer[3];
|
|
ReadSize -= 3;
|
|
isUTF8 = TRUE;
|
|
}
|
|
else
|
|
BufPtr = Buffer;
|
|
|
|
if (isUTF8)
|
|
{
|
|
char* utf8 = BufPtr;
|
|
|
|
ByteSize = MultiByteToWideChar(CP_UTF8, 0, utf8, ReadSize, NULL, 0) + 1;
|
|
Buffer = Alloc(ByteSize * sizeof(wchar_t));
|
|
BufPtr = Buffer;
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, utf8, ReadSize, BufPtr, ByteSize);
|
|
}
|
|
|
|
while (1) {
|
|
|
|
WCHAR *Cr = wcschr(BufPtr, L'\r');
|
|
WCHAR *Lf = wcschr(BufPtr, L'\n');
|
|
ULONG Len;
|
|
|
|
if (BufPtr[0] == L'\r' && BufPtr[1] == L'\n')
|
|
++LineNum;
|
|
|
|
if (Cr == BufPtr || Lf == BufPtr) {
|
|
++BufPtr;
|
|
continue;
|
|
}
|
|
|
|
if (Cr && Cr < Lf)
|
|
Len = Cr - BufPtr;
|
|
else if (Lf)
|
|
Len = Lf - BufPtr;
|
|
else
|
|
break;
|
|
|
|
if (Lf != Cr + 1) {
|
|
fprintf(stderr, "\ncrlf error - %s - line %d - %*.*S\n", path, LineNum, Len, Len, BufPtr);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (*BufPtr != L'#') {
|
|
|
|
Len = AddTextEntry(path, BufPtr, msgs, LineNum);
|
|
for (i = 0; i < Len; ++i)
|
|
if (BufPtr[i] == L'\r' && BufPtr[i + 1] == L'\n') {
|
|
++LineNum;
|
|
++i;
|
|
}
|
|
|
|
++LineNum;
|
|
}
|
|
|
|
BufPtr += Len;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// GetMsgForError
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
const WCHAR *GetMsgForError(struct MSG *msg)
|
|
{
|
|
static WCHAR text[4096];
|
|
WCHAR *iptr, *optr;
|
|
ULONG fac, sev;
|
|
|
|
swprintf(text, L"%04d;", msg->code & 0xFFFF);
|
|
|
|
fac = (msg->code >> 16) & 0xFFF;
|
|
sev = (msg->code >> 30) & 3;
|
|
|
|
if (fac == 0)
|
|
wcscat(text, L"txt;");
|
|
else if (fac == 0xFFF)
|
|
wcscat(text, L"ins;");
|
|
else {
|
|
|
|
if ((fac & 0x101) == 0x101)
|
|
wcscat(text, L"evt;");
|
|
if ((fac & 0x102) == 0x102)
|
|
wcscat(text, L"pop;");
|
|
|
|
if (sev == 1)
|
|
wcscat(text, L"inf;");
|
|
else if (sev == 2)
|
|
wcscat(text, L"wrn;");
|
|
else if (sev == 3)
|
|
wcscat(text, L"err;");
|
|
}
|
|
|
|
swprintf(text + wcslen(text), L"%02d\n", msg->ver);
|
|
|
|
iptr = msg->text;
|
|
optr = text + wcslen(text);
|
|
while (*iptr) {
|
|
*optr = *iptr;
|
|
if (*iptr != L'\r')
|
|
++optr;
|
|
++iptr;
|
|
}
|
|
*optr = L'\n';
|
|
++optr;
|
|
*optr = L'\n';
|
|
++optr;
|
|
*optr = L'\0';
|
|
|
|
return text;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// DiscardOldText
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
void DiscardOldText(const UCHAR *Name, LIST *msgs)
|
|
{
|
|
struct MSG *mi, *mj;
|
|
|
|
mi = List_Head(msgs);
|
|
while (mi) {
|
|
|
|
mj = List_Head(msgs);
|
|
while (mj) {
|
|
|
|
if (mj != mi && mj->code == mi->code) {
|
|
|
|
if (mj->ver == mi->ver) {
|
|
|
|
fprintf(stderr, "*==========\n* Duplicate Messages in %s\n*==========\n\n", Name);
|
|
fprintf(stderr, "%S", GetMsgForError(mi));
|
|
fprintf(stderr, "\nA B O R T I N G\n\n\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (mj->ver > mi->ver) {
|
|
|
|
mi->ver = mj->ver;
|
|
mi->text = mj->text;
|
|
}
|
|
|
|
List_Remove(msgs, mj);
|
|
mi = NULL;
|
|
break;
|
|
}
|
|
|
|
mj = List_Next(mj);
|
|
}
|
|
|
|
if (mi)
|
|
mi = List_Next(mi);
|
|
else
|
|
mi = List_Head(msgs);
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// FindTextEntry
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
struct MSG *FindTextEntry(LIST *msgs, ULONG code, ULONG ver)
|
|
{
|
|
struct MSG *msg = List_Head(msgs);
|
|
while (msg) {
|
|
if (msg->code == code && msg->ver == ver)
|
|
break;
|
|
msg = List_Next(msg);
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// CompareText
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
void CompareText(UCHAR *Name, LIST *Foreign, LIST *English)
|
|
{
|
|
struct MSG *me, *mf;
|
|
BOOLEAN AtLeastOne = FALSE;
|
|
|
|
fprintf(stderr, "*==========\n* Missing Messages in %s\n*==========\n\n", Name);
|
|
|
|
//
|
|
// report English text missing in Foreign file
|
|
//
|
|
|
|
me = List_Head(English);
|
|
while (me) {
|
|
|
|
mf = FindTextEntry(Foreign, me->code, me->ver);
|
|
if (! mf) {
|
|
|
|
AtLeastOne = TRUE;
|
|
fprintf(stderr, "%S", GetMsgForError(me));
|
|
|
|
mf = Alloc(sizeof(struct MSG));
|
|
|
|
mf->code = me->code;
|
|
mf->ver = me->ver;
|
|
|
|
mf->text = me->text;
|
|
|
|
List_Insert_After(Foreign, NULL, mf);
|
|
}
|
|
|
|
me = List_Next(me);
|
|
}
|
|
|
|
if (! AtLeastOne)
|
|
fprintf(stderr, "There are no missing messages.\n\n");
|
|
|
|
//
|
|
// report Foreign text missing in English file
|
|
//
|
|
|
|
AtLeastOne = FALSE;
|
|
|
|
mf = List_Head(Foreign);
|
|
while (mf) {
|
|
|
|
me = FindTextEntry(English, mf->code, mf->ver);
|
|
if (! me) {
|
|
|
|
if (! AtLeastOne) {
|
|
fprintf(stderr, "*==========\n* Extraneous Messages in %s\n*==========\n\n", Name);
|
|
AtLeastOne = TRUE;
|
|
}
|
|
|
|
fprintf(stderr, "%S", GetMsgForError(mf));
|
|
}
|
|
|
|
mf = List_Next(mf);
|
|
}
|
|
|
|
if (AtLeastOne)
|
|
fprintf(stderr, "\n\n");
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// AddLanguage
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
void AddLanguage(LIST *langs, LIST *msgs, const UCHAR *filename)
|
|
{
|
|
struct LANG *lang;
|
|
WCHAR *ptr;
|
|
|
|
lang = Alloc(sizeof(struct LANG));
|
|
|
|
swprintf(lang->name, L"%S", filename + 5);
|
|
|
|
ptr = wcschr(lang->name, L'-');
|
|
if (! ptr) {
|
|
fprintf(stderr, "bad file name - %s\n", filename);
|
|
return;
|
|
}
|
|
*ptr = L'\0';
|
|
|
|
++ptr;
|
|
lang->code = _wtoi(ptr);
|
|
if (! lang->code) {
|
|
fprintf(stderr, "bad file name - %s\n", filename);
|
|
return;
|
|
}
|
|
|
|
memcpy(&lang->msgs, msgs, sizeof(LIST));
|
|
List_Insert_After(langs, NULL, lang);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Put
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
void Put(HANDLE hFile, const WCHAR *Text)
|
|
{
|
|
ULONG Written;
|
|
WriteFile(hFile, Text, wcslen(Text) * sizeof(WCHAR), &Written, NULL);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// WriteMessageFile
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
void WriteMessageFile(LIST *langs)
|
|
{
|
|
HANDLE hFile;
|
|
struct LANG *lang;
|
|
WCHAR text[128];
|
|
LIST *English;
|
|
struct MSG *me, *mf;
|
|
|
|
//
|
|
// create output file
|
|
//
|
|
|
|
hFile = CreateFile(L"msgs.mc", FILE_GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
fprintf(stderr, "can\'t create output file\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
//
|
|
// write FacilityNames
|
|
//
|
|
|
|
Put(hFile, L"FacilityNames=(\r\n"
|
|
L" Text=0\r\n"
|
|
L" Event=0x101:MSG_FACILITY_EVENT\r\n"
|
|
L" Popup=0x102:MSG_FACILITY_POPUP\r\n"
|
|
L" EventPopup=0x103\r\n"
|
|
L")\r\n\r\n");
|
|
|
|
//
|
|
// write LanguageNames
|
|
//
|
|
|
|
lang = List_Head(langs);
|
|
English = &lang->msgs;
|
|
lang = List_Next(lang);
|
|
if (lang) {
|
|
Put(hFile, L"LanguageNames=(\r\n");
|
|
while (lang) {
|
|
swprintf(text, L" %s=0x%X:MSG%05X\r\n",
|
|
lang->name, lang->code, lang->code);
|
|
Put(hFile, text);
|
|
lang = List_Next(lang);
|
|
}
|
|
Put(hFile, L")\r\n\r\n");
|
|
}
|
|
|
|
//
|
|
// write messages
|
|
//
|
|
|
|
me = List_Head(English);
|
|
while (me) {
|
|
|
|
ULONG fac = (me->code >> 16) & 0xFFF;
|
|
ULONG sev = (me->code >> 30) & 3;
|
|
|
|
if (fac != 0xFFF) {
|
|
|
|
swprintf(text, L"MessageId=%d", me->code & 0xFFFF);
|
|
|
|
wcscat(text, L" Facility=");
|
|
if (fac == 0)
|
|
wcscat(text, L"Text");
|
|
else if (fac == 0x101)
|
|
wcscat(text, L"Event");
|
|
else if (fac == 0x102)
|
|
wcscat(text, L"Popup");
|
|
else if (fac == 0x103)
|
|
wcscat(text, L"EventPopup");
|
|
|
|
wcscat(text, L" Severity=");
|
|
if (sev == 1)
|
|
wcscat(text, L"Informational");
|
|
else if (sev == 2)
|
|
wcscat(text, L"Warning");
|
|
else if (sev == 3)
|
|
wcscat(text, L"Error");
|
|
else
|
|
wcscat(text, L"Success");
|
|
|
|
swprintf(text + wcslen(text),
|
|
L" SymbolicName=MSG_%04d\r\n"
|
|
L"Language=English\r\n",
|
|
me->code & 0xFFFF);
|
|
|
|
Put(hFile, text);
|
|
|
|
Put(hFile, me->text);
|
|
Put(hFile, L"\r\n");
|
|
|
|
lang = List_Head(langs);
|
|
lang = List_Next(lang);
|
|
while (lang) {
|
|
|
|
mf = FindTextEntry(&lang->msgs, me->code, me->ver);
|
|
if (mf) {
|
|
swprintf(text, L"Language=%s\r\n", lang->name);
|
|
Put(hFile, text);
|
|
Put(hFile, mf->text);
|
|
Put(hFile, L"\r\n");
|
|
}
|
|
|
|
lang = List_Next(lang);
|
|
}
|
|
|
|
Put(hFile, L"\r\n");
|
|
}
|
|
|
|
me = List_Next(me);
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// FindCopyTextForNsis
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
WCHAR *FindCopyTextForNsis(struct MSG *mf, LIST *msgs)
|
|
{
|
|
if (_wcsnicmp(mf->text, L"=copy:", 6) == 0) {
|
|
|
|
ULONG code = _wtoi(mf->text + 6);
|
|
struct MSG *msg = List_Head(msgs);
|
|
while (msg) {
|
|
if (msg->code == code) {
|
|
|
|
// eliminate terminating dot for NSIS text
|
|
|
|
ULONG len = wcslen(msg->text);
|
|
WCHAR *text = Alloc((len + 1) * sizeof(WCHAR));
|
|
memcpy(text, msg->text, (len + 1) * sizeof(WCHAR));
|
|
if (len && text[len - 1] == L'.') {
|
|
|
|
while (len) {
|
|
--len;
|
|
if (! len)
|
|
break;
|
|
if (text[len - 1] != L'\n' &&
|
|
text[len - 1] != L'\r')
|
|
break;
|
|
}
|
|
|
|
text[len] = L'\0';
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
msg = List_Next(msg);
|
|
}
|
|
}
|
|
|
|
return mf->text;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// WriteNsisFiles
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
void WriteNsisFiles(LIST *langs)
|
|
{
|
|
struct LANG *lang;
|
|
WCHAR text[4096];
|
|
WCHAR uplang[64];
|
|
HANDLE hFile;
|
|
LIST *English;
|
|
struct MSG *me, *mf;
|
|
|
|
//
|
|
// create output file
|
|
//
|
|
|
|
lang = List_Head(langs);
|
|
English = &lang->msgs;
|
|
while (lang) {
|
|
|
|
swprintf(text, L"SbieRelease\\NsisText_%s.txt", lang->name);
|
|
|
|
hFile = CreateFile(text, FILE_GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
fprintf(stderr, "can\'t create output file - %S\n", text);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
text[0] = 0xFEFF;
|
|
text[1] = L'\0';
|
|
Put(hFile, text);
|
|
|
|
wcscpy(uplang, lang->name);
|
|
_wcsupr(uplang);
|
|
|
|
me = List_Head(English);
|
|
while (me) {
|
|
|
|
ULONG fac = (me->code >> 16) & 0xFFF;
|
|
if (fac == 0xFFF) {
|
|
|
|
WCHAR *iptr, *optr;
|
|
|
|
mf = FindTextEntry(&lang->msgs, me->code, me->ver);
|
|
if (mf) {
|
|
|
|
swprintf(text, L"LangString MSG_%04d ${LANG_%s} \"",
|
|
mf->code & 0xFFFF, uplang);
|
|
|
|
iptr = FindCopyTextForNsis(mf, &lang->msgs);
|
|
optr = text + wcslen(text);
|
|
while (*iptr) {
|
|
|
|
if ((*iptr == L'\r') || (*iptr == L'\n')) {
|
|
++iptr;
|
|
if ((*iptr == L'\r') || (*iptr == L'\n'))
|
|
++iptr;
|
|
wcscpy(optr, L"$\\n");
|
|
optr += 3;
|
|
|
|
} else {
|
|
*optr = *iptr;
|
|
++iptr;
|
|
++optr;
|
|
}
|
|
}
|
|
|
|
wcscpy(optr, L"\"\r\n");
|
|
|
|
Put(hFile, text);
|
|
}
|
|
}
|
|
|
|
me = List_Next(me);
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
lang = List_Next(lang);
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// main
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
int __cdecl main(int argc, char *argv[])
|
|
{
|
|
static const UCHAR *EnglishFileName = "Text-English-1033.txt";
|
|
int i;
|
|
LIST English;
|
|
LIST Langs;
|
|
|
|
ReadTextFile(EnglishFileName, &English);
|
|
DiscardOldText(EnglishFileName, &English);
|
|
|
|
List_Init(&Langs);
|
|
AddLanguage(&Langs, &English, EnglishFileName);
|
|
|
|
for (i = 1; i < argc; ++i) {
|
|
|
|
UCHAR *dot = strrchr(argv[i], '.');
|
|
if ((! dot) ||
|
|
_stricmp(dot, ".txt") != 0 ||
|
|
_strnicmp(argv[i], "Text-", 5) != 0)
|
|
{
|
|
fprintf(stderr, "ignoring file - %s\n", argv[i]);
|
|
continue;
|
|
}
|
|
|
|
if (_stricmp(argv[i], EnglishFileName) != 0) {
|
|
|
|
LIST Foreign;
|
|
ReadTextFile(argv[i], &Foreign);
|
|
DiscardOldText(argv[i], &Foreign);
|
|
CompareText(argv[i], &Foreign, &English);
|
|
|
|
AddLanguage(&Langs, &Foreign, argv[i]);
|
|
}
|
|
}
|
|
|
|
WriteMessageFile(&Langs);
|
|
WriteNsisFiles(&Langs);
|
|
|
|
return 0;
|
|
}
|