mtx-rsync.lua /size: 5871 b    last modification: 2020-07-01 14:35
1if not modules then modules = { } end modules ['mtx-rsync'] = {
2    version   = 1.000,
3    comment   = "companion to mtxrun.lua",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9-- This is an experimental script that will be extended over time and
10-- is used by myself. An example or a copy spec:
11--
12--
13-- local devdir = "m:/develop/services"
14-- local orgdir = "m:/pod/m4all"
15--
16-- return {
17--     {
18--         origin = { devdir, "framework/scripts/d-dispatchers.lua"},
19--         target = { orgdir, "framework/scripts" },
20--     },
21--     {
22--         origin = { devdir, "framework/scripts/common/*"},
23--         target = { orgdir, "framework/scripts/common" },
24--     },
25--     {
26--         origin = { devdir, "framework/scripts/d-buildtool.lua"  },
27--         target = { orgdir, "framework/scripts" }
28--     },
29--     {
30--         origin = { devdir, "framework/scripts/buildtool/*"},
31--         target = { orgdir, "framework/scripts/buildtool" },
32--     },
33--     {
34--         origin = { devdir, "framework/m4all*" },
35--         target = { orgdir, "framework" },
36--     },
37--     {
38--         origin = { devdir, "framework/configurations/*m4all*"},
39--         target = { orgdir, "framework/configurations" },
40--     },
41--     {
42--         recurse = true,
43--         origin  = { devdir, "context/tex/texmf-project/tex/context/user/m4all/*" },
44--         target  = { orgdir, "context/tex/texmf-project/tex/context/user/m4all" },
45--     },
46-- }
47
48local helpinfo = [[
49<?xml version="1.0"?>
50<application>
51 <metadata>
52  <entry name="name">mtx-rsync</entry>
53  <entry name="detail">Rsync Helpers</entry>
54  <entry name="version">0.10</entry>
55 </metadata>
56 <flags>
57  <category name="basic">
58   <subcategory>
59    <flag name="job"><short>use given file as specification</short></flag>
60    <flag name="dryrun"><short>show what would happen</short></flag>
61    <flag name="force"><short>force run</short></flag>
62   </subcategory>
63  </category>
64 </flags>
65</application>
66]]
67
68local application = logs.application {
69    name     = "mtx-rsync",
70    banner   = "Rsync Helpers 0.10",
71    helpinfo = helpinfo,
72}
73
74local format, gsub = string.format, string.gsub
75local concat = table.concat
76
77local report_message = logs.new("rsync message")
78local report_dryrun  = logs.new("rsync dryrun")
79local report_normal  = logs.new("rsync normal")
80local report_command = logs.new("rsync command")
81
82local cleanup
83
84if os.type == "windows" then
85    os.setenv("CYGWIN","nontsec")
86    cleanup = function(name)
87        return (gsub(name,"([a-zA-Z]):/", "/cygdrive/%1/"))
88    end
89else
90    cleanup = function(name)
91        return name
92    end
93end
94
95function rsynccommand(origin,target,dryrun,recurse,delete,exclude)
96    local command = "rsync -t -p"
97    if dryrun then
98        command = command .. " -n"
99    end
100    if recurse then
101        command = command .. " -r"
102    end
103    if type(exclude) == "table" then
104        for i=1,#exclude do
105            local e = exclude[i]
106            if e then
107                command = command .. ' --exclude "' .. e .. '"'
108            end
109        end
110    elseif type(exclude) == "string" then
111        command = command .. " --exclude-from " .. exclude
112    end
113    if delete and recurse then
114        command = command .. " --delete"
115    end
116    return format('%s %s %s',command,origin,target)
117end
118
119scripts       = scripts or { }
120scripts.rsync = scripts.rsync or { }
121local rsync   = scripts.rsync
122
123rsync.mode = "command"
124
125function rsync.run(origin,target,message,recurse,delete,exclude)
126    if type(origin) == "table" then
127        origin = concat(origin,"/")
128    end
129    if type(target) == "table" then
130        target = concat(target,"/")
131    end
132    origin = cleanup(origin)
133    target = cleanup(target)
134    local path = gsub(target,"^/cygdrive/(.)","%1:")
135    if not lfs.isdir(path) then
136        report_message("creating target dir %s",path)
137        dir.makedirs(path) -- as rsync only creates them when --recursive
138    end
139    if message then
140        report_message(message)
141    end
142    if rsync.mode == "dryrun" then
143        local command = rsynccommand(origin,target,true,recurse,delete,exclude)
144        report_dryrun(command.."\n")
145        os.execute(command)
146    elseif rsync.mode == "force" then
147        local command = rsynccommand(origin,target,false,recurse,delete,exclude)
148        report_normal(command.."\n")
149        os.execute(command)
150    else
151        local command = rsynccommand(origin,target,true,recurse,delete,exclude)
152        report_command(command)
153    end
154end
155
156function rsync.job(list)
157    if type(list) == "string" and lfs.isfile(list) then
158        list = dofile(list)
159    end
160    if type(list) ~= "table" then
161        report_message("invalid job specification")
162        return
163    end
164    for i=1,#list do
165        local li = list[i]
166        local origin  = li.origin
167        local target  = li.target
168        local message = li.message
169        local recurse = li.recurse
170        local delete  = li.delete
171        local exclude = li.exclude
172        if origin and #origin > 0 and target and #target > 0 then -- string or table
173            rsync.run(origin,target,message,recurse,delete,exclude)
174        else
175            report_message("invalid job specification at index %s",i)
176        end
177    end
178end
179
180if environment.ownscript then
181    -- stand alone
182else
183    report(application.banner)
184    return rsync
185end
186
187local arguments = environment.arguments
188local files     = environment.files
189
190if arguments.dryrun then
191    rsync.mode = "dryrun"
192elseif arguments.force then
193    rsync.mode = "force"
194end
195
196if arguments.exporthelp then
197    application.export(arguments.exporthelp,environment.files[1])
198elseif arguments.job then
199    rsync.job(files[1])
200elseif files[1] and files[2] then
201    rsync.run(files[1],files[2])
202else
203    application.help()
204end
205