Option Explicit
'***************************************************************************************************************************
' DISCLAIMER:
' - Neither the author of this script, nor Symantec, 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 Symantec.
' - This script is not supported by Symantec.
' - This script has not been tested by Symantec.
' - 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 Symantec's 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. 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:
' 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 -version v7.6.1.2 -download
' 6) After script completes, perform your usual validity and integrity checks of the downloaded files.
'***************************************************************************************************************************
' 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 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.
'***************************************************************************************************************************
' 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 name 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.
'***************************************************************************************************************************
Const cs_script_version = "v1.09"
'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 = "v7.6.1.2" '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...
Dim gb_arg_all, gb_arg_both, gb_arg_check, gb_arg_collect, gb_arg_debug, gb_arg_depth, gb_arg_discover, gb_arg_download, gb_arg_file
Dim gb_arg_ignore, gb_arg_list, gb_arg_match, gb_arg_open, gb_arg_priority, gb_arg_pull, gb_arg_rediscover, gb_arg_save, gb_arg_sleep
Dim gb_arg_slots, gb_arg_techdoc, gb_arg_test, gb_arg_tries, gb_arg_version
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_version
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
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_log( gs_script_fac & "no issues noted..." )
Call s_log( gs_script_fac & "script completed successfully..." )
Else
Call s_log( gs_script_fac & "experienced `" & gd_issues.Count & "` issue(s), you probably need to re-run the script..." )
If gd_issues.Count > 20 Then Call s_log( gs_script_fac & "too many issues to list, listing first 20 only..." )
Dim ls_issue, ll_issue
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
Call s_log( gs_script_fac & "script completed with warnings..." )
End If
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
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, "
Dim ls_downloads_spec, lo_downloads_chan, ls_download, ls_techs, ll_tech, ls_url, ls_version, ls_tech
Dim ls_col_spec, ls_test_command, ll_test_status, ll_test_versions, ll_test_submitted
Dim ls_misc_prefix, ls_misc_start, ll_misc_start, ls_misc_range, ll_misc_range, ll_misc_next, ll_misc
Dim ls_readall, ls_old_urls, ll_old_url, ls_old_url, ld_new_techs, ls_new_tech, ls_new_techs, ls_download_file, ls_url_fields, ll_url_field
Dim lo_regexp, lc_matches, lo_match, lb_wanted
If gb_arg_pull Then 'pull is used when the script spawns itself for downloads...
Call s_http( gs_arg_pull_url, gs_arg_pull_file ) 'now actually perform the download...
Exit Sub 'no need to carry on...
End If
If gb_arg_test Then
Call s_log( "" )
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
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" & " -v " & ls_version & " -dis -col -sav -ch mine & pause & exit"
ll_test_status = go_wsh.Run( ls_all_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
Next
Call s_log( cs_fac & "done, submitted `" & ll_test_submitted & "` of a possible `" & ll_test_versions & "` versions..." )
Exit Sub
End If
If gb_arg_list Then
Call s_log( "" )
Call s_log( cs_fac & "list of available downloads..." )
Call s_log( cs_fac & " Version TechNote/Doc/Link" )
Call s_log( cs_fac & " ------- -----------------" )
For Each ls_version In gd_versions.Keys
ls_tech = gd_versions.Item( ls_version )
Select Case ls_version
Case "misc", "random", "test" 'do not list these...
Case Else
Call s_log( cs_fac & " " & Left( ls_version & Space(11), 11 ) & " " & ls_tech )
End Select
Next
End If
If gb_arg_open Then
Call s_log( "" )
If gb_arg_techdoc Then
ls_tech = gs_arg_techdoc
Else
ls_techs = Split( gd_versions.Item( gs_arg_version ), "," )
ls_tech = ls_techs( 0 )
End If
Select Case Left( ls_tech, 3 )
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...
Case Else
ls_url = ls_tech
End Select
Call s_log( cs_fac & "opening browser page to `" & ls_url & "`..." )
go_wsh.Run ls_url, 9, False
End If
If gb_arg_discover Or gb_arg_download Or gb_arg_rediscover Then
ls_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 `" & ls_downloads_spec & "`..." )
go_fso.CopyFile gs_arg_file, ls_downloads_spec, True
End If
'the next three major code blocks cover:
'...re-discovery which loads/reads a previously saved file 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...
'the purpose of the 'rediscover' function, is to re-process download links, so that we don't have search (discover) through nested/linked web pages...
If gb_arg_rediscover Then
Call s_log( "" )
Call s_log( cs_fac & "rediscover..." )
ls_readall = go_fso.OpenTextFile( ls_downloads_spec ).Readall
ls_old_urls = Split( ls_readall, vbCrlf )
ls_new_techs = ""
Set ld_new_techs = CreateObject( "Scripting.Dictionary" )
Call s_log( cs_fac & "found `" & ( UBound( ls_old_urls ) + 1 ) & "` old URLs to rediscover, will apply match..." )
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 & "`..." )
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
ls_url_fields = Split( ls_old_url, "/" )
For ll_url_field = ( UBound( ls_url_fields ) - 2 ) To UBound( ls_url_fields )
If LCase( Left( ls_url_fields( ll_url_field ), 4 ) ) = "tech" Then
ls_new_tech = ls_url_fields( ll_url_field )
If Not ld_new_techs.Exists( ls_new_tech ) Then
ls_new_techs = ls_new_techs & "," & ls_new_tech
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 ) )
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 & "`..." )
End If
If gb_arg_discover Then
Call s_log( "" )
Call s_log( cs_fac & "discover..." )
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
If gb_arg_techdoc Then
ls_techs = Split( gs_arg_techdoc, "," )
Else
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 & "`..." )
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 Left( ls_tech, 3 )
Case "TEC" : ls_misc_prefix = "TECH"
Case "DOC" : ls_misc_prefix = "DOC"
Case Else : Call s_abort( cs_fac & "unexpected tech prefix on `" & ls_tech & "`..." )
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
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
For ll_tech = 0 To UBound( ls_techs ) 'now begin downloads...
Select Case 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...
Case "htt"
ls_url = ls_techs( ll_tech )
Case Else
Call s_abort( cs_fac & "unexpected TECH or URL of `" & ls_techs( ll_tech ) & "`..." )
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_discover( 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..." )
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 lo_downloads_chan = go_fso.CreateTextFile( ls_downloads_spec, True ) 'save list of downloads, as we may want to download later...
For Each ls_download In gd_downloads.Keys
lo_downloads_chan.WriteLine ls_download
Next
lo_downloads_chan.Close
Call s_log( cs_fac & "saved `" & gd_downloads.Count & "` to downloads file `" & ls_downloads_spec & "`..." )
End If
Call s_log( "" )
Call s_log( cs_fac & "finished discover for version `" & gs_arg_version & "`..." )
End If
If gb_arg_download Then
Call s_log( "" )
Call s_log( cs_fac & "download..." )
If Not gb_arg_discover Then 'if there was no discovery phase just now...
If Not go_fso.FileExists( ls_downloads_spec ) Then 'then has the list of downloads already been discovered?
Call s_log( cs_fac & "cannot find downloads file `" & ls_downloads_spec & "`..." )
Exit Sub
End If
Set lo_downloads_chan = go_fso.OpenTextFile( ls_downloads_spec, ci_for_reading )
Do 'load the list of previously discovered download links...
ls_download = lo_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 lo_downloads_chan.AtEndOfStream
lo_downloads_chan.Close
Call s_log( cs_fac & "loaded `" & gd_downloads.Count & "` potential downloads..." )
End If
Call s_download() 'now get on with downloading...
Call s_log( "" )
Call s_log( cs_fac & "finished download for version `" & gs_arg_version & "`..." )
End If
End Sub
Sub s_discover( ps_url, pl_depth )
Const cs_fac = "%s_discover, "
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_http( 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
'save a copy of the un-altered page in original Unicode UTF-8 format...
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 load the content in 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
go_fso.DeleteFile( gs_temp_spec ) 'we no longer need this temporary file...
'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
If gb_arg_save Then
'split up the HTML and save it as a text file...
ls_text = ls_page
ls_text = Replace( ls_text, "><", ">" & vbCrlf & "<" ) 'break HTML fields apart...
ls_text = Replace( ls_text, ">" & vbLf & "<", ">" & vbCrlf & "<" ) '(sighs) some HTML fields have LF between them...
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
Call s_log( cs_fac & "searching for downloads..." )
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 = 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 'a link without a leading http://site.site.site component...
ls_link = "http://www.symantec.com" & ls_link
End If
If Not gd_downloads.Exists( ls_link ) Then
Call s_log( cs_fac & "found download `" & 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 links...
' examples of page links:
'
'
'
'
' 'note the trailing space (sighs)...
' 'new format with \ escaping quotes...
Call s_log( cs_fac & "searching for links..." )
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 that form a 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 = 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 Not gd_links.Exists( ls_link ) Then
gd_links.Add ls_link, ""
If Not ld_links.Exists( ls_link ) Then
ld_links.Add ls_link, "" 'a potential link was found...
End If
End If
End If
next
Next
Set lo_regexp = Nothing
'now process the newly discovered links...
Call s_log( cs_fac & "found `" & ld_links.Count & "` new links..." )
For Each ls_link In ld_links.Keys
Call s_discover( ls_link, ll_depth ) 'a recursive call to itself...
Next
End Sub
Sub s_http( ps_url, ps_file )
Const cs_fac = "%s_http, "
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
'this routine is used for two things:
'...to pull down a web page, and save it to temporary file, which is then read to acquire the raw HTML code that actually is the web page...
'...or download a wanted file, and save to our target 'version' download folder...
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 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
'begin actual web-page pull, or file download...
If gb_arg_pull Then Call s_log( cs_fac & "getting `" & ps_file & "`..." )
lb_got = False
ll_try = 0
'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 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...
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...
'first stage, 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...
'so 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 lo_send_status = 0 Then
WScript.Quit( lo_http.Status )
Else
WScript.Quit( lo_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 ) )
'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
'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
lo_stream.SaveToFile ps_file
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_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...
'...the first 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)...
'...the next two stages are both embedded within a work processing loop...
'...which firstly looks for empty slots and moves work from ld_todo to ld_doing...
'...and secondly moves completed work from ld_doing to ld_done.
If gd_downloads.Count = 0 Then
Call s_log( cs_fac & "downloads list is empty, nothing to download..." )
Exit Sub
End If
Set ld_todo = CreateObject( "Scripting.Dictionary" ) 'these are the lists of work to be done, i.e. downloads to initiate...
Set ld_doing = CreateObject( "Scripting.Dictionary" ) 'the current downloads...
Set ld_done = CreateObject( "Scripting.Dictionary" ) 'the completed downloads...
'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 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 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 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
'we've got some work to do now...
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
gd_ram_free_pct_cur = 0.0 'these three will be continually re-populated during the loop below, each time fd_ram_free_pct() is called...
gd_ram_free_pct_min = 100.0
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...
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
Next
'there are two inner For loops within the main Do loop...
'...the first inner For loop looks for work to initiate, i.e. to load in to an empty slot...
'...the second inner For loop 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()) )
If Not lb_busy Then 'if all slots are not already fully occupied, then...
'look for work to do...
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 - but note that if we 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'be 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 call ourselves, i.e. this script calls 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 ) '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 ondicate a busy/loaded slot...
ld_todo.Remove( ls_spec ) 'move this piece of work from todo, to...
ld_doing.Add ls_spec, "" '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
'that's us done with looking for work to initiate, for this iteration...
'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 'eveything 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 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 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 'slot has finished, so destroy the 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
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 complete anything AND we didn't start anything, then sleep...
Loop Until ( ( ld_todo.Count = 0 ) And ( ld_doing.Count = 0 ) ) 'until there is nothing left to do AND eveything that was being done is complete...
Call s_log( "" ) 'now report how much free RAM we started with, how low it went, and how much is free 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, "
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_setv( ps_version, pl_depth, ps_match, ps_techs ) 'this is called to populate 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_versions()
Const cs_fac = "%s_versions, "
'for details re regular expressions in VBScript, see here:
' http://msdn.microsoft.com/en-us/library/ms974570.aspx
'examples...
' Call s_setv( version, depth, match, list )
' Call s_setv( "v9.9.9.9", 2, ".*9\.9\.9\.9.*", "TECH123456+123" )
'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" )
'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" )
gd_ignores.Add "v7.6.0.1", ".*_FR\..*|.*_JA\..*|.*_ZH\..*"
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" )
'manuals...
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( "man5330" ,2, ".*" , "DOC7757" )
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.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" )
'others...
Call s_setv( "lbn-app-eeb" ,3, ".*" , "TECH145136" )
Call s_setv( "lbn-nbu-eeb" ,3, ".*" , "TECH74904" )
Call s_setv( "rootapp" ,3, ".*" , "http://www.symantec.com/business/support/index?page=productlanding&key=58991" )
Call s_setv( "rootnbu" ,3, ".*" , "http://www.symantec.com/business/support/index?page=productlanding&key=15143" )
Call s_setv( "vms" ,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, ".*\.pdf|.*\.xls|.*\.doc" , "TECH127089,TECH76648,TECH76495,TECH126904,TECH126901,TECH126902,TECH125836" )
Call s_setv( "compatall" ,2, ".*\.pdf|.*\.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_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_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_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 "-depth", "-dep", "-dept"
If gb_arg_debug 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 is required with `-techdoc`..." & vbCrlf
Else
gs_arg_techdoc = WScript.Arguments.Item( ll_i + 1 )
ll_i = ll_i + 1
If ( Left( gs_arg_techdoc, 4 ) <> "TECH" ) And ( Left( gs_arg_techdoc, 3 ) <> "DOC" ) Then
ls_args_bad = ls_args_bad & cs_fac & "value `" & gs_arg_techdoc & "` is not a valid TECH/DOC name used with `-techdoc`..." & vbCrlf
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 "-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_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 -discover, or -download, or -file with -pull..." & vbCrlf
End If
If ( Not gb_arg_list ) And ( Not gb_arg_both ) And ( Not gb_arg_discover ) And ( Not gb_arg_download ) And ( Not gb_arg_pull ) Then
ls_args_bad = ls_args_bad & cs_fac & "must specify one of -list, -both, -discover, -download..." & vbCrlf
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_test And gb_arg_download Then
ls_args_bad = ls_args_bad & cs_fac & "cannot specify -all and -download together..." & 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 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_file Then
If ( Not gb_arg_version ) Or ( gs_arg_version <> "misc" ) Then Call s_abort( cs_fac & "when using -file, then `-version misc` must also be specified..." )
End If
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
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 file of all web pages." & vbCrlf
ls_message = ls_message & cs_fac & " -deb[ug] Enable debug logging." & 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] TECHnnnn[+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 & " -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 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
Case 70 'permission denied...
Exit Function
Case Else
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
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_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 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 ) )
ll_pos = Instr( ls_part, "?" )
If ll_pos = 0 Then
ls_file = ls_part
Else
ls_file = Mid( ls_part, 1, Instr( ls_part, "?" ) - 1 )
End If
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 fd_ram_free_pct() 'determine free RAM, and keep a record...
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 "6.0"
ls_os = "Win2008"
Case "6.1"
ls_os = "Win2008 R2"
Case "6.2"
ls_os = "Win2012"
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_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