SHOW:
|
|
- or go back to the newest paste.
1 | --[[ | |
2 | PROGDOR file bundling program | |
3 | ||
4 | Download with: | |
5 | pastebin get YXx5jjMV progdor | |
6 | std ld progdor progdor | |
7 | ||
8 | - | This is a stable release. You fool! |
8 | + | This is a beta release. You fool! |
9 | To add: | |
10 | +pastebin upload | |
11 | +pastebin download | |
12 | --]] | |
13 | ||
14 | local doCompress = false --even if this is false, it will decompress compressed files. nifty, huh? | |
15 | ||
16 | local doPastebin = false | |
17 | local tArg = {...} | |
18 | local input, outpath | |
19 | if tArg[1] == "-p" then --the p is for pastebin | |
20 | doPastebin = true | |
21 | input = tArg[2] | |
22 | outpath = tArg[3] | |
23 | else | |
24 | input = tArg[1] | |
25 | outpath = tArg[2] | |
26 | end | |
27 | ||
28 | local progdor = fs.getName(shell.getRunningProgram()) | |
29 | local dir = shell.dir() | |
30 | local displayHelp = function() | |
31 | local txt = progdor.." <input> [output]\nCompression is "..tostring(doCompress):upper().."." | |
32 | return print(txt) | |
33 | end | |
34 | ||
35 | local yield = function() | |
36 | os.queueEvent("yield") | |
37 | os.pullEvent("yield") | |
38 | end | |
39 | ||
40 | -- CCA API START -- | |
41 | ||
42 | local bit = bit32 | |
43 | local function pack(bn1, bn2) | |
44 | return bit.band(bn1, 0xFF), bit.rshift(bn1, 8) + bit.lshift(bit.band(bn2, 0xF), 4), bit.rshift(bn2, 4) | |
45 | end | |
46 | local function upack(b1, b2, b3) | |
47 | return (b1 + bit.lshift(bit.band(b2, 0xF), 8)), (bit.lshift(b3,4) + bit.band(bit.rshift(b2, 4), 0xF)) | |
48 | end | |
49 | local function createDict(bool) | |
50 | local ret = {} | |
51 | for i = 1, 255 do | |
52 | if bool then | |
53 | ret[string.char(i)] = i | |
54 | else | |
55 | ret[i] = string.char(i) | |
56 | end | |
57 | end | |
58 | if not bool then ret[256] = 256 end | |
59 | return ret | |
60 | end | |
61 | local function cp(sInput) | |
62 | local dic = createDict(true) | |
63 | local s = "" | |
64 | local ch | |
65 | local dlen = 256 | |
66 | local result = {} | |
67 | local temp | |
68 | for i = 1, #sInput do | |
69 | if dlen == 4095 then | |
70 | result[#result + 1] = dic[s] | |
71 | result[#result + 1] = 256 | |
72 | dic = createDict(true) | |
73 | dlen = 256 | |
74 | s = "" | |
75 | end | |
76 | ch = sInput:sub(i, i) | |
77 | temp = s..ch | |
78 | if dic[temp] then | |
79 | s = temp | |
80 | else | |
81 | result[#result + 1] = dic[s] | |
82 | dlen = dlen +1 | |
83 | dic[temp] = dlen | |
84 | s = ch | |
85 | end | |
86 | end | |
87 | result[#result + 1] = dic[s] | |
88 | ||
89 | return result | |
90 | end | |
91 | local function dc(data) | |
92 | local dic = createDict(false) | |
93 | local entry | |
94 | local ch | |
95 | local currCode | |
96 | local result = {} | |
97 | result[#result + 1] = dic[data[1]] | |
98 | prefix = dic[data[1]] | |
99 | for i = 2, #data do | |
100 | currCode = data[i] | |
101 | if currCode == 256 then | |
102 | dic = createDict(false) | |
103 | prefix = "" | |
104 | else | |
105 | entry = dic[currCode] | |
106 | if entry then--exists in dictionary | |
107 | ch = entry:sub(1, 1) | |
108 | result[#result + 1] = entry | |
109 | if prefix ~= "" then | |
110 | dic[#dic+1] = prefix .. ch | |
111 | end | |
112 | else | |
113 | ch = prefix:sub(1, 1) | |
114 | result[#result + 1] = prefix..ch | |
115 | dic[#dic + 1] = prefix..ch | |
116 | end | |
117 | ||
118 | prefix = dic[currCode] | |
119 | end | |
120 | end | |
121 | ||
122 | return table.concat(result) | |
123 | end | |
124 | local function trim(inp) | |
125 | for i = 0,2 do | |
126 | if inp[#inp] == 0 then | |
127 | inp[#inp] = nil | |
128 | end | |
129 | end | |
130 | end | |
131 | local function decompress(input) | |
132 | local rec = {} | |
133 | for i = 1, #input, 3 do | |
134 | if i % 66 == 0 then | |
135 | yield() | |
136 | end | |
137 | rec[#rec+1], rec[#rec+2] = upack(input[i], input[i+1] or 0, input[i+2] or 0) | |
138 | end | |
139 | trim(rec) | |
140 | return dc(rec) | |
141 | end | |
142 | local function compress(input) | |
143 | local rec = {} | |
144 | local data = cp(input) | |
145 | for i=1, #data, 2 do | |
146 | yield() | |
147 | rec[#rec+1], rec[#rec+2], rec[#rec+3] = pack(data[i], data[i+1] or 0) | |
148 | end | |
149 | trim(rec) | |
150 | return rec | |
151 | end | |
152 | ||
153 | -- CCA API END -- | |
154 | ||
155 | local fixstr = function(str) | |
156 | return str:gsub("\\(%d%d%d)",string.char) | |
157 | end | |
158 | ||
159 | local explode = function(div,str) | |
160 | if (div=='') then return false end | |
161 | local pos,arr = 0,{} | |
162 | for st,sp in function() return string.find(str,div,pos,true) end do | |
163 | table.insert(arr,str:sub(pos,st-1)) | |
164 | pos = sp + 1 | |
165 | end | |
166 | table.insert(arr,str:sub(pos)) | |
167 | return arr | |
168 | end | |
169 | local sanitize = function(sani,tize) | |
170 | local _,x = string.find(sani,tize) | |
171 | if x then | |
172 | return sani:sub(x+1) | |
173 | else | |
174 | return sani | |
175 | end | |
176 | end | |
177 | local tablize = function(input) | |
178 | if type(input) == "string" then | |
179 | return explode("\n",input) | |
180 | elseif type(input) == "table" then | |
181 | return table.concat(input,"\n") | |
182 | end | |
183 | end | |
184 | local compyress = function(input) | |
185 | return string.char(unpack(compress(input))) | |
186 | end | |
187 | local decompyress = function(input) | |
188 | local out = {} | |
189 | for a = 1, #input do | |
190 | table.insert(out,string.byte(input:sub(a,a))) | |
191 | end | |
192 | return decompress(out) | |
193 | end | |
194 | local listAll | |
195 | listAll = function(_path, _files, noredundant) | |
196 | local path = _path or "" | |
197 | local files = _files or {} | |
198 | if #path > 1 then table.insert(files, path) end | |
199 | for _, file in ipairs(fs.list(path)) do | |
200 | local path = fs.combine(path, file) | |
201 | if (file ~= thisProgram) then | |
202 | local guud = true | |
203 | if guud then | |
204 | if fs.isDir(path) then | |
205 | listAll(path, files, noredundant) | |
206 | else | |
207 | table.insert(files, path) | |
208 | end | |
209 | end | |
210 | end | |
211 | end | |
212 | if noredundant then | |
213 | for a = 1, #files do | |
214 | if fs.isDir(tostring(files[a])) then | |
215 | if #fs.list(tostring(files[a])) ~= 0 then | |
216 | table.remove(files,a) | |
217 | end | |
218 | end | |
219 | end | |
220 | end | |
221 | return files | |
222 | end | |
223 | if not (input) then | |
224 | return displayHelp() | |
225 | end | |
226 | if not outpath then | |
227 | outpath = input | |
228 | end | |
229 | ||
230 | local choice = function(input,verbose) | |
231 | if not input then | |
232 | input = "yn" | |
233 | end | |
234 | if verbose then | |
235 | write("[") | |
236 | for a = 1, #input do | |
237 | write(input:sub(a,a):upper()) | |
238 | if a < #input then | |
239 | write(",") | |
240 | end | |
241 | end | |
242 | write("]?") | |
243 | end | |
244 | local evt,char | |
245 | repeat | |
246 | evt,char = os.pullEvent("char") | |
247 | until string.find(input:lower(),char:lower()) | |
248 | if verbose then | |
249 | print(char:upper()) | |
250 | end | |
251 | local pos = string.find(input:lower(),char:lower()) | |
252 | return pos, char:lower() | |
253 | end | |
254 | ||
255 | local postToPastebin = function(name, contents) | |
256 | local key = "0ec2eb25b6166c0c27a394ae118ad829" | |
257 | local response = http.post( | |
258 | "http://pastebin.com/api/api_post.php", | |
259 | "api_option=paste&".. | |
260 | "api_dev_key="..key.."&".. | |
261 | "api_paste_format=lua&".. | |
262 | "api_paste_name="..textutils.urlEncode(name).."&".. | |
263 | "api_paste_code="..textutils.urlEncode(contents) | |
264 | ) | |
265 | if response then | |
266 | local sResponse = response.readAll() | |
267 | response.close() | |
268 | local sCode = string.match( sResponse, "[^/]+$" ) | |
269 | return sCode | |
270 | else | |
271 | return false | |
272 | end | |
273 | return | |
274 | end | |
275 | ||
276 | function doPack(input,output,doCompress,verbose) --make sure that shell exists before using verbose mode | |
277 | local tx = term.getTextColor() | |
278 | if not doPastebin then | |
279 | if not fs.exists(input) then return 3 end | |
280 | if fs.isReadOnly(output) then return 5 end | |
281 | end | |
282 | local packageSelf = true | |
283 | local packageReadOnly = true | |
284 | local ro_asked = false | |
285 | local ps_asked = false | |
286 | if fs.isDir(input) then --if not a package | |
287 | local out = {} | |
288 | local list = listAll(input,nil,true) | |
289 | if verbose then | |
290 | for a = 1, #list do --this checks for self and read-only files | |
291 | if fs.isReadOnly(list[a]) and (not ro_asked) then | |
292 | write("Include read-only files? ") | |
293 | if choice("yn",true) == 2 then | |
294 | packageReadOnly = false | |
295 | end | |
296 | ro_asked = true | |
297 | end | |
298 | if fs.combine("",list[a]) == shell.getRunningProgram() and (not ps_asked) then | |
299 | write("Include self? ") | |
300 | if choice("yn",true) == 2 then | |
301 | packageSelf = false | |
302 | end | |
303 | ps_asked = true | |
304 | end | |
305 | end | |
306 | end | |
307 | for a = 1, #list do --this loop kills fascists | |
308 | local is_self = fs.combine("",list[a]) == fs.combine("",shell.getRunningProgram()) | |
309 | if not ((is_self and not packageSelf) or (fs.isReadOnly(list[a]) and not packageReadOnly)) then | |
310 | if verbose then | |
311 | write("[") | |
312 | if term.isColor() then term.setTextColor(colors.lightGray) end | |
313 | write(sanitize(list[a],fs.combine(dir,input))) | |
314 | term.setTextColor(tx) | |
315 | write("]") | |
316 | end | |
317 | if fs.isDir(list[a]) then | |
318 | out[sanitize(list[a],fs.combine(dir,input))] = true | |
319 | else | |
320 | local file = fs.open(list[a],"r") | |
321 | local cont = file.readAll() | |
322 | file.close() | |
323 | if doCompress then | |
324 | out[sanitize(list[a],fs.combine(dir,input))] = tablize(compyress(cont)) | |
325 | else | |
326 | out[sanitize(list[a],fs.combine(dir,input))] = tablize(cont) | |
327 | end | |
328 | end | |
329 | local tx = term.getTextColor() | |
330 | if fs.getName(list[a]):lower() == "peasant" then | |
331 | if term.isColor() then | |
332 | term.setTextColor(colors.orange) | |
333 | end | |
334 | print(" BURNINATED") | |
335 | else | |
336 | if term.isColor() then | |
337 | term.setTextColor(colors.green) | |
338 | end | |
339 | print(" GOOD") | |
340 | end | |
341 | term.setTextColor(tx) | |
342 | else | |
343 | if fs.getName(list[a]):lower() == "peasant" then | |
344 | print("Spared "..list[a]) | |
345 | else | |
346 | print("Skipped "..list[a]) | |
347 | end | |
348 | end | |
349 | end | |
350 | local fullOutput = tostring(doCompress).."\n"..fixstr(textutils.serialize(out)) | |
351 | local sCode | |
352 | if doPastebin then | |
353 | print("Uploading...") | |
354 | sCode = postToPastebin(input,fullOutput) | |
355 | return 7, "Code = '"..sCode.."'" | |
356 | else | |
357 | if fs.isDir(output) then fs.delete(output) end | |
358 | local file = fs.open(output,"w") | |
359 | file.write(fullOutput) | |
360 | file.close() | |
361 | return 1 | |
362 | end | |
363 | else --if a package | |
364 | local list, isCompy | |
365 | if not doPastebin then | |
366 | local file = fs.open(input,"r") | |
367 | isCompy = file.readLine() | |
368 | list = file.readAll() | |
369 | file.close() | |
370 | else | |
371 | local file = http.get("http://pastebin.com/raw/"..tostring(input)) | |
372 | if file then | |
373 | isCompy = file.readLine() | |
374 | list = file.readAll() | |
375 | else | |
376 | return 6 | |
377 | end | |
378 | end | |
379 | local list = textutils.unserialize(list) | |
380 | if type(list) ~= "table" then | |
381 | return 4 | |
382 | end | |
383 | if fs.exists(output) then | |
384 | fs.delete(output) | |
385 | end | |
386 | local amnt = 0 | |
387 | for k,v in pairs(list) do | |
388 | amnt = amnt + 1 | |
389 | end | |
390 | local num = 0 | |
391 | for k,v in pairs(list) do | |
392 | num = num + 1 | |
393 | if v == true then | |
394 | fs.makeDir(fs.combine(output,fs.combine(k,dir))) | |
395 | else | |
396 | local file = fs.open(fs.combine(output,fs.combine(k,dir)),"w") | |
397 | if verbose then | |
398 | write("[") | |
399 | if term.isColor() then term.setTextColor(colors.lightGray) end | |
400 | write(k) | |
401 | term.setTextColor(tx) | |
402 | write("]") | |
403 | end | |
404 | if isCompy:gsub(" ","") == "true" then | |
405 | file.write(decompyress(tablize(v))) | |
406 | else | |
407 | file.write(tablize(v)) | |
408 | end | |
409 | file.close() | |
410 | local tx = term.getTextColor() | |
411 | if fs.getName(k):lower() == "peasant" then | |
412 | if term.isColor() then | |
413 | term.setTextColor(colors.orange) | |
414 | end | |
415 | print(" UNBURNINATED") | |
416 | else | |
417 | if term.isColor() then | |
418 | term.setTextColor(colors.green) | |
419 | end | |
420 | print(" GOOD") | |
421 | end | |
422 | term.setTextColor(tx) | |
423 | end | |
424 | end | |
425 | return 2 | |
426 | end | |
427 | end | |
428 | ||
429 | local success, res, otherRes = pcall( function() return doPack(input,outpath,doCompress,true) end ) --functionized it! | |
430 | ||
431 | if not success then | |
432 | term.setTextColor(colors.white) | |
433 | print("\n***Something went wrong!***") | |
434 | return printError(res) | |
435 | end | |
436 | ||
437 | if res then | |
438 | local msgs = { | |
439 | [1] = "Successfully packed '"..input.."/' as '"..outpath.."'", | |
440 | [2] = "Successfully unpacked '"..input.."' to '"..outpath.."/'", | |
441 | [3] = "That file/folder does not exist.", | |
442 | [4] = "That file isn't a packed folder.", | |
443 | [5] = "You don't have permission.", | |
444 | [6] = "Failed to connect.", | |
445 | [7] = "Uploaded successfully.", | |
446 | } | |
447 | print(msgs[res]) | |
448 | if otherRes then | |
449 | print(otherRes) | |
450 | end | |
451 | end |