(.+)<\/ProjectName>/);
if (filename) {
//return only first item of regexp
return filename[1];
} else {
//There is no specified ProjectName, thus use filename without extension as our langpack name
return FSO.GetBaseName(plugin_project_file);
}
}
//Get MUUID of plugin from source file.
function GetMUUID(folder, array) {
//first, find necessary file list, we are looking for UNICODE_AWARE function, usually this function are in *.cpp, sometimes in *.c and *.h
//init fillelist array
var curfile,
muuidfilelist = [],
muuid = "",
find = /()/,
i = 0,
values = [],
allstrings = "",
string = "",
vals = "";
//search for files in "folder" by mask, put result into muuidfilelist
FindFiles(folder, "\\.cpp$|\\.c$|\\.h$", muuidfilelist);
//now we have files, let's put them to Enumerator
var filesenum = new Enumerator(muuidfilelist);
//cycle through file list and lookup each file for UNICODE_AWARE
while (!filesenum.atEnd()) {
//curfile is our current file in files enumerator
curfile = filesenum.item();
//this is a regexp to search UNICODE_AWARE
find = /(?:UNICODE_AWARE(?:\s*?\|\s*?STATIC_PLUGIN)?,[\s\S]*?\{)(.+?)(?=\}\s{0,2}\})/g;
//read file fully into var "allstrings"
allstrings = ReadFile(curfile);
//search regexp in "allstrings" and put results into var "string"
string = find.exec(allstrings);
//if current file have found UNICODE_AWARE, var "string" exists, so parse it.
if (string) {
//remove spaces, "0x" and "{", in second [1] item of array "string". RegExp "find" have subregexp (.+?), which are our MUUID in "string[1]"
vals = string[1].replace(/0x|\{|\x20/g, "");
//now split values of muuid in "vals" by "," into array "values"
values = vals.split(",");
//we get array of values, if length of this array not equal to 12 values (starting from zero, thus length is 11), that's mean we found something else, not MUUID. Log it and quit, if length is 12, so check it and generate MUUID
if (values.length == 11) {
//now check, is there some values, which have omitted zero after 0x, like in alarms: " 0x4dd7762b, 0xd612, 0x4f84, { 0xaa, 0x86, 0x(no_zero_here_)6, 0x8f, 0x17, 0x85, 0x9b, 0x6d}"
//first value in values have to be 8 bytes, while length less than 8, add leading "0" to values[0],
while (values[0].length < 8) {
values[0] = "0" + values[0];
}
//next two values have to be 4 bytes, adding leading zeros, while length less than 4.
for (i = 1; i <= 2; i++) {
while (values[i].length < 4) {
values[i] = "0" + values[i];
}
}
//other values have to be 2 bytes, same as above, adding zeros
for (i = 3; i <= 10; i++) {
while (values[i].length < 2) {
values[i] = "0" + values[i];
}
}
//Push to array founded #muuid
muuid = "#muuid {" + values[0] + "-" + values[1] + "-" + values[2] + "-" + values[3] + values[4] + "-" + values[5] + values[6] + values[7] + values[8] + values[9] + values[10] + "}";
}
}
//moving to next file
filesenum.moveNext();
}
//if we didn't find muuid, put alarm into "muuid"
if (!muuid) {
muuid = ";#muuid for " + plugin + " not found, please specify manually!";
}
//output result into array
array.push(muuid);
//log output
if (log) {
WScript.Echo(muuid);
}
}
//read text file, removing all commented text for further processing
function ReadFile(file) {
//If file zero size, return;
if (FSO.GetFile(file).Size === 0) return;
//reading current file
var file_stream = FSO.GetFile(file).OpenAsTextStream(ForReading, TristateUseDefault);
//read file fully into var
var allstrings = file_stream.ReadAll();
//remove all comments. The text starting with \\
//(but not with ":\\" it's a link like https://miranda-ng.org/
//and ")//" -there is one comment right after needed string)
//and remove multi-line comments, started with /* and ended with */
var text = allstrings.replace(/(?:[^\):])(\/{2}.+?(?=$))|(\s\/\*[\S\s]+?\*\/)/mg, ".");
//close file
file_stream.Close();
return text;
}
//Parsing filelist into stringsarray by parsefunction (ParseSourceFile OR ParseRCFile)
function ParseFiles(filelist, stringsarray, parsefunction) {
var current_strings = 0,
crap_strings = 0,
curfile = {},
filetext = "",
curfilepath = "";
//create enumerator filesenum from filelist
var filesenum = new Enumerator(filelist);
//cycle through file list
while (!filesenum.atEnd()) {
//record into current_strings current length of stringsarray
current_strings = stringsarray.length;
//record into crap_strings current length of crap array
crap_strings = crap.length;
//curfile is our current file in files enumerator
curfile = filesenum.item();
//read file (filtering comments) into filetext
filetext = ReadFile(curfile);
//now apply a parsing function to current filetext, and put result into stringsarray
parsefunction(filetext, stringsarray);
//string variable to cut out a trunkPath from absolute path
curfilepath = curfile.Path;
//if after parsing file our stringsarray length greater then var "current_strings", so parsed file return some strings. Thus, we need add a comment with filename
if (stringsarray.length > current_strings) {
stringsarray.splice(current_strings, 0, ";file " + curfilepath.substring(trunkPath.length));
}
//do the same for crap array, add a ;file +relative path to file with crap
if (crap.length > crap_strings) {
crap.splice(crap_strings, 0, ";file " + curfilepath.substring(trunkPath.length));
}
//move to next file
filesenum.moveNext();
}
}
//*.RC files line-by-line parser for RC_File, return result into "array"
function ParseRCFile(FileTextVar, array) {
var string = "",
onestring = "";
var find = /^(?!\/{1,2})\s*(CONTROL|(?:DEF)?PUSHBUTTON|[LRC]TEXT|(?:AUTO)?RADIOBUTTON|GROUPBOX|(?:AUTO)?CHECKBOX|CAPTION|MENUITEM|POPUP)\s*"((?:(?:""[^"]+?"")*[^"]*?)*)"\s*?(,|$|\\)/mgi;
//now make a job, till end of matching regexp
while ((string = find.exec(FileTextVar)) !== null) {
// check for some garbage like "List1","Tab1" etc. in *.rc files, we do not need this.
onestring = string[2].replace(/^(((List|Tab|Tree|Spin|Custom|Slider|DateTimePicker|Radio|Check|HotKey|Progress)\d)|(whiterect|IndSndList|&?[Oo][Kk]|AOL|APOP|BBS|Bing|CTCP|DCC|Foodnetwork|Google|Google Talk|GPG|Hotmail|ICQ|ICQ Corp|ID|IP|ISDN|iTunes|Jabber|JID|Miranda|Miranda NG|mRadio|MSN|NickServ|OSD|OTR|PCS|PGP|S.ms|SMS|SSL|Steam|Steam Guard|Tox|Twitter|Yahoo|Winamp \(\*\)|Windows|X400|&\w)|(%.(.*%)?))$/g, "");
// ignore some popup menu craps
if (string[1] == "POPUP" && onestring.match(/^([a-zA-Z ]*(menu|context|popup(?!s)))|([A-Z][a-z]+([A-Z][a-z]*)+)|(new item)$/g)) {
continue;
}
//if there is double "", replace with single one
onestring = onestring.replace(/\"{2}/g, "\"");
//check result. If it does not match [a-z] (no any letter in results, such as "..." or "->") it's a crap, break further actions.
if (!onestring.match(/[a-z]/i)) {
onestring = "";
}
//if still something in onestring, push to array
if (onestring) {
array.push("[" + onestring + "]");
}
}
}
//Source files C++ (*.h,*.c,*.cpp) and *.pas,*.dpr,*.inc (Pascal) multiline parser for translations using LPGEN() LPGENT() TranslateT() Translate() _T() TranslateW()
function ParseSourceFile(FileTextVar, array) {
var string = "",
onestring = "",
trimedstring = "",
noslashstring = "",
nofirstlaststring = "",
stringtolangpack = "",
clearstring = "";
//not store ?: functions LPGEN or LPGENT? or Translate(T or W) or _T, than any unnecessary space \s, than not stored ?: "(" followed by ' or " (stored and used as \1) than \S\s - magic with multiline capture, ending with not stored ?= \1 (we get " or ' after "("), than none or few spaces \x20 followed by )/m=multiline g=global
//var find= /(?:LPGENT?|Translate[TW]?|_T)(?:\s*?\(\s*?L?\s*)(['"])([\S\s]*?)(?=\1,?\x20*?(?:tmp)?\))/mg;
//comment previous line and uncomment following line to output templates without _T() function in source files. Too many garbage from _T()..
var find = /(?:LPGEN[TW]?|Translate[TW]?)(?:\s*?\(\s*?L?\s*)((?:(?:"[^"\\]*(?:\\[\S\s][^"\\]*)*")\s*)*)(?:\s*?,?\s*?(?:tmp)?\))/gm;
//now make a job, till end of matching regexp
while ((string = find.exec(FileTextVar)) !== null) {
//first, init empty var
//replace newlines and all spaces and tabs between two pairs of " or ' with the void string ("") in first [1] subregexp ([\S\s]*?), and Delphi newlines "'#13#10+" replace
onestring = string[1].replace(/["']?(?:\#13\#10)*?\\?\r*\n(?:(?:\x20|\t)*['"])?/g, "");
//trim single-line whitespaces - multi-line parsing catches whitespaces after last " in single-line case
trimedstring = onestring.replace(/[\s]*$/g, "");
//remove trailing slash from the string. This is a tree item, slash is a crap :)
noslashstring = trimedstring.replace(/\/(?=$)/g, "");
//remove first and last "
nofirstlaststring = noslashstring.slice(1, -1);
//remove escape slashes before ' and "
stringtolangpack = nofirstlaststring.replace(/\\(")/g, "$1");
///if our string still exist, and length at least one symbol
if (stringtolangpack.length > 0) {
//brand new _T() crap filtering engine :)
clearstring = filter_T(stringtolangpack);
//finally put string into array including cover brackets []
if (clearstring) {
array.push("[" + clearstring + "]");
}
}
}
}
//filter _T() function results
function filter_T(string) {
//filter for exact matched strings
var filter1 = /^(&?[Oo][Kk]|AOL|APOP|BBS|Bing|CTCP|DCC|Foodnetwork|Google|GPG|Hotmail|ICQ|ICQ Corp|ID|IP|ISDN|iTunes|Jabber|JID|Miranda|MirandaG15|Miranda NG|mRadio|MSN|NickServ|OSD|OTR|PCS|PGP|SMS|SSL|SteamID|Steam Guard|Tox|Twitter|Yahoo|Winamp \(\*\)|Windows|X400)$/g;
//filter string starting from following words
var filter2 = /^(SOFTWARE\\|SYSTEM\\|http|ftp|UTF-|utf-|TEXT|EXE|exe|txt|css|html|dat[^a]|txt|MS\x20|CLVM|TM_|CLCB|CLSID|CLUI|HKEY_|MButton|BUTTON|WindowClass|MHeader|RichEdit|RICHEDIT|STATIC|EDIT|CList|listbox|LISTBOX|combobox|COMBOBOX|TitleB|std\w|iso-|windows-||\w\\\w|urn\:|<\?xml|<\!|h\d|\.!\.).*$/g;
//filter string ending with following words
var filter3 = /^.+(001|\/value|\*!\*|=)$/g;
//filter from Kildor
var filter4 = /^((d\s\w)|\[\/?(\w|url|img|size|quote|color)(=\w*)?\]?|(\\\w)|(%\w+%)|(([\w-]+\.)*\.(\w{2,4}|travel|museum|xn--\w+))|\W|\s|\d)+$/gi;
//filter from Kildor for remove filenames and paths.
//var filter5=/^[\w_:%.\\\/*-]+\.\w+$/g;
//apply filters to our string
var test1 = filter1.test(string);
var test2 = filter2.test(string);
var test3 = filter3.test(string);
var test4 = filter4.test(string);
//test5=filter5.test(string);
//if match (test1) first filter and NOT match other tests, thus string are good, return this string back.
//if (test1 && !test2 && !test3 && !test4 && !test5) {
//if (!test1 && !test2 && !test3 && !test4 && !test5) {
if (!test1 && !test2 && !test3 && !test4) {
return string;
} else {
//in other case, string is a garbage, put into crap array.
crap.push(string);
}
return;
}
function ReadWholeFile(path, codepage) {
if (codepage === undefined) {
codepage = "utf-8";
}
var adTypeText = 2;
var bs = WScript.CreateObject("ADODB.Stream");
bs.Type = adTypeText;
bs.CharSet = codepage;
bs.Open();
bs.LoadFromFile(path);
var text = bs.ReadText;
//remove all comments. The text starting with \\ (but not with ":\\" it's a links like https://miranda-ng.org/ and ")//" -there is one comment right after needed string)
//and remove multi-line comments, started with /* and ended with */
text = text.replace(/(?:[^\):])(\/{2}.+?(?=$))|(\s\/\*[\S\s]+?\*\/)/mg, ".");
bs.Close();
return text;
}
//Parse Version.h file to get one translated string from "Description" and make a plugin template header.
function ParseVersion_h(pluginfolder, array) {
//cleanup var
var VersionFile,
allstrings = "";
//Let's try default locations of version.h file;
//Check pluginfolder root.
if (FSO.FileExists(FSO.BuildPath(pluginfolder, "version.h"))) {
VersionFile = FSO.BuildPath(pluginfolder, "version.h");
}
//Check src\include subfolder of plugin root folder
if (FSO.FileExists(FSO.BuildPath(pluginfolder, "src\\include\\version.h"))) {
VersionFile = FSO.BuildPath(pluginfolder, "src\\include\\version.h");
}
//Check .\src subfolder
if (FSO.FileExists(FSO.BuildPath(pluginfolder, "src\\version.h"))) {
VersionFile = FSO.BuildPath(pluginfolder, "src\\version.h");
}
//If we still not found version.h, return
if (!VersionFile) return;
//read file fully into var allstrings
allstrings = ReadWholeFile(VersionFile);
//define RegExp for defines.
var filename = /(?:#define\s+_*?FILENAME\s+")(.+)(?=")/m;
var pluginname = /(?:#define\s+_*?PLUG(?:IN)?_?NAME\s+")(.+)(?=")/i;
var author = /(?:#define\s+_*?(?:PLUGIN_?)?AUTHORS?\s+")(.+)(?=")/i;
var MAJOR_VERSION = /(?:#define\s+_*?(?:MAJOR_VERSION|VER_MAJOR)\s+)(\d+)/i;
var MINOR_VERSION = /(?:#define\s+_*?(?:MINOR_VERSION|VER_MINOR)\s+)(\d+)/i;
var RELEASE_NUM = /(?:#define\s+_*?(?:RELEASE_NUM|VER_REVISION|VER_RELEASE)\s+)(\d+)/i;
var BUILD_NUM = /(?:#define\s+_*?(?:BUILD_NUM|VER_BUILD)\s+)(\d+)/i;
var VERSION_STRING = /(?:#define\s+_*?VERSION_STRING\s+")([\d\.]+)\"/i;
var description = /(?:#define\s+_*?(?:PLUGIN_|MTEXT_)?DESC(?:RIPTION|_STRING)?\s+")(.+)(?=")/i;
//exec RegExps
filename = filename.exec(allstrings);
pluginname = pluginname.exec(allstrings);
MAJOR_VERSION = MAJOR_VERSION.exec(allstrings);
MINOR_VERSION = MINOR_VERSION.exec(allstrings);
RELEASE_NUM = RELEASE_NUM.exec(allstrings);
BUILD_NUM = BUILD_NUM.exec(allstrings);
VERSION_STRING = VERSION_STRING.exec(allstrings);
author = author.exec(allstrings);
description = description.exec(allstrings);
//add a header start mark
array.push(";============================================================");
//push results of regexp vars into array
if (filename) {
array.push("; File: " + filename[1]);
} else {
array.push("; File: " + plugin + ".dll");
}
if (pluginname) {
array.push("; Plugin: " + pluginname[1]);
} else {
array.push("; Plugin: " + plugin);
}
if (VERSION_STRING) {
array.push("; Version: " + VERSION_STRING[1]);
}
if (MAJOR_VERSION && !VERSION_STRING) {
array.push("; Version: " + MAJOR_VERSION[1] + "." + MINOR_VERSION[1] + "." + RELEASE_NUM[1] + "." + BUILD_NUM[1]);
}
if (!MAJOR_VERSION && !VERSION_STRING) {
array.push("; Version: x.x.x.x");
}
if (author) {
array.push("; Authors: " + fixHexa(author[1]));
} else {
array.push("; Authors: ");
}
//add a header end mark
array.push(";============================================================");
if (description) {
array.push("[" + description[1] + "]");
}
}
//Replaces \x?? hex codes with their char representation
function fixHexa(string) {
return string.replace(/\\x([a-fA-F0-9]{2})" "/g, function () {
return String.fromCharCode(parseInt(arguments[1], 16));
});
}
//Removes duplicates, not mine, found at http://dreaminginjavascript.wordpress.com/2008/08/22/eliminating-duplicates/
function eliminateDuplicates(arr) {
var i,
len = arr.length,
out = [],
obj = {};
for (i = 0; i < len; i++) {
obj[arr[i]] = 0;
}
for (i in obj) {
out.push(i);
}
return out;
}
//Output array of strings into file
function WriteToFile(array, langpack) {
var i = 0,
len = 0;
//Create file, overwrite if exists
var langpackfile = FSO.CreateTextFile(langpack, overwritefile, unicode);
//Finally, write strings from array to file
len = array.length - 1;
for (i = 0; i <= len; i++) {
langpackfile.WriteLine(array[i]);
}
//Close file
langpackfile.Close();
}
//Write UTF-8 file
function WriteToUnicodeFile(array, langpack) {
var i = 0,
len = 0;
stream.Open();
len = array.length - 1;
for (i = 0; i <= len; i++) {
stream.WriteText(array[i] + "\r\n");
}
stream.SaveToFile(langpack, 2);
stream.Close();
}
//Write file as UTF-8 without BOM
function WriteToUnicodeFileNoBOM(array, filename) {
var UTFStream = WScript.CreateObject("ADODB.Stream");
var BinaryStream = WScript.CreateObject("ADODB.Stream");
var i = 0,
len = 0,
adTypeBinary = 1,
adTypeText = 2,
adModeReadWrite = 3,
adSaveCreateOverWrite = 2;
UTFStream.Type = adTypeText;
UTFStream.Mode = adModeReadWrite;
UTFStream.Charset = "utf-8";
UTFStream.Open();
len = array.length - 1;
for (i = 0; i <= len; i++) {
UTFStream.WriteText(array[i] + "\r\n");
}
UTFStream.Position = 3; // skip BOM
BinaryStream.Type = adTypeBinary;
BinaryStream.Mode = adModeReadWrite;
BinaryStream.Open();
// Strips BOM (first 3 bytes)
UTFStream.CopyTo(BinaryStream);
BinaryStream.SaveToFile(filename, adSaveCreateOverWrite);
BinaryStream.Flush();
BinaryStream.Close();
UTFStream.Close();
}