Commit e417cae4 authored by Yuanle Song's avatar Yuanle Song
Browse files

rename config files and list files to .txt files;

install local-list.txt and locl-exclude.txt by default, but don't
overwrite if already exists on target system.
parent 8ef50903
Loading
Loading
Loading
Loading
+64 −37
Original line number Diff line number Diff line
// Learn more about F# at http://fsharp.org
//
//   - mbackup config file
//     %programdata%/mbackup/mbackup-config.txt
//   - backup file list
//     /%appdata%/mbackup/mbackup-default.list
//     /%appdata%/mbackup/user-default.list
//     /%appdata%/mbackup/local.list (optional)
//     %programdata%/mbackup/default-list.txt
//     %programdata%/mbackup/user-default-list.txt
//     %programdata%/mbackup/local-list.txt (optional)
//   - exclude pattern
//     /%appdata%/mbackup/mbackup-default.exclude
//     /%appdata%/mbackup/local.exclude (optional)
//     %programdata%/mbackup/default-exclude.txt
//     %programdata%/mbackup/local-exclude.txt (optional)

module Mbackup.Program

@@ -30,6 +32,20 @@ let ExitUserError = 4
let version = Reflection.Assembly.GetEntryAssembly().GetName().Version
let versionStr = version.ToString()

// base filename for use in mbackup for windows.
// I use .txt and .log extension because user can open/edit them easily.
module MbackupFileName =
    let DefaultList = "default-list.txt"
    let DefaultExclude = "default-exclude.txt"
    let LocalList = "local-list.txt"
    let LocalExclude = "local-exclude.txt"
    let UserDefaultList = "user-default-list.txt"
    let Config = "mbackup-config.txt"

    // run time files
    let GeneratedList = "mbackup-list.txt"
    let Log = "mbackup.log"

[<SuppressMessage("*", "UnionCasesNames")>]
type CLIArguments =
    | [<AltCommandLine("-n")>] Dry_Run
@@ -87,7 +103,7 @@ let runtimeDirWin = appDataLocalDirWin + "mbackup\\"
let runtimeDir = appDataLocalDir + "mbackup/"

// return true if target is a local dir. local dir can be unix style or windows style.
let isLocalTarget (target: string) = target.StartsWith "/" || Regex.IsMatch(target, "^[c-z]:", RegexOptions.IgnoreCase)
let isLocalTarget (target: string) = target.StartsWith "/" || Regex.IsMatch(target, "^[a-z]:", RegexOptions.IgnoreCase)

// expand user file to mingw64 rsync supported path.
// abc -> /cygdrive/c/Users/<user>/abc
@@ -122,19 +138,20 @@ let expandUserFile (fn: string) =
    if fn.StartsWith("/") then fn
    else userHome + fn

// generate mbackup.list file
let generateMbackupList (logger: Logger) =
    // TODO how to only regenerate if source file have changed? should I bundle GNU make with mbackup?
    // just compare mbackup.list mtime with its source files?
    let mbackupDefaultList = userConfigDirWin + "mbackup-default.list"
    let mbackupLocalList = userConfigDirWin + "local.list"
    let mbackupUserDefaultList = userConfigDirWin + "user-default.list"
    let mbackupList = runtimeDirWin + "mbackup.list"

    // local functions
// read mbackup list file
let readMbackupListFile fn =
    let dropEmptyLinesAndComments lines =
        Seq.filter (fun (line: string) -> not (line.TrimStart().StartsWith("#") || line.TrimEnd().Equals(""))) lines
    let readMbackupListFile fn = File.ReadAllLines(fn) |> dropEmptyLinesAndComments
    File.ReadAllLines(fn) |> dropEmptyLinesAndComments

// generate MbackupFileName.GeneratedList file
let generateMbackupList (logger: Logger) =
    // TODO how to only regenerate if source file have changed? should I bundle GNU make with mbackup?
    // just compare MbackupFileName.GeneratedList mtime with its source files?
    let mbackupDefaultList = userConfigDirWin + MbackupFileName.DefaultList
    let mbackupLocalList = userConfigDirWin + MbackupFileName.LocalList
    let mbackupUserDefaultList = userConfigDirWin + MbackupFileName.UserDefaultList
    let mbackupList = runtimeDirWin + MbackupFileName.GeneratedList

    try
        let defaultListLines = readMbackupListFile mbackupDefaultList |> Seq.map toMingwPath
@@ -146,21 +163,19 @@ let generateMbackupList (logger: Logger) =
            with
            | :? FileNotFoundException -> (true, Seq.empty)
            | ex ->
                logger.Error "Read mbackupLocalList failed: %s" ex.Message
                logger.Error "Read mbackupLocalList %s failed: %s" mbackupLocalList ex.Message
                (false, Seq.empty)
        match localListLinesMaybe with
        | (false, _) -> failwith "Read mbackup local.list file failed"
        | (false, _) -> failwith "Read mbackupLocalList failed"
        | (true, localListLines) ->
            let userDefaultListLines = readMbackupListFile mbackupUserDefaultList |> Seq.map expandUserFile
            let allLines = Seq.append (Seq.append defaultListLines localListLines) userDefaultListLines
            // For mbackup-default.list and local.list, exclude empty lines and comment lines.
            // skip and give a warning on non-absolute path.
            // For user-default.list, auto prefix user's home dir, auto expand Documents, Downloads etc special folder.
            // For DefaultList and LocalList, exclude empty lines and comment lines.
            // TODO skip and give a warning on non-absolute path.
            // For UserDefaultList, auto prefix user's home dir, auto expand Documents, Downloads etc special folder.
            Directory.CreateDirectory(runtimeDirWin) |> ignore
            File.WriteAllLines(mbackupList, allLines)
            logger.Info
                "mbackup.list file written: %s"
                mbackupList
            logger.Info "GeneratedList written: %s" mbackupList
            true
    with
    | :? IOException as ex ->
@@ -207,7 +222,7 @@ let main argv =
        parser.Parse argv
    let rc = {
        MbackupRuntimeConfig.Config =
          let mbackupConfigFile = userConfigDirWin + "mbackup.txt"
            let mbackupConfigFile = userConfigDirWin + MbackupFileName.Config
            WellsConfig(mbackupConfigFile)
        Logger = logger
        Options = options
@@ -228,17 +243,29 @@ let main argv =
        List.append rsyncCmd
            ("-h --stats -togr --delete --delete-excluded --ignore-missing-args".Split [| ' ' |] |> Array.toList)

    let mbackupFile = runtimeDir + "mbackup.list"
    if not (generateMbackupList logger) then failwith "Generate mbackup.list failed"
    let rsyncCmd = List.append rsyncCmd [ sprintf "--files-from=%s" mbackupFile ]
    if not (generateMbackupList logger) then
        failwith (sprintf "Generate %s failed" MbackupFileName.GeneratedList)
    let generatedFileList = runtimeDir + MbackupFileName.GeneratedList
    let rsyncCmd = List.append rsyncCmd [ sprintf "--files-from=%s" generatedFileList ]

    let excludeFile = userConfigDir + "mbackup-default.exclude"
    let rsyncCmd = List.append rsyncCmd [ sprintf "--exclude-from=%s" excludeFile ]
    let localExcludeFile = userConfigDir + "local.exclude"
    let rsyncCmd = appendWhen (IO.File.Exists localExcludeFile) rsyncCmd (sprintf "--exclude-from=%s" localExcludeFile)
    let rsyncCmd = List.append rsyncCmd [ sprintf "--exclude-from=%s" (userConfigDir + MbackupFileName.DefaultExclude) ]

    let runtimeLocalExcludeFile = runtimeDir + MbackupFileName.LocalExclude
    let rsyncCmd =
        let localExcludeFile = userConfigDir + MbackupFileName.LocalExclude
        if File.Exists localExcludeFile then
            let convertAbsPathToMingwStyle (line: string) = 
               if Regex.IsMatch(line, "[a-z]:", RegexOptions.IgnoreCase) then
                   toMingwPath line
               else
                   line
            let lines =
                readMbackupListFile localExcludeFile
                |> Seq.map convertAbsPathToMingwStyle
            File.WriteAllLines(runtimeLocalExcludeFile, lines)
        appendWhen (File.Exists localExcludeFile) rsyncCmd (sprintf "--exclude-from=%s" runtimeLocalExcludeFile)

    let localLogFile = runtimeDir + "mbackup.log"
    let rsyncCmd = List.append rsyncCmd [ sprintf "--log-file=%s" localLogFile ]
    let rsyncCmd = List.append rsyncCmd [ sprintf "--log-file=%s" (runtimeDir + MbackupFileName.Log) ]

    // precedence: command line argument > environment variable > config file
    let normalizeTarget target =
@@ -271,7 +298,7 @@ let main argv =
            Directory.CreateDirectory(userConfigDirWin) |> ignore
            logger.Info
                "Note: if you run the following rsync command yourself, make sure the generated file list (%s) is up-to-date.\n%s"
                mbackupFile (rsyncExe + " " + rsyncArgs)
                generatedFileList (rsyncExe + " " + rsyncArgs)
            let processStartInfo =
                ProcessStartInfo(
                    FileName = rsyncExe,
+6 −0
Original line number Diff line number Diff line
# files/dirs to exclude, for syntax, check rsync patterns
# This file is reserved when uninstall/upgrade mbackup.
# lines started with # are comments.
# example:
# *.o
# C:\foo\bar.iso
+6 −0
Original line number Diff line number Diff line
# local dirs to backup.
# This file is reserved when uninstall/upgrade mbackup.
# lines started with # are comments.
# example:
# C:\mydir
# D:\some dir\some file.doc
Loading