// // This JavaScript program copies an entire directory with // all of its files and sub-directories to a new location. // Files that already exist in the new location and have the // same size and automatically skipped. If they have a // smaller size, they will be overwritten with the // larger file. // // Written by Zsolt N. Perry in April 2023, Pensacola, Fla. // For questions, comments, feature requests, or // bug reports, write to zsnp@juno.com. // // This JavaScript program is freeware. If you want // to incorporate it or parts of it into your program, // just copy and paste whatever you need! There is no // need to ask for permission. This software is // distributed "AS IS." There is no warranty of any kind. // The author will not be held liable for any loss // resulting from the use or misuse of this software. // ////////////////////////////////////////////////// FROM = "C:\\HOME\\DOCUMENTS"; // Copy all files from this directory TO = "D:\\HOME\\DOCUMENTS"; // to this directory OUTPUT_FILE = "C:\\TEMP\\COULDNT_COPY_THESE.TXT"; // Make sure this directory exists for the output file. // Check this file for a list of files that couldn't be copied at the end. try { WshShell = new ActiveXObject("WScript.Shell"); FSO = new ActiveXObject('Scripting.FileSystemObject'); } catch(e) { EXIT("Cannot access File System."); } COPYCOUNT = 0; CANNOT_COPY = []; OpenStatusWindow(); ClearStatus(); PostStatus("Preparing to copy..."); CopyDIR(FROM); CreateFile(OUTPUT_FILE, CANNOT_COPY.join("\r\n"), 1); ALERT("DONE.\n\n" + Commify(COPYCOUNT) + " files were copied."); CloseStatusWindow(); EXIT(); ////////////////////////////////////////////////// // File | v2023.4.29 // This function reads the contents of an entire // directory with all its sub-directories and // copies each file that does not exist // yet in the destination. // // Usage: ARRAY = CopyDIR(PATH) // function CopyDIR(PATH) { var Z, F, FC, File, FullName, NEWNAME, NEWFILE, NAMEONLY; // Build target filename: PATH += ""; NEWNAME = TO + "\\" + PATH.slice(FROM.length); NEWNAME = NEWNAME.replace(/\\\\/g, "\\"); // Replace double backslash NEWNAME = NEWNAME.replace(/[\\ ]+$/, ""); // Remove last backslash CreateDirectory(NEWNAME); try { F = FSO.GetFolder(PATH); } catch (e) { return []; } for (FC = new Enumerator(F.files); !FC.atEnd(); FC.moveNext()) { FullName = FC.item() + ""; // Read files... NEWFILE = TO + "\\" + FullName.slice(FROM.length); NEWFILE = NEWFILE.replace(/\\\\/g, "\\"); // Replace double backslash NEWFILE = NEWFILE.replace(/[\\ ]+$/, ""); // Remove last backslash if (FileSize(FullName) != FileSize(NEWFILE)) // Copy if the two files are not the same { NAMEONLY = cut(FullName, "\\", 0x10101); if (IsValidFileName(NAMEONLY) == 1) CopyFile(FullName, NEWFILE); else CANNOT_COPY.push(FullName + " >|> " + NEWFILE); } ClearStatus(); PostStatus("Reading directory..."); } // Look into sub-directories... for (FC = new Enumerator(F.SubFolders); !FC.atEnd(); FC.moveNext()) CopyDIR(FC.item()); } ////////////////////////////////////////////////// // File | v2023.5.3 // This function returns 1 if the file name given // is made up of valid characters. It will return // 2 if the file name contains characters between // 127 and 255. This is still valid, but it's using // the extended ASCII character set. The function // will return 4 if the file name contains any // Unicode characters. These values can add up, so it // is possible the function will return 7, which would // indicate that the file name is valid and it contains // both Unicode characters and extended characters. // // If the file name is empty or if the file name is // longer than 256 characters or if the file name contains // ? * < > | then this function will return zero. // // NOTE: When calling this function, make sure that you are // providing the FILE NAME ONLY without the path! // If you include the path, the return value might be incorrect. // For example, if the path and the file name together are // longer than 256 bytes, the function will say that // it's an invalid file name! // // Usage: INTEGER = IsValidFileName(FILENAME) // function IsValidFileName(FILENAME) { FILENAME += ""; var RESULT = 1; var i, c, L = FILENAME.length; // Total length of file name cannot be zero or longer than 256 bytes. if (L == 0 || L > 256) return 0; for (i = 0; i < L; i++) { c = FILENAME.charCodeAt(i); if (c == 9) continue; // TAB is okay. if (c > 255) RESULT |= 4; // Check for Unicode characters. else if (c > 126) RESULT |= 2; // Check for extended ASCII characters. else if (c < 32 || c == 42 || c == 60 || c == 62 || c == 63 || c == 124) return 0; // Check for illegal characters. } // Make sure the file is made up of something // other than just a bunch of periods. return (/[^\.]/.test(FILENAME)) ? RESULT : 0; } ////////////////////////////////////////////////// // File | v2023.4.29 // This function copies a file in binary mode. // Returns 1 on success or zero if an error occurred. // Usage: STATUS = CopyFile(FROM, TO) // function CopyFile(SRC, DST) { var i, FILE, FOLDER, TREE, STATUS; // Copying... ClearStatus(); PostStatus("Copying:\n" + SRC + " (" + Commify(FileSize(SRC)) + " bytes)\n\nto\n\n" + DST); //ALERT("ABOUT TO COPY: \n" + SRC + "\n\nTO:\n\n" + DST); if (DST.indexOf("\\") >= 0) { FOLDER = DST.replace(/\\[^\\]*$/, ''); // Remove whatever comes after the last backslash try { STATUS = CreateDirectory(FOLDER); if (STATUS == 0) EXIT("Could not create directory: " + FOLDER); } catch (e) { EXIT("Failed to create folder for:\n\n" + DST); } } // Copy file... try { FILE = FSO.GetFile(SRC); FILE.Copy(DST); COPYCOUNT++; return 1; } catch (e) {} return 0; } ////////////////////////////////////////////////// // File | v2023.4.29 // This function creates a single directory if it // doesn't already exist yet. Returns: // 0 = Error occurred // 1 = Directory successfully created // 2 = Directory already exists // // Usage: INTEGER = CreateDirectory(PATH) // function CreateDirectory(PATH) { if (typeof(PATH) != 'string') return 0; if (/[\|\*\?\<\>]/.test(PATH)) return 0; // Check if there are any illegal characters PATH = PATH.replace(/\//g, "\\"); // Replace all forward slash with backslash PATH = PATH.replace(/\\\\/g, "\\"); // Replace all double backslash with single PATH = PATH.replace(/^\s*\"/, ""); // Remove double quotes prefix PATH = PATH.replace(/\"\s*$/, ""); // Remove double quotes suffix PATH = PATH.replace(/[^\\]+\\$/, ""); // Remove last backslash try { // ALERT("CreateDir: Check if folder exists: " + PATH); if (FSO.FolderExists(PATH)) return 2; FSO.CreateFolder(PATH); // Double check if folder exists now return (FSO.FolderExists(PATH)) ? 1 : 0; } catch (e) {} return 0; } ////////////////////////////////////////////////// // File | v2023.4.29 // This function returns a file's size in bytes. // Returns -1 if an error occurs or if the file doesn't exist. // Usage: INTEGER = FileSize(FILENAME) // function FileSize(FILENAME) { var F, SIZE = 0; try { if (!FSO.FileExists(FILENAME)) return -1; F = FSO.GetFile(FILENAME); SIZE = F.Size; } catch (e) { return -1; } return SIZE; } ////////////////////////////////////////////////// // This function opens a small browser window // for displaying status messages. // Usage: OpenStatusWindow() // function OpenStatusWindow() { MSIE = WScript.CreateObject("InternetExplorer.Application"); MSIE.Visible = 1; MSIE.ToolBar = 0; MSIE.StatusBar = 0; MSIE.FullScreen = 0; MSIE.Navigate("about:blank"); MSIE.RegisterAsDropTarget = 0; WScript.Sleep(100); MSIE.Height = 200; MSIE.Width = 440; MSIE.Top = 5; WScript.Sleep(100); MSIE.Left = (MSIE.document.parentWindow.screen.availWidth - 440) - 5; WScript.Sleep(200); MSIE.Document.open(); MSIE.Document.write("Status
"); MSIE.Document.close(); return MSIE; } ////////////////////////////////////////////////// function ClearStatus() { try { MSIE.Document.MAIN.INPUT.value = ''; } catch (e) {} } function PostStatus(MSG) { try { MSIE.Document.MAIN.INPUT.value += MSG + "\n"; } catch (e) {} } function CloseStatusWindow(W) { try { MSIE.Quit(); } catch (e) {} } ////////////////////////////////////////////////// // Displays a message box. function ALERT(MSG) { WScript.Echo(MSG); } // Terminates the program and maybe displays a message. function EXIT(MSG) { if (typeof(MSG) == "string" && MSG.length) ALERT(MSG); WScript.Quit(0); } ////////////////////////////////////////////////// // Math | v2023.4.11 // This function inserts commas into a number at // every 3 digits and returns a string. // Usage: STRING = Commify(INTEGER OR STRING) // Copied from www.PerlMonks.org/?node_id=157725 // and ported to JavaScript. // function Commify(N) { if (typeof(N) == 'undefined' || N == null) return '0'; N += ''; if (N.length == 0) return '0'; N = N.split('').reverse().join(''); N = N.replace(/(\d\d\d)(?=\d)(?!\d*\.)/g, function (a, b) { return b + ","; }); return N.split('').reverse().join(''); } ////////////////////////////////////////////////// // File | v2023.4.29 // This function creates and overwrites a file and // with a plain text string. When the third argument // is omitted, the file is saved in ASCII mode, and // if the third argument is 1, the file is saved in // Unicode text format. Return 1 on success or // zero if an error occurred. // // Usage: INTEGER = CreateFile(FILENAME, STRING, [UNICODE]) // function CreateFile(FILENAME, CONTENT, UNICODE) { UNICODE |= 0; try { F = FSO.CreateTextFile(FILENAME, 1, UNICODE); F.Write(CONTENT); F.Close(); return 1; } catch (e) {} return 0; } ////////////////////////////////////////////////// // String | v2023.2.12 // This function splits a string into two parts // along the first occurrence of substring. // Returns the first part if CMD is 0x10. // Returns the second part if CMD is 1. // If substring is not found, returns the // original string if CMD is 0x100. // Ignores case when CMD is 0x1000. // Starts searching from end of string when CMD is 0x10000. // // Usage: cut(STR, SUB, CMD) // function cut(STR, SUB, CMD) { STR = (typeof(STR) == 'undefined' || STR == null) ? '' : STR + ''; SUB = (typeof(SUB) == 'undefined' || SUB == null) ? '' : SUB + ''; CMD = (typeof(CMD) == 'undefined') ? 0x111 : CMD | 0; var P = (CMD & 0x1000) ? ((CMD & 0x10000) ? STR.toUpperCase().lastIndexOf(SUB) : STR.toUpperCase().indexOf(SUB)) : ((CMD & 0x10000) ? STR.lastIndexOf(SUB) : STR.indexOf(SUB)); if (P < 0) return (CMD & 256) ? STR : ''; return (CMD & 16 ? STR.substr(0, P) : '') + (CMD & 1 ? STR.slice(P + SUB.length) : ''); } //////////////////////////////////////////////////