Option Explicit '*************************************************************************************************************************** '*************************************************************************************************************************** ' DISCLAIMER ' ---------- ' - Neither the author of this script, nor Veritas, shall be liable for any harm or loss generated by the use and/or ' mis-use of this script. ' - This script is not endorsed by Veritas. ' - This script is not supported by Veritas. ' - This script has not been tested by Veritas. ' - Use of this script is entirely at the end user's own risk. ' - This script is free to use and distribute and modify, as long as the version table below is maintained with this script. '*************************************************************************************************************************** '*************************************************************************************************************************** ' IMPORTANT ' --------- ' - Before using this script... ' ...please ensure that you read both 'notes' sections below... ' ...otherwise, bad things could happen to your host/system/server. '*************************************************************************************************************************** '*************************************************************************************************************************** ' Read-Me Notes ' ------------- ' 1) The total size of the NetBackup patch kits for any individual patch version can be quite large, at around 20GB. ' Do not run this script if you have in place download limits, and/or download charging limits, on your internet ' connection. ' 2) It is strongly recommended to not run this script on front line production servers... ' ...and it is recommended to not run this script on backup servers... ' ...and do not run this script on a server/workstation instance that you regard as important... ' ...instead, run this script on a server/workstation for which it does not matter whether RAM or any disk becomes full. ' 3) This script may cause significant consumption of RAM and/or page file space and/or disk space across several volumes. ' Do NOT run this script on systems with less than 20GB free on the system drive, or less than 20GB free on the drive ' upon which this script is being run. For example, whilst a 1.5GB file is being downloaded it is buffered into RAM by ' the HTTP object, before then being copied in RAM to create an ADO stream object, before then being saved to disk as ' the desired target file name - thus there can be lengthy times as an HTTP object fills up to 1.5GB of RAM, and short ' moments when RAM consumption is actually 3.0GB (for any individual 1.5GB download). This is further compounded when ' running multiple downloads. ' 4) There is no network bandwidth limiting. Downloads will consume as much network bandwidth as the system, LAN, internet ' and Veritas' own web service allows. ' 5) It appears that download links (within the TECH note pages) are intermittently randomized by the back-end web servers, ' and so you will have to 're-discover' if http 403 errors are seen during downloads. ' 6) Remember to always scan downloaded files for viruses and threats, and alwways check the consistency/validity of ' downloaded executables, zips, tars, rpms... and always check the MD5 and/or SHA checksums if you can. '*************************************************************************************************************************** '*************************************************************************************************************************** ' Notes re Using This Script ' -------------------------- ' 1) This script was developed and tested on Windows 2008 R2 SP1, with VBScript v5.8. It should work just fine with other ' versions of Windows - however, the http page fetching object named "MSXML2.ServerXMLHTTP" may not be available in your ' version of the O/S. If it is not available in your version of Windows/VBScript, then you may have to research ' alternatives, and amend and re-test this script. ' 2) More than one instance of this script can be run concurrently. However, this could obviously consume more RAM and ' network bandwidth. ' 3) The script requires the creation/existence of a sub-folder which has a folder name the same as the version being ' downloaded, within which to create work files, and to save downloaded files to. ' 4) If you have a cache/store of some patch kits already downloaded then use the -check "X:\Path" argument so that this ' script does not waste time and resources downloading files that it doesn't actually need to. ' 5) The script defaults to at least checking the version sub-folder to detect whether a file has already been downloaded, ' so you could decide to simply use sub-folders under this script to act as your main cache/store of patch kits. ' 6) It is advised/recommended to place this script on a non-system volume, which is usually something other than the C:\ ' drive, to avoid the system drive becoming full - as the patch kits are quite large. ' 7) An example of using this script: ' - save a copy of the script to a folder, e.g: E:\NBU-KITS\nbu-kits.vbs ' - start a DOS/CMD box ' > cd /d E:\NBU-KITS ' > dir ' > cscript nbu-kits.vbs -help ' > cscript nbu-kits.vbs -list ' > mkdir v7.6.1.2 ' > cscript nbu-kits.vbs -discover -download -version v7.6.1.2 ' 8) Each main running instance of this script will default to six concurrent downloads performed by spawned sub-processes. ' If you need to reduce this, then use the '-slots n' argument and specify a lower number of concurrent downloads. ' 9) The actual file downloads are performed by spawned concurrent instances of this script. Therefore, during downloads ' you will see additional 'cscript' processes in Task Manager. ' 10) If the main running instance of this script is interrupted (or fails) or is terminated (e.g. using Ctrl-C), then ' any spawned sub-process instances that are performing downloads are also automatically and immediately terminated ' by Windows. This could occur just as a downloaded file is being written to disk. It is advised to always check the ' consistency of downloaded .exe .gz .tar .zip files, after downloads have completed, using a tool such as "7za t *", ' which will verify archive/compression/store checksums and archive consistency. ' 11) During a test of five concurrent version downloads, each of which instantiated six further concurrent spawned actual ' file download sub-processes (totalling 30 active file downloads), the host system was observed to consume over 26GB ' of RAM. If a similar such situation were to occur on a system/server with limited RAM and/or page file space, then ' the following will very likely happen: ' - exhaustion of RAM ' - excessive system paging ' - exhaustion of page file ' - corrupt, failed downloads ' - sluggish, unresponsive (or possibly hung) host/system/server ' ...therefore, do NOT run more main instances (i.e. -version), or spawned sub-process downloads (i.e. -slots n),... ' ...than the host/system/server resources can support. ' 12) The default priority for spawned sub-processes is 'belownormal', i.e. to soak up spare CPU cycles. This can be changed ' by using the '-priority' argument. ' 13) This script will not download files if there is less than 4GB of free disk space on the local volume. This is because ' it is possible that two or more concurrent downloads, each of 1.5GB, could each test free space at the same time and ' both determine that there is sufficient free space and so begin their downloads. The space check tries to avoid this, ' however it is still possible (although fairly unlikely) that disk space could fill up to the point where there is not ' enough free space for the smallest download - or a perfect storm of two or three downloads that all check and at exactly ' the same time, and so all write/save to disk at exactly the same time, and all of them just happen to be a perfect fit ' for the free disk space - and so either completely consume free space, or leave very little spare. I've tried to avoid ' this happening - but without a proper spin-lock on space acquisisation, then it could still happen. ' 14) The very old versions do not have master/top/primary web pages (of links to other pages which contain the links to ' the actual files to download), so ranges of TECH note numbers have had to be used. The problem with this is that ' a few different web pages for different products are sprinkled through some of the ranges - so, when downloading ' binary files, this script filters and only downloads files with names matching the version. When downloading a range ' of tech note numbers, this behaviour can be overridden with the '-match' argument, to select files for download based ' on presence of a different string in the file name. ' 15) The reason that a sub-folder must be manually created is so that this forces one to think before downloading. ' 16) And another reason to have a sub-folder, is so that a log file can be written per version. ' 17) And, because we use one log file in the sub-folder (per version), this prevents us from downloading the same version ' more than once at the same time, because the log file is locked and can only be held open by one running main parent ' script instance. '*************************************************************************************************************************** '*************************************************************************************************************************** ' Quick Start ' ----------- ' 1) Copy this script to a folder somewhere, e.g.: ' D:\NBU-admin\scripts\nbu-kits\nbu-kits.vbs ' 2) Start a DOS/cmd box: ' Start / Run / cmd ' 3) Change current directory to the folder: ' cd /d D:\NBU-admin\scripts\nbu-kits ' 4) Create a sub-folder to contain downloaded files: ' mkdir v7.6.1.2 ' 5) Run the script to discover and download: ' cscript nbu-kits.vbs -discover -download -version v7.6.1.2 ' 6) After script completes, perform your usual validity and integrity checks of the downloaded files. '*************************************************************************************************************************** '*************************************************************************************************************************** ' Some Quick Examples ' ------------------- ' To download all of the v7.x compatibility documents: ' cscript nbu-kits.vbs -both -version compatv7 ' ' This script can also used to grab a copy of any single web page from the internet, for example using: ' cscript nbu-kits.vbs -discover -version test -save -url http://www.veritas.com/community/backup-and-recovery/forums/netbackup ' ...which will also save a copy of that web page in two forms: ' _test.htm the raw downloaded page ' _test.txt a cleaned-up (but not guaranteed to work) version of thr HTML code which should be a little easier to read. ' ' If you know a web URL for a file name, you can use something like this to pull a file down from the internet, to any local folder path: ' cscript nbu-kits.vbs -version misc -pull FULL-URL FULL-FOLDER-FILE-PATH-SPECICATION ' ...for example you can do something like this: ' cscript nbu-kits.vbs -version misc -pull http://support.apple.com/downloads/DL185/en_US/FujiXeroxPrinterDrivers.dmg D:\dummy.bin ' echo %errorlevel% '*************************************************************************************************************************** '*************************************************************************************************************************** ' Some Examples Of Things Not To Do ' --------------------------------- ' It's probably best not to try things like these: ' ' To scan through the entire NetBackup tree: ' cscript nbu-kits.vbs -discover -version rootnbu -depth 9 ' ' To scan through the first hundred-thousand TECH pages: ' cscript nbu-kits.vbs -discover -version misc -depth 1 -techdoc TECH0+99999 '*************************************************************************************************************************** '*************************************************************************************************************************** ' Usage / Help ' ------------ ' To see the usage help for this script, run: ' cscript nbu-kits.vbs -h '*************************************************************************************************************************** '*************************************************************************************************************************** ' About Variable Names In This Script ' ----------------------------------- ' In this script, variant/variable names take the form of: ' xy_name ' ...where 'x' is the scope, i.e. one of: ' c constant ' f denotes a function that returns a value ' g global, i.e. a variant is visible to main code, and all subs and all functions ' l local to a sub or function ' p parameter passed to a sub or function ' ...where 'y' indicates the typical (expected) data type within the variant/variable: ' b boolean, or byte ' c collection object ' d date, double (real) float, or dictionary ' i short (16-bit) word integer ' l long (32-bit) integer ' o object ' s string ' x could contain any datatype '*************************************************************************************************************************** '*************************************************************************************************************************** ' Ideas For Further Development ' ----------------------------- ' 1) Would be good to also capture, extract and save MD5 and SHA1 details, if present within web pages. ' 5) Check free RAM whilst running, and if necessary reduce slot count, by trimming back last slot as it finishes. ' 9) Retrieve the date and/or size of a download, and check/compare the dates and size against what is already held. ' 14) Support -no prefix on some arguments, e.g. -check and -nocheck (so that files are re-downloaded). ' 15) When loading the gd_downloads() table, use the item as the filename, and check for duplicates before adding to table. ' 20) Have a -folder option to override target downloads folder default of version string. ' 23) Also handle Backup Exec patches and manuals/docs? Apple MacOSX patches? ' 25) Determine latest, and make this the default, version from the main NetBackup and main Appliance pages. ' 26) Place script usage near the top of script, and change the s_usage() routine to read this script to display usage. '*************************************************************************************************************************** '*************************************************************************************************************************** ' Version History ' --------------- ' Name: nbu-kits.vbs ' Purpose: Discover and/or download NetBackup, and Appliance, patch kits. ' ' Vers Date Who Description ' ---- ---- --- ----------- ' v0.01 30-Aug-2014 sdo Initial version. ' v0.02 30-Aug-2014 sdo Add recursion. ' v0.03 30-Aug-2014 sdo Break http in to own sub. ' v0.04 31-Aug-2014 sdo Use dictionary tables to avoid duplication. ' v0.05 31-Aug-2014 sdo Better argument structure, discover and download. ' v0.06 31-Aug-2014 sdo Save file of downloads. ' v0.07 31-Aug-2014 sdo Added facility to messages. ' v0.08 31-Aug-2014 sdo Added multi-download loop, to call self, and return status. ' v0.09 31-Aug-2014 sdo Retrieve stdout and stderr, display timings and download rate. ' v0.10 1-Sep-2014 sdo Added more versions, adjust max depth depending upon version. ' v0.11 1-Sep-2014 sdo Pattern match the older style links. ' v0.12 1-Sep-2014 sdo When pulling a file, skip download if file already exists. ' v0.14 1-Sep-2014 sdo Changed the name of this script from spider-pig to nbu-kits. ' v0.15 1-Sep-2014 sdo Use script name in file names. ' v0.16 1-Sep-2014 sdo Was missing loading of gd_links (i.e. the list of all links seen). ' v0.17 1-Sep-2014 sdo Added a loop counter and time stamping. ' v0.18 2-Sep-2014 sdo Write to a log file. Skip already downloaded files. ' v0.19 2-Sep-2014 sdo Bit of a review/tidy up. Add v7.0 and v7.1 patches. Better sleep loop. ' v0.20 2-Sep-2014 sdo Fixed the download loop bug (couldn't see the wood for the trees). ' v0.21 2-Sep-2014 sdo Use ls_slots(n) empty string as flag for available download slot. ' v0.22 2-Sep-2014 sdo More efficient nested download loops, and busy detection, and expanded read-me notes below. ' v0.23 3-Sep-2014 sdo Multiple pattern match for links, use page not lines, only re-save if debugging. ' v0.24 3-Sep-2014 sdo Better pattern match, handle discover test, renamed some variables, exit discover sooner. ' v0.25 3-Sep-2014 sdo Handle multiple tech notes per version. Added more versions. ' v0.26 4-Sep-2014 sdo Regexp pattern sub-matching. ' v0.27 4-Sep-2014 sdo Removed redundant debug code. Handle download re-tries. Added root pages. ' v0.28 4-Sep-2014 sdo Added compatibility docs. Handle failed copy of http body to ADO. ' v0.29 6-Sep-2014 sdo Extensive changes to support; better log file, better argument and parameter handling. ' v0.30 6-Sep-2014 sdo All files in download path sub-folder, which now allows multiple running main instances. ' v0.31 7-Sep-2014 sdo Added code to check folder tree for files already downloaded, i.e. the -check option. ' v0.32 7-Sep-2014 sdo Option to save first TECH/DOC as HTML file. ' v0.33 8-Sep-2014 sdo Added -list, added compatv6/7, added -open, regexp single quoted links, save amended page. ' v0.34 8-Sep-2014 sdo Some web-pages have embedded LF between HTML fields which don't show in Notepad (sighs). ' v0.35 8-Sep-2014 sdo Ensure to read page as ASCII. Added -collect option. Tighten up the regular expressions. ' v0.36 9-Sep-2014 sdo Added notes re using script. Report free RAM whilst looping, and at end of all downloads. ' v0.37 10-Sep-2014 sdo Handle http object not ready to retrieve status. Added an -all option to test bulk discovery. ' v0.38 12-Sep-2014 sdo Added LBN EEBs. Better http status checking. Quieten some logging. Trailing space on links. ' v0.39 12-Sep-2014 sdo Option to scan a range of tech or doc notes using the '+' option. Check free disk space. ' v0.40 13-Sep-2014 sdo Handle multiple ranges. Ignore some hard-coded unwanted files. ' v0.41 14-Sep-2014 sdo Added some old obscure versions. Detect and report if issues experienced. ' v0.42 15-Sep-2014 sdo Handle short arguments. Started code to reduce priority of spawned download sub-processes. ' v0.43 16-Sep-2014 sdo For old versions only, match version in file name for download. Improved disk space checks. ' v0.44 17-Sep-2014 sdo Improved random version selection. Added code to reduce priority of spawned sub-processes. ' v0.45 18-Sep-2014 sdo More technotes in the version chains. Expanded match filtering. ' v0.46 19-Sep-2014 sdo Added -file argument to process a pre-saved list of downloads. ' v0.47 20-Sep-2014 sdo Added code for -rediscover argument, to re-process a file of downloads. ' v0.48 21-Sep-2014 sdo Use regular expression for pattern match. ' v0.49 22-Sep-2014 sdo Added -ignore option, also to use regular expression. ' v0.50 23-Sep-2014 sdo No need for sub-matches loop on -match and -ignore processing. Tidy usage show. ' v0.51 26-Sep-2014 sdo Use a sub to load versions, depths and matches. ' v0.52 20-Oct-2014 sdo More comments around complex areas of code. ' v0.53 21-Oct-2014 sdo Finish adding pattern matches for versions. ' v0.54 25-Oct-2014 sdo The NetBackup v7 HCL page has a different format of link. ' v0.55 8-Nov-2014 sdo Change s_check sub in to a function, more comments, break stdout+stderr into a sub. ' v0.56 10-Nov-2014 sdo Added v7.6.0.4 and v2.6.0.4. ' v1.00 11-Nov-2014 sdo Added documentation set and VC plugin to vX.6.0.4 lists. ' v1.01 26-Nov-2014 sdo Added v7.6.1 docs, v2.6.0.4 docs, v2.6.1 docs, NB5330 docs. ' v1.02 15-Jan-2015 sdo Added v2.6.1 patch kit. ' v1.03 16-Jan-2015 sdo Added more comments to code. ' v1.04 19-Jan-2015 sdo Added disclaimer. ' v1.05 3-Mar-2015 sdo Added NetBackup v7.6.1.1, and Appliance v2.6.1.1. ' v1.06 3-Mar-2015 sdo Default version now v7.6.1.1, fixed some spellings/grammar/typos in comments/notes. ' v1.07 4-Mar-2015 sdo Added PDDO appliance v1.4.5. ' v1.08 21-Apr-2015 sdo Updates to cater for web site changes. ' v1.09 2-Jun-2015 sdo Added downloads for v7.6.1.2 and man7.6.1.2, and v2.6.1.2 and man2.6.1.2. ' v1.10 8-Jul-2015 sdo Added v7.7 documentation. ' v1.11 27-Sep-2015 sdo Added v7.7.1 documentation. ' v1.12 28-Sep-2015 sdo Updated to download v7.7 pdf for compatv7. Added selfservice documentation. ' v1.13 13-Jan-2016 sdo Split s_main() into smaller subs. Added v2.7.1. Updated some comments. ' v1.14 14-Jan-2016 sdo Handle the new Veritas based format for page links and download file names. ' v1.15 15-Jan-2016 sdo ll_send_status wasn't correctly checked. Added more comments. Option to delete files. ' v1.16 16-Jan-2016 ado Added option to specify a URL. Added yet more comments. ' v1.17 17-Jan-2016 sdo Handle stream save failure. Improve handling of ranges from CLI. ' v1.18 2-Feb-2016 sdo Added NetBackup v7.7.2 docs, and Appliance v2.7.2 binaries and docs. '*************************************************************************************************************************** '*************************************************************************************************************************** Const cs_script_version = "v1.18" 'scripting and script management constants... Const ci_for_reading = 1 Const ci_for_writing = 2 Const ci_for_appending = 8 Const ci_open_as_default = -2 Const ci_open_as_unicode = -1 Const ci_open_as_ascii = 0 Const ci_if_exist_overwrite = -1 Const ci_if_exist_fail = 0 Const cl_prio_low = 64 'aka idle... Const cl_prio_belownormal = 16384 Const cl_prio_normal = 32 Const ci_windows_folder = 0 Const ci_system_folder = 1 Const ci_temporary_folder = 2 Const cs_stars = "************************************************************" Const cs_bs = "\" Const cs_quote = "'" 'a single quote... Const cs_quotes = """" 'looks odd, but this does resolve to one double-quote character, i.e. one of these: " 'constants pertinent to this particular script... Const cl_max_depth = 9 'max depth for recursive discover... Const cl_max_sleep = 120 'max number of seconds to sleep between checks for completed downloads... Const cl_max_slots = 12 'max spawned concurrent download .Exec process slots... Const cl_max_tries = 5 'max number of tries for http page fetch... Const cd_min_free_space = 4.0 'minumum required free disk space... Const cs_def_check = "D:\NetBackup" 'the default folder path to check for previously downloaded files... Const cl_def_depth = 3 'the default depth, i.e. how deep to follow links in web pages... Const cs_def_priority = "belownormal" 'the default priority of the spawned Exec processes that perform the actual file downloads... Const cl_def_tries = 2 'the default number of tries when fetching web pages... Const cl_def_sleep = 30 'the default sleep duration when downloads slots are busy... Const cl_def_slots = 6 'the default number of concurrent downloads, and six is the typical maximum of manual IE and/or Firefox... Const cs_def_version = "compatv7" 'the default version to download, if -version is not specified as an argument... 'the usual global script management variables... Dim go_wsh, go_fso, go_app, go_net, go_ads, go_loc, go_wmi, gs_computer Dim gs_script_spec, gs_script_path, gs_script_name, gs_script_title, gs_script_fac, gd_script_start, gd_script_end, gs_script_engine Dim gs_temp_path, gs_temp_file, gs_temp_spec, gs_log_spec, go_log_chan 'global argument variables, which are particular to this script... 'were arguments specified on command line, boolean true/false... Dim gb_arg_all, gb_arg_both, gb_arg_check, gb_arg_collect, gb_arg_debug, gb_arg_delete, gb_arg_depth, gb_arg_discover, gb_arg_download Dim gb_arg_file, gb_arg_ignore, gb_arg_list, gb_arg_match, gb_arg_open, gb_arg_priority, gb_arg_pull, gb_arg_rediscover, gb_arg_save Dim gb_arg_sleep, gb_arg_slots, gb_arg_techdoc, gb_arg_test, gb_arg_tries, gb_arg_url, gb_arg_version 'argument values from command line, in string form... Dim gs_arg_check, gs_arg_depth, gs_arg_file, gs_arg_ignore, gs_arg_match, gs_arg_priority, gs_arg_pull_url, gs_arg_pull_file Dim gs_arg_sleep, gs_arg_slots, gs_arg_techdoc, gs_arg_tries, gs_arg_url, gs_arg_version 'argument values from command line, in numeric form... Dim gl_arg_depth, gl_arg_sleep, gl_arg_slots, gl_arg_tries 'global data variables, which are particular to this script... Dim gd_versions, gd_links, gd_downloads, gs_downloads_path, gs_downloads_spec, go_downloads_chan Dim gd_ram_free_pct_stt, gd_ram_free_pct_cur, gd_ram_free_pct_min, gd_ram_free_pct_max Dim gd_issues, gd_depths, gd_matches, gd_ignores 'now begin code execution... gd_script_start = Now Call s_init() 'initialize variables, check arguments, open log file... Call s_main() 'discover and/or download... gd_script_end = Now If gb_arg_pull Then WScript.Quit(0) 'no script summary for spawned sub-processes... Call s_log( "" ) Call s_log( gs_script_fac & "script started: " & fs_datetime( gd_script_start ) ) Call s_log( gs_script_fac & "script finsished: " & fs_datetime( gd_script_end ) ) Call s_log( gs_script_fac & "script duration: " & Trim( fs_duration( gd_script_start, gd_script_end ) ) ) Call s_log( "" ) If gd_issues.Count > 0 Then Call s_issues_list() Call s_log( gs_script_fac & "script completed with warnings..." ) WScript.Quit(1) End If Call s_log( gs_script_fac & "no issues noted..." ) Call s_log( gs_script_fac & "script completed successfully..." ) WScript.Quit(0) Sub s_init() Const cs_fac = "%s_init, " Dim ls_fields Set go_fso = CreateObject( "Scripting.FileSystemObject" ) Set go_wsh = CreateObject( "WScript.Shell" ) Set go_app = CreateObject( "Shell.Application" ) Set go_net = CreateObject( "WScript.Network" ) On Error Resume Next Set go_ads = CreateObject( "ADSystemInfo" ) On Error Goto 0 Set go_wmi = GetObject( "winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2" ) go_loc = SetLocale( "en-gb" ) gs_script_spec = WScript.ScriptFullName gs_script_path = go_fso.GetParentFolderName( gs_script_spec ) gs_script_name = go_fso.GetBaseName( gs_script_spec ) gs_script_title = gs_script_name & " (" & cs_script_version & ")" gs_script_fac = "%" & gs_script_name & ", " gs_temp_path = go_fso.GetSpecialFolder( ci_temporary_folder ) gs_temp_file = go_fso.GetTempName gs_temp_spec = gs_temp_path & cs_bs & gs_temp_file gs_computer = go_net.ComputerName gs_script_engine = LCase( go_fso.GetBaseName( WScript.FullName ) ) Select Case gs_script_engine Case "cscript" Case "wscript" Call s_abort( cs_fac & "script cannot be run using script engine `" & gs_script_engine & "`..." ) Case Else Call s_abort( cs_fac & "unknown script engine `" & gs_script_engine & "`..." ) End Select If Instr( 1, gs_script_name, " " ) <> 0 Then Call s_abort( cs_fac & "script name cannot contain spaces..." ) Set gd_versions = CreateObject( "Scripting.Dictionary" ) 'list of NetBackup patch kits, and their primary TECH note, keyed by version... Set gd_depths = CreateObject( "Scripting.Dictionary" ) 'list of depths, keyed by version... Set gd_matches = CreateObject( "Scripting.Dictionary" ) 'list of matches, keyed by version... Set gd_ignores = CreateObject( "Scripting.Dictionary" ) 'list of ignores, keyed by version... Set gd_links = CreateObject( "Scripting.Dictionary" ) 'list of all links seen, so that we don't double-up... Set gd_downloads = CreateObject( "Scripting.Dictionary" ) 'list of all download links... Set gd_issues = CreateObject( "Scripting.Dictionary" ) 'list of issues encountered... Call s_versions() 'need to load versions before checking arguments... Call s_arguments() 'need to identify version from arguments before creating log file... If gb_arg_both Or gb_arg_discover Or gb_arg_download Or gb_arg_rediscover Then Call s_create_log() 'we now know everything we need to be able to open a log file... End If 'we do not open a log file for pull, as this would cause script to fail... If gb_arg_debug And ( Not gb_arg_pull ) Then Call s_log( cs_fac & "temporary file for web page downloads is `" & gs_temp_spec & "`..." ) End Sub Sub s_main() Const cs_fac = "%s_main, " If gb_arg_pull Then 'pull is used when the script spawns itself for actual downloads... Call s_pull( gs_arg_pull_url, gs_arg_pull_file ) 'now actually perform an actual download... Exit Sub 'no need to carry on... End If If gb_arg_test Then Call s_test() Exit Sub End If If gb_arg_list Then Call s_list() If gb_arg_open Then Call s_open() If gb_arg_rediscover Or gb_arg_discover Or gb_arg_download Then gs_downloads_spec = gs_downloads_path & cs_bs & "_" & gs_script_name & "-" & gs_arg_version & "-downloads.txt" End If If gb_arg_file Then Call s_log( cs_fac & "copying `" & gs_arg_file & "` to downloads file `" & gs_downloads_spec & "`..." ) go_fso.CopyFile gs_arg_file, gs_downloads_spec, True End If 'the next three major code blocks cover: '...re-discovery which loads/reads a previously saved file of download links, and populates 'gs_arg_techdoc' with a list of tech notes to discover... '...discovery which processes the list of tech-notes, fetches web-pages, searches those web-pages for download links, and writes a file of download links... '...download which loads/reads the file of download links, and attempts the downloads... '...and... '...the purpose of the 'rediscover' function... '...is to re-process download links, so that we don't have to search again (discover) through nested/linked web pages from the internet... If gb_arg_rediscover Then Call s_rediscover() If gb_arg_discover Then Call s_discover() If gb_arg_download Then Call s_download() End Sub Sub s_test() Const cs_fac = "%s_test, " Dim ll_test_versions, ll_test_submitted, ls_version, ls_test_command, ll_test_status 'this routine will kick off one to many additional 'discovery' processes for any and all sub-folders that match known versions... '...i.e. this is an en-masse test routine and wouldn't normally be used. Call s_log( "" ) Call s_log( cs_fac & "starting..." ) Call s_log( cs_fac & "submitting discovery of one or more versions..." ) ll_test_versions = 0 ll_test_submitted = 0 For Each ls_version In gd_versions.Keys Select Case ls_version Case "misc", "random", "test" Case Else ll_test_versions = ll_test_versions + 1 If go_fso.FolderExists( gs_script_path & cs_bs & ls_version ) Then ls_test_command = "cmd /k cscript " & gs_script_name & ".vbs" & " -version " & ls_version & " -discover -collect -save & pause & exit" ll_test_status = go_wsh.Run( ls_test_command, 9 , False ) If ll_test_status = 0 Then Call s_log( cs_fac & "submitted discovery of version `" & ls_version & "`..." ) WScript.Sleep 100 ll_test_submitted = ll_test_submitted + 1 Else Call s_log( cs_fac & "status `" & ll_test_status & "`, failed to run command: " & ls_test_command ) End If Else Call s_log( cs_fac & "cannot submit discovery of version `" & ls_version & "`, sub-folder is missing..." ) End If End Select Next Call s_log( cs_fac & "submitted `" & ll_test_submitted & "` of a possible `" & ll_test_versions & "` versions..." ) Call s_log( cs_fac & "done..." ) End Sub Sub s_list() Const cs_fac = "%s_list, " Dim ls_version, ls_tech Call s_log( "" ) Call s_log( cs_fac & "starting..." ) Call s_log( cs_fac & " Version TechNote/Doc/Link" ) Call s_log( cs_fac & " ------- -----------------" ) For Each ls_version In gd_versions.Keys Select Case ls_version Case "misc", "random", "test" 'do not list these versions/options... Case Else ls_tech = gd_versions.Item( ls_version ) Call s_log( cs_fac & " " & fs_left( ls_version, 11 ) & " " & ls_tech ) End Select Next Call s_log( cs_fac & "done..." ) End Sub Sub s_open() 'this routine opens the first technote in the default browser... Const cs_fac = "%s_open, " Dim ls_tech, ls_techs, ls_url Call s_log( "" ) Call s_log( cs_fac & "starting..." ) If gb_arg_techdoc Then ls_tech = gs_arg_techdoc Else ls_techs = Split( gd_versions.Item( gs_arg_version ), "," ) 'if there are multiple short names... ls_tech = ls_techs( 0 ) '...then just take the first one... End If ls_url = "" If IsNumeric( ls_tech ) Then 'a purely numeric short document number is assumed to be... ls_url = fs_veritas_url( ls_tech ) '...the new page name format for the Veritas site... Else Select Case UCase( Left( ls_tech, 3 ) ) 'the older style Symantec format, some of which still link... Case "TEC", "DOC" ' ls_url = "http://www.symantec.com/docs/" & ls_tech 'v1.07... ' ls_url = "https://support.symantec.com/en_US/article." & ls_tech & ".html" 'v1.08... ls_url = "https://www.veritas.com/support/en_US/article." & ls_tech & ".html" 'v1.14... Case Else ls_url = ls_tech 'assume the URL is already a proper URL... End Select End If If ls_url = "" Then Call s_log( cs_fac & "unable to determine a URL to open..." ) Else Call s_log( cs_fac & "opening browser page to `" & ls_url & "`..." ) go_wsh.Run ls_url, 9, False 'just try to open, don't bother checking whether it worked... End If Call s_log( cs_fac & "done..." ) End Sub Sub s_rediscover() Const cs_fac = "%s_rediscover, " Dim ls_readall, ls_old_urls, ls_new_techs, ld_new_techs, ll_old_url, ls_old_url, lb_wanted, ls_download_file Dim lo_regexp, lc_matches, lo_match, ls_url_fields, ll_url_field, ls_new_tech 'there are four stages to re-discovery... 'stage 1 - load the previously saved download links... 'stage 2 - check the previously discovered download links and only filter those with filenames that meet our selection list... 'stage 3 - exract the TECH or DOC or Veritas numbers from the remaining download links... 'stage 4 - reload the 'list' of tech notes to process through discovery... Call s_log( "" ) Call s_log( cs_fac & "starting..." ) 'stage 1 - load the previously saved download links... ls_readall = go_fso.OpenTextFile( gs_downloads_spec ).Readall ls_old_urls = Split( ls_readall, vbCrlf ) Call s_log( cs_fac & "found `" & ( UBound( ls_old_urls ) + 1 ) & "` old URLs to rediscover, will apply match..." ) ls_new_techs = "" Set ld_new_techs = CreateObject( "Scripting.Dictionary" ) For ll_old_url = 0 To UBound( ls_old_urls ) ls_old_url = Trim( ls_old_urls( ll_old_url ) ) If ls_old_url <> "" Then lb_wanted = False ls_download_file = fs_url_to_file( ls_old_url ) If gb_arg_debug Then Call s_log( cs_fac & "checking match for `" & gs_arg_match & "` to file `" & ls_download_file & "`..." ) 'stage 2 - check the previously discovered download links and only filter those with filenames that meet our selection list... Set lo_regexp = CreateObject( "VBScript.RegExp" ) lo_regexp.IgnoreCase = True lo_regexp.MultiLine = True lo_regexp.Global = True lo_regexp.Pattern = gs_arg_match Set lc_matches = lo_regexp.Execute( ls_download_file ) For Each lo_match In lc_matches If lo_match <> "" Then lb_wanted = True If gb_arg_debug Then Call s_log( cs_fac & "found a match `" & lo_match & "`..." ) End If Next 'match... Set lo_regexp = Nothing If lb_wanted Then 'if we think that we want this file name... ls_url_fields = Split( ls_old_url, "/" ) 'stage 3 - exract the TECH or DOC or Veritas numbers from the remaining download links... 'now step backwards through the fields towards the beginning of the URL path... For ll_url_field = fx_max( 0, ( UBound( ls_url_fields ) - 1 ) ) To fx_max( 0, ( UBound( ls_url_fields ) - 2 ) ) Step -1 If gb_arg_debug Then call s_log( cs_fac & "checking URL field `" & ls_url_fields( ll_url_field ) & "`..." ) 'and when we find the TECH or DOC or Veritas number... If ( UCase( Left( ls_url_fields( ll_url_field ), 4 ) ) = "TECH" ) Or ( UCase( Left( ls_url_fields( ll_url_field ), 3 ) ) = "DOC" ) Or ( IsNumeric( ls_url_fields( ll_url_field ) ) ) Then ls_new_tech = ls_url_fields( ll_url_field ) If Not ld_new_techs.Exists( ls_new_tech ) Then 'if we have not seen this one before... ls_new_techs = ls_new_techs & "," & ls_new_tech 'then make a note of that TECH or DOC or Veritas number... ld_new_techs.Add ls_new_tech, "" End If End If Next 'url field... End If End If Next 'url... ls_new_techs = Mid( ls_new_techs, 2, Len( ls_new_techs ) ) 'stage 4 - reload the 'list' of tech notes to process through discovery... '...i.e. override the 'versions' list of tech notes, and isntead pretend that they were specified at the command line... '...so that 'discovery' will use this list... gb_arg_techdoc = True gs_arg_techdoc = ls_new_techs Call s_log( cs_fac & "rediscovered `" & ld_new_techs.Count & "` tech notes `" & ls_new_techs & "`..." ) Call s_log( cs_fac & "done..." ) End Sub Sub s_discover() Const cs_fac = "%s_discover, " Dim ls_col_spec, ls_techs, ll_misc_next, ls_misch_techs, ll_tech, ls_tech, ls_misc_prefix Dim ls_misc_range, ll_misc_range, ls_misc_start, ll_misc_start, ll_misc, ls_url, ls_download 'there are several stages to discovery... 'stage 1 - decide whether we're using a list of tech notes from the command line, or the default list for the version... 'stage 2 - process the array of (was originally a list of comma separated) tech notes, and expand any that use '+' to indicate a range of numbers... 'stage 3 - reload the array of tech notes from the expanded temporary list... 'stage 4 - the main loop which gets web pages and searches through them, and which populates the list of files to later download... 'stage 5 - save the list of discovered files which could/can/will be downloaded... Call s_log( "" ) Call s_log( cs_fac & "starting..." ) If gb_arg_collect Then ls_col_spec = gs_script_path & cs_bs & gs_arg_version & cs_bs & "_" & gs_arg_version & ".col" If go_fso.FileExists( ls_col_spec ) Then go_fso.DeleteFile( ls_col_spec ) End If 'stage 1 - decide whether we're using a list of tech notes from the command line, or the default list for the version... If gb_arg_url Then Redim ls_techs(0) ls_techs(0) = gs_arg_url Else If gb_arg_techdoc Then 'if a redicover has been done, then this is true... ls_techs = Split( gs_arg_techdoc, "," ) 'and so we use the rediscovered list... Else 'whereas a plain discover will use the version starting point... Call s_log( cs_fac & "initial tech note(s) `" & gd_versions.Item( gs_arg_version ) & "`..." ) ls_techs = Split( gd_versions.Item( gs_arg_version ), "," ) End If Call s_log( cs_fac & "match regular expression is `" & gs_arg_match & "`..." ) End If 'stage 2 - process the array of (was originally a list of comma separated) tech notes, and expand any that use '+' to indicate a range of numbers... ll_misc_next = 0 'the next element to be occupied... Redim ls_misc_techs( ll_misc_next ) 'we need an empty starting array... For ll_tech = 0 To UBound( ls_techs ) 'process the received list... ls_tech = ls_techs( ll_tech ) If Instr( 1, ls_tech, "+" ) = 0 Then 'there is no range on this one... Redim Preserve ls_misc_techs( ll_misc_next ) ls_misc_techs( ll_misc_next ) = ls_tech ll_misc_next = ll_misc_next + 1 Else 'this tech note has a range on it... Select Case UCase( Left( ls_tech, 3 ) ) Case "TEC" : ls_misc_prefix = "TECH" Case "DOC" : ls_misc_prefix = "DOC" Case Else If Not IsNumeric( Left( ls_tech, 1 ) ) Then Call s_abort( cs_fac & "unexpected tech prefix on `" & ls_tech & "`..." ) End If ls_misc_prefix = "" End Select ls_misc_range = Mid( ls_tech, Instr( 1, ls_tech, "+" ) + 1, Len( ls_tech ) ) 'get the trailing range number... ll_misc_range = CLng( ls_misc_range ) ls_misc_start = Mid( ls_tech, Len( ls_misc_prefix ) + 1, Len( ls_tech ) ) 'get the starting tech note number and the range... ls_misc_start = Mid( ls_misc_start, 1, Instr( 1, ls_misc_start, "+" ) - 1 ) 'remove the range... ll_misc_start = CLng( ls_misc_start ) 'and we're left with the starting tech note number... If gb_arg_debug Then call s_log( cs_fac & "range is `" & ll_misc_start & "` plus `" & ll_misc_range & "`..." ) For ll_misc = 0 To ll_misc_range 'add our new range to the list of misc tech notes... Redim Preserve ls_misc_techs( ll_misc_next ) 'we already new how long the list might need to be... ls_misc_techs( ll_misc_next ) = ls_misc_prefix & CStr( ll_misc_start + ll_misc ) 'now occupy that final element... ll_misc_next = ll_misc_next + 1 'prepare for next (possible) element... Next 'misc... End If Next 'tech... If ll_misc_next = 0 Then Call s_abort( cs_fac & "there are no tech notes to process..." ) End If 'stage 3 - reload the array of tech notes from the expanded temporary list... Redim ls_techs( UBound( ls_misc_techs ) ) 'copy the misc array... For ll_tech = 0 To UBound( ls_misc_techs ) ls_techs( ll_tech ) = ls_misc_techs( ll_tech ) If gb_arg_debug Then call s_log( cs_fac & "will process `" & ls_techs( ll_tech ) & "`..." ) Next 'stage 4 - the main loop which gets web pages and searches through them, and which populates the list of files to later download... For ll_tech = 0 To UBound( ls_techs ) 'now begin diving through the tech notes... Select Case UCase( Left( ls_techs( ll_tech ), 3 ) ) Case "TEC", "DOC" ' ls_url = "http://www.symantec.com/business/support/index?page=content&id=" & ls_techs( ll_tech ) 'old style... ' ls_url = "http://www.symantec.com/docs/" & ls_techs( ll_tech ) 'new style, up to and including v1.07... ' ls_url = "https://support.symantec.com/en_US/article." & ls_techs( ll_tech ) & ".html" 'from v1.08... ls_url = "https://www.veritas.com/support/en_US/article." & ls_techs( ll_tech ) & ".html" 'from v1.14... Case "HTT" ls_url = ls_techs( ll_tech ) Case Else If IsNumeric( ls_techs( ll_tech ) ) Then 'a plain number indicates a Veritas page number... ls_url = fs_veritas_url( ls_techs( ll_tech ) ) 'so use a nine digit page number... Else Call s_abort( cs_fac & "unexpected TECH or URL of `" & ls_techs( ll_tech ) & "`..." ) End If End Select If Not gd_links.Exists( ls_url ) Then gd_links.Add ls_url, "" 'stops us from following a page's own link to itself... If gb_arg_debug Then call s_log( cs_fac & "URL to process is `" & ls_url & "`..." ) Call s_dive( ls_url, 0 ) 'start the recursive depth dive, we're at depth zero... Next Call s_log( "" ) Call s_log( cs_fac & "followed `" & gd_links.Count & "` links..." ) Call s_log( cs_fac & "found `" & gd_downloads.Count & "` downloads..." ) 'stage 5 - save the list of discovered files which could be downloaded... If gd_downloads.Count = 0 Then 'nothing found to download, so don't write an empty file... Call s_log( cs_fac & "not writing empty file..." ) Else 'we found something! yay! Set go_downloads_chan = go_fso.CreateTextFile( gs_downloads_spec, True ) 'save list of downloads, as we may want to download later... For Each ls_download In gd_downloads.Keys go_downloads_chan.WriteLine ls_download Next go_downloads_chan.Close Call s_log( cs_fac & "saved `" & gd_downloads.Count & "` to downloads file `" & gs_downloads_spec & "`..." ) End if Call s_log( cs_fac & "done..." ) End Sub Sub s_dive( ps_url, pl_depth ) Const cs_fac = "%s_dive, " Dim ls_page, lo_file, ls_prev, ll_depth, ld_links, ls_save_spec, lo_save_chan, lo_temp_stream Dim lo_regexp, ls_pattern, lc_matches, lo_match, ls_link, lo_temp_chan, ls_col_spec, lo_col_chan, ls_text ll_depth = pl_depth + 1 'set the new depth level... If ll_depth > gl_arg_depth Then Exit Sub 'are we too deep? Call s_log( "" ) Call s_log( cs_fac & "depth `" & ll_depth & "`, version `" & gs_arg_version & "`, URL `" & ps_url & "`..." ) Set ld_links = CreateObject( "Scripting.Dictionary" ) 'at this depth level, the links that we find... If go_fso.FileExists( gs_temp_spec ) Then go_fso.DeleteFile( gs_temp_spec ) 'delete the previous web page download... Call s_pull( ps_url, gs_temp_spec ) 'pull a web page, to the temporary file... If Not go_fso.FileExists( gs_temp_spec) Then 'failed to pull the web page... Call s_log( cs_fac & "unexpected, the web page file is missing, skipping this link..." ) Exit Sub End If 'do we want to save a copy of the un-altered page in original Unicode UTF-8 format (for debugging)... If gb_arg_save Then ls_save_spec = gs_downloads_path & cs_bs & "_" & gs_arg_version & ".htm" If gb_arg_debug Then Call s_log( cs_fac & "saving first page to `" & ls_save_spec & "`..." ) go_fso.CopyFile gs_temp_spec, ls_save_spec, True If gb_arg_debug Then Call s_log( cs_fac & "saved..." ) End If 'read the downloaded web page - BUT - it will be in Unicode UTF-8 format, so when we load it... '...also, at the same time, convert the file content in to ASCII format instead... Set lo_temp_stream = CreateObject( "ADODB.Stream" ) lo_temp_stream.Open lo_temp_stream.Type = 2 'adTypeText lo_temp_stream.Charset = "us-ascii" lo_temp_stream.LoadFromFile gs_temp_spec ls_page = lo_temp_stream.ReadText lo_temp_stream.Close Set lo_temp_stream = Nothing 'we no longer need this temporary file... go_fso.DeleteFile( gs_temp_spec ) 'do we want to collect web pages into one file (for debuging)... If gb_arg_collect Then ls_col_spec = gs_script_path & cs_bs & gs_arg_version & cs_bs & "_" & gs_arg_version & ".col" Set lo_col_chan = go_fso.OpenTextFile( ls_col_spec, ci_for_appending, True, ci_open_as_ascii ) lo_col_chan.WriteLine "#" lo_col_chan.WriteLine "#" lo_col_chan.WriteLine "# " & cs_stars lo_col_chan.WriteLine "# " & cs_stars lo_col_chan.WriteLine "# " & gs_script_name & " page: " & ps_url lo_col_chan.WriteLine "#" lo_col_chan.Write ls_page lo_col_chan.Close End If 'for the first page only, do we want to split up the HTML and save it as a text file (for debugging)... If gb_arg_save Then ls_text = ls_page Do 'keep trimming... ls_prev = ls_text ls_text = Replace( ls_text, vbTab & vbTab ,vbTab ) 'reduce dual tabs... ls_text = Replace( ls_text, vbLf & vbLf ,vbLf ) 'reduce dual line feeds... ls_text = Replace( ls_text, vbCrlf & vbCrlf ,vbCrlf ) 'reduce dual blank lines... ls_text = Replace( ls_text, vbTab & vbCrlf ,vbCrlf ) 'remove trailing tabs... ls_text = Replace( ls_text, vbCrlf & vbTab ,vbCrlf ) 'remove leading tabs... ls_text = Replace( ls_text, " " & vbCrlf ,vbCrlf ) 'remove trailing spaces... ls_text = Replace( ls_text, vbCrlf & " " ,vbCrlf ) 'remove leading spaces... ls_text = Replace( ls_text, "> " ,">" ) ls_text = Replace( ls_text, " <" ,"<" ) ls_text = Replace( ls_text, "><" ,">" & vbCrlf & "<" ) 'break HTML fields apart... ls_text = Replace( ls_text, ">" & vbLf & "<" ,">" & vbCrlf & "<" ) 'break HTML fields apart... ls_text = Replace( ls_text, ">" & vbTab & "<" ,">" & vbCrlf & "<" ) Loop Until ls_text = ls_prev 'until the changes no longer have any effect... ls_text = Replace( ls_text, ">http://" ,">" & vbCrlf & "http://") 'force trailing descriptive text to new line... ls_text = Replace( ls_text, " 'and an example of a link without a leading 'http://site.site.site' component: ' 'which, in a browser, translates to: ' http://www.symantec.com/business/support/library/BUSINESS/xCL/TECH76495/nbu_7x_hcl.pdf '...however... '...with the Symantec and Veritas split, the downloads are now in this form: ' url : "https://download.veritas.com/resources/content/live/DOCUMENTATION/8000/DOC8958/en_US/NetBackup771_Upgrade_Guide.pdf?__gda__=1452896845_6b7fd3c5a21d612734151a0bdd226182", Call s_log( cs_fac & "searching for files..." ) Set lo_regexp = CreateObject( "VBScript.RegExp" ) lo_regexp.IgnoreCase = True lo_regexp.MultiLine = True lo_regexp.Global = True ls_pattern = "" ' ls_pattern = ls_pattern & "|" & "]*)"" target=""_blank"">" 'v1.07... ' ls_pattern = ls_pattern & "|" & "]*)"">" 'v1.07... ' ls_pattern = ls_pattern & "|" & "]*)"">" 'v1.08... ls_pattern = ls_pattern & "|" & "url : ""(https?://[^""]*download.veritas.com/[^""]*)""," 'v1.14... new format... ls_pattern = Mid( ls_pattern, 2, Len( ls_pattern ) ) lo_regexp.Pattern = ls_pattern Set lc_matches = lo_regexp.Execute( ls_page ) 'this searches for file download links embedded within the raw HTML code of a fetched web page... For Each lo_match In lc_matches For Each ls_link In lo_match.SubMatches If ls_link <> "" Then If LCase( Left( ls_link, 34 ) ) = LCase( "/business/support/library/BUSINESS" ) Then 'fudge a file link without a leading http://site.site.site component... ls_link = "http://www.symantec.com" & ls_link '...but there shouldn't be any of these anymore... End If '...so the above two lines are here just in case. If Not gd_downloads.Exists( ls_link ) Then Call s_log( cs_fac & "found file `" & fs_url_to_file( ls_link ) & "`" ) gd_downloads.Add ls_link, "" 'a potential download was found... End If End If Next Next Set lo_regexp = Nothing If ll_depth >= gl_arg_depth Then Exit Sub 'there is no need to go any deeper... 'look for further page links that we can dive through... ' examples of page links: ' ' ' ' ' 'note the trailing space (sighs)... ' 'another new format, this time with \ escaping quotes... Call s_log( cs_fac & "searching for pages..." ) Set lo_regexp = CreateObject( "VBScript.RegExp" ) lo_regexp.IgnoreCase = True lo_regexp.MultiLine = True lo_regexp.Global = True 'this is the list of patterns to match within the raw HTML code/text which constitute a link to another web page... ls_pattern = "" ls_pattern = ls_pattern & "|" & "<[^']*'(http://[esw]\w+\.[sv]\w+\.com/docs/\w+)\s*'[^>]*>" ls_pattern = ls_pattern & "|" & "<[^""]*""(http://[esw]\w+\.[sv]\w+\.com/docs/\w+)\s*""[^>]*>" ls_pattern = ls_pattern & "|" & "<[^""]*""(http://\w+\.\w+\.com/business/support/index\?page=content&id=TECH\d{2,6})\s*""[^>]*>" ls_pattern = ls_pattern & "|" & "<[^""]*\\""(http://[esw]\w+\.[sv]\w+\.com/docs/\w+)\\""[^>]*>" 'v1.09 for v7.6.1.2... ls_pattern = ls_pattern & "|" & "<[^""]*""(/content/support/.*/article\.[dt]\w+\.html)""[^>]*>" 'v1.14 for linked pages without a site ref... ls_pattern = Mid( ls_pattern, 2, Len( ls_pattern ) ) lo_regexp.Pattern = ls_pattern Set lc_matches = lo_regexp.Execute( ls_page ) 'this searches the web page text and populates a collection object with the matches found... For Each lo_match In lc_matches For Each ls_link In lo_match.SubMatches If ls_link <> "" Then If gb_arg_debug Then Call s_log( cs_fac & "found page `" & ls_link & "`..." ) If LCase( Left( ls_link, 17 ) ) = LCase( "/content/support/" ) Then 'fudge a page link without a leading http://site.site.site component... ls_link = "http://www.veritas.com" & ls_link '...because some links assume a leading site... End If If Not gd_links.Exists( ls_link ) Then 'if we have not seen this page link before... gd_links.Add ls_link, "" If Not ld_links.Exists( ls_link ) Then ld_links.Add ls_link, "" 'another potential page link was found... End If End If End If next Next Set lo_regexp = Nothing Call s_log( cs_fac & "found `" & ld_links.Count & "` new pages..." ) 'now process the newly discovered page links... For Each ls_link In ld_links.Keys Call s_dive( ls_link, ll_depth ) 'a recursive call to itself... Next End Sub Sub s_pull( ps_url, ps_file ) Const cs_fac = "%s_pull, " Dim lo_http, lo_stream, ld_http_start, ld_http_end, ll_http_seconds Dim ld_size_KB, ld_size_MB, ld_size_GB, ls_size, ld_KBs, ld_MBs, ls_rate, lb_got Dim ll_try, ll_send_status, ll_waits, lb_wait, ll_http_status, ll_stream_status Dim ls_drive, ld_required_GB, ll_open_status, ll_save_status 'this routine is used for two similar purposes: '...to pull down a web page, and save it to temporary file (to acquire the raw HTML code that actually is the web page)... '...or download a wanted file, and save this to our target 'version' download folder... '...both of which are in fact exactly the same process of: '...get a copy of an internet resource and write it to a file... If gb_arg_pull And go_fso.FileExists( ps_file ) Then Call s_log( cs_fac & "already downloaded, skipping file `" & ps_file & "`..." ) Exit Sub End If 'check free space, i.e. don't waste time/resources downloading a file that we might not be able to save... ls_drive = go_fso.GetDriveName( ps_file ) If ls_drive = "" Then Call s_abort( cs_fac & "failed to determine drive from `" & ps_file & "`..." ) If gb_arg_pull Then If Not fb_free_gb( ls_drive, cd_min_free_space ) Then Call s_abort( cs_fac & "insufficient free space on drive `" & ls_drive & "`..." ) End If End If 'there are four stages to retrieving a web page (aka downloading a file)... '...stage 1 - prepare the HTTP object... '...stage 2 - execute an HTTP protocol 'get' action - which copies a web page or file (from the internet) into a RAM buffer... '...stage 3 - copy the HTTP buffer (in RAM) to a stream buffer (also in RAM)... '...stage 4 - write the stream buffer (from RAM) to disk... If gb_arg_pull Then call s_log( cs_fac & "getting `" & ps_file & "`..." ) lb_got = False ll_try = 0 Do ll_try = ll_try + 1 ll_send_status = 999 'in case the execution of an object method fails, and doesn't actually... ll_http_status = 999 'actually return a useful status, we load these with 999, so ... ll_stream_status = 999 'we can detect if a method call fails without returning anything... Set lo_http = CreateObject( "MSXML2.ServerXMLHTTP" ) ld_http_start = Now 'so that we can time a '-pull', i.e. time the download of an actual wanted file... 'stage 1 - prepare to fetch a web page, or file... On Error Resume Next 'the next command prepares an HTTP 'get' to fetch a web page over the internet... lo_http.Open "GET", ps_url, False 'third param False, means we will wait (for completion or timeout or failure)... ll_open_status = Err.Number On Error Goto 0 If ll_open_status <> 0 Then Call s_log( cs_fac & "http open status `" & fs_status( ll_open_status ) & "` calling with URL of `" & ps_url & "`, script aborting..." ) WScript.Quit( ll_open_status ) End If 'if we get here, then the 'call/method' to prepare the HTTP object/engine was sucessful... '...i.e. we successfully prepared to fetch a page... '...but we haven't actually retrieved the page or file yet... 'stage 2 - now we 'send' the 'get' command to the target web-server to fetch the page or file (into a RAM buffer)... On Error Resume Next lo_http.Send() ll_send_status = Err.Number On Error Goto 0 If ll_send_status = 0 Then ll_http_status = lo_http.Status If ll_http_status = 200 Then lb_got = True ld_http_end = Now Else Call s_issue( cs_fac & "http download failed, status `" & fs_status( ll_http_status ) & "`..." ) End If Else Call s_issue( cs_fac & "http send failed, status `" & fs_status( ll_send_status ) & "`..." ) End If If Not lb_got Then WScript.Sleep 1000 Loop Until ( lb_got = True ) Or ( ll_try >= gl_arg_tries ) 'when we get here, we may, or may not, have successfully retreived the page/file... '...so now check to see whether we actually succeeded in 'getting' a page/file... If Not lb_got Then If gb_arg_pull then Call s_issue( cs_fac & "exhausted tries, script aborting..." ) If ll_send_status = 0 Then WScript.Quit( lo_http.Status ) Else WScript.Quit( ll_send_status ) End If Else Call s_issue( cs_fac & "exhausted tries, skipping/ignoring page..." ) Exit Sub 'return to discovery... End If End if 'if we get here then the get/fetch/download of a page/file into RAM appears to have been successful... Call s_log( cs_fac & "duration `" & Trim( fs_duration( ld_http_start, ld_http_end ) ) & "`..." ) ll_http_seconds = CLng( DateDiff( "s", ld_http_start, ld_http_end ) ) 'stage 3 - now we need to copy the contents of the HTTP object RAM buffer into a stream buffer, so that the contents can later be saved to disk... Set lo_stream = CreateObject( "ADODB.Stream" ) lo_stream.Open lo_stream.Type = 1 'adTypeBinary On Error Resume Next lo_stream.Write lo_http.ResponseBody 'copy the http page/file to a stream object... ll_stream_status = Err.Number On Error Goto 0 If ll_stream_status <> 0 Then Call s_issue( cs_fac & "failed to copy http body to ADO stream, status `" & ll_stream_status & "`..." ) If gb_arg_pull Then WScript.Quit( ll_stream_status ) Else Exit Sub End If End If 'if we get here then the copy of the raw web page/file from HHTP object RAM buffer to stream RAM buffer was successful... '...which means that we no longer need the HTTP object, so let's free up RAM by destroying the object... Set lo_http = Nothing 'the http object can be discarded now, because we have the page/file within the stream object... ' lo_stream.Position = 0 'set to start of stream - we don't appear to need this command - but left here for reference... 'if we were doing a file download (and not just a web page fetch), then display/report some download rate statistics... If gb_arg_pull Then ld_size_KB = Round( lo_stream.Size / ( 1000.0 ), 0 ) ld_size_MB = Round( lo_stream.Size / ( 1000.0 * 1000.0 ), 1 ) ld_size_GB = Round( lo_stream.Size / ( 1000.0 * 1000.0 * 1000.0 ), 2 ) ls_size = ld_size_KB & "KB" If ld_size_MB > 1.0 Then ls_size = ld_size_MB & "MB" If ld_size_GB > 1.0 Then ls_size = ld_size_GB & "GB" If ll_http_seconds = 0 Then ls_rate = "..." Else ld_KBs = Round( CDbl( ld_size_KB ) / CDbl( ll_http_seconds ), 0 ) ld_MBs = Round( CDbl( ld_size_MB ) / CDbl( ll_http_seconds ), 1 ) If ld_MBs < 1.0 Then ls_rate = " at rate `" & CStr( ld_KBs ) & "KB/s`..." Else ls_rate = " at rate `" & CStr( ld_MBs ) & "MB/s`..." End If End If Call s_log( cs_fac & "downloaded `" & ls_size & "`" & ls_rate ) End If 'stage 4 - write the stream buffer (from RAM) to disk... '...but first check whether we have enough free disk space to actually be able to write the contents of the stream RAM buffer to disk... If gb_arg_pull Then ld_required_GB = ld_size_GB + 1.0 If Not fb_free_gb( ls_drive, ld_required_GB ) Then Call s_abort( cs_fac & "not enough free disk space on `" & ls_drive & "` to write downloaded file..." ) End If Else ld_required_GB = 0.5 If Not fb_free_gb( ls_drive, ld_required_GB ) Then Call s_issue( cs_fac & "not enough free disk space on `" & ls_drive & "` to save web page..." ) Exit Sub End If End If 'now write the stream to disk, which... '...either writes the contents of a web page (i.e. raw HTML code/text) to a temporary file name... '...or writes the contents of the download file that we wanted to our target version download folder... If go_fso.FileExists( ps_file ) Then go_fso.DeleteFile( ps_file ) 'the .SaveToFile function requires that the target folder actually exists... '...but if the target folder does not exist, we don't get told, we just get an abstract '3004' error... '...see this page for ADO error status meanings: https://msdn.microsoft.com/en-us/library/ms681549 On Error Resume Next lo_stream.SaveToFile ps_file ll_save_status = Err.Number On Error Goto 0 If ll_save_status <> 0 Then Call s_log( cs_fac & "failed to save to file `" & ps_file & "`, status `" & ll_save_status & "`..." ) WScript.Quit( ll_save_status ) End If lo_stream.Close 'we longer need the contents of the stream RAM bufffer, so destroy it and free up RAM from the stream buffer... Set lo_stream = Nothing End Sub Sub s_download() Const cs_fac = "%s_download, " Dim ls_download 'the stages to download are fairly straight forward at this point... 'stage 1 - load the list of download links if there wasn't a discovery phase... 'stage 2 - call the work load processing routine to do the actual file download work... Call s_log( "" ) Call s_log( cs_fac & "starting..." ) 'did we perform a discovery phase becoming reaching this point? If Not gb_arg_discover Then 'if there was no discovery phase just now... If Not go_fso.FileExists( gs_downloads_spec ) Then 'then has the list of downloads already been discovered? Call s_log( cs_fac & "cannot find downloads file `" & gs_downloads_spec & "`..." ) Exit Sub End If 'stage 1 - load the list of download links... Set go_downloads_chan = go_fso.OpenTextFile( gs_downloads_spec, ci_for_reading ) Do 'load the list of previously discovered download links... ls_download = go_downloads_chan.ReadLine If gd_downloads.Exists( ls_download ) Then 'something weird has hapenned (a manual edit perhaps?)... Call s_log( cs_fac & "dropping duplicate download `" & ls_download & "`..." ) Else gd_downloads.Add ls_download, "" 'save it to our list of downloads to process... End If Loop Until go_downloads_chan.AtEndOfStream go_downloads_chan.Close Call s_log( cs_fac & "loaded `" & gd_downloads.Count & "` potential downloads..." ) End If If gd_downloads.Count = 0 Then Call s_log( cs_fac & "downloads list is empty, nothing to download..." ) Exit Sub End If 'stage 2 - Heigh Ho!! Off to work we go!! Call s_work() 'now get on with actual downloading... Call s_log( "" ) Call s_log( cs_fac & "done..." ) End Sub Sub s_work() Const cs_fac = "%s_work, " Dim ls_url, ls_file, ls_spec, lo_folder Dim ld_todo, ld_doing, ld_done, ll_max_slots, ll_slot, ls_command Dim ll_loops, lb_busy, lb_wanted, ls_extn Dim lo_regexp, lc_matches, lo_match, ls_match 'this routine has three main stages... '...stage 1 - is to read through the list of discovered potential downloads which is stored in gd_downloads... '... and to filter this list in to another list of actual work todo (i.e. load the ld_todo list)... '...and the next two stages are both embedded within a work processing loop... '...stage 2 - which firstly looks for empty slots and moves work from ld_todo to ld_doing... '...stage 3 - and secondly moves completed work from ld_doing to ld_done. Set ld_todo = CreateObject( "Scripting.Dictionary" ) 'the list of work to be done, i.e. downloads to initiate... Set ld_doing = CreateObject( "Scripting.Dictionary" ) 'the currently active running downloads... Set ld_done = CreateObject( "Scripting.Dictionary" ) 'the completed downloads... 'stage 1 - now process the list of potential downloads, and determine... '...which of them pattern match to download... '...which of them pattern match to ignore... '...which of them have already been downloaded... '...and so populate the 'ld_todo' list with the downloads that are required... '...and after all that, check whether we still actually have anything to do... For Each ls_url In gd_downloads ls_file = fs_url_to_file( ls_url ) lb_wanted = True 'WScript.Echo ">>>" & ls_url & "<<< >>>" & ls_file & "<<<" If gb_arg_match Then lb_wanted = False Set lo_regexp = CreateObject( "VBScript.RegExp" ) lo_regexp.IgnoreCase = True lo_regexp.MultiLine = True lo_regexp.Global = True lo_regexp.Pattern = gs_arg_match Set lc_matches = lo_regexp.Execute( ls_file ) For Each lo_match In lc_matches If lo_match <> "" Then lb_wanted = True Next Set lo_regexp = Nothing End If If gb_arg_ignore Then Set lo_regexp = CreateObject( "VBScript.RegExp" ) lo_regexp.IgnoreCase = True lo_regexp.MultiLine = True lo_regexp.Global = True lo_regexp.Pattern = gs_arg_ignore Set lc_matches = lo_regexp.Execute( ls_file ) For Each lo_match In lc_matches If lo_match <> "" Then lb_wanted = False Next Set lo_regexp = Nothing End If If True Then Set lo_regexp = CreateObject( "VBScript.RegExp" ) lo_regexp.IgnoreCase = True lo_regexp.MultiLine = True lo_regexp.Global = True lo_regexp.Pattern = ".*_FR\.zip|.*_JA\.zip|.*_ZH\.zip" Set lc_matches = lo_regexp.Execute( ls_file ) For Each lo_match In lc_matches If lo_match <> "" Then lb_wanted = False Next Set lo_regexp = Nothing End If If Not lb_wanted Then Call s_log( cs_fac & "ignoring unwanted file `" & ls_file & "`..." ) Else ls_spec = gs_downloads_path & cs_bs & ls_file If ld_todo.Exists( ls_spec ) Then Call s_log( cs_fac & "already in list `" & ls_file & "`..." ) Else If gb_arg_delete And go_fso.FileExists( ls_spec ) Then 'v1.15... delete previously downloaded file? go_fso.DeleteFile( ls_spec ) Call s_log( cs_fac & "deleted `" & ls_spec & "`..." ) End If If go_fso.FileExists( ls_spec ) Then Call s_log( cs_fac & "already downloaded `" & ls_file & "`..." ) Else If gb_arg_check Then Set lo_folder = go_fso.GetFolder( gs_arg_check ) If fb_check_found( ls_file, lo_folder ) Then Call s_log( cs_fac & "found file `" & ls_file & "` under check path, skipping..." ) Else ld_todo.Add ls_spec, ls_url End If End If End If End If End If Next If ld_todo.Count = 0 Then Call s_log( cs_fac & "all files were either ignored or have already been downloaded, nothing to do..." ) Exit Sub End If Call s_log( "" ) Call s_log( cs_fac & "found `" & ld_todo.Count & "` file(s) to be downloaded..." ) For Each ls_spec In ld_todo.Keys Call s_log( cs_fac & " file: " & fs_url_to_file( ld_todo.Item( ls_spec ) ) ) Next 'now get ready to start actual downloads... '...but first... '...let's baseline our free RAM... gd_ram_free_pct_cur = 0.0 'these three will be continually re-populated during the loop below... gd_ram_free_pct_min = 100.0 '...each time that function fd_ram_free_pct() is called... gd_ram_free_pct_max = 0.0 gd_ram_free_pct_stt = fd_ram_free_pct() 'get the starting free RAM percentage, and initially populate the three global variables above... '...and then initialise the "download" slots... ll_max_slots = fx_min( gl_arg_slots, ld_todo.Count ) Redim ls_slots( ll_max_slots ) 'this array has two purposes, empty element = no download in progress, and an occupied element = URL of file to download AND occupied slot... Redim lo_slots( ll_max_slots ) 'this array holds the spawned sub-process object... For ll_slot = 1 To ll_max_slots ls_slots( ll_slot ) = "" 'an empty download slot, i.e. we have the capability to initiate a download by spawning a sub-process... Set lo_slots( ll_slot ) = Nothing 'does not yet contain a reference to a spawned sub-process... Next 'stages 2 and 3 - Scoody-dooby-doo, where are you? We've got some work to do now... 'there are two inner For loops within the main Do loop... '...inner loop A - stage 2 - looks for work to initiate, i.e. to load in to an empty slot... '...inner loop B - stage 3 - looks for completed work, and thus reverts slots back to an empty status... ll_loops = 0 lb_busy = False 'we cannot possibly be busy yet... Do ll_loops = ll_loops + 1 'just a simple iteration counter... Call s_log( "" ) Call s_log( cs_fac & "loop: " & ll_loops & " start: todo: " & ld_todo.Count & " doing: " & ld_doing.Count & " done: " & ld_done.Count & " free: " & fs_pct(fd_ram_free_pct()) ) 'inner loop A - stage 2 - start - look for work to do... If Not lb_busy Then 'if all slots are not already fully occupied, then we can take on more work... For Each ls_spec In ld_todo.Keys 'the two loops are nested in this order because there are usually more files than slots... lb_busy = True 'now assume that we're busy, i.e. that we're not going to be able to initiate new work... 'but note that if we do actually add new work, then this flag gets reset, because... 'there could yet still be more free slots to do more work... For ll_slot = 1 To ll_max_slots 'look for an empty slot... If ls_slots( ll_slot ) = "" Then 'we've found an empty slot... ls_url = ld_todo.Item( ls_spec ) Call s_log( cs_fac & "...starting slot: " & ll_slot & " file: " & go_fso.GetFileName( ls_spec ) ) 'now we actually spawn ourselves, i.e. this script spawns itself... '...create a command line to run (note how we use the -pull argument)... ls_command = "cscript //nologo " & gs_script_name & ".vbs -vers " & gs_arg_version & " -pull " & cs_quotes & ls_url & cs_quotes & " " & cs_quotes & ls_spec & cs_quotes If gb_arg_debug Then 'pass on the fact if we're debugging... ls_command = ls_command & " -debug" Call s_log( cs_fac & "spawning command `" & ls_command & "`..." ) End If Set lo_slots( ll_slot ) = go_wsh.Exec( ls_command ) 'Alakazam!! (spawn the sub-process)... Call s_priority( lo_slots( ll_slot ).ProcessID, gs_arg_priority ) 'reduce the priority of the spawned sub-process... ls_slots( ll_slot ) = ls_spec 'to indicate a busy/loaded slot... ld_todo.Remove( ls_spec ) 'move this piece of work from the todo list... ld_doing.Add ls_spec, "" 'to the doing list... WScript.Sleep 500 'wait for half a second, i.e. give the command enough time to fail completely/quickly... lb_busy = False 'we populated an empty slot, so there could be other empty slots, so let's look for another... Exit For 'no need to step through remaining slots for THIS file, as this file has now been allocated to a slot, so let's just skip slot processing and move on to the next file... End If Next 'll_slot If lb_busy Then Exit For 'if there are no longer any slots available, then there is no need to step through the remaining files... Next 'ls_spec... End If 'inner loop A - stage 2 - end - that's us done with looking for work to initiate, for this iteration... 'inner loop B - stage 3 - start - now check for completed work... lb_busy = True 'assume that all slots that can be occupied, are occupied... For ll_slot = 1 To ll_max_slots 'we now check for finished sub-processes (may have finished ok, or may have failed)... If ls_slots( ll_slot ) <> "" Then 'if it is/was an active slot... ls_spec = ls_slots( ll_slot ) 'remind ourselves of the file spec that we were supposed to be pulling down... Select Case lo_slots( ll_slot ).Status 'has the spawned exec process finished? Case 0 'it is still running... Call s_log( cs_fac & "...waiting slot: " & ll_slot & " file: " & go_fso.GetFileName( ls_spec ) ) Case 1 'it has finished... Call s_log( cs_fac & "...finished slot: " & ll_slot & " exitcode: " & lo_slots( ll_slot ).ExitCode ) Select Case lo_slots( ll_slot ).ExitCode 'grab the final exit code from spawned sub-process... Case 0 'it seems to have gone okay... If Not go_fso.FileExists( ls_spec ) Then 'one of our download files is missing... Call s_issue( cs_fac & "...unexpected, slot `" & ll_slot & "` was successful but cannot find file `" & go_fso.GetFileName( ls_spec ) & "`..." ) End If Case Else 'the -pull of a file that we wanted to download has failed for some reason... Call s_issue( cs_fac & "...slot `" & ll_slot & "` failed with exitcode `" & lo_slots( ll_slot ).ExitCode & "`..." ) End Select ld_doing.Remove( ls_spec ) 'remove the completed entry from the doing list... ld_done.Add ls_spec, "" 'and note it in the done list... ls_slots( ll_slot ) = "" 'and now mark the slot as free... lb_busy = False 'there is an empty slot now, so don't sleep, and instead we'll quickly loop again to potentially start another download... Call s_std( "stdout", lo_slots( ll_slot ).StdOut.ReadAll ) 'report the results... If lo_slots( ll_slot ).ExitCode <> 0 Then 'if the spawn sub-process had problems... Call s_std( "stderr", lo_slots( ll_slot ).StdErr.ReadAll ) '...then list the errors too... End If Set lo_slots( ll_slot ) = Nothing 'Avada Kedavra!! (slot has finished, so destroy the spawned/exec process object)... Case Else 'this should never happen... Call s_abort( cs_fac & "...unexpected exec object status `" & lo_slots( ll_slot ).Status & "`..." ) End Select End If Next 'll_slot 'inner loop B - stage 3 - end - that's us done with checking for completed work, for this iteration... Call s_log( cs_fac & "loop: " & ll_loops & " end: todo: " & ld_todo.Count & " doing: " & ld_doing.Count & " done: " & ld_done.Count & " free: " & fs_pct( fd_ram_free_pct() ) ) If lb_busy Then WScript.Sleep gl_arg_sleep * 1000 'if we didn't initiate anything AND we didn't complete anything, then sleep... Loop Until ( ( ld_todo.Count = 0 ) And ( ld_doing.Count = 0 ) ) 'until there is nothing to initiate AND eveything being done is complete... Call s_log( "" ) 'now report free RAM, four figures: start/low/high/now... Call s_log( cs_fac & "RAM free start: " & fs_pct( gd_ram_free_pct_stt ) ) Call s_log( cs_fac & "RAM free min: " & fs_pct( gd_ram_free_pct_min ) ) Call s_log( cs_fac & "RAM free max: " & fs_pct( gd_ram_free_pct_max ) ) Call s_log( cs_fac & "RAM free current: " & fs_pct( fd_ram_free_pct() ) ) End Sub Sub s_std( ps_type, ps_std ) 'splits the text from stdout, or stderr, and displays it... Const cs_fac = "%s_std, " 'an extra space on the end here, for pretty text line-up... Dim ls_std, ls_text, ll_i ls_std = Trim( ps_std ) If Len( ls_std ) > 0 Then If gb_arg_debug Then Call s_log( cs_fac & "..." & ps_type & "..." ) ls_text = Split( ls_std, vbCrlf ) 'convert string to an array... For ll_i = 0 To UBound( ls_text ) If Trim( ls_text( ll_i ) ) <> "" Then Call s_log( cs_fac & "..." & ls_text( ll_i ) ) Next End If End Sub Sub s_versions() Const cs_fac = "%s_versions, " 'for details re regular expressions in VBScript, see here: ' http://msdn.microsoft.com/en-us/library/ms974570.aspx 'this Sub repeatedly calls another Sub to populate three lists, each of which is keyed by version... '...a list of depths per version '...a list of matches per version '...a list of tech notes per version '...examples of which are: ' Call s_setv( version, depth, match, tech notes ) ' Call s_setv( "v9.9.9.9", 2, ".*9\.9\.9\.9.*", "TECH123456+123" ) '...and there is a fourth list, which is also keyed by version, which is the list of ignores... '...and scattered below are a few instances of these... 'NB50x0 de-dupe (PDDO) appliances... Call s_setv( "v1.2" ,1, ".*" , "TECH147814,DOC3413+1" ) Call s_setv( "v1.3.0.1b" ,1, ".*" , "TECH162312,DOC4108" ) Call s_setv( "v1.3.0.2b" ,1, ".*" , "TECH166091,DOC4464" ) Call s_setv( "v1.4" ,1, ".*" , "TECH168925,DOC4649" ) Call s_setv( "v1.4.1.1" ,1, ".*" , "TECH172069,TECH172070" ) Call s_setv( "v1.4.2" ,1, ".*" , "TECH186726,DOC5447" ) Call s_setv( "v1.4.3.1" ,1, ".*" , "TECH194055,DOC5799" ) Call s_setv( "v1.4.4" ,1, ".*" , "TECH205535,DOC6430" ) Call s_setv( "v1.4.5" ,1, ".*" , "TECH224577,DOC7661,DOC7662" ) 'NB52x0 master/media server appliances... Call s_setv( "v1.1.0.2" ,1, ".*" , "TECH150229,TECH147813" ) Call s_setv( "v1.2b" ,1, ".*" , "TECH164242,DOC3778" ) Call s_setv( "v2.0" ,1, ".*" , "TECH169143,DOC4583,DOC4598" ) Call s_setv( "v2.0.1" ,1, ".*" , "TECH172673,DOC4726" ) Call s_setv( "v2.0.2" ,1, ".*" , "TECH179335,DOC5137" ) Call s_setv( "v2.0.3" ,1, ".*" , "TECH181689,DOC5345" ) Call s_setv( "v2.5b" ,1, ".*2.5-2.*|.*2.5.0.*|.*\.pdf" , "TECH198449,DOC5512,TECH148678" ) Call s_setv( "v2.5.1b" ,1, ".*2.5.1.*|.*\.pdf" , "TECH199726,DOC5797,TECH148678" ) Call s_setv( "v2.5.2" ,1, ".*2.5.2.*|.*\.pdf" , "TECH202301,DOC6161,TECH148678" ) Call s_setv( "v2.5.3" ,1, ".*2.5.3.*|.*\.pdf" , "TECH204639,DOC6492,TECH148678" ) Call s_setv( "v2.5.4" ,1, ".*2.5.4.*|.*\.pdf|.*_254\.zip" , "TECH211140,DOC6088,TECH148678,DOC7047" ) Call s_setv( "v2.6.0.1" ,1, ".*2.6.0.1.*|.*\.pdf|.*_26.*\.zip" , "TECH205913,DOC6139,DOC6736,DOC7041" ) Call s_setv( "v2.6.0.2" ,1, ".*2.6.0.2.*|.*\.pdf|.*NBAPP_addon_.*7\.6\.0\.2.*|.*[27].*6.*0.*2.*\.zip" , "TECH215209,DOC7156,DOC7153,DOC7158" ) Call s_setv( "v2.6.0.3" ,1, ".*2.6.0.3.*|.*\.pdf|.*NBAPP_addon_.*7\.6\.0\.3.*|.*[27].*6.*0.*3.*\.zip" , "TECH217250,DOC7458,DOC7455,TECH224758,DOC7465" ) Call s_setv( "v2.6.0.4" ,1, ".*2.6.0.4.*|.*\.pdf|.*NBAPP_addon_.*7\.6\.0\.4.*|.*[27].*6.*0.*4.*\.zip" , "TECH223530,DOC7646,DOC7450,DOC6085,DOC7653,TECH223745" ) Call s_setv( "v2.6.1" ,1, ".*2.6.1.*|.*\.pdf|.*NBAPP_addon_.*7\.6\.1.*|.*[27].*6.*1.*\.zip" , "TECH215210,DOC7939" ) Call s_setv( "v2.6.1.1" ,1, ".*2.6.1.1.*|.*\.pdf|.*NBAPP_addon_.*7\.6\.1\.1.*|.*[27].*6.*1.*1.*\.zip" , "TECH227005,DOC8012,DOC8021,DOC7793,DOC6085" ) Call s_setv( "v2.6.1.2" ,1, ".*2.6.1.2.*|.*\.pdf|.*NBAPP_addon_.*7\.6\.1\.2.*|.*[27].*6.*1.*2.*\.zip" , "TECH228624,DOC8185,DOC8194,DOC8198,DOC6085" ) Call s_setv( "v2.7.1" ,1, ".*2.7.1.*|.*\.pdf|.*NBAPP_addon_.*7\.7\.1.*|.*[27].*7.*\.zip" , "95813,97639,3705,76639,76667,4603" ) Call s_setv( "v2.7.2" ,1, ".*2.7.2.*|.*\.pdf|.*NBAPP_addon_.*7\.7\.2.*|.*[27].*7.*\.zip" , "97985,100512,100523,99946,100627,4603" ) 'traditional NetBackup... 'these older versions do not have main/root/top web pages, so ranges and specific tech note numbers have had to be used... Call s_setv( "v3.4" ,1, "^NB_34_.*" , "TECH21913,TECH23554,TECH24445" ) Call s_setv( "v4.5mp1" ,1, "^NB.*_45_1.*" , "TECH21117+4" ) Call s_setv( "v4.5mp2" ,1, "^NB.*_45_2.*" , "TECH22449+3" ) Call s_setv( "v4.5mp3" ,1, "^NB.*_45_3.*" , "TECH23731+3" ) Call s_setv( "v4.5mp4" ,1, "^NB.*_45_4_M.*" , "TECH24871+5" ) Call s_setv( "v4.5mp5" ,1, "^NB.*_45_5_M.*" , "TECH26862+3" ) Call s_setv( "v4.5mp6" ,1, "^NB.*_45_6_M.*" , "TECH29912+13" ) Call s_setv( "v4.5mp7" ,1, "^NB.*_45_7_M.*" , "TECH32832+64,TECH34168,TECH34829+11,TECH35009" ) Call s_setv( "v4.5mp8" ,1, "^NB.*_45_8_M.*" , "TECH36041+62,TECH36215,TECH36236,TECH43149" ) Call s_setv( "v4.5mp9" ,1, "^NB.*_45_9_M.*" , "TECH43733+46,TECH44313+10,TECH47022+46" ) Call s_setv( "v4.5fp4" ,1, "^NB.*_45_4_F.*" , "TECH88636,TECH88889,TECH88896,TECH88903,TECH88910" ) Call s_setv( "v4.5fp5" ,1, "^NB.*_45_5_F.*" , "TECH27043+4" ) Call s_setv( "v4.5fp6" ,1, "^NB.*_45_6_F.*" , "TECH29927+10" ) Call s_setv( "v4.5fp7" ,1, "^NB.*_45_7_F.*" , "TECH33092+68,TECH34167,TECH34817+11,TECH35100" ) Call s_setv( "v4.5fp8" ,1, "^NB.*_45_8_F.*" , "TECH36334+63,TECH39348,TECH43151" ) Call s_setv( "v4.5fp9" ,1, "^NB.*_45_9_F.*" , "TECH44123+47,TECH44302+10,TECH47069+45" ) Call s_setv( "v5.0.s0428" ,1, ".*_50_S0428.*" , "TECH30802+7" ) Call s_setv( "v5.0mp1" ,1, ".*_50_1.*_M.*" , "TECH31559+52,TECH31741,TECH31784" ) Call s_setv( "v5.0mp2" ,1, ".*_50_2.*_M.*" , "TECH32333+23,TECH32671,TECH32672" ) Call s_setv( "v5.0mp3" ,1, ".*_50_3.*_M.*" , "TECH33854+69,TECH35101+1,TECH36870+1" ) Call s_setv( "v5.0mp4" ,1, ".*_50_4.*_M.*" , "TECH36117+75,TECH36549,TECH36872+1" ) Call s_setv( "v5.0mp5" ,1, ".*_50_5.*_M.*" , "TECH38267+66,TECH39349,TECH43152,TECH44325+8,TECH44729+1,TECH45504+7" ) Call s_setv( "v5.0mp6" ,1, ".*_50_6.*_M.*" , "TECH45403+67,TECH46891,TECH46963+58" ) Call s_setv( "v5.0mp7" ,1, ".*_50_7.*_M.*" , "TECH49887+70,TECH51442,TECH55619+1" ) Call s_setv( "v5.0mp8" ,1, ".*_50_8.*_M.*" , "TECH59565" ) Call s_setv( "v5.1mp1" ,1, ".*_51_1.*_M.*" , "TECH34578+64,TECH35104+1,TECH36875+1,TECH38242,TECH38584+5" ) Call s_setv( "v5.1mp2" ,1, ".*_51_2.*_M.*" , "TECH34628,TECH36968+65,TECH37440,TECH38593+5,TECH39033" ) Call s_setv( "v5.1mp3" ,1, ".*_51_3.*_M.*" , "TECH38624+66,TECH39351,TECH42569+14,TECH43153,TECH44071,TECH44334+10,TECH44731+10" ) Call s_setv( "v5.1mp4" ,1, ".*_51_4.*_M.*" , "TECH45014+77,TECH45575,TECH45989,TECH46892+69,TECH49359,TECH49843" ) Call s_setv( "v5.1mp5" ,1, ".*_51_5.*_M.*" , "TECH47509+90,TECH47618,TECH47675" ) Call s_setv( "v5.1mp6" ,1, ".*_51_6.*_M.*" , "TECH49977+77,TECH51240,TECH51443" ) Call s_setv( "v5.1mp7" ,1, ".*_51_7.*_M.*" , "TECH59417+81,TECH60077+2" ) Call s_setv( "v6.0.s07" ,1, ".*_60_0S0007_M.*|.*\.pdf", "TECH44345+11" ) Call s_setv( "v6.0mp1" ,1, ".*_60_1.*_M.*|.*\.pdf" , "TECH45674+39,TECH46425,TECH46680+3" ) Call s_setv( "v6.0mp2" ,1, ".*_60_2.*_M.*|.*\.pdf" , "TECH46822+63,TECH47154,TECH47688,TECH47958" ) Call s_setv( "v6.0mp3" ,1, ".*_60_3.*_M.*|.*\.pdf" , "TECH48334+32" ) Call s_setv( "v6.0mp4" ,1, ".*_60_4.*_M.*|.*\.pdf" , "TECH49639+49,TECH50252+2,TECH51444,TECH56274" ) Call s_setv( "v6.0mp5" ,1, ".*_60_5.*_M.*|.*\.pdf" , "TECH52922+51,TECH53046,TECH53090,TECH53100,TECH54499,TECH58330,TECH58331" ) 'onwards, these v6.x (and higher) versions have a leading main/root/top tech note... Call s_setv( "v6.0mp6" ,1, ".*_60_6.*_M.*|.*\.pdf" , "TECH58042,TECH57962+54" ) Call s_setv( "v6.0mp7" ,1, ".*_60_7_.*|.*\.pdf" , "TECH63555,TECH63274+59,TECH63468+2,TECH64276" ) Call s_setv( "v6.0mp7s01" ,1, ".*_60_7S01_M.*|.*\.pdf" , "TECH67963,TECH67844+3" ) Call s_setv( "v6.0mp7s02" ,1, ".*_60_7S02_M.*|.*\.pdf" , "TECH128019,TECH127974+17,TECH128165,TECH129110" ) Call s_setv( "v6.5" ,1, ".*_6.*5_.*|.*\.pdf" , "TECH129530,TECH71614,TECH71307,TECH72889,TECH70321,TECH51321,TECH48687" ) Call s_setv( "v6.5.1" ,1, ".*6\.5\.1.*|.*\.pdf" , "TECH58236,TECH56305,TECH55909+29,TECH56082+1,TECH56822,TECH56874+8,TECH57882+1" ) Call s_setv( "v6.5.2" ,1, ".*6\.5\.2.*|.*\.pdf" , "TECH60065,TECH60729+37,TECH61473+24,TECH61508,TECH62359+3" ) Call s_setv( "v6.5.3" ,1, ".*6\.5\.3.[^1].*|.*\.pdf", "TECH65404,TECH65275+39,TECH65334+2,TECH65342+4,TECH125192" ) Call s_setv( "v6.5.3.1" ,1, ".*6\.5\.3\.1.*|.*\.pdf" , "TECH67957,TECH67872+38" ) Call s_setv( "v6.5.4" ,1, ".*6\.5\.4.*|.*\.pdf" , "TECH71200,TECH71281+85,TECH73043,TECH74367,TECH74900,TECH67795,TECH67838,TECH124572,TECH125193,TECH125657" ) Call s_setv( "v6.5.5" ,1, ".*6\.5\.5.*|.*\.pdf" , "TECH76642,TECH77275+46,TECH73814,TECH124571,TECH124590,TECH124769,TECH125456,TECH125468,TECH127844,TECH148678" ) Call s_setv( "v6.5.6" ,1, ".*6\.5\.6.*|.*\.pdf" , "TECH129076,TECH129316+47,TECH129531+2,TECH135709,TECH141606,TECH143778,TECH147402,TECH148678,TECH152708" ) Call s_setv( "v7.0" ,2, ".*7\.0_.*|.*\.pdf" , "TECH126230,TECH129188,TECH129834,TECH130438,TECH130743,TECH130744,TECH134983,TECH136812,TECH148678,TECH51267" ) Call s_setv( "v7.0.1" ,2, ".*7.0.1.*|.*\.pdf" , "TECH139088,TECH137370,TECH137767,TECH138254,TECH138266,TECH139335,TECH140567,TECH141502,TECH142799,TECH144387,TECH147404,TECH148678,TECH150081,TECH152726,TECH153190,TECH212154" ) Call s_setv( "v7.1" ,2, ".*7.1_.*|.*\.pdf" , "TECH125338,TECH148678,TECH153976,TECH154818,TECH156114,TECH156136,TECH156810" ) Call s_setv( "v7.1.0.1" ,2, ".*7.1.0.1.*|.*\.pdf" , "TECH158802,TECH148678,TECH162434,TECH163968" ) Call s_setv( "v7.1.0.2" ,2, ".*7.1.0.2.*|.*\.pdf" , "TECH165302,TECH142262,TECH148678,TECH159453,TECH160729,TECH165305,TECH170557,TECH170965,TECH174446,TECH176819" ) Call s_setv( "v7.1.0.3" ,2, ".*7.1.0.3.*|.*\.pdf" , "TECH174256,TECH148678,TECH176536,TECH177264,TECH178219,TECH178219,TECH179483,TECH184533" ) Call s_setv( "v7.1.0.4" ,2, ".*7.1.0.4.*|.*\.pdf" , "TECH181132,TECH148678" ) Call s_setv( "v7.5" ,2, ".*7.5_.*|.*\.pdf" , "TECH148678,TECH180966,TECH182539,TECH185933,TECH205346" ) Call s_setv( "v7.5.0.1" ,2, ".*7.5.0.1.*|.*\.pdf" , "TECH184314,TECH148678" ) 'there never was a 7.5.0.2 release for NetBackup... Call s_setv( "v7.5.0.3" ,2, ".*7.5.0.3.*|.*\.pdf" , "TECH189607,TECH148678" ) Call s_setv( "v7.5.0.4" ,2, ".*7.5.0.4.*|.*\.pdf" , "TECH194138,TECH148678" ) Call s_setv( "v7.5.0.5" ,2, ".*7.5.0.5.*|.*\.pdf" , "TECH199269,TECH148678" ) Call s_setv( "v7.5.0.6" ,2, ".*7.5.0.6.*|.*\.pdf" , "TECH204644,TECH148678" ) Call s_setv( "v7.5.0.7" ,2, ".*7.5.0.7.*|.*\.pdf" , "TECH209024,TECH148678,TECH216367" ) 'v7.6.0.1 was a base release, not a patch release... Call s_setv( "v7.6.0.1" ,2, ".*7.6.0.1.*|.*\.pdf|.*_76_PDF.*" , "DOC6138,TECH213999,DOC6446" ) Call s_setv( "v7.6.0.2" ,2, ".*7.6.0.2.*|.*\.pdf|.*7602.*\.zip" , "TECH214999,TECH217531,TECH223790" ) Call s_setv( "v7.6.0.3" ,2, ".*7.6.0.3.*|.*\.pdf|.*7603.*\.zip" , "TECH217819,TECH156730+1,TECH218104,TECH223243,TECH223471,TECH223790,TECH224758" ) Call s_setv( "v7.6.0.4" ,2, ".*7.6.0.4.*|.*\.pdf|.*7604.*\.zip" , "TECH223695,DOC7450,DOC6085" ) Call s_setv( "v7.6.1" ,1, "..*7.6.1.*|.*\.pdf|.*761.*\.zip" , "DOC7675,DOC7939" ) Call s_setv( "v7.6.1.1" ,2, ".*7.6.1.1.*|.*\.pdf|.*7611.*\.zip" , "DOC7793,TECH226944,DOC6085" ) Call s_setv( "v7.6.1.2" ,2, ".*7.6.1.2.*|.*\.pdf|.*7612.*\.zip" , "DOC8198,TECH230565,DOC6085" ) Call s_setv( "v7.7" ,2, ".*77_.*|.*EEB.*\.pdf|.*7\.7_.*\.zip|.*7?5_.*|.*7?6_.*|.*7?7_.*|.*7?6 .*", "94423,4603" ) Call s_setv( "v7.7.1" ,1, ".*771_.*|.*EEB.*\.pdf|.*7\.7\.1_.*\.zip" , "76639,76667,4603" ) Call s_setv( "v7.7.2" ,1, ".*772_.*|.*EEB.*\.pdf|.*7\.7\.2_.*\.zip" , "99946,100627,4603" ) 'manuals for Appliance... Call s_setv( "man2.6.0.1" ,1, ".*" , "DOC7041" ) Call s_setv( "man2.6.0.2" ,1, ".*" , "DOC7158" ) Call s_setv( "man2.6.0.3" ,1, ".*" , "DOC7465" ) Call s_setv( "man2.6.0.4" ,1, ".*" , "DOC7653" ) Call s_setv( "man2.6.1" ,1, ".*" , "DOC7598" ) Call s_setv( "man2.6.1.1" ,1, ".*" , "DOC8012,DOC8021,DOC7793,DOC6085" ) Call s_setv( "man2.6.1.2" ,1, ".*" , "DOC8185,DOC8194,DOC8198,DOC6085" ) Call s_setv( "man2.7.1" ,1, ".*" , "97629,97639,76639,76667,4603" ) Call s_setv( "man2.7.2" ,1, ".*" , "100512,100523,99946,100627,4603" ) 'manuals for NetBackup... Call s_setv( "man4.5" ,3, ".*" , "http://docs.oracle.com/cd/E19670-01/index.html" ) Call s_setv( "man5.0" ,3, ".*" , "TECH50515" ) Call s_setv( "man5.1" ,3, ".*" , "TECH50516" ) Call s_setv( "man6.0" ,3, ".*" , "TECH50517" ) Call s_setv( "man6.5" ,3, ".*" , "TECH52878" ) Call s_setv( "man7" ,4, ".*" , "DOC5332" ) Call s_setv( "man7.0" ,3, ".*" , "TECH126327,TECH51267" ) Call s_setv( "man7.0.1" ,3, ".*" , "TECH139094" ) Call s_setv( "man7.1" ,3, ".*" , "DOC3906,TECH154178" ) Call s_setv( "man7.5" ,3, ".*" , "DOC5138" ) Call s_setv( "man7.6" ,3, ".*" , "DOC6488" ) Call s_setv( "man7.6.1" ,1, ".*" , "DOC7939" ) Call s_setv( "man7.6.1.1" ,1, ".*" , "DOC7793,DOC6085,DOC8126" ) Call s_setv( "man7.6.1.2" ,1, ".*" , "DOC8198,DOC6085" ) Call s_setv( "man7.7" ,2, ".*77_.*|.*EEB.*\.pdf|.*7\.7_.*\.zip|.*7?5_.*|.*7?6_.*|.*7?7_.*|.*7?6 .*", "94423,4603" ) Call s_setv( "man7.7.1" ,1, ".*771_.*|.*EEB.*\.pdf|.*7\.7\.1_.*\.zip" , "76639,76667,4603" ) Call s_setv( "man7.7.2" ,1, ".*772_.*|.*EEB.*\.pdf|.*7\.7\.2_.*\.zip" , "99946,100627,4603" ) 'others... Call s_setv( "selfservice" ,3, ".*" , "TECH228776,DOC8214+2,DOC8228,DOC8608" ) Call s_setv( "lbn-app-eeb" ,3, ".*" , "TECH145136" ) Call s_setv( "lbn-nbu-eeb" ,3, ".*" , "TECH74904" ) Call s_setv( "rootapp" ,3, ".*" , "https://www.veritas.com/support/en_US/58991.html" ) Call s_setv( "rootnbu" ,3, ".*" , "https://www.veritas.com/support/en_US/15143.html" ) Call s_setv( "openvms" ,1, ".*" , "TECH51267,TECH54499,TECH59565,TECH60761,TECH63470,TECH65308,TECH71349,TECH77313,TECH129355,TECH139335,TECH162434,TECH165305,TECH174235" ) 'compatibility... Call s_setv( "compatv6" ,2, ".*\.pdf|.*\.xls|.*\.doc" , "TECH70729,TECH48687,TECH43684,TECH43676,TECH22688,TECH45978,TECH43619,TECH43358,TECH56544,TECH64542" ) Call s_setv( "compatv7" ,2, "nbu_7.*\.pdf|nbu_7.*\.html|[Nn][Ee][Tt].*\.pdf|[Nn][Aa][Ss].*\.pdf" , "TECH59978,TECH31885" ) Call s_setv( "compatall" ,2, ".*\.pdf|nbu_.*\.html|.*\.xls|.*\.doc" , "TECH59978" ) 'specials... Call s_setv( "misc" ,1, ".*" , "misc" ) Call s_setv( "random" ,1, ".*" , "random" ) Call s_setv( "test" ,1, "^OpsCtr.*\.zip$" , "TECH184353,TECH189637,TECH194164,TECH199265,TECH204676,TECH209054" ) End Sub Sub s_setv( ps_version, pl_depth, ps_match, ps_techs ) 'this populates three dictionary objects (i.e. lists)... Const cs_fac = "%s_setv, " If gd_versions.Exists( ps_version ) Then Call s_log( cs_fac & "unexpected dual version `" & ps_version & "`, skipping..." ) Exit Sub End If gd_versions.Add ps_version, ps_techs gd_depths.Add ps_version, pl_depth gd_matches.Add ps_version, ps_match End Sub Sub s_create_log() Const cs_fac = "%s_create_log, " Dim ls_ads_username, ld_now, ls_now_yyyymmdd On Error Resume Next ls_ads_username = go_ads.UserName If Err Then ls_ads_username = "(not available)" On Error Goto 0 gs_log_spec = gs_downloads_path & cs_bs & "_" & gs_script_name & "-" & gs_arg_version & ".log" On Error Resume Next Set go_log_chan = go_fso.OpenTextFile( gs_log_spec, ci_for_writing, True ) Select Case Err.Number Case 0 Case 70 Call s_abort( cs_fac & "failed to open log file `" & gs_log_spec & "` for writing, another instance of this script is probably already running..." ) Case Else Call s_abort( cs_fac & "failed to open log file `" & gs_log_spec & "` for writing..." ) End Select On Error Goto 0 Call s_log( cs_stars ) Call s_log( "log file: " & gs_log_spec ) Call s_log( "script file: " & gs_script_spec ) Call s_log( "version: " & cs_script_version ) Call s_log( "run date: " & fs_datetime(Now) ) Call s_log( "arguments: " & fs_arguments_list() ) Call s_log( "" ) Call s_log( "username: " & go_net.UserName ) Call s_log( "domain: " & go_net.UserDomain ) Call s_log( "computer name: " & go_net.ComputerName ) Call s_log( "sys info: " & ls_ads_username ) Call s_log( "" ) Call s_log( "o/s version: " & fs_get_os_version() ) Call s_log( "run by: " & WScript.FullName ) Call s_log( "interactive: " & WScript.Interactive ) Call s_log( "script process: " & WScript.Name & " v" & WScript.Version & "." & WScript.BuildVersion ) Call s_log( "script engine: " & ScriptEngine & " v" & ScriptEngineMajorVersion & "." & ScriptEngineMinorVersion & "." & ScriptEngineBuildVersion ) Call s_log( cs_stars ) End Sub Sub s_arguments() Const cs_fac = "%s_arguments, " Dim ls_args_bad, ll_i, lo_regexp, lc_matches, ll_error, ls_lines If WScript.Arguments.Count = 0 Then Call s_usage() Call s_quit( "" ) End If ls_args_bad = "" If WScript.Arguments.Count < 1 Then ls_args_bad = ls_args_bad & cs_fac & "script requires at least one parameter..." & vbCrlf If WScript.Arguments.Count > 30 Then ls_args_bad = ls_args_bad & cs_fac & "script does not accept more than thirty parameters..." & vbCrlf gb_arg_all = False gb_arg_both = False gb_arg_check = False gs_arg_check = "" gb_arg_collect = False gb_arg_debug = False gb_arg_delete = False gb_arg_depth = False gs_arg_depth = "" gl_arg_depth = 0 gb_arg_discover = False gb_arg_download = False gb_arg_file = False gs_arg_file = "" gb_arg_ignore = False gs_arg_ignore = "" gb_arg_list = False gb_arg_match = False gs_arg_match = "" gb_arg_open = False gb_arg_priority = False gs_arg_priority = "" gb_arg_pull = False gs_arg_pull_url = "" gs_arg_pull_file= "" gb_arg_rediscover=False gb_arg_sleep = False gs_arg_sleep = "" gl_arg_sleep = 0 gb_arg_slots = False gs_arg_slots = "" gl_arg_slots = 0 gb_arg_techdoc = False gs_arg_techdoc = "" gb_arg_test = False gb_arg_tries = False gs_arg_tries = "" gl_arg_tries = 0 gb_arg_url = False gs_arg_url = "" gb_arg_version = False gs_arg_version = "" 'basic argument checking... For ll_i = 0 To WScript.Arguments.Count - 1 Select Case LCase( WScript.Arguments.Item( ll_i ) ) Case "-help", "-h", "-he", "-hel", "-?" Call s_usage() Call s_quit( "" ) ' Case "-all", "-a", "-al" 'not implemented, yet... ' If gb_arg_all Then ' ls_args_bad = ls_args_bad & cs_fac & "cannot specify -all more than once..." & vbCrlf ' End If ' gb_arg_all = True Case "-both", "-b", "-bo", "-bot" If gb_arg_both Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -both more than once..." & vbCrlf End If If gb_arg_discover Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -both and -discover..." & vbCrlf If gb_arg_download Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -both and -download..." & vbCrlf gb_arg_both = True gb_arg_discover = True gb_arg_download = True Case "-check", "-ch", "-che", "-chec" If gb_arg_check Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -check more than once..." & vbCrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "a valid folder path is required when using `-check`..." & vbCrlf Else gs_arg_check = WScript.Arguments.Item( ll_i + 1 ) ll_i = ll_i + 1 Select Case LCase( gs_arg_check ) Case "cd", "current", "here", "pwd" gs_arg_check = gs_script_path Case "mine" gs_arg_check = cs_def_check End Select If Not go_fso.FolderExists( gs_arg_check ) Then ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_check & "` is not a valid folder path used with `-check`..." & vbCrlf End If End If End If gb_arg_check = True Case "-collect", "-co", "-col", "-coll", "-colle", "-collec" If gb_arg_collect Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -collect more than once..." & vbCrlf End If gb_arg_collect = True Case "-debug", "-deb", "-debu" If gb_arg_debug Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -debug more than once..." & vbCrlf End If gb_arg_debug = True Case "-delete", "-del", "-dele", "-delet" If gb_arg_delete Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -delete more than once..." & vbCrlf End If gb_arg_delete = True Case "-depth", "-dep", "-dept" If gb_arg_depth Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -depth more than once..." & vbCrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "an integer is required when using `-depth`..." & vbCrlf Else gs_arg_depth = WScript.Arguments.Item( ll_i + 1 ) ll_i = ll_i + 1 If IsNumeric( gs_arg_depth ) Then gl_arg_depth = CLng( gs_arg_depth ) If ( gl_arg_depth < 1 ) Or ( gl_arg_depth > cl_max_depth ) Then ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_depth & "` is out of range `1` to `" & cl_max_depth & "` for `-depth`..." & vbCrlf End If Else ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_depth & "` is not a valid number used with `-depth`..." & vbCrlf End If End If End If gb_arg_depth = True Case "-discover", "-di", "-dis", "-disc", "-disco", "-discov", "-discove" If gb_arg_discover Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -discover more than once..." & vbCrlf End If If gb_arg_both Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -discover and -both..." & vbCrlf gb_arg_discover = True Case "-download", "-do", "-dow", "-down", "-downl", "-downlo", "-downloa" If gb_arg_download Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -download more than once..." & vbCrlf End If If gb_arg_both Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -download and -both..." & vbCrlf gb_arg_download = True Case "-file", "-f", "-fi", "-fil" If gb_arg_file Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -file more than once..." & vbCrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "a file-name is required when using `-file`..." & vbCrlf Else gs_arg_file = WScript.Arguments.Item( ll_i + 1 ) ll_i = ll_i + 1 If Not go_fso.FileExists( gs_arg_file ) Then ls_args_bad = ls_args_bad & cs_fac & "unable to locate specified file `" & gs_arg_file & "'..." & vbCrlf End If End If End If gb_arg_file = True Case "-ignore", "-i", "-ig", "-ign", "-igno", "-ignor" If gb_arg_ignore Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -ignore more than once..." & vbcrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "a string is required when using `-ignore`..." & vbCrlf Else gs_arg_ignore = WScript.Arguments.Item( ll_i + 1 ) ll_i = ll_i + 1 End If End If gb_arg_ignore = True Case "-list", "-l", "-li", "-lis" If gb_arg_list Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -list more than once..." & vbCrlf End If gb_arg_list = True Case "-match", "-m", "-ma", "-mat", "-matc" If gb_arg_match Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -match more than once..." & vbCrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "a string is required when using `-match`..." & vbCrlf Else gs_arg_match = WScript.Arguments.Item( ll_i + 1 ) ll_i = ll_i + 1 End If End If gb_arg_match = True Case "-open", "-o", "op", "-ope" If gb_arg_open Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -open more than once..." & vbCrlf End If gb_arg_open = True Case "-priority", "-pr", "-pri", "-prio", "-prior", "-priori", "-priorit" If gb_arg_priority Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -priority more than once..." & vbCrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "a string is required when using `-priority`..." & vbCrlf Else gs_arg_priority = WScript.Arguments.Item( ll_i + 1 ) ll_i = ll_i + 1 Select Case gs_arg_priority Case "low", "belownormal", "normal" Case Else ls_args_bad = ls_args_bad & cs_fac & "priority must be one of `low`, `belownormal`, or `normal`..." & vbCrlf End Select End If End If gb_arg_priority = True Case "-pull", "-pu", "-pul" If gb_arg_pull Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -pull more than once..." & vbCrlf Else If ( ll_i + 2 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "a url and a file-spec are required with `-pull`..." & vbCrlf Else gs_arg_pull_url = WScript.Arguments.Item( ll_i + 1 ) gs_arg_pull_file = WScript.Arguments.Item( ll_i + 2 ) ll_i = ll_i + 2 If Left( gs_arg_pull_url, 7 ) <> "http://" Then If Left( gs_arg_pull_url, 8 ) <> "https://" Then ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_pull_url & "` is not a valid URL used with `-pull`..." & vbCrlf End If End If 'WScript.Echo ">>>" & go_fso.GetBaseName( gs_arg_pull_file ) & "." & go_fso.GetExtensionName( gs_arg_pull_file ) & "<<<" ' If gs_arg_pull_file <> go_fso.GetBaseName( gs_arg_pull_file ) & "." & go_fso.GetExtensionName( gs_arg_pull_file ) Then ' ls_args_bad = ls_args_bad & cs_fac & "Value `" & gs_arg_pull_file & "` is not a valid file name used with `-pull`..." & vbCrlf ' End If End If End If gb_arg_pull = True Case "-rediscover", "-r", "-re", "-red", "-redi", "-redis", "-redisc", "-redisco", "-rediscov", "-rediscove" If gb_arg_rediscover Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -rediscover more than once..." & vbCrlf End If gb_arg_rediscover = True Case "-save", "-sa", "-sav" If gb_arg_save Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -save more than once..." & vbCrlf End If gb_arg_save = True Case "-sleep", "-sle", "-slee" If gb_arg_sleep Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -sleep more than once..." & vbCrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "an integer is required when using `-sleep..." & vbCrlf Else gs_arg_sleep = WScript.Arguments.Item( ll_i + 1 ) ll_i = ll_i + 1 If IsNumeric( gs_arg_sleep ) Then gl_arg_sleep = CLng( gs_arg_sleep ) If ( gl_arg_sleep < 1 ) Or ( gl_arg_sleep > cl_max_sleep ) Then ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_sleep & "` is out of range `1` to `" & cl_max_sleep & "` for `-sleep`..." & vbCrlf End If Else ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_sleep & "` is not a valid number used with `-sleep`..." & vbCrlf End If End If End If gb_arg_sleep = True Case "-slots", "-slo", "-slot" If gb_arg_slots Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -slots more than once..." & vbCrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "an integer is required when using `-slots`..." & vbCrlf Else gs_arg_slots = WScript.Arguments.Item( ll_i + 1 ) ll_i = ll_i + 1 If IsNumeric( gs_arg_slots ) Then gl_arg_slots = CLng( gs_arg_slots ) If ( gl_arg_slots < 1 ) Or ( gl_arg_slots > cl_max_slots ) Then ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_slots & "` is out of range `1` to `" & cl_max_slots & "` for `-slots`..." & vbCrlf End If Else ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_slots & "` is not a valid number used with `-slots`..." & vbCrlf End If End If End If gb_arg_slots = True Case "-techdoc", "-tec", "-tech", "-techd", "-techdo", "-tn" If gb_arg_techdoc Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -techdoc more than once..." & vbCrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "a 'TECHnnnnn' or 'DOCnnnnn' name, or number, is required with `-techdoc`..." & vbCrlf Else gs_arg_techdoc = WScript.Arguments.Item( ll_i + 1 ) ll_i = ll_i + 1 If Not IsNumeric( Left( gs_arg_techdoc, 1 ) ) Then 'v1.14 allow a Veritas number through... Select Case UCase( Left( gs_arg_techdoc, 3 ) ) Case "DOC", "TEC" 'the web site is case sensitive... gs_arg_techdoc = UCase( gs_arg_techdoc ) Case "HTT" Case Else ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_techdoc & "` is not a valid TECH/DOC name used with `-techdoc`..." & vbCrlf End Select End If End If End If gb_arg_techdoc = True Case "-test", "-tes" If gb_arg_test Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -test more than once..." & vbCrlf End If gb_arg_test = True Case "-tries", "-tr", "-tri", "-trie", "-try" If gb_arg_tries Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -tries more than once..." & vbCrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "an integer is required when using `-tries`..." & vbCrlf Else gs_arg_tries = WScript.Arguments.Item( ll_i + 1 ) ll_i = ll_i + 1 If IsNumeric( gs_arg_tries ) Then gl_arg_tries = CLng( gs_arg_tries ) If ( gl_arg_tries < 1 ) Or ( gl_arg_tries > cl_max_tries ) Then ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_tries & "` is out of range `1` to `" & cl_max_tries & "` for `-tries`..." & vbCrlf End If Else ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_tries & "` is not a valid number used with `-tries`..." & vbCrlf End If End If End If gb_arg_tries = True Case "-url", "-u", "-ur" If gb_arg_url Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -url more than once..." & vbCrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "a string is required when using `-url`..." & vbCrlf Else gs_arg_url = WScript.Arguments.Item( ll_i + 1 ) ll_i = ll_i + 1 End If End If gb_arg_url = True Case "-version", "-v", "-ve", "-ver", "-vers", "-versi", "-versio" If gb_arg_version Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -version more than once..." & vbCrlf Else If ( ll_i + 1 ) >= WScript.Arguments.Count Then ls_args_bad = ls_args_bad & cs_fac & "a version `v9.9[.9[.9]]` is required with `-verison`..." & vbCrlf Else gs_arg_version = LCase( WScript.Arguments.Item( ll_i + 1 ) ) ll_i = ll_i + 1 If Not gd_versions.Exists( gs_arg_version ) Then ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_version & "` is not a valid version name used with `-version`..." & vbCrlf End If End If End If gb_arg_version = True Case Else ls_args_bad = ls_args_bad & cs_fac & "unexpected argument `" & WScript.Arguments.Item( ll_i ) & "`..." & vbCrlf End Select Next 'check occluded arguments... If ( gb_arg_both Or gb_arg_rediscover Or gb_arg_discover Or gb_arg_download Or gb_arg_file ) And gb_arg_pull Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify any of -both, or -rediscover, or -discover, or -download, or -file with -pull..." & vbCrlf End If If ( Not gb_arg_both ) And ( Not gb_arg_discover ) And ( Not gb_arg_download ) And ( Not gb_arg_list ) And ( Not gb_arg_pull ) Then ls_args_bad = ls_args_bad & cs_fac & "must specify at least one of -list, -both, -discover, -download..." & vbCrlf End If If gb_arg_check And gb_arg_delete Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -check with -delete..." & vbCrlf End If If gb_arg_delete And ( Not gb_arg_download ) Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -delete without -download..." & vbCrlf End If If gb_arg_download And gb_arg_test Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -download and -test together..." & vbCrlf End If If gb_arg_file Then If ( Not gb_arg_version ) Or ( gs_arg_version <> "misc" ) Then ls_args_bad = ls_args_bad & cs_fac & "when using -file, then `-version misc` must also be specified..." & vbCrlf End If End If If gb_arg_rediscover And ( Not gb_arg_discover ) Then ls_args_bad = ls_args_bad & cs_fac & "can only specify -rediscover with -discover..." & vbCrlf End If If gb_arg_rediscover And gb_arg_techdoc Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -rediscover and -techdoc together..." & vbCrlf End If If gb_arg_techdoc And gb_arg_url Then ls_args_bad = ls_args_bad & cs_fac & "cannot specify -techdoc and -url together..." & vbCrlf End If If ls_args_bad <> "" Then ls_lines = Split( ls_args_bad, vbCrlf ) For ll_i = 0 To Ubound( ls_lines ) If ls_lines( ll_i ) <> "" Then Call s_log( ls_lines( ll_i ) ) Next Call s_abort( cs_fac & "problem with specified arguments and/or parameters..." ) End If 'the basic argument syntax and value checking above is now complete... '...now begin detailed validation and processing or command line arguments... '...i.e. more detailed checking of values, and checking for... '...mutually inclusive and/or mutually exclusive arguments... If gb_arg_version And ( gs_arg_version = "random" ) Then Randomize Dim ll_cnt, ll_random, ls_version, ls_versions(99) ll_cnt = 0 For Each ls_version In gd_versions.Keys Select Case ls_version Case "misc", "random", "test" Case Else If go_fso.FolderExists( gs_script_path & cs_bs & ls_version ) Then ll_cnt = ll_cnt + 1 ls_versions( ll_cnt ) = ls_version End If End Select Next If ll_cnt = 0 Then Call s_abort( cs_fac & "unable to locate a pre-existing folder for random download..." ) ll_random = Int( ll_cnt * Rnd ) + 1 gs_arg_version = ls_versions( ll_random ) Call s_log( cs_fac & "random version selected is `" & gs_arg_version & "`..." ) End If If Not gb_arg_tries Then gb_arg_tries = True gs_arg_tries = CStr( cl_def_tries ) gl_arg_tries = CLng( gs_arg_tries ) If gb_arg_debug And ( Not gb_arg_pull ) Then Call s_log( cs_fac & "defaulted -tries to `" & gs_arg_tries & "`..." ) End If If Not gb_arg_sleep Then gb_arg_sleep = True gs_arg_sleep = CStr( cl_def_sleep ) gl_arg_sleep = CLng( gs_arg_sleep ) If gb_arg_debug And ( Not gb_arg_pull ) Then Call s_log( cs_fac & "defaulted -sleep to `" & gs_arg_sleep & "`..." ) End If If Not gb_arg_slots Then gb_arg_slots = True gs_arg_slots = CStr( cl_def_slots ) gl_arg_slots = CLng( gs_arg_slots ) If gb_arg_debug And ( Not gb_arg_pull ) Then Call s_log( cs_fac & "defaulted -slots to `" & gs_arg_slots & "`..." ) End If If gb_arg_both Or gb_arg_discover Or gb_arg_download Or gb_arg_rediscover Then If Not gb_arg_version Then gb_arg_version = True gs_arg_version = cs_def_version If gb_arg_debug Then Call s_log( cs_fac & "defaulted -version to `" & gs_arg_version & "`..." ) End If If Not gb_arg_depth Then gb_arg_depth = True If gd_depths.Exists( gs_arg_version ) Then gl_arg_depth = gd_depths.Item( gs_arg_version ) Else Call s_log( cs_fac & "no default depth for version `" & gs_arg_version & "`..." ) gl_arg_depth = 1 End If gs_arg_depth = CStr( gl_arg_depth ) If gb_arg_debug Then Call s_log( cs_fac & "defaulted -depth to `" & gs_arg_depth & "`..." ) End If If Not gb_arg_check Then gb_arg_check = True If gb_arg_version Then gs_arg_check = gs_script_path & cs_bs & gs_arg_version Else gs_arg_check = gs_script_path End If If gb_arg_debug Then Call s_log( cs_fac & "defaulted -check to `" & gs_arg_check & "`..." ) If Not go_fso.FolderExists( gs_arg_check ) Then Call s_abort( cs_fac & "the specified -check path `" & gs_arg_check & "` does not exist, re-specify..." ) End If End If End If If Not gb_arg_depth Then gb_arg_depth = True gs_arg_depth = CStr( cl_def_depth ) gl_arg_depth = CLng( gs_arg_depth ) If gb_arg_debug And ( Not gb_arg_pull ) Then Call s_log( cs_fac & "defaulted -depth to `" & gs_arg_depth & "`..." ) End If If Not gb_arg_priority Then gb_arg_priority = True gs_arg_priority = cs_def_priority If gb_arg_debug And ( Not gb_arg_pull ) Then Call s_log( cs_fac & "defaulted sub-process priority to `" & gs_arg_priority & "`..." ) End If Select Case gs_arg_priority Case "low", "belownormal", "normal" Case Else Call s_abort( cs_fac & "unknown priority of `" & gs_arg_priority & "`..." ) End Select If gb_arg_pull Then If Not gb_arg_version Then Call s_abort( cs_fac & "argument -version must be specified with -pull..." ) End If End If If gb_arg_techdoc Then If ( Not gb_arg_version ) Or ( gs_arg_version <> "misc" ) Then Call s_abort( cs_fac & "when using -techdoc the -version must be misc..." ) End If If ( Not gb_arg_discover ) And ( Not gb_arg_download ) Then Call s_abort( cs_fac & "need to specify -discover and/or -download when using -techdoc..." ) End If ' If Instr( 1, gs_arg_techdoc, "+" ) <> 0 Then 'commented this out to allow a range to be specified... ' gs_arg_version = Mid( gs_arg_techdoc, 1, Instr( 1, gs_arg_techdoc, "+" ) - 1 ) ' End If End If If gb_arg_discover Or gb_arg_download Or gb_arg_pull Then If Not gb_arg_version Then Call s_abort( cs_fac & "argument `-version v9.9[.9[.9]]` must be specified with any/either of `-both` `-discover` `-download` or `-pull`..." ) End If gs_downloads_path = gs_script_path & cs_bs & gs_arg_version If Not go_fso.FolderExists( gs_downloads_path ) Then Call s_abort( cs_fac & "downloads folder `" & gs_downloads_path & "` is missing, please create and re-try..." ) End If End If If Not gb_arg_match Then gb_arg_match = True If gd_matches.Exists( gs_arg_version ) Then gs_arg_match = gd_matches.Item( gs_arg_version ) Else gs_arg_match = ".*" End If End If If Not gb_arg_ignore Then If gd_ignores.Exists( gs_arg_version ) Then gb_arg_ignore = True gs_arg_ignore = gd_ignores.Item( gs_arg_version ) End If End If If gb_arg_ignore Then 'to test whether the regular expression is valid... Set lo_regexp = CreateObject( "VBScript.RegExp" ) lo_regexp.IgnoreCase = True lo_regexp.MultiLine = True lo_regexp.Global = True lo_regexp.Pattern = gs_arg_ignore On Error Resume Next Set lc_matches = lo_regexp.Execute( "abc123" ) ll error = Err.Number On Error Goto 0 Set lc_matches = Nothing If ll_error <> 0 Then Call s_anort( cs_fac & "invalid regular expression `" & gs_aeg_ignore & "` for -ignore..." ) End If End If If gb_arg_match Then 'also test whether the match string is a valid regular expression... Set lo_regexp = CreateObject( "VBScript.RegExp" ) lo_regexp.IgnoreCase = True lo_regexp.MultiLine = True lo_regexp.Global = True lo_regexp.Pattern = gs_arg_match On Error Resume Next Set lc_matches = lo_regexp.Execute( "abc123" ) ll_error = Err.Number On Error Goto 0 Set lc_matches = Nothing If ll_error <> 0 Then Call s_abort( cs_fac & "invalid regular expression `" & gs_arg_match & "` for -match..." ) End If End If If gb_arg_ignore And gb_arg_match Then If LCase( gs_arg_ignore ) = LCase( gs_arg_match ) Then Call s_abort( cs_fac & "pattern for -ignore cannot be the same as the pattern for -match..." ) End If End If End Sub Sub s_usage() Const cs_fac = "%s_usage, " Dim ls_message ls_message = "" ls_message = ls_message & cs_fac & "usage is:" & vbCrlf ls_message = ls_message & cs_fac & " -b[oth] Discover and download." & vbCrlf ls_message = ls_message & cs_fac & " -ch[eck] path A folder tree path to check for files already downloaded." & vbCrlf ls_message = ls_message & cs_fac & " -co[llect] Collect a text file of all web pages." & vbCrlf ls_message = ls_message & cs_fac & " -deb[ug] Enable debug logging." & vbCrlf ls_message = ls_message & cs_fac & " -del[ete] Delete previously downloaded file before re-trying download." & vbCrlf ls_message = ls_message & cs_fac & " -dep[th] n How deep to follow links within tech note web-pages." & vbCrlf ls_message = ls_message & cs_fac & " -di[scover] Discover, and save a list of download links." & vbCrlf ls_message = ls_message & cs_fac & " -do[wnload] Load the previously discovered links, and download." & vbCrlf ls_message = ls_message & cs_fac & " -f[ile] file.txt Load a file of previously discovered downloads." & vbCrlf ls_message = ls_message & cs_fac & " -i[gnore] pattern Opposite of match." & vbCrlf ls_message = ls_message & cs_fac & " -l[ist] List the versions known by this script." & vbCrlf ls_message = ls_message & cs_fac & " -m[atch] pattern Regular expression match within name of file to possibly download."& vbcrlf ls_message = ls_message & cs_fac & " -o[pen] Also open the root tech note in a browser." & vbCrlf ls_message = ls_message & cs_fac & " -pr[iority] low|belownormal Set to priority of spawned exec processes." & vbCrlf ' ls_message = ls_message & cs_fac & " -pu[ll] url file The spawning code calls this to actually download a file." & vbCrlf ls_message = ls_message & cs_fac & " -r[ediscover] Re-process a previously discovered list of downloads." & vbCrlf ls_message = ls_message & cs_fac & " -sa[ve] Save the first/root/top TECH/DOC note as a web page HTML file." & vbCrlf ls_message = ls_message & cs_fac & " -sle[ep] n The interval between busy download checks." & vbcrlf ls_message = ls_message & cs_fac & " -slo[ts] n Number of concurrent download slots." & vbCrlf ls_message = ls_message & cs_fac & " -tec[hdoc] [TECH][DOC]nnnn[+n] One specific, or multiple, or a range of tech notes." & vbCrlf ' ls_message = ls_message & cs_fac & " -tes[t] Test discovery of all known versions for which sub-folders exist." & vbCrlf ls_message = ls_message & cs_fac & " -tr[ies] n How many times to try a download of a tech note page, or file." & vbCrlf ' ls_message = ls_message & cs_fac & " -u[rl] http://blah.blah.blah Specify the " & vbCrlf ls_message = ls_message & cs_fac & " -v[ersion] v9.9[.9[.9]] The patch kit version to discover and/or download." & vbCrlf ls_message = ls_message & cs_fac & "...any/all parameters for arguments can be enclosed within double quotes..." & vbCrlf ls_message = Left( ls_message, Len( ls_message ) - 2 ) Call s_log( ls_message ) End Sub '************************************************************************************** '************************ Script Specific Subs and Functions ************************ '************************************************************************************** Function fb_check_found( ps_file, po_folder ) 'check to see whether a file name already exists in a folder tree... Const cs_fac = "%fb_check_found, " Dim lo_subfolder, lo_tmp, ll_tmp, ll_error If go_fso.FileExists( po_folder.Path & cs_bs & ps_file ) Then If gb_arg_debug Then Call s_log( cs_fac & "found file `" & ps_file & "` in folder `" & po_folder.Path & "`..." ) fb_check_found = True Exit Function End If fb_check_found = False ' If gb_arg_debug Then Call s_log( cs_fac & "checking folder `" & po_folder.Path & "`..." ) Set lo_tmp = po_folder.SubFolders On Error Resume Next ll_tmp = lo_tmp.Count ll_error = Err.Number On Error Goto 0 Select Case ll_error Case 0 'we were able/allowed to step down in to a sub-folder... Case 70 'permission denied... Exit Function Case Else 'any other error experienced when attempting to step down in to a sub-folder... Call s_log( cs_fac & "unable to retrieve sub-folder count for `" & po_folder.Path & "`, sttaus `" & ll_error & "`..." ) Exit Function End Select For Each lo_subfolder In po_folder.SubFolders 'now recurse down through any further sub-folders... If fb_check_found( ps_file, lo_subfolder ) Then fb_check_found = True Exit Function End If Next End Function Sub s_issue( ps_issue ) 'capture an issue in the issue list... Const cs_fac = "%s_issue, " Dim ls_issue Call s_log( ps_issue ) ls_issue = fs_hhmmss() & " " & ps_issue If Not gd_issues.Exists( ls_issue ) Then gd_issues.Add ls_issue, "" End Sub Sub s_issues_list() Const cs_fac = "%s_issues_list, " Dim ll_issue, ls_issue Call s_log( gs_script_fac & "experienced `" & gd_issues.Count & "` issue(s), you may need to review the log file..." ) If gd_issues.Count > 20 Then Call s_log( gs_script_fac & "too many issues to list, listing first 20 only..." ) ll_issue = 0 For Each ls_issue In gd_issues.Keys Call s_log( gs_script_fac & "..." & ls_issue ) ll_issue = ll_issue + 1 If ll_issue >= 20 Then Exit For Next End Sub Sub s_priority( px_processid, ps_priority ) 'set the priority of one of our own sub-processes that we created... Const cs_fac = "%s_priority, " Dim lc_processes, lo_process, ll_priority, ll_error Select Case LCase( ps_priority ) Case "low" : ll_priority = cl_prio_low 'aka idle... Case "belownormal" : ll_priority = cl_prio_belownormal Case "normal" : ll_priority = cl_prio_normal Case Else Call s_log( cs_fac & "ignoring unexpected priority of `" & ps_priority & "`..." ) Exit Sub End Select Set lc_processes = go_wmi.ExecQuery( "Select ProcessID from Win32_Process Where ProcessID = " & px_processid ) For Each lo_process In lc_processes On Error Resume Next lo_process.SetPriority( ll_priority ) ll_error = Err.Number On Error Goto 0 If ll_error = 0 Then If gb_arg_debug Then Call s_log( cs_fac & "spawned sub-process `" & px_processid & "` priority set to `" & ps_priority & "`..." ) Else Call s_log( cs_fac & "failed to set priority of spawned sub-process `" & px_processid & "`, error `" & ll_error & "`..." ) End If Next End Sub Function fb_free_gb( ps_drive, pd_limit ) 'is there enough free disk space? Const cs_fac = "%fb_free_gb, " Dim lo_drive, ld_free_gb Set lo_drive = go_fso.GetDrive( ps_drive ) ld_free_gb = lo_drive.FreeSpace / ( 1000.0 * 1000.0 * 1000.0 ) If gb_arg_debug And ( Not gb_arg_pull ) Then Call s_log( cs_fac & "free space on `" & ps_drive & "` is `" & FormatNumber( ld_free_gb, 2 ) & "GB`..." ) fb_free_gb = True If ld_free_gb < 0.0 Then 'if long integer is blown, then assume that this means that there is plenty of free space... Else '...free space is zero or a positive number... If ld_free_gb < pd_limit Then fb_free_gb = False End If End if End Function Function fs_url_to_file( ps_url ) 'strip down a URL to a file name... Const cs_fac = "%fs_url_to_file, " Dim ls_parts, ls_part, ls_file, ll_pos ls_parts = Split( ps_url, "/" ) ls_part = ls_parts( UBound( ls_parts ) ) 'take the last part after the last slash... ll_pos = Instr( ls_part, "?" ) If ll_pos = 0 Then 'if there is no trailing "?" then... ls_file = ls_part Else 'there was a trailing "?", so strip off that part... ls_file = Mid( ls_part, 1, Instr( ls_part, "?" ) - 1 ) End If 'now attempt to clean up the potential file name that we've extracted... 'and de-process any previously substituted/escaped characters... ls_file = Replace( ls_file, "%2520" ," " ) '%20 must have been double substituted to become %2520... ls_file = Replace( ls_file, "%20" ," " ) ls_file = Replace( ls_file, "%21" ,"" ) ls_file = Replace( ls_file, "%25" ,"" ) 'replace '%' with nothing... ls_file = Replace( ls_file, "%26" ,"-" ) 'ASCII 0x26 is an amphersand..."&" ...but change to hyphen "-" for Windows file names... ls_file = Replace( ls_file, "%27" ,"'" ) ls_file = Replace( ls_file, "%28" ,"(" ) ls_file = Replace( ls_file, "%29" ,")" ) fs_url_to_file = ls_file End Function Function fs_veritas_url( ps_num ) 'since the split from Symantec to Veritas, the Veritas page names have a numeric format... Const cs_fac = "%fs_veritas_url, " If Not IsNumeric( ps_num ) Then Call s_abort( cs_fac & "bad number `" & ps_num & "`..." ) ' fs_veritas_url = "http://www.veritas.com/docs/" & Right( "000000000" & CStr( ps_num ), 9 ) fs_veritas_url = "https://www.veritas.com/support/en_US/article." & Right( "000000000" & CStr( ps_num ), 9 ) End Function Function fd_ram_free_pct() 'determine current free RAM, and keep a record of the high and lows... Const cs_fac = "%fd_ram_free_pct, " Dim lo_wmi, lc_col, lo_com, ld_GB, ld_ram_total, ld_ram_free ld_GB = 1000.0 * 1000.0 * 1000.0 Set lc_col = go_wmi.ExecQuery( "Select TotalPhysicalMemory from Win32_ComputerSystem" ) For Each lo_com In lc_col ld_ram_total = lo_com.TotalPhysicalMemory / ld_GB Next Set lc_col = go_wmi.ExecQuery( "Select AvailableBytes from Win32_PerfFormattedData_PerfOS_Memory" ) For Each lo_com In lc_col ld_ram_free = lo_com.AvailableBytes / ld_GB Next gd_ram_free_pct_cur = ld_ram_free / ld_ram_total gd_ram_free_pct_min = fx_min( gd_ram_free_pct_min, gd_ram_free_pct_cur ) gd_ram_free_pct_max = fx_max( gd_ram_free_pct_max, gd_ram_free_pct_cur ) fd_ram_free_pct = gd_ram_free_pct_cur End Function '************************************************************* '******************* Minor sub-routines ******************** '************************************************************* Sub s_log( ps_message ) Dim ls_message ls_message = fs_hhmmss() & " " & ps_message WScript.Echo ls_message On Error Resume Next go_log_chan.WriteLine ls_message On Error Goto 0 End Sub Sub s_abort( ps_message ) Const cs_fac = "%s_abort, " If ps_message <> "" Then Call s_log( ps_message ) Call s_log( cs_fac & "script aborted..." ) WScript.Quit( -1 ) End Sub Sub s_quit( ps_message ) Const cs_fac = "%s_quit, " If ps_message <> "" Then Call s_log( ps_message ) Call s_log( cs_fac & "script quit..." ) WScript.Quit( 0 ) End Sub '***************************************************************************** '************************ General Purpose Functions ************************ '***************************************************************************** Function fs_arguments_list() Dim ls_out, ll_cnt, ls_arg, ll_i, lo_arg ll_cnt = 0 ls_out = "" For Each lo_arg In WScript.Arguments ls_out = ls_out & "arg:" & ll_cnt & "=[" & lo_arg & "] " ll_cnt = ll_cnt + 1 Next fs_arguments_list = Trim( ls_out ) End Function Function fs_get_os_version() Const cs_fac = "%fs_get_os_version, " Dim lo_wmi, lo_systems, lo_os, ls_os, ll_cnt fs_get_os_version = "(unknown)" Set lo_wmi = GetObject( "WinMgmts:\\.\root\cimv2" ) Set lo_systems = lo_wmi.InstancesOf( "Win32_OperatingSystem" ) On Error Resume Next ll_cnt = lo_systems.Count If Err.Number <> 0 Then Call s_abort( cs_fac & "unable to connect to WMI object to retrieve OS version..." ) End If On Error Goto 0 For Each lo_os In lo_systems 'Only one instance is ever returned (the currently active OS). Select Case lo_os.OSType Case 16 : ls_os = "Win95" Case 17 : ls_os = "Win98" Case 18 Select Case Left( lo_os.Version, 3 ) Case "4.0" : ls_os = "WinNT4" Case "5.0" : ls_os = "Win2000" Case "5.1" : ls_os = "WinXP" Case "5.2" : ls_os = "Win2003" Case "5.3" : ls_os = "Win2003 R2" Case "6.0" : ls_os = "Win2008" Case "6.1" : ls_os = "Win2008 R2" Case "6.2" : ls_os = "Win2012" Case "6.3" : ls_os = "Win2012 R2" Case "10.0" : ls_os = "Win10" Case Else : ls_os = "WinNT v" & lo_os.Version End Select If lo_os.ServicePackMajorVersion > 0 Then ls_os = ls_os & " SP" & lo_os.ServicePackMajorVersion End If Case Else ls_os = "(unknown " & ls_os.OSType & "-" & ls_os.Version & "-" & lo_os.ServicePackMajorVersion & ")" End Select Next fs_get_os_version = ls_os End Function Function fx_min( px_a, px_b ) If px_a < px_b Then fx_min = px_a Else fx_min = px_b End If End Function Function fx_max( px_a, px_b ) If px_a > px_b Then fx_max = px_a Else fx_max = px_b End If End Function Function fs_hhmmss() Dim ld_dt ld_dt = Now fs_hhmmss = fs_zeroes( DatePart( "h", ld_dt ), 2 ) & ":" & fs_zeroes( DatePart( "n", ld_dt ), 2 ) & ":" & fs_zeroes( DatePart( "s", ld_dt ), 2 ) End Function Function fs_duration( pd_start, pd_end ) Dim ll_diff, ll_dddd, ll_hh, ll_mm, ll_ss, ls_duration ll_diff = DateDiff( "s", pd_start, pd_end ) ll_ss = ll_diff - ( ( ll_diff \ 60 ) * 60 ) ll_diff = ll_diff \ 60 ll_mm = ll_diff - ( ( ll_diff \ 60 ) * 60 ) ll_diff = ll_diff \ 60 ll_hh = ll_diff - ( ( ll_diff \ 24 ) * 24 ) ll_diff = ll_diff \ 24 ll_dddd = ll_diff ls_duration = "" If ll_dddd > 0 Then ls_duration = ls_duration & fs_right( ll_dddd, 4 ) & " " ls_duration = ls_duration & fs_zeroes( ll_hh, 2 ) & ":" Else ls_duration = ls_duration & fs_zeroes( ll_hh, 2 ) & ":" End If ls_duration = ls_duration & fs_zeroes( ll_mm, 2 ) & ":" ls_duration = ls_duration & fs_zeroes( ll_ss, 2 ) fs_duration = ls_duration End Function Function fs_zeroes( pl_number, pl_length ) Dim ls_result ls_result = String( pl_length, "0" ) & CStr( pl_number ) ls_result = Right( ls_result, pl_length ) fs_zeroes = ls_result End Function Function fs_datetime( pd_datetime ) Dim ld_datetime, ls_result If VarType( pd_datetime ) = vbDate Then ld_datetime = pd_datetime Else ld_datetime = Now End If ls_result = WeekDayName( WeekDay( ld_datetime ), False, 1 ) ls_result = ls_result & " " & FormatDateTime( ld_datetime, vbLongdate ) ls_result = ls_result & " " & FormatDateTime( ld_datetime, vbLongtime ) fs_datetime = ls_result End Function Function fs_left( ps_text, pl_len ) If Len( ps_text ) >= pl_len Then fs_left = ps_text Else fs_left = Left( ps_text & Space( pl_len ), pl_len ) End If End Function Function fs_right( ps_text, pl_len ) If Len( ps_text ) >= pl_len Then fs_right = ps_text Else fs_right = Right( Space( pl_len ) & ps_text, pl_len ) End If End Function Function fs_number( pd_num, pl_dig, pl_dec ) Dim ls_ret, ll_len ls_ret = FormatNumber( pd_num, pl_dec ) ll_len = pl_dig + pl_dec + 1 If Len( ls_ret ) >= ll_len Then fs_number = ls_ret Else fs_number = Right( Space( ll_len ) & ls_ret, ll_len ) End If End Function Function fs_pct( pd_num ) Dim ls_pct ls_pct = FormatPercent( pd_num, 1, True, False, True ) fs_pct = Right( Space(5) & ls_pct, 6 ) End Function Function fs_status( pl_num ) If pl_num >= 0 Then fs_status = CStr( pl_num ) Else fs_status = "0x" & Right( "00000000" & Hex( pl_num ), 8 ) End If End Function