You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
383 lines
10 KiB
383 lines
10 KiB
//
|
|
// File: tidy.js
|
|
//
|
|
// JScript file which walks a directory tree removing all read/write files
|
|
// from the tree. It will skip over files that are in the current SD
|
|
// changelist as well as copying questionable read/write files to the temp
|
|
// directory in case they were really needed.
|
|
//
|
|
// A "questionable" file is any read/write file that is not in an object
|
|
// directory (obj, objd) and is not clearly a generated file (such as a lib,
|
|
// pdb, etc).
|
|
//
|
|
// This script assumes the current directory is the root of whatever project
|
|
// you want tidied. This limitation can be removed once support for getting
|
|
// the local path rather than the depot path of opened files is into SD.
|
|
//
|
|
// Written by Lyle Corbin, 8/18/99
|
|
//
|
|
|
|
var g_FSObj = new ActiveXObject("Scripting.FileSystemObject");
|
|
var g_Shell = new ActiveXObject("WScript.Shell");
|
|
|
|
var g_fDebug = false;
|
|
var g_fVerbose = false;
|
|
var g_fForce = false;
|
|
var g_folderTemp;
|
|
var g_cKilledFiles = 0;
|
|
var g_cMovedFiles = 0;
|
|
var g_cKilledDirs = 0;
|
|
|
|
if (WScript.Arguments.length > 0)
|
|
{
|
|
var args = WScript.Arguments;
|
|
var i;
|
|
|
|
for (i = 0; i < args.length; i++)
|
|
{
|
|
switch (args(i).toLowerCase())
|
|
{
|
|
case '/?':
|
|
case '-?':
|
|
Usage();
|
|
WScript.Quit(0);
|
|
break;
|
|
|
|
case '/d':
|
|
case '-d':
|
|
g_fDebug = true;
|
|
break;
|
|
|
|
case '/v':
|
|
case '-v':
|
|
g_fVerbose = true;
|
|
break;
|
|
|
|
case '/f':
|
|
case '-f':
|
|
g_fForce = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
var tempdir = g_FSObj.GetSpecialFolder(2).Path; // Temp directory
|
|
var tempfile = tempdir + '\\' + g_FSObj.GetTempName();
|
|
|
|
// This limitation of running from the root of an SD client view can be removed
|
|
// once support for giving the local paths to opened files is available in SD.
|
|
if (!g_FSObj.FileExists('sd.ini'))
|
|
{
|
|
WScript.Echo('\nThis script must be run from the root directory of a SD project.\n\t(e.g. d:\\newnt\\base)');
|
|
Usage();
|
|
WScript.Quit(1);
|
|
}
|
|
|
|
if (g_fDebug)
|
|
{
|
|
WScript.Echo('\nDebug Mode: no actions will actually be performed!');
|
|
}
|
|
|
|
// First, call Source Depot and get a list of the "opened" files in this
|
|
// project.
|
|
|
|
var iRet = g_Shell.Run('cmd /c sd -s opened > ' + tempfile, 2, true);
|
|
|
|
if (iRet != 0)
|
|
{
|
|
WScript.Echo('Error: SD returned failure code ' + iRet);
|
|
WScript.Quit(1);
|
|
}
|
|
|
|
var file = g_FSObj.OpenTextFile(tempfile, 1, false); // Open for reading only
|
|
|
|
// Build a list of all the files that we should skip
|
|
|
|
var aOutFiles = new Array();
|
|
|
|
while (!file.AtEndOfStream)
|
|
{
|
|
line = file.ReadLine();
|
|
fields = line.split(' ');
|
|
|
|
if (fields.length == 0)
|
|
continue;
|
|
|
|
switch (fields[0].toLowerCase())
|
|
{
|
|
case 'info:':
|
|
filename = SDPathToLocalPath(fields[1]);
|
|
|
|
filename = g_FSObj.GetAbsolutePathName(filename);
|
|
|
|
// Do a sanity check on our parsing of the SD output file and verify
|
|
// that all the files it gave us actually exist (except for files
|
|
// marked for deletion).
|
|
if (fields[3] != 'delete' && !g_FSObj.FileExists(filename))
|
|
{
|
|
WScript.Echo('Error parsing SD output: file ' + filename + ' marked as open doesnt exist!');
|
|
WScript.Quit(1);
|
|
}
|
|
|
|
aOutFiles[filename.toLowerCase()] = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
file.Close();
|
|
|
|
if (g_fDebug)
|
|
{
|
|
WScript.Echo('Files opened in this project: ');
|
|
for (i in aOutFiles)
|
|
{
|
|
WScript.Echo('\t' + i);
|
|
}
|
|
}
|
|
|
|
g_FSObj.DeleteFile(tempfile);
|
|
|
|
if (!g_fForce)
|
|
{
|
|
// Make sure we have a place to archive old files
|
|
|
|
var tidyFolder;
|
|
|
|
tempdir += '\\tidy';
|
|
|
|
if (!g_FSObj.FolderExists(tempdir))
|
|
{
|
|
tidyFolder = g_FSObj.CreateFolder(tempdir);
|
|
}
|
|
else
|
|
{
|
|
tidyFolder = g_FSObj.GetFolder(tempdir);
|
|
}
|
|
|
|
tempdir += '\\' + g_FSObj.GetFolder('.').Name;
|
|
|
|
if (!g_FSObj.FolderExists(tempdir))
|
|
{
|
|
g_folderTemp = g_FSObj.CreateFolder(tempdir);
|
|
}
|
|
else
|
|
{
|
|
g_folderTemp = g_FSObj.GetFolder(tempdir);
|
|
}
|
|
}
|
|
|
|
DeleteReadWriteFiles('.');
|
|
|
|
if (g_fDebug)
|
|
{
|
|
WScript.Echo('\nDebug Mode: no actions were actually performed!');
|
|
}
|
|
|
|
WScript.Echo('\n' + g_cKilledFiles + ' files were deleted.');
|
|
|
|
if (g_cMovedFiles > 0)
|
|
{
|
|
WScript.Echo(g_cMovedFiles + ' files were moved to ' + g_folderTemp.Path);
|
|
WScript.Echo(' for safekeeping. If a file you need is gone, look there.');
|
|
}
|
|
|
|
WScript.Echo(g_cKilledDirs + ' object directories were deleted.');
|
|
|
|
WScript.Echo('');
|
|
|
|
WScript.Quit(0);
|
|
|
|
// ************************************************************************
|
|
|
|
function Usage()
|
|
{
|
|
WScript.Echo("\nUsage: tidy [/?dvf]\n");
|
|
WScript.Echo("\t/? - Show this usage.");
|
|
WScript.Echo("\t/d - Debug mode (don't delete anything).");
|
|
WScript.Echo("\t/v - Verbose mode");
|
|
WScript.Echo("\t/f - Don't save files not in an obj dir in %temp%\\tidy\\<projname>");
|
|
WScript.Echo("");
|
|
}
|
|
|
|
function SDPathToLocalPath(path)
|
|
{
|
|
var re = new RegExp("//depot/main/", "i")
|
|
var re2 = new RegExp("//depot/main/Root", "i")
|
|
|
|
var index = path.lastIndexOf('#');
|
|
|
|
if (index != -1)
|
|
{
|
|
path = path.slice(0, index);
|
|
}
|
|
|
|
path = path.replace(re2, '.\\');
|
|
|
|
return path.replace(re, '..\\');
|
|
}
|
|
|
|
function DeleteReadWriteFiles(path)
|
|
{
|
|
var sf;
|
|
var curdir;
|
|
var aSubFolders = new Array();
|
|
var i;
|
|
var fInObjDir = false;
|
|
|
|
try
|
|
{
|
|
curdir = g_FSObj.GetFolder(path);
|
|
}
|
|
catch(ex)
|
|
{
|
|
WScript.Echo('Error opening directory ' + path + ': ' + ex.description);
|
|
return;
|
|
}
|
|
|
|
// Are we an object directory? If so, just delete the whole thing.
|
|
if ( curdir.Name.toLowerCase() == 'obj'
|
|
|| curdir.Name.toLowerCase() == 'objd'
|
|
|| curdir.Name.toLowerCase() == 'objp') // Any other names?
|
|
{
|
|
g_cKilledDirs++;
|
|
|
|
if (g_fVerbose)
|
|
{
|
|
WScript.Echo('Delete directory ' + curdir.Path);
|
|
}
|
|
|
|
if (!g_fDebug)
|
|
{
|
|
try
|
|
{
|
|
curdir.Delete();
|
|
|
|
return;
|
|
}
|
|
catch(ex)
|
|
{
|
|
// Probably means there are read-only files in this directory.
|
|
// Go through subdirs and delete read/write files explicitely
|
|
// instead.
|
|
//
|
|
if (g_fVerbose)
|
|
{
|
|
WScript.Echo('Could not delete ' + curdir.Path + ': ' + ex.description);
|
|
}
|
|
|
|
fInObjDir = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// First, collect the list of subfolder names
|
|
sf = new Enumerator(curdir.SubFolders);
|
|
for (; !sf.atEnd(); sf.moveNext())
|
|
{
|
|
aSubFolders[aSubFolders.length] = sf.item().Path;
|
|
}
|
|
|
|
// Now, walk the files in this directory and clear out read/write files.
|
|
// We save these files in the temp directory (unless force is on)
|
|
|
|
sf = new Enumerator(curdir.Files);
|
|
for (; !sf.atEnd(); sf.moveNext())
|
|
{
|
|
// Is this a checked out file we want to skip?
|
|
// NOTE - THIS CHECK MUST BE TOTALLY RELIABLE, OR DATA COULD BE LOST!
|
|
if (aOutFiles[sf.item().Path.toLowerCase()])
|
|
{
|
|
WScript.Echo('Skipping opened file ' + sf.item().Path + '...');
|
|
continue;
|
|
}
|
|
|
|
if ((sf.item().Attributes & 0x1F) == 0) // A "normal" file
|
|
{
|
|
var ext = g_FSObj.GetExtensionName(sf.item().Name).toLowerCase();
|
|
|
|
if ( g_fForce
|
|
|| fInObjDir
|
|
|| ext == 'lib' // Don't archive files obviously not
|
|
|| ext == 'pdb' // user-created.
|
|
|| ext == 'bin'
|
|
|| ext == 'res'
|
|
|| ext == 'map'
|
|
|| ext == 'obj'
|
|
|| ext == 'pch')
|
|
{
|
|
g_cKilledFiles++;
|
|
|
|
if (g_fVerbose)
|
|
{
|
|
WScript.Echo('Delete file ' + sf.item().Path);
|
|
}
|
|
|
|
if (!g_fDebug)
|
|
{
|
|
try
|
|
{
|
|
sf.item().Delete();
|
|
}
|
|
catch(ex)
|
|
{
|
|
WScript.Echo('Could not delete ' + sf.item().Path + ': ' + ex.description);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_cMovedFiles++;
|
|
|
|
var dest = g_folderTemp.Path + '\\' + sf.item().Name;
|
|
|
|
if (g_fVerbose)
|
|
{
|
|
WScript.Echo('Move file ' + sf.item().Path + ' to ' + dest);
|
|
}
|
|
|
|
if (!g_fDebug)
|
|
{
|
|
try
|
|
{
|
|
if (g_FSObj.FileExists(dest))
|
|
{
|
|
g_FSObj.DeleteFile(dest);
|
|
}
|
|
|
|
sf.item().Move(dest);
|
|
}
|
|
catch(ex)
|
|
{
|
|
WScript.Echo('Could not move ' + sf.item().Path + ': ' + ex.description);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now, recurse into subdirectories
|
|
for (i = 0; i < aSubFolders.length; i++)
|
|
{
|
|
// Is the subdirectory the root of another SD project (determined by
|
|
// the existence of an sd.ini file)? If so, skip it.
|
|
// We only check this for directories that are immediate children
|
|
// of the starting directory.
|
|
|
|
if (path != '.' || !g_FSObj.FileExists(aSubFolders[i] + '\\sd.ini'))
|
|
{
|
|
DeleteReadWriteFiles(aSubFolders[i]);
|
|
}
|
|
else if (g_fVerbose || g_fDebug)
|
|
{
|
|
WScript.Echo('Directory ' + aSubFolders[i] + ' is the root of another project! Skipping...');
|
|
}
|
|
}
|
|
}
|