using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using CommandLine.Utility; using System.IO; using System.Diagnostics; namespace Snap2HTML { public partial class frmMain : Form { // This runs on a separate thread from the GUI private void backgroundWorker_DoWork( object sender, DoWorkEventArgs e ) { var settings = (SnapSettings)e.Argument; // Get files & folders var content = GetContent( settings, backgroundWorker ); if( backgroundWorker.CancellationPending ) { backgroundWorker.ReportProgress( 0, "User cancelled" ); return; } if( content == null ) { backgroundWorker.ReportProgress( 0, "Error reading source" ); return; } // Calculate some stats int totDirs = 0; int totFiles = 0; long totSize = 0; foreach( var folder in content ) { totDirs++; foreach( var file in folder.Files ) { totFiles++; totSize += Utils.ParseLong( file.GetProp( "Size" ) ); } } // Let's generate the output backgroundWorker.ReportProgress( 0, "Generating HTML file..." ); // Read template var sbTemplate = new StringBuilder(); try { using( System.IO.StreamReader reader = new System.IO.StreamReader( System.IO.Path.GetDirectoryName( Application.ExecutablePath ) + System.IO.Path.DirectorySeparatorChar + "template.html" ) ) { sbTemplate.Append(reader.ReadToEnd()); } } catch( System.Exception ex ) { MessageBox.Show( "Failed to open 'Template.html' for reading...", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error ); backgroundWorker.ReportProgress( 0, "An error occurred..." ); return; } // Build HTML sbTemplate.Replace( "[TITLE]", settings.title ); sbTemplate.Replace( "[APP LINK]", "http://www.rlvision.com" ); sbTemplate.Replace( "[APP NAME]", Application.ProductName ); sbTemplate.Replace( "[APP VER]", Application.ProductVersion.Split( '.' )[0] + "." + Application.ProductVersion.Split( '.' )[1] ); sbTemplate.Replace( "[GEN TIME]", DateTime.Now.ToString( "t" ) ); sbTemplate.Replace( "[GEN DATE]", DateTime.Now.ToString( "d" ) ); sbTemplate.Replace( "[NUM FILES]", totFiles.ToString() ); sbTemplate.Replace( "[NUM DIRS]", totDirs.ToString() ); sbTemplate.Replace( "[TOT SIZE]", totSize.ToString() ); if( chkLinkFiles.Checked ) { sbTemplate.Replace( "[LINK FILES]", "true" ); sbTemplate.Replace( "[LINK ROOT]", settings.linkRoot.Replace( @"\", "/" ) ); sbTemplate.Replace( "[SOURCE ROOT]", settings.rootFolder.Replace( @"\", "/" ) ); string link_root = settings.linkRoot.Replace( @"\", "/" ); if( Utils.IsWildcardMatch( @"?:/*", link_root, false ) ) // "file://" is needed in the browser if path begins with drive letter, else it should not be used { sbTemplate.Replace( "[LINK PROTOCOL]", @"file://" ); } else { sbTemplate.Replace( "[LINK PROTOCOL]", "" ); } } else { sbTemplate.Replace( "[LINK FILES]", "false" ); sbTemplate.Replace( "[LINK PROTOCOL]", "" ); sbTemplate.Replace( "[LINK ROOT]", "" ); sbTemplate.Replace( "[SOURCE ROOT]", settings.rootFolder.Replace( @"\", "/" ) ); } // Write output file try { using( System.IO.StreamWriter writer = new System.IO.StreamWriter( settings.outputFile ) ) { var template = sbTemplate.ToString(); var startOfData = template.IndexOf( "[DIR DATA]" ); writer.Write(template.Substring(0, startOfData)); BuildJavascriptContentArray( content, 0, writer, backgroundWorker ); if( backgroundWorker.CancellationPending ) { backgroundWorker.ReportProgress( 0, "User cancelled" ); return; } writer.Write( template.Substring( startOfData + 10) ); } sbTemplate = null; if( settings.openInBrowser ) { System.Diagnostics.Process.Start( settings.outputFile ); } } catch( Exception ex ) { MessageBox.Show( "Failed to open file for writing:\n\n" + ex, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error ); backgroundWorker.ReportProgress( 0, "An error occurred..." ); return; } // Ready! Cursor.Current = Cursors.Default; backgroundWorker.ReportProgress( 100, "Ready!" ); } // --- Helper functions (must be static to avoid thread problems) --- private static List GetContent( SnapSettings settings, BackgroundWorker bgWorker ) { var stopwatch = new Stopwatch(); stopwatch.Start(); var result = new List(); // Get all folders var dirs = new List(); dirs.Insert( 0, settings.rootFolder ); DirSearch( settings.rootFolder, dirs, settings.skipHiddenItems, settings.skipSystemItems, stopwatch, bgWorker ); dirs = Utils.SortDirList( dirs ); if( bgWorker.CancellationPending ) { return null; } var totFiles = 0; stopwatch.Restart(); try { string modified_date; string created_date; // Parse each folder for( int d = 0; d < dirs.Count; d++ ) { // Get folder properties var dirName = dirs[d]; var currentDir = new SnappedFolder( Path.GetFileName( dirName ), Path.GetDirectoryName( dirName ) ); if( dirName == Path.GetPathRoot( dirName ) ) { currentDir = new SnappedFolder( "", dirName ); } modified_date = ""; created_date = ""; try { modified_date = Utils.ToUnixTimestamp( System.IO.Directory.GetLastWriteTime( dirName ).ToLocalTime() ).ToString(); created_date = Utils.ToUnixTimestamp( System.IO.Directory.GetCreationTime( dirName ).ToLocalTime() ).ToString(); } catch( Exception ex ) { Console.WriteLine( "{0} Exception caught.", ex ); } currentDir.Properties.Add( "Modified", modified_date ); currentDir.Properties.Add( "Created", created_date ); // Get files in folder List files; try { files = new List( System.IO.Directory.GetFiles( dirName, "*.*", System.IO.SearchOption.TopDirectoryOnly ) ); } catch( Exception ex ) { Console.WriteLine( "{0} Exception caught.", ex ); result.Add( currentDir ); continue; } files.Sort(); // Get file properties foreach( string sFile in files ) { totFiles++; if(stopwatch.ElapsedMilliseconds >= 50) { bgWorker.ReportProgress( 0, "Reading files... " + totFiles + " (" + sFile + ")" ); stopwatch.Restart(); } if( bgWorker.CancellationPending ) { return null; } var currentFile = new SnappedFile( Path.GetFileName( sFile ) ); try { System.IO.FileInfo fi = new System.IO.FileInfo( sFile ); var isHidden = ( fi.Attributes & System.IO.FileAttributes.Hidden ) == System.IO.FileAttributes.Hidden; var isSystem = ( fi.Attributes & System.IO.FileAttributes.System ) == System.IO.FileAttributes.System; if( ( isHidden && settings.skipHiddenItems ) || ( isSystem && settings.skipSystemItems ) ) { continue; } currentFile.Properties.Add( "Size", fi.Length.ToString() ); modified_date = "-"; created_date = "-"; try { modified_date = Utils.ToUnixTimestamp( fi.LastWriteTime.ToLocalTime() ).ToString(); created_date = Utils.ToUnixTimestamp( fi.CreationTime.ToLocalTime() ).ToString(); } catch( Exception ex ) { Console.WriteLine( "{0} Exception caught.", ex ); } currentFile.Properties.Add( "Modified", modified_date ); currentFile.Properties.Add( "Created", created_date ); } catch( Exception ex ) { Console.WriteLine( "{0} Exception caught.", ex ); } currentDir.Files.Add( currentFile ); } result.Add( currentDir ); } } catch( System.Exception ex ) { Console.WriteLine( "{0} exception caught: {1}", ex, ex.Message ); } return result; } // Recursive function to get all folders and subfolders of given path path private static void DirSearch( string sDir, List lstDirs, bool skipHidden, bool skipSystem, Stopwatch stopwatch, BackgroundWorker backgroundWorker ) { if( backgroundWorker.CancellationPending ) return; try { foreach( string d in System.IO.Directory.GetDirectories( sDir ) ) { bool includeThisFolder = true; //if( d.ToUpper().EndsWith( "SYSTEM VOLUME INFORMATION" ) ) includeThisFolder = false; // exclude folders that have the system or hidden attr set (if required) if( skipHidden || skipSystem ) { var attr = new DirectoryInfo( d ).Attributes; if( skipHidden ) { if( ( attr & FileAttributes.Hidden ) == FileAttributes.Hidden ) { includeThisFolder = false; } } if( skipSystem ) { if( ( attr & FileAttributes.System ) == FileAttributes.System ) { includeThisFolder = false; } } } if( includeThisFolder ) { lstDirs.Add( d ); if( stopwatch.ElapsedMilliseconds >= 50 ) { backgroundWorker.ReportProgress( 0, "Getting folders... " + lstDirs.Count + " (" + d + ")" ); stopwatch.Restart(); } DirSearch( d, lstDirs, skipHidden, skipSystem, stopwatch, backgroundWorker ); } } } catch( System.Exception ex ) { Console.WriteLine( "ERROR in DirSearch():" + ex.Message ); } } private static void BuildJavascriptContentArray( List content, int startIndex, StreamWriter writer, BackgroundWorker bgWorker ) { // Data format: // Each index in "dirs" array is an array representing a directory: // First item in array: "directory path*always 0*directory modified date" // Note that forward slashes are used instead of (Windows style) backslashes // Then, for each each file in the directory: "filename*size of file*file modified date" // Second to last item in array tells the total size of directory content // Last item in array refrences IDs to all subdirectories of this dir (if any). // ID is the item index in dirs array. // Note: Modified date is in UNIX format var lineBreakSymbol = ""; // Could be set to \n to make the html output more readable, at the expense of increased size // Assign an ID to each folder. This is equal to the index in the JS data array var dirIndexes = new Dictionary(); for( var i = 0; i < content.Count; i++ ) { dirIndexes.Add( content[i].FullPath, ( i + startIndex ).ToString() ); } // Build a lookup table with subfolder IDs for each folder var subdirs = new Dictionary>(); foreach( var dir in content ) { subdirs.Add( dir.FullPath, new List() ); } if( !subdirs.ContainsKey( content[0].Path ) && content[0].Name != "" ) { subdirs.Add( content[0].Path, new List() ); } foreach( var dir in content ) { if( dir.Name != "" ) { try { subdirs[dir.Path].Add( dirIndexes[dir.FullPath] ); } catch( Exception ex ) { // orphan file or folder? } } } // Generate the data array var result = new StringBuilder(); foreach( var currentDir in content ) { result.Append( "D.p([" + lineBreakSymbol ); var sDirWithForwardSlash = currentDir.FullPath.Replace( @"\", "/" ); result.Append( "\"" ).Append( Utils.MakeCleanJsString( sDirWithForwardSlash ) ).Append( "*" ).Append( "0" ).Append( "*" ).Append( currentDir.GetProp( "Modified" ) ).Append( "\"," + lineBreakSymbol ); long dirSize = 0; foreach( var currentFile in currentDir.Files ) { result.Append( "\"" ).Append( Utils.MakeCleanJsString( currentFile.Name ) ).Append( "*" ).Append( currentFile.GetProp( "Size" ) ).Append( "*" ).Append( currentFile.GetProp( "Modified" ) ).Append( "\"," + lineBreakSymbol ); dirSize += Utils.ParseLong( currentFile.GetProp( "Size" ) ); } // Add total dir size result.Append( "" ).Append( dirSize ).Append( "," + lineBreakSymbol ); // Add reference to subdirs result.Append( "\"" ).Append( String.Join( "*", subdirs[currentDir.FullPath].ToArray() ) ).Append( "\"" + lineBreakSymbol ); // subdirs // Finalize result.Append( "])" ); result.Append( "\n" ); // Write result in chunks to limit memory consumtion if( result.Length > 10240 ) { writer.Write( result.ToString() ); result.Clear(); } if( bgWorker.CancellationPending ) { return; } } writer.Write( result.ToString() ); return; } } }