// 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: // // // // // // // 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]; }