#!/usr/local/bin/lua
--[[
  upgrade-options.lua - upgrades UltraDefrag configuration files.
  Copyright (c) 2018-2024 Green Gate Systems, LLC.

  ALL RIGHTS RESERVED. YOU MAY NOT DISTRIBUTE OR MODIFY THIS SOFTWARE.

  THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EITHER
  EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
  AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD
  THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
  SERVICING, REPAIR OR CORRECTION.

  IN NO EVENT WILL THE AUTHORS AND COPYRIGHT HOLDERS BE LIABLE TO YOU
  FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
  DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING
  BUT NOT LIMITED TO LOSS OF DATA OR A FAILURE OF THE PROGRAM TO OPERATE
  WITH ANY OTHER PROGRAMS).

  USAGE:

    lua upgrade-options.lua {UltraDefrag installation directory} [options]

  OPTIONS:
  
    --transfer-gui-settings       - take into account UD_SORTING and UD_SORTING_ORDER
                                    environment variables; UltraDefrag installer uses
                                    this option to transfer settings from gui.ini file
                                    to options.lua file
    --use-environment-variables   - take into account all UltraDefrag specific
                                    environment variables
    --generate-auto-defrag-script - generate auto-defrag.cmd script

--]]

-- CONFIGURATION FILE
config_file_contents = [[
-------------------------------------------------------------------------------
-- UltraDefrag configuration file
-------------------------------------------------------------------------------
-- This file contains all UltraDefrag settings. It controls behaviour
-- of the graphical interface, the Windows Explorer's context menu hander,
-- the automatic defragmentation module and, finally, the boot time interface.
--
-- In most cases there's no need to edit this file manually as all the most
-- useful settings are easily configurable via the settings dialog of the
-- graphical interface.
--
-- This file is written in the Lua programming language. There's no need
-- to learn it thoroughly to edit this file, only basic knowledge is required.
-- To get an idea of how it works we do recommend you to read the official Lua
-- reference manual at https://www.lua.org/manual/5.1/
--
-- Also it's worth mentioning that you can use Unicode characters in strings
-- in this file as UltraDefrag uses an improved version of Lua which supports
-- them. All you have to do is to open this file in the Notepad++ editor and
-- then save it in UTF-8 (without BOM) encoding.
--
-- Also Notepad++ features syntax highlighting which makes it much easier
-- to edit the file. The latest version of Notepad++ can be downloaded
-- for free from http://www.notepad-plus-plus.org/
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- 1. Common options
-------------------------------------------------------------------------------
-- These options control behaviour of all the UltraDefrag interfaces
-- mentioned above.
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- A. Defragmentation options
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- Set it to 1 (one) to remove temporary files before defragmentation.
-------------------------------------------------------------------------------

clean_temp_files = $clean_temp_files

-------------------------------------------------------------------------------
-- List of semicolon separated paths which have to be defragmented.
-- Examples:
--   in_filter = "*.jpg;*.png;*.gif" -- defragment pictures only
--   in_filter = "?:\\windows\\*"    -- defragment windows folders only
-- Keep it empty ("") or set to "*" to defragment everything.
-------------------------------------------------------------------------------

in_filter = "$in_filter"

-------------------------------------------------------------------------------
-- List of semicolon separated paths which have to be skipped.
-- Examples:
--   ex_filter = "*temp*;*tmp*;*recycle*"      -- exclude temporary content
--   ex_filter = "*.avi;*.mkv;*\\video_ts\\*"  -- exclude movies
-------------------------------------------------------------------------------

ex_filter = "$ex_filter"

-------------------------------------------------------------------------------
-- Popular patterns intended for inclusion in filters.
-- For more extensions visit http://www.fileinfo.com/
-------------------------------------------------------------------------------

archive_patterns = "$archive_patterns"
audio_patterns = "$audio_patterns"
video_patterns = "$video_patterns"
disk_image_patterns = "$disk_image_patterns"

-------------------------------------------------------------------------------
-- Set these variables to 1 to include patterns defined above to in_filter.
-------------------------------------------------------------------------------

include_archive = $include_archive
include_audio = $include_audio
include_video = $include_video
include_disk_image = $include_disk_image

-------------------------------------------------------------------------------
-- Set these variables to 1 to include patterns defined above to ex_filter.
-- By default all the variables are set to 1 because archives, multimedia
-- files and virtual machines rarely benefit from defragmentation.
-------------------------------------------------------------------------------

exclude_archive = $exclude_archive
exclude_audio = $exclude_audio
exclude_video = $exclude_video
exclude_disk_image = $exclude_disk_image

-------------------------------------------------------------------------------
-- Skip fragments bigger than specified.
-- Example:
--   fragment_size_threshold = "20 MB"
-- Set it to an empty string ("") or zero ("0") to process fragments
-- of all sizes.
-------------------------------------------------------------------------------

fragment_size_threshold = "$fragment_size_threshold"

-------------------------------------------------------------------------------
-- Skip files bigger than specified.
-- Example:
--   file_size_threshold = "100 MB"
-- Keep it empty ("") to defragment files of all sizes.
-------------------------------------------------------------------------------

file_size_threshold = "$file_size_threshold"

-------------------------------------------------------------------------------
-- Skip files having less fragments than specified.
-- Example:
--   fragments_threshold = 5 -- skip files having less than 5 fragments
-- Set this parameter to zero to defragment files regardless of their
-- number of fragments.
-------------------------------------------------------------------------------

fragments_threshold = $fragments_threshold

-------------------------------------------------------------------------------
-- During SSD defragmentation skip files having less fragments than specified.
-- Example:
--   ssd_fragments_threshold = 20 -- skip files having less than 20 fragments
-- Set this parameter to zero to defragment files regardless of their
-- number of fragments.
-------------------------------------------------------------------------------

ssd_fragments_threshold = $ssd_fragments_threshold

-------------------------------------------------------------------------------
-- B. Optimization options
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- File sorting criteria. Available options are: PATH (sort files by path),
-- SIZE (sort files by size), C_TIME (sort files by the creation time),
-- M_TIME (sort files by the last modification time) and A_TIME (sort files
-- by the last access time).
-- Example:
--   file_sorting_criteria = "PATH" -- sort files by path
-------------------------------------------------------------------------------

file_sorting_criteria = "$file_sorting_criteria"

-------------------------------------------------------------------------------
-- File sorting order. Available options are: ASC (ascending) and DESC
-- (descending).
-- Example:
--   file_sorting_order = "ASC" -- sort files in ascending order
-------------------------------------------------------------------------------

file_sorting_order = "$file_sorting_order"

-------------------------------------------------------------------------------
-- Skip files bigger than specified.
-- Example:
--   optimizer_file_size_threshold = "20 MB"
-- Keep it empty ("") to sort out files of all sizes. Though, we do recommend
-- to use the default value ("20 MB") as sorting of big files doesn't affect
-- system performance and just wastes time. Also keep in mind that UltraDefrag
-- needs larger free space gaps to sort out larger files, so in case they are
-- not available on the disk, large files won't get sorted out no matter what
-- value is set for this option.
-------------------------------------------------------------------------------

optimizer_file_size_threshold = "$optimizer_file_size_threshold"

-------------------------------------------------------------------------------
-- If this option is set to 1 then during SSD optimization
-- UltraDefrag will defragment and trim the disk only.
-------------------------------------------------------------------------------

ssd_defragment_and_trim_only = $ssd_defragment_and_trim_only

-------------------------------------------------------------------------------
-- Set this option to 1 to ignore all the filters set above.
-------------------------------------------------------------------------------

deep_action = $deep_action

-------------------------------------------------------------------------------
-- C. Miscellaneous options
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- Skip disks entirely when their fragmentation level is below than specified.
-- Example:
--   fragmentation_threshold = 10 -- ignore fragmentation level below 10%
-- Set it to zero (0) to process disks regardless of their fragmentation level.
-- Note: this parameter doesn't affect the MFT optimization task.
-------------------------------------------------------------------------------

fragmentation_threshold = $fragmentation_threshold

-------------------------------------------------------------------------------
-- Terminate the job when the specified time interval elapses.
-- Example:
--   time_limit = "6h 30m"
-- Keep it empty ("") or set to zero ("0") to let the job complete
-- no matter how long will it take.
-------------------------------------------------------------------------------

time_limit = "$time_limit"

-------------------------------------------------------------------------------
-- The progress refresh interval, in milliseconds. The default value is 100.
-------------------------------------------------------------------------------

refresh_interval = $refresh_interval

-------------------------------------------------------------------------------
-- Set it to 1 (one) to disable generation of the file fragmentation reports.
-------------------------------------------------------------------------------

disable_reports = $disable_reports

-------------------------------------------------------------------------------
-- Set it to 1 to simulate the disk processing. No files will get moved
-- physically, but you'll be able to quickly check how various disk
-- processing algorithms work.
-------------------------------------------------------------------------------

dry_run = $dry_run

-------------------------------------------------------------------------------
-- Set it to DETAILED for troubleshooting, otherwise keep it empty ("")
-- or set to NORMAL. Note that the detailed logging consumes more time,
-- memory and disk space.
-------------------------------------------------------------------------------

dbgprint_level = "$dbgprint_level"

-------------------------------------------------------------------------------
-- Set it to redirect debugging output to a log file. Keep it
-- empty ("") if everything works fine and no logging is needed.
-- Examples:
--   log_file_path = "C:\\Windows\\UltraDefrag\\logs\\ultradefrag.log"
--   log_file_path = ".\\logs\\ultradefrag.log"
-- Environment variables can be used as shown below:
--   log_file_path = os.getenv("TEMP") .. "\\UltraDefrag_Logs\\ultradefrag.log"
-- Note:
--   Unicode characters cannot be included into the log file path.
-------------------------------------------------------------------------------

log_file_path = "$log_file_path"

-------------------------------------------------------------------------------
-- 2. Windows Explorer's context menu options
-------------------------------------------------------------------------------
-- These options control what happens when you defragment
-- files through their context menu in Windows Explorer.
-------------------------------------------------------------------------------

if shellex_flag then
    -- the context menu handler takes into account everything defined above
    -- as well as options defined here exclusively for it $shellex_options
end

-------------------------------------------------------------------------------
-- 3. Graphical interface options
-------------------------------------------------------------------------------
-- These options control look and feel of the graphical interface.
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- Set it to 1 to minimize the application's window
-- to the taskbar notification area (system tray).
-------------------------------------------------------------------------------

minimize_to_system_tray = $minimize_to_system_tray

-------------------------------------------------------------------------------
-- Size of the cluster map cells, in pixels. The default value is 4.
-------------------------------------------------------------------------------

map_block_size = $map_block_size

-------------------------------------------------------------------------------
-- Width of the grid line, in pixels. The default value is 1.
-------------------------------------------------------------------------------

grid_line_width = $grid_line_width

-------------------------------------------------------------------------------
-- The grid line color, in RGB format. Black (0;0;0) is used by default,
-- visit http://www.colorpicker.com/ to pick others.
-------------------------------------------------------------------------------

grid_color_r = $grid_color_r
grid_color_g = $grid_color_g
grid_color_b = $grid_color_b

-------------------------------------------------------------------------------
-- The free space color, in RGB format. White (255;255;255) is used by default,
-- visit http://www.colorpicker.com/ to pick others.
-------------------------------------------------------------------------------

free_color_r = $free_color_r
free_color_g = $free_color_g
free_color_b = $free_color_b

-------------------------------------------------------------------------------
-- The shutdown confirmation timeout, in seconds. The default value
-- is 60, set it to zero to skip any confirmation. Note: it works
-- the same way when you confirm hibernation, logoff or reboot.
-------------------------------------------------------------------------------

seconds_for_shutdown_rejection = $seconds_for_shutdown_rejection

-------------------------------------------------------------------------------
-- The following option has been retired: restore_default_window_size.
-- Remove the width and height parameters from the {installation folder}\gui.ini
-- file manually to restore the default window size on the next startup.
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- 4. Automatic defragmentation options
-------------------------------------------------------------------------------
-- These options control automatic defragmentation. Don't modify them
-- directly, open the Settings dialog in UltraDefrag GUI instead,
-- adjust automatic defragmentation options there and then click OK.
-- The program will save the options and generate the auto-defrag.cmd
-- script taking them all into account.
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- Being set to 1 this option enables automatic defragmentation.
-------------------------------------------------------------------------------

auto_defrag_enabled = $auto_defrag_enabled

-------------------------------------------------------------------------------
-- List of drives to defragment automatically.
-------------------------------------------------------------------------------

auto_defrag_drives_list = "$auto_defrag_drives_list"

-------------------------------------------------------------------------------
-- Being set to 1 this option forces the automatic defragmentation module
-- to use the custom script.
-------------------------------------------------------------------------------

auto_defrag_use_custom_script = $auto_defrag_use_custom_script

-------------------------------------------------------------------------------
-- 5. File fragmentation reports options
-------------------------------------------------------------------------------
-- These options define what happens when you open a file fragmentation
-- report, either from the graphical interface or from Windows Explorer.
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- Set it to zero to disable HTML reports generation.
-------------------------------------------------------------------------------

produce_html_report = $produce_html_report

-------------------------------------------------------------------------------
-- Set it to 1 to enable generation of plain text reports.
-------------------------------------------------------------------------------

produce_plain_text_report = $produce_plain_text_report

-------------------------------------------------------------------------------
-- To adjust style of HTML reports edit the following file:
-- {installation folder}\scripts\udreport.css
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- this number helps to upgrade configuration file correctly, don't change it
-------------------------------------------------------------------------------

version = $current_version

-------------------------------------------------------------------------------
-- this code concatenates filter variables, don't modify it
-------------------------------------------------------------------------------

orig_ex_filter = ex_filter
if exclude_archive ~= 0 then ex_filter = ex_filter .. ";" .. archive_patterns end
if exclude_audio ~= 0 then ex_filter = ex_filter .. ";" .. audio_patterns end
if exclude_disk_image ~= 0 then ex_filter = ex_filter .. ";" .. disk_image_patterns end
if exclude_video ~= 0 then ex_filter = ex_filter .. ";" .. video_patterns end

orig_in_filter = in_filter
if include_archive ~= 0 then in_filter = in_filter .. ";" .. archive_patterns end
if include_audio ~= 0 then in_filter = in_filter .. ";" .. audio_patterns end
if include_disk_image ~= 0 then in_filter = in_filter .. ";" .. disk_image_patterns end
if include_video ~= 0 then in_filter = in_filter .. ";" .. video_patterns end

-------------------------------------------------------------------------------
-- this code sets environment variables for UltraDefrag, don't modify it
-------------------------------------------------------------------------------

-- common variables
os.setenv("UD_ORIG_IN_FILTER",orig_in_filter)
os.setenv("UD_ORIG_EX_FILTER",orig_ex_filter)
os.setenv("UD_EXCLUDE_ARCHIVES",exclude_archive)
os.setenv("UD_EXCLUDE_AUDIO",exclude_audio)
os.setenv("UD_EXCLUDE_VIDEO",exclude_video)
os.setenv("UD_EXCLUDE_DISK_IMAGES",exclude_disk_image)
os.setenv("UD_CLEAN_TEMP_FILES",clean_temp_files)
os.setenv("UD_IN_FILTER",in_filter)
os.setenv("UD_EX_FILTER",ex_filter)
os.setenv("UD_FRAGMENT_SIZE_THRESHOLD",fragment_size_threshold)
os.setenv("UD_FILE_SIZE_THRESHOLD",file_size_threshold)
os.setenv("UD_FRAGMENTS_THRESHOLD",fragments_threshold)
os.setenv("UD_SSD_FRAGMENTS_THRESHOLD",ssd_fragments_threshold)
os.setenv("UD_SORTING",file_sorting_criteria)
os.setenv("UD_SORTING_ORDER",file_sorting_order)
os.setenv("UD_OPTIMIZER_FILE_SIZE_THRESHOLD",optimizer_file_size_threshold)
os.setenv("UD_SSD_DEFRAGMENT_AND_TRIM_ONLY",ssd_defragment_and_trim_only)
os.setenv("UD_DEEP_ACTION",deep_action)
os.setenv("UD_FRAGMENTATION_THRESHOLD",fragmentation_threshold)
os.setenv("UD_TIME_LIMIT",time_limit)
os.setenv("UD_REFRESH_INTERVAL",refresh_interval)
os.setenv("UD_DISABLE_REPORTS",disable_reports)
os.setenv("UD_DBGPRINT_LEVEL",dbgprint_level)
os.setenv("UD_LOG_FILE_PATH",log_file_path)
os.setenv("UD_DRY_RUN",dry_run)

-- GUI specific variables
os.setenv("UD_SECONDS_FOR_SHUTDOWN_REJECTION",seconds_for_shutdown_rejection)
os.setenv("UD_MINIMIZE_TO_SYSTEM_TRAY",minimize_to_system_tray)
os.setenv("UD_MAP_BLOCK_SIZE",map_block_size)
os.setenv("UD_GRID_LINE_WIDTH",grid_line_width)
os.setenv("UD_GRID_COLOR_R",grid_color_r)
os.setenv("UD_GRID_COLOR_G",grid_color_g)
os.setenv("UD_GRID_COLOR_B",grid_color_b)
os.setenv("UD_FREE_COLOR_R",free_color_r)
os.setenv("UD_FREE_COLOR_G",free_color_g)
os.setenv("UD_FREE_COLOR_B",free_color_b)

-- automatic defragmentation specific variables
os.setenv("UD_AUTO_DEFRAG_ENABLED",auto_defrag_enabled)
os.setenv("UD_AUTO_DEFRAG_DRIVES_LIST",auto_defrag_drives_list)
os.setenv("UD_AUTO_DEFRAG_USE_CUSTOM_SCRIPT",auto_defrag_use_custom_script)

-------------------------------------------------------------------------------
-- END OF FILE
-------------------------------------------------------------------------------
]]

auto_defrag_custom_script_ancient_header = [[
::
:: UltraDefrag uses this script for automatic defragmentation%.
:: Feel free to adjust it to make it more suitable for your needs%.
::
:: Visit https://en%.wikibooks%.org/wiki/Windows_Batch_Scripting
:: to learn more about the script syntax and available commands%.
::
:: For syntax highlighting open this file in Notepad%+%+ editor
:: %(http://www%.notepad%-plus%-plus%.org/%)%.
::
]]

auto_defrag_custom_script_old_header = [[
::
:: UltraDefrag %- a powerful disk defragmenter for Windows%.
::
:: auto%-defrag%-custom%.cmd %- custom script for automatic defragmentation%.
::
:: Visit https://en%.wikibooks%.org/wiki/Windows_Batch_Scripting
:: for general information about the script syntax%.
::
:: Refer to the Console chapter of the UltraDefrag Handbook for detailed
:: information about the %`udefrag%` command and environment variables
:: controlling its behaviour%. You should have received the handbook
:: along with this program%, if not%, you can find it at the official
:: UltraDefrag website %(https://ultradefrag%.net/%)
::
:: To use Unicode characters in filters edit this file in the Notepad%+%+
:: editor and then save it in UTF%-8 %(without BOM%) encoding%.
::
:: Also Notepad%+%+ features syntax highlighting which makes it much easier
:: to edit the file%. The latest version of Notepad%+%+ can be downloaded
:: for free from http://www%.notepad%-plus%-plus%.org/
::
]]

auto_defrag_custom_script_new_header = [[
::
:: UltraDefrag - a powerful disk defragmenter for Windows.
::
:: auto-defrag-custom.cmd - custom script for automatic defragmentation.
::
:: Visit https://en.wikibooks.org/wiki/Windows_Batch_Scripting
:: for general information about the script syntax.
::
:: Refer to the UltraDefrag CLI section of UltraDefrag documentation
:: at https://https://ultradefrag.net/support/ultradefrag-cli/
:: for detailed information about the `udefrag` command and environment
:: variables controlling its behaviour.
::
:: To use Unicode characters in filters edit this file in the Notepad++
:: editor and then save it in UTF-8 (without BOM) encoding.
::
:: Also Notepad++ features syntax highlighting which makes it much easier
:: to edit the file. The latest version of Notepad++ can be downloaded
:: for free from http://www.notepad-plus-plus.org/
::
]]

auto_defrag_custom_script_old_ssd_settings = [[
:: if your system drive is an SSD uncomment the following
:: line to exclude slightly fragmented content as well
:: set UD_FRAGMENTS_THRESHOLD=20
]]

auto_defrag_custom_script_new_ssd_settings = [[
:: on solid state drives exclude slightly fragmented content as well
set UD_SSD_FRAGMENTS_THRESHOLD=20
]]

auto_defrag_custom_script_old_ex_filter = [[
set UD_EX_FILTER=%%UD_EX_FILTER%%;%*Windows%.old%*

]]

auto_defrag_custom_script_new_ex_filter = [[
set UD_EX_FILTER=%%UD_EX_FILTER%%;*Windows.old*;*\Windows\servicing\*

]]

function extract_preferences(file)
    local path = instdir .. "\\" .. file
    local f = io.open(path, "r")
    if f then
        f:close()
        dofile(path)
    end
end

function correct_filter(filter)
    local patterns = {}
    for s in string.gmatch(_G[filter],"([^;]+)") do
        if string.sub(s,-2,-1) == "\\\\" then
            table.insert(patterns,string.sub(s,1,-2) .. "*")
        elseif string.sub(s,-1) == "\\" then
            table.insert(patterns,s .. "*")
        else
            table.insert(patterns,s)
        end
    end
    
    _G[filter] = ""
    local n = table.maxn(patterns)
    for i, v in ipairs(patterns) do
        _G[filter] = _G[filter] .. v
        if i < n then
            _G[filter] = _G[filter] .. ";"
        end
    end
end

function get_preferences()
    -- the version of the old configuration file
    version = 0
    
    -- set defaults
    clean_temp_files = 1
    in_filter = ""
    ex_filter = "*system volume information*;*temp*;*tmp*;*recycle*;*dllcache*;*ServicePackFiles*;*.log"
    archive_patterns = "*.7z;*.7z.*;*.arj;*.bkf;*.bz2;*.bzip2;*.cab;*.cpio;*.deb;*.dmg;*.gz;*.gzip;*.lha;*.lzh"
    archive_patterns = archive_patterns .. ";*.lzma;*.rar;*.rpm;*.swm;*.tar;*.taz;*.tbz;*.tbz2;*.tgz;*.tpz;*.txz"
    archive_patterns = archive_patterns .. ";*.xar;*.xz;*.z;*.zip"
    audio_patterns = "*.aif;*.cda;*.flac;*.iff;*.kpl;*.m3u;*.m4a;*.mid;*.mp3;*.mpa;*.ra;*.wav;*.wma"
    disk_image_patterns = "*.fat;*.hdd;*.hfs;*.img;*.iso;*.ntfs;*.squashfs;*.vdi;*.vhd;*.vmdk;*.wim"
    video_patterns = "*.3g2;*.3gp;*.asf;*.asx;*.avi;*.flv;*.mov;*.mp4;*.mpg;*.rm;*.srt;*.swf;*.vob;*.wmv"
    include_archive = 0
    include_audio = 0
    include_video = 0
    include_disk_image = 0
    exclude_archive = 1
    exclude_audio = 1
    exclude_video = 1
    exclude_disk_image = 1
    fragment_size_threshold = "20 MB"
    sizelimit = ""
    fragments_threshold = 0
    ssd_fragments_threshold = 20
    file_sorting_criteria = "PATH"
    file_sorting_order = "ASC"
    optimizer_file_size_threshold = "20 MB"
    ssd_defragment_and_trim_only = 1
    deep_action = 0
    fragmentation_threshold = 0
    time_limit = ""
    refresh_interval = 100
    disable_reports = 0
    dbgprint_level = ""
    log_file_path = ".\\logs\\ultradefrag.log"
    dry_run = 0
    seconds_for_shutdown_rejection = 60
    minimize_to_system_tray = 0
    map_block_size = 4
    grid_line_width = 1
    grid_color_r = 0
    grid_color_g = 0
    grid_color_b = 0
    free_color_r = 255
    free_color_g = 255
    free_color_b = 255
    
    produce_html_report = 1
    produce_plain_text_report = 0
    
    auto_defrag_enabled = 1
    auto_defrag_drives_list = "C"
    auto_defrag_use_custom_script = 0
    
    -- get user preferences
    path = instdir .. "\\conf\\options.lua"
    f = io.open(path, "r")
    if f then
        f:close()
        dofile(path)
    else
        -- try to collect something from obsolete configuration files
        extract_preferences("options\\guiopts.lua")
        extract_preferences("options\\udreportopts.lua")
        extract_preferences("options\\udreportopts-custom.lua")
        extract_preferences("options.lua")
    end

    -- upgrade preferences
    if version == 0 then
        -- upgrade filters, now they should include wildcards
        in_filter = ""
        ex_filter = "*system volume information*;*temp*;*tmp*;*recycle*;*dllcache*;*ServicePackFiles*"
    end

    if not file_size_threshold then
        -- sizelimit has been superseded by file_size_threshold
        file_size_threshold = sizelimit
    end

    local path_upgrade_needed = 0
    if version < 8 then path_upgrade_needed = 1 end
    if version > 99 and version < 107 then path_upgrade_needed = 1 end
    if path_upgrade_needed ~= 0 and log_file_path == "" then
        -- enable logging to file, to simplify troubleshooting
        log_file_path = ".\\logs\\ultradefrag.log"
    end

    if orig_in_filter then in_filter = orig_in_filter end
    if orig_ex_filter then ex_filter = orig_ex_filter end

    -- exclude stuff which rarely benefits from defragmentation by default
    if version < 300 then
        if not string.find(ex_filter,"%*%.log") then
            ex_filter = ex_filter .. ";*.log"
        end
        if not string.find(archive_patterns,"%*%.bkf") then
            local n
            archive_patterns, n = string.gsub(archive_patterns,"%*%.arj;%*%.bz2","%*%.arj;%*%.bkf;%*%.bz2")
            if n == 0 then
                archive_patterns = archive_patterns .. ";*.bkf"
            end
        end
        exclude_archive = 1
        exclude_audio = 1
        exclude_video = 1
        exclude_disk_image = 1
    end

    if version < 301 then
        if not string.find(ex_filter,"%*Windows%.old%*") then
            local n
            ex_filter, n = string.gsub(ex_filter,"%*%.log","%*Windows%.old%*;%*%.log")
            if n == 0 then
                ex_filter = ex_filter .. ";*Windows.old*"
            end
        end
    end
    
    if version < 308 then
        if not string.find(ex_filter,"%*%\\Windows%\\servicing%\\%*") then
            ex_filter = ex_filter .. ";*\\Windows\\servicing\\*"
        end
    end
    
    -- correct filters
    if version == 301 then
        correct_filter("in_filter")
        correct_filter("ex_filter")
    end
end

function expand(s)
  s = string.gsub(s, "$([%w_]+)", function (n)
        return string.gsub(tostring(_G[n]), "\\", "\\\\")
      end)
  return s
end

function save_preferences(f)
    f:write(expand(config_file_contents))
end

function produce_auto_defrag_script()
    local path = instdir .. "\\tasks\\auto-defrag.cmd"
    local f = assert(io.open(path, "w"))
    
    f:write("@echo off\n")
    f:write(":: Do not edit this script manually, open the Settings dialog\n")
    f:write(":: in UltraDefrag GUI instead, adjust automatic defragmentation\n")
    f:write(":: options there and then click OK. The program will save the options\n")
    f:write(":: and generate this script taking them all into account.\n\n")
    
    if auto_defrag_enabled ~= 1 and auto_defrag_enabled ~= "1" then
        f:write(":: automatic defragmentation is disabled\n")
    else
        f:write(":: set UTF-8 code page\n")
        f:write("chcp 65001\n\n")
        
        if auto_defrag_use_custom_script == 1 or auto_defrag_use_custom_script == "1" then
            f:write("call \"%UD_INSTALL_DIR%\\tasks\\auto-defrag-custom.cmd\"\n")
        else
            expanded_in_filter = in_filter
            if include_archive ~= 0 then expanded_in_filter = expanded_in_filter .. ";" .. archive_patterns end
            if include_audio ~= 0 then expanded_in_filter = expanded_in_filter .. ";" .. audio_patterns end
            if include_disk_image ~= 0 then expanded_in_filter = expanded_in_filter .. ";" .. disk_image_patterns end
            if include_video ~= 0 then expanded_in_filter = expanded_in_filter .. ";" .. video_patterns end

            expanded_ex_filter = ex_filter
            if exclude_archive ~= 0 then expanded_ex_filter = expanded_ex_filter .. ";" .. archive_patterns end
            if exclude_audio ~= 0 then expanded_ex_filter = expanded_ex_filter .. ";" .. audio_patterns end
            if exclude_disk_image ~= 0 then expanded_ex_filter = expanded_ex_filter .. ";" .. disk_image_patterns end
            if exclude_video ~= 0 then expanded_ex_filter = expanded_ex_filter .. ";" .. video_patterns end

            f:write(":: set UltraDefrag specific environment variables\n")
            f:write("set UD_CLEAN_TEMP_FILES=",clean_temp_files,"\n")
            f:write("set UD_IN_FILTER=",expanded_in_filter,"\n")
            f:write("set UD_EX_FILTER=",expanded_ex_filter,"\n")
            f:write("set UD_FRAGMENT_SIZE_THRESHOLD=",fragment_size_threshold,"\n")
            f:write("set UD_FILE_SIZE_THRESHOLD=",file_size_threshold,"\n")
            f:write("set UD_FRAGMENTS_THRESHOLD=",fragments_threshold,"\n")
            f:write("set UD_SSD_FRAGMENTS_THRESHOLD=",ssd_fragments_threshold,"\n")
            f:write("set UD_SORTING=",file_sorting_criteria,"\n")
            f:write("set UD_SORTING_ORDER=",file_sorting_order,"\n")
            f:write("set UD_OPTIMIZER_FILE_SIZE_THRESHOLD=",optimizer_file_size_threshold,"\n")
            f:write("set UD_SSD_DEFRAGMENT_AND_TRIM_ONLY=",ssd_defragment_and_trim_only,"\n")
            f:write("set UD_FRAGMENTATION_THRESHOLD=",fragmentation_threshold,"\n")
            f:write("set UD_TIME_LIMIT=",time_limit,"\n")
            f:write("set UD_DISABLE_REPORTS=",disable_reports,"\n")
            f:write("set UD_DBGPRINT_LEVEL=",dbgprint_level,"\n")
            f:write("set UD_LOG_FILE_PATH=%UD_INSTALL_DIR%\\logs\\auto-defrag.log\n\n")
            
            local drives = string.gsub(auto_defrag_drives_list,";",": ")
            if #drives ~= 0 then drives = drives .. ":" end
            
            f:write(":: defragment selected disks\n")
            f:write("udefrag --wait ")
            
            if deep_action == 1 or deep_action == "1" then
                f:write("--deep ")
            end
            
            f:write(drives,"\n")
        end
    end

    f:flush()
    f:close()
    
    -- adjust custom script during the installation
    path = instdir .. "\\tasks\\auto-defrag-custom.cmd"
    f = assert(io.open(path, "r"))
    local contents = f:read("*all")
    f:close()
    
    local new_contents = string.gsub(contents,auto_defrag_custom_script_ancient_header,auto_defrag_custom_script_new_header)
    new_contents = string.gsub(new_contents,auto_defrag_custom_script_old_header,auto_defrag_custom_script_new_header)
    new_contents = string.gsub(new_contents,auto_defrag_custom_script_old_ssd_settings,auto_defrag_custom_script_new_ssd_settings)
    new_contents = string.gsub(new_contents,auto_defrag_custom_script_old_ex_filter,auto_defrag_custom_script_new_ex_filter)
    
    f = assert(io.open(path, "w"))
    f:write(new_contents)
    f:flush()
    f:close()
end

-- THE MAIN CODE STARTS HERE
-- the current version of the configuration file
-- 0 - 99 for v5; 100 - 199 for v6; 200 - 299 for v7; 300+ for v8+
current_version = 312
shellex_options = ""
_G_copy = {}

-- parse command line
instdir = arg[1]
assert(instdir, "upgrade-options.lua: the first argument is missing")

transfer_gui_settings = nil
use_environment_variables = nil
generate_auto_defrag_script = nil

i = 2
while arg[i] do
    if arg[i] == '--transfer-gui-settings' then
        transfer_gui_settings = 1
    end

    if arg[i] == '--use-environment-variables' then
        use_environment_variables = 1
    end
    
    if arg[i] == '--generate-auto-defrag-script' then
        generate_auto_defrag_script = 1
    end
    
    i = i + 1
end

-- save preferences passed through environment variables
env_in_filter = os.getenv('UD_ORIG_IN_FILTER')
if not env_in_filter then env_in_filter = "" end

env_ex_filter = os.getenv('UD_ORIG_EX_FILTER')
if not env_ex_filter then env_ex_filter = "" end

env_exclude_archive = os.getenv('UD_EXCLUDE_ARCHIVES')
if not env_exclude_archive then env_exclude_archive = 1 end
env_exclude_audio = os.getenv('UD_EXCLUDE_AUDIO')
if not env_exclude_audio then env_exclude_audio = 1 end
env_exclude_video = os.getenv('UD_EXCLUDE_VIDEO')
if not env_exclude_video then env_exclude_video = 1 end
env_exclude_disk_image = os.getenv('UD_EXCLUDE_DISK_IMAGES')
if not env_exclude_disk_image then env_exclude_disk_image = 1 end

env_fragment_size_threshold = os.getenv('UD_FRAGMENT_SIZE_THRESHOLD')
if not env_fragment_size_threshold then env_fragment_size_threshold = "" end
env_file_size_threshold = os.getenv('UD_FILE_SIZE_THRESHOLD')
if not env_file_size_threshold then env_file_size_threshold = "" end
env_fragments_threshold = os.getenv('UD_FRAGMENTS_THRESHOLD')
if not env_fragments_threshold then env_fragments_threshold = 0 end
env_ssd_fragments_threshold = os.getenv('UD_SSD_FRAGMENTS_THRESHOLD')
if not env_ssd_fragments_threshold then env_ssd_fragments_threshold = 0 end

env_file_sorting_criteria = os.getenv('UD_SORTING')
if not env_file_sorting_criteria then env_file_sorting_criteria = "PATH" end
env_file_sorting_order = os.getenv('UD_SORTING_ORDER')
if not env_file_sorting_order then env_file_sorting_order = "ASC" end
env_optimizer_file_size_threshold = os.getenv('UD_OPTIMIZER_FILE_SIZE_THRESHOLD')
if not env_optimizer_file_size_threshold then env_optimizer_file_size_threshold = "" end
env_ssd_defragment_and_trim_only = os.getenv('UD_SSD_DEFRAGMENT_AND_TRIM_ONLY')
if not env_ssd_defragment_and_trim_only then env_ssd_defragment_and_trim_only = 0 end

env_auto_defrag_enabled = os.getenv('UD_AUTO_DEFRAG_ENABLED')
if not env_auto_defrag_enabled then env_auto_defrag_enabled = 0 end
env_auto_defrag_drives_list = os.getenv('UD_AUTO_DEFRAG_DRIVES_LIST')
if not env_auto_defrag_drives_list then env_auto_defrag_drives_list = "" end
env_auto_defrag_use_custom_script = os.getenv('UD_AUTO_DEFRAG_USE_CUSTOM_SCRIPT')
if not env_auto_defrag_use_custom_script then env_auto_defrag_use_custom_script = 0 end

env_fragmentation_threshold = os.getenv('UD_FRAGMENTATION_THRESHOLD')
if not env_fragmentation_threshold then env_fragmentation_threshold = 0 end

env_map_block_size = os.getenv('UD_MAP_BLOCK_SIZE')
if not env_map_block_size then env_map_block_size = 4 end
env_grid_line_width = os.getenv('UD_GRID_LINE_WIDTH')
if not env_grid_line_width then env_grid_line_width = 1 end

env_free_color_r = os.getenv('UD_FREE_COLOR_R')
if not env_free_color_r then env_free_color_r = 255 end
env_free_color_g = os.getenv('UD_FREE_COLOR_G')
if not env_free_color_g then env_free_color_g = 255 end
env_free_color_b = os.getenv('UD_FREE_COLOR_B')
if not env_free_color_b then env_free_color_b = 255 end

env_grid_color_r = os.getenv('UD_GRID_COLOR_R')
if not env_grid_color_r then env_grid_color_r = 0 end
env_grid_color_g = os.getenv('UD_GRID_COLOR_G')
if not env_grid_color_g then env_grid_color_g = 0 end
env_grid_color_b = os.getenv('UD_GRID_COLOR_B')
if not env_grid_color_b then env_grid_color_b = 0 end

env_minimize_to_system_tray = os.getenv('UD_MINIMIZE_TO_SYSTEM_TRAY')
if not env_minimize_to_system_tray then env_minimize_to_system_tray = 0 end

env_deep_action = os.getenv('UD_DEEP_ACTION')
if not env_deep_action then env_deep_action = 0 end
env_dry_run = os.getenv('UD_DRY_RUN')
if not env_dry_run then env_dry_run = 0 end

env_clean_temp_files = os.getenv('UD_CLEAN_TEMP_FILES')
if not env_clean_temp_files then env_clean_temp_files = 0 end

-- get preferences for the context menu handler
shellex_flag = 1
get_preferences()
for k,v in pairs(_G) do _G_copy[k] = v end

-- get preferences for the graphical interface
shellex_flag = nil
get_preferences()

-- shellex_options = difference between two sets of preferences
for k,v in pairs(_G) do
    local t = type(v)
    if t == 'string' or t == 'number' or t == 'boolean' then
        if _G_copy[k] ~= _G[k] and k ~= 'shellex_options' then
            shellex_options = shellex_options .. '\n    ' .. k .. ' = '
            if t == 'string' then shellex_options = shellex_options .. '\"' end
            shellex_options = shellex_options .. _G_copy[k]
            if t == 'string' then shellex_options = shellex_options .. '\"' end
        end
    end
end

-- initially custom options should redefine the log path
if version < 114 then
    shellex_options = '\n    log_file_path = \"'
    if os.getenv('UD_INSTALL_DIR') then
        shellex_options = shellex_options .. os.getenv('UD_INSTALL_DIR') .. '\\logs\\'
    else
        shellex_options = shellex_options .. os.getenv('TMP') .. '\\UltraDefrag_Logs\\'
    end
    shellex_options = shellex_options .. 'udefrag-shellex.log\"'
end

-- transfer preferences from gui.ini file
if transfer_gui_settings then
    file_sorting_criteria = string.upper(env_file_sorting_criteria)
    file_sorting_order = string.upper(env_file_sorting_order)
    deep_action = env_deep_action
end

-- update preferences using environment variables
if use_environment_variables then
    in_filter = env_in_filter
    ex_filter = env_ex_filter
    exclude_archive = env_exclude_archive
    exclude_audio = env_exclude_audio
    exclude_video = env_exclude_video
    exclude_disk_image = env_exclude_disk_image
    fragment_size_threshold = env_fragment_size_threshold
    file_size_threshold = env_file_size_threshold
    fragments_threshold = env_fragments_threshold
    ssd_fragments_threshold = env_ssd_fragments_threshold
    file_sorting_criteria = env_file_sorting_criteria
    file_sorting_order = env_file_sorting_order
    optimizer_file_size_threshold = env_optimizer_file_size_threshold
    ssd_defragment_and_trim_only = env_ssd_defragment_and_trim_only
    auto_defrag_enabled = env_auto_defrag_enabled
    auto_defrag_drives_list = env_auto_defrag_drives_list
    auto_defrag_use_custom_script = env_auto_defrag_use_custom_script
    fragmentation_threshold = env_fragmentation_threshold
    map_block_size = env_map_block_size
    grid_line_width = env_grid_line_width
    free_color_r = env_free_color_r
    free_color_g = env_free_color_g
    free_color_b = env_free_color_b
    grid_color_r = env_grid_color_r
    grid_color_g = env_grid_color_g
    grid_color_b = env_grid_color_b
    minimize_to_system_tray = env_minimize_to_system_tray
    deep_action = env_deep_action
    dry_run = env_dry_run
    clean_temp_files = env_clean_temp_files
end

-- generate auto-defrag.cmd script when requested
if generate_auto_defrag_script then
    produce_auto_defrag_script()
end

-- upgrade configuration file when needed
if version < current_version or transfer_gui_settings or use_environment_variables then
    path = instdir .. "\\conf\\options.lua"
    -- make a backup copy
    f = io.open(path, "r")
    if f then
        contents = f:read("*all")
        f:close()
        f = assert(io.open(path .. ".old", "w"))
        f:write(contents)
        f:close()
    end

    -- save the upgraded configuration
    f = assert(io.open(path, "w"))
    save_preferences(f)

    -- flush configuration file to the disk
    -- right now to prepare it for tracking
    -- via FindFirstChangeNotification()
    f:flush()

    f:close()
end
