windows-nt/Source/XPSP1/NT/sdktools/mtscript/scripts/utilthrd.js
2020-09-26 16:20:57 +08:00

723 lines
19 KiB
JavaScript

// Set TestUnEval to 1 to enable uneval testing
@set @TestUnEval = 0
//---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995
//
// File: utilthrd.js
//
// Contents: Script which contains a bunch of utility functions used by
// other threads. It sets up function pointers on the PrivateData
// object which is how these functions can be utilized.
//
//----------------------------------------------------------------------------
Include('types.js');
Include('utils.js');
//Include('stopwatch.js');
// File System Object
var g_FSObj;
var g_DepthCounter = 0;
function utilthrd_js::OnScriptError(strFile, nLine, nChar, strText, sCode, strSource, strDescription)
{
return CommonOnScriptError("utilthrd_js", strFile, nLine, nChar, strText, sCode, strSource, strDescription);
}
function utilthrd_js::ScriptMain()
{
JAssert(typeof(PrivateData) == 'object', 'PrivateData not initialized!');
g_FSObj = new ActiveXObject("Scripting.FileSystemObject"); // Parse input Parameter List
PrivateData.objUtil.fnLoadXML = XMLLoad;
PrivateData.objUtil.fnUneval = uneval;
PrivateData.objUtil.fnDeleteFileNoThrow = DeleteFileNoThrow;
PrivateData.objUtil.fnMoveFileNoThrow = MoveFileNoThrow;
PrivateData.objUtil.fnCreateFolderNoThrow = CreateFolderNoThrow;
PrivateData.objUtil.fnDirScanNoThrow = DirScanNoThrow;
PrivateData.objUtil.fnCopyFileNoThrow = CopyFileNoThrow;
PrivateData.objUtil.fnCreateHistoriedFile = CreateHistoriedFile;
PrivateData.objUtil.fnCreateNumberedFile = CreateNumberedFile;
// PrivateData.objUtil.fnBeginWatch = BeginWatch;
// PrivateData.objUtil.fnDumpTimes = DumpTimes;
PrivateData.objUtil.fnMyEval = MyEval;
g_DepthCounter = 0;
SignalThreadSync('UtilityThreadReady');
CommonVersionCheck(/* $DROPVERSION: */ "V(########) F(!!!!!!!!!!!!!!)" /* $ */);
@if (@TestUnEval == 1)
LogMsg("TESTUNEVAL IS " + @TestUnEval);
var o1 = new Object();
var o2 = new Object();
var o3 = new Object();
var o4 = new Object();
var or1, or2, or3, or4;
o1.a = "hello 'there' bozo";
o2.a = 'hello "there" bozo';
o3.a = "hello \\there\\ bo \\\\ zo";
o4.a = "hello \n foo \r bar";
or1 = evtest(o1);
or2 = evtest(o2);
or3 = evtest(o3);
or4 = evtest(o4);
debugger;
@end
WaitForSync('UtilityThreadExit', 0);
}
@if (@TestUnEval == 1)
function evtest(obj)
{
var u;
var result;
try
{
u = PrivateData.objUtil.fnUneval(obj);
}
catch(ex)
{
debugger;
return;
}
try
{
result = MyEval(u);
}
catch(ex)
{
debugger;
return;
}
return result;
}
@end
//+---------------------------------------------------------------------------
//
// Function: uneval
//
// Synopsis: Takes an object and returns a string. The string can be given
// to the 'eval' function which will then return a copy of the
// object.
//
// Arguments: [obj] -- Object to 'stringize';
//
//----------------------------------------------------------------------------
function uneval(obj)
{
++g_DepthCounter;
try
{
if (!unevalInitialized)
{
initUneval();
unevalInitialized = true;
}
if (g_DepthCounter != 1)
LogMsg("Oops - uneval reentered!");
PrivateData.objUtil.unevalNextID = 0;
var s = 'var undefined;' + unevalDecl(obj) + unevalInst(obj);
unevalClear(obj);
}
catch(ex)
{
LogMsg("Uneval threw " + ex);
--g_DepthCounter;
throw(ex);
}
--g_DepthCounter;
return s;
}
//+---------------------------------------------------------------------------
//
// Function: XMLLoad
//
// Synopsis: Loads an XML file into the given object
//
// Arguments: [obj] -- Object to set values into (must be a 'new Object()')
// [url] -- URL of XML file
// [strSchema] -- If not null, the loaded XML file must reference
// a schema by the given name.
// [aStrMap] -- String mapping for substitutions
//
// Returns: 'ok', or a string giving error information
//
// Notes: To see what this function does, consider the following XML:
//
// <root>
// <Template Name="Template1" fBuild="false">
// <URL>foobar.xml</URL>
// </Template>
// <Template Name="Template2" fBuild="true">
// <URL>barfoo.xml</URL>
// </Template>
// </root>
//
// This function, given the above XML file, will make 'obj' look
// the same as if the following JScript had been written:
//
// obj.Template = new Array();
//
// obj.Template[0].Name = 'Template1';
// obj.Template[0].fBuild = false;
// obj.Template[0].URL = 'foobar.xml';
//
// obj.Template[1].Name = 'Template2';
// obj.Template[1].fBuild = true;
// obj.Template[1].URL = 'barfoo.xml';
//
//----------------------------------------------------------------------------
function XMLLoad(obj, url, strSchema, aStrMap)
{
var newurl;
var objXML = new ActiveXObject("Microsoft.XMLDOM");
var aNewStrMap = new Array();
newurl = url;
InitStringMaps(aStrMap, aNewStrMap);
try
{
var fRet;
var fDownloaded = false;
var strError;
objXML.async = false;
if (url.slice(0, 5) == 'XML: ')
{
fDownloaded = true;
newurl = CreateLocalTemplate(g_FSObj, url.slice(5), strSchema);
}
if (!newurl)
{
return 'Unable to make local copy of XML file';
}
fRet = objXML.load(newurl);
if (fDownloaded)
{
g_FSObj.DeleteFile(newurl, true);
}
if (!fRet)
{
with (objXML.parseError)
{
if (reason.length > 0)
{
strError = 'error loading XML file: ' + reason + '\n(' + newurl + ' line ' + line + ') :\n"' + srcText + '"';
if (errorCode == -2146697208) // W3_EVENT_CANNOT_CREATE_CLIENT_CONN
strError += "\nYou may have exceeded the maximum number of connections to this IIS server";
return strError;
}
else
return '1: could not load XML file: ' + newurl;
}
}
}
catch(ex)
{
return '2: could not load XML file ' + newurl + ': ' + ex;
}
if (!objXML.documentElement)
{
return '3: could not load XML file: ' + newurl;
}
try
{
ReadXMLNodesIntoObject(obj, objXML.documentElement, aNewStrMap, (strSchema != null));
}
catch(ex)
{
return "ReadXMLNodesIntoObject failed: " + ex;
}
return 'ok';
}
//+---------------------------------------------------------------------------
//
// Function: CreateLocalTemplate
//
// Synopsis: Creates a temporary file with the XML that was downloaded to
// us by the UI, and copies the schema file into the same
// directory so it can be referenced.
//
// Arguments: [objFS] -- FileSystem object
// [xml] -- XML given to us by the UI.
// [strSchema] -- Name of the schema file we should copy.
//
// Notes: Will throw exceptions on errors.
//
//----------------------------------------------------------------------------
function CreateLocalTemplate(objFS, xml, strSchema)
{
var tempdir = objFS.GetSpecialFolder(2 /* Temp Folder */).Path;
var tempfile = objFS.GetTempName();
var xmlfile;
xmlfile = objFS.CreateTextFile(tempdir + '\\' + tempfile, true);
xmlfile.Write(xml);
xmlfile.Close();
xmlfile = null;
DeleteFileNoThrow(tempdir + '\\' + strSchema, true);
objFS.CopyFile(ScriptPath + '\\' + strSchema, tempdir + '\\' + strSchema, true);
return tempdir + '\\' + tempfile;
}
//+---------------------------------------------------------------------------
//
// Function: ReadXMLNodesIntoObject
//
// Synopsis: Given an XML element node, read in the values and/or subobjects
// of that node into the given object.
//
// Arguments: [obj] -- Object to read values into
// [node] -- XML node that contains the data we're reading
// [aStrMap] -- String mapping for substitutions
// [fSchema] -- If true, all attributes and elements must have
// a matching schema definition.
//
//----------------------------------------------------------------------------
function ReadXMLNodesIntoObject(obj, node, aStrMap, fSchema)
{
var childnode;
var nodelist;
var attlist;
var att;
attlist = node.attributes;
for (att = attlist.nextNode();
att;
att = attlist.nextNode())
{
AddNodeToObject(att, obj, aStrMap, fSchema);
}
nodelist = node.childNodes;
for (childnode = nodelist.nextNode();
childnode;
childnode = nodelist.nextNode())
{
AddNodeToObject(childnode, obj, aStrMap, fSchema);
}
return true;
}
function AddNodeToObject(node, obj, aStrMap, fSchema)
{
var name;
var type;
var value;
var fIsArray;
var cChildren;
var define;
var subobj = obj;
// Do we recognize this node type? If not, skip it.
if ( node.nodeTypeString != 'element'
&& node.nodeTypeString != 'attribute')
{
return;
}
name = node.nodeName;
define = node.definition;
if (node.nodeTypeString == 'element')
{
type = node.getAttribute("type");
}
else
{
// We never want the type attribute to get put as a value on the obj.
// It should merely affect how we create it.
if (name == 'type')
{
return;
}
type = null;
}
if ( fSchema
&& name != 'xmlns'
&& ( !define
|| define.getAttribute("name") != name))
{
var err = new Error(-1, 'Element ' + name + ' has no type information! Verify that a schema was referenced.');
throw(err);
}
// If the only child of this node is a text node, then we just grab
// the value. Otherwise we walk its children (elements & attributes).
cChildren = node.childNodes.length + ((node.attributes) ? node.attributes.length : 0);
// Don't consider the type attribute to be a 'child'
if (type != null)
{
JAssert(cChildren > 0, 'Invalid number of children during XML parse!');
cChildren--;
}
if ( cChildren == 0
|| ( cChildren == 1
&& node.childNodes.length == 1
&& ( node.firstChild.nodeTypeString == 'text'
|| node.firstChild.nodeTypeString == 'cdatasection')))
{
value = node.nodeTypedValue;
if (typeof(value) == 'string')
{
// Make sure boolean values end up as booleans not strings
if (value.toLowerCase() == 'true')
{
value = true;
}
else if (value.toLowerCase() == 'false')
{
value = false;
}
else
{
value = value.Substitute(aStrMap);
}
}
if (obj[name] != null || (type && type == 'array'))
{
// The value of this field was already set. Turn it into an
// array.
EnsureArray(obj, name);
obj[name][obj[name].length] = value;
}
else
{
obj[name] = value;
}
}
else
{
fIsArray = false;
if (obj[name] != null || (type && type == 'array'))
{
// We've already encountered one of these. Make it into an array.
fIsArray = true;
EnsureArray(obj, name);
}
subobj = new Object();
if (fIsArray)
{
obj[name][obj[name].length] = subobj;
}
else
{
obj[name] = subobj;
}
}
if (node.nodeTypeString == 'element')
{
ReadXMLNodesIntoObject(subobj, node, aStrMap, fSchema);
}
}
// DeleteFileNoThrow(strFileName, fForce)
// Wrap the FSObj.DeleteFile call to prevent it from
// throwing its errors.
// This is good when you do not really care if the
// file you are trying to delete does not exist.
function DeleteFileNoThrow(strFileName, fForce)
{
try
{
LogMsg("DELETE FILE " + strFileName);
g_FSObj.DeleteFile(strFileName, true);
}
catch(ex)
{
return ex;
}
return null;
}
// MoveFileNoThrow(strSrc, strDst)
// Wrap the FSObj.MoveFile call to prevent it from
// throwing its errors.
function MoveFileNoThrow(strSrc, strDst)
{
try
{
LogMsg("Move file from " + strSrc + " to " + strDst);
g_FSObj.MoveFile(strSrc, strDst);
}
catch(ex)
{
LogMsg("MoveFile failed from " + strSrc + " to " + strDst + " " + ex);
return ex;
}
return null;
}
// CopyFileNoThrow(strSrc, strDst)
// Wrap the FSObj.CopyFile call to prevent it from
// throwing its errors.
function CopyFileNoThrow(strSrc, strDst)
{
try
{
LogMsg("COPY FILE from " + strSrc + " to " + strDst);
g_FSObj.CopyFile(strSrc, strDst, true);
}
catch(ex)
{
LogMsg("Copy failed from " + strSrc + " to " + strDst + " " + ex);
return ex;
}
return null;
}
// CreateFolderNoThrow(strSrc, strDst)
// Wrap the FSObj.MakeFolder call to prevent it from
// throwing its errors.
function CreateFolderNoThrow(strName)
{
try
{
LogMsg(strName);
g_FSObj.CreateFolder(strName);
}
catch(ex)
{
return ex;
}
return null;
}
// DirScanNoThrow(strDir)
// Wrap the FSObj.Directory scan functionality to prevent it from
// throwing its errors.
function DirScanNoThrow(strDir)
{
var aFiles = new Array();
try
{
LogMsg("DIRSCAN " + strDir);
var folder;
var fc;
folder = g_FSObj.GetFolder(strDir);
fc = new Enumerator(folder.files);
for (; !fc.atEnd(); fc.moveNext())
{
aFiles[aFiles.length] = fc.item().Name; // fc.item() returns entire path, fc.item().Name is just the filename
}
}
catch(ex)
{
aFiles.ex = ex;
}
return aFiles;
}
// CreateNumberedFileName(strFileName, nNumber, cDigits, strSeperator)
// Add a number to the supplied filename.
// Ensure that the number has cDigits digits.
// Prefix the number with strSeperator.
// For example:
// foo.txt --> foo_001.txt
function CreateNumberedFileName(strFileName, nNumber, cDigits, strSeperator)
{
var i;
var strNumber;
var strBase;
var strExt;
strNumber = PadDigits(nNumber, cDigits);
strSplit = strFileName.SplitFileName();
return strSplit[0] + strSplit[1] + strSeperator + strNumber + strSplit[2];
}
// CreateHistoriedFile(strBaseName, nLimit)
// Create a numbered history of files base on the
// supplied filename. For example, if you supply "log.txt"
// this function will renumber files
// log.txt -> log_01.txt
// log_01.txt -> log_02.txt
// log_10.txt -> deleted
//
function CreateHistoriedFile(strBaseName, nLimit)
{
var i;
var strNewName;
var strOldName;
var cDigits = 3;
var file;
var strTempDir;
try
{
strTempDir = g_FSObj.GetSpecialFolder(2).Path; // Temp Folder
strBaseName = strTempDir + '\\' + LocalMachine + '_' + strBaseName;
if (nLimit)
{
strNewName = CreateNumberedFileName(strBaseName, nLimit, cDigits, "_");
DeleteFileNoThrow(strNewName, true);
for(i = nLimit - 1; i > 0; --i)
{
strOldName = CreateNumberedFileName(strBaseName, i, cDigits, "_");
MoveFileNoThrow(strOldName, strNewName);
strNewName = strOldName;
}
MoveFileNoThrow(strBaseName, strNewName);
}
file = g_FSObj.CreateTextFile(strBaseName, true);
}
catch(ex)
{
LogMsg("an error occurred while executing 'CreateHistoriedFile('" + strBaseName + "') " + ex);
throw ex;
}
return file;
}
// CreateNumberedFile(strBaseName, nLimit)
// This is similar to CreateHistoriedFile(), but it uses a more
// robust naming scheme.
// Here the newly created file is numbered one greater than
// any other same named logfiles in the given directory.
// So, the first time you call this function the created file
// would be called
// log.01.txt
// The next time it will be
// log.02.txt
//
// Note: This function preceeds the number with a dot instead of
// underscore to prevent confusion with CreateHistoriedFile().
//
// Returns:
// array of TextString and the filename
function CreateNumberedFile(strBaseName, nLimit)
{
var i;
var strFileName;
var cDigits = 3;
var file;
try
{
// First, locate the highest index in the directory.
var folder;
var enumFolder;
var re;
var reResult;
var nLargestIndex = 0;
var strTempDir;
strTempDir = g_FSObj.GetSpecialFolder(2).Path; // Temp Folder
strBaseName = strTempDir + '\\' + LocalMachine + '_' + strBaseName;
strSplit = strBaseName.SplitFileName();
// Make an RE of the form: "/^filenamebase.([0-9]+]).ext$/i"
re = new RegExp("^" + g_FSObj.GetBaseName(strBaseName) + ".([0-9]+)" + strSplit[2] + "$", "i");
folder = g_FSObj.GetFolder(g_FSObj.GetParentFolderName(strBaseName));
enumFolder = new Enumerator(folder.files);
// First, scan for the largest index for the given filename
for (; !enumFolder.atEnd(); enumFolder.moveNext())
{
strFileName = enumFolder.item();
reResult = re.exec(strFileName.Name);
if (reResult != null)
{
if (Number(reResult[1]) > nLargestIndex)
nLargestIndex = Number(reResult[1]);
if (reResult[1].length > cDigits)
cDigits = reResult[1].length;
}
}
// Create a file with the next largest index
strFileName = CreateNumberedFileName(strBaseName, nLargestIndex + 1, cDigits, ".");
OUTPUTDEBUGSTRING("strFileName is " + strFileName);
file = g_FSObj.CreateTextFile(strFileName, true);
// Now attempt to delete any files older than "nLimit"
enumFolder = new Enumerator(folder.files);
for (; !enumFolder.atEnd(); enumFolder.moveNext())
{
strFileName = enumFolder.item();
reResult = re.exec(strFileName.Name);
if (reResult != null)
{
if (Number(reResult[1]) < nLargestIndex - nLimit)
{
OUTPUTDEBUGSTRING("Deleteing file " + strFileName.Name);
DeleteFileNoThrow(strFileName.Path, true);
}
}
}
}
catch(ex)
{
OUTPUTDEBUGSTRING("an error occurred while executing 'CreateNumberedFile('" + strBaseName + "') " + ex);
throw ex;
}
return [file, strBaseName];
}