~ubuntu-branches/ubuntu/vivid/luarocks/vivid-proposed

« back to all changes in this revision

Viewing changes to src/luarocks/fs/lua.lua

  • Committer: Package Import Robot
  • Author(s): Enrico Tassi
  • Date: 2014-10-11 15:26:47 UTC
  • mfrom: (1.2.6)
  • Revision ID: package-import@ubuntu.com-20141011152647-bkfciayfdz6elvv3
Tags: 2.2.0+dfsg-1
* New upstream release
* patch 0001-Fixed-detection-of-Debian-paths removed (applied upstream)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
 
2
2
--- Native Lua implementation of filesystem and platform abstractions,
3
3
-- using LuaFileSystem, LZLib, MD5 and LuaCurl.
4
 
module("luarocks.fs.lua", package.seeall)
 
4
-- module("luarocks.fs.lua")
 
5
local fs_lua = {}
5
6
 
6
7
local fs = require("luarocks.fs")
7
8
 
10
11
local util = require("luarocks.util")
11
12
local path = require("luarocks.path")
12
13
 
13
 
local socket_ok, http = pcall(require, "socket.http")
14
 
local _, ftp = pcall(require, "socket.ftp")
15
 
local zip_ok, lrzip = pcall(require, "luarocks.tools.zip")
16
 
local unzip_ok, luazip = pcall(require, "zip"); _G.zip = nil
17
 
local lfs_ok, lfs = pcall(require, "lfs")
18
 
local md5_ok, md5 = pcall(require, "md5")
19
 
local posix_ok, posix = pcall(require, "posix")
20
 
 
21
 
local tar = require("luarocks.tools.tar")
 
14
local socket_ok, zip_ok, unzip_ok, lfs_ok, md5_ok, posix_ok, _
 
15
local http, ftp, lrzip, luazip, lfs, md5, posix
 
16
 
 
17
if cfg.fs_use_modules then
 
18
   socket_ok, http = pcall(require, "socket.http")
 
19
   _, ftp = pcall(require, "socket.ftp")
 
20
   zip_ok, lrzip = pcall(require, "luarocks.tools.zip")
 
21
   unzip_ok, luazip = pcall(require, "zip"); _G.zip = nil
 
22
   lfs_ok, lfs = pcall(require, "lfs")
 
23
   md5_ok, md5 = pcall(require, "md5")
 
24
   posix_ok, posix = pcall(require, "posix")
 
25
end
 
26
 
22
27
local patch = require("luarocks.tools.patch")
23
28
 
24
29
local dir_stack = {}
25
30
 
26
31
math.randomseed(os.time())
27
32
 
28
 
dir_separator = "/"
 
33
local dir_separator = "/"
29
34
 
30
35
--- Quote argument for shell processing.
31
36
-- Adds single quotes and escapes.
32
37
-- @param arg string: Unquoted argument.
33
38
-- @return string: Quoted argument.
34
 
function Q(arg)
 
39
function fs_lua.Q(arg)
35
40
   assert(type(arg) == "string")
36
41
 
37
42
   -- FIXME Unix-specific
38
 
   return "'" .. arg:gsub("\\", "\\\\"):gsub("'", "'\\''") .. "'"
39
 
end
40
 
 
41
 
local function normalize(name)
42
 
   return name:gsub("\\", "/"):gsub("/$", "")
 
43
   return "'" .. arg:gsub("'", "'\\''") .. "'"
43
44
end
44
45
 
45
46
--- Test is file/dir is writable.
48
49
-- for checking the result of subsequent operations.
49
50
-- @param file string: filename to test
50
51
-- @return boolean: true if file exists, false otherwise.
51
 
function is_writable(file)
 
52
function fs_lua.is_writable(file)
52
53
   assert(file)
53
 
   file = normalize(file)
 
54
   file = dir.normalize(file)
54
55
   local result
55
56
   if fs.is_dir(file) then
56
57
      local file2 = dir.path(file, '.tmpluarockstestwritable')
69
70
--- Create a temporary directory.
70
71
-- @param name string: name pattern to use for avoiding conflicts
71
72
-- when creating temporary directory.
72
 
function make_temp_dir(name)
 
73
-- @return string or (nil, string): name of temporary directory or (nil, error message) on failure.
 
74
function fs_lua.make_temp_dir(name)
73
75
   assert(type(name) == "string")
74
 
   name = normalize(name)
 
76
   name = dir.normalize(name)
75
77
 
76
78
   local temp_dir = (os.getenv("TMP") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-" .. tostring(math.floor(math.random() * 10000))
77
 
   if fs.make_dir(temp_dir) then
 
79
   local ok, err = fs.make_dir(temp_dir)
 
80
   if ok then
78
81
      return temp_dir
79
82
   else
80
 
      return nil
81
 
   end
 
83
      return nil, err
 
84
   end
 
85
end
 
86
 
 
87
local function quote_args(command, ...)
 
88
   local out = { command }
 
89
   for _, arg in ipairs({...}) do
 
90
      assert(type(arg) == "string")
 
91
      out[#out+1] = fs.Q(arg)
 
92
   end
 
93
   return table.concat(out, " ")
82
94
end
83
95
 
84
96
--- Run the given command, quoting its arguments.
89
100
-- @param ... Strings containing additional arguments, which are quoted.
90
101
-- @return boolean: true if command succeeds (status code 0), false
91
102
-- otherwise.
92
 
function execute(command, ...)
 
103
function fs_lua.execute(command, ...)
93
104
   assert(type(command) == "string")
 
105
   return fs.execute_string(quote_args(command, ...))
 
106
end
94
107
 
95
 
   for _, arg in ipairs({...}) do
96
 
      assert(type(arg) == "string")
97
 
      command = command .. " " .. fs.Q(arg)
 
108
--- Run the given command, quoting its arguments, silencing its output.
 
109
-- The command is executed in the current directory in the dir stack.
 
110
-- Silencing is omitted if 'verbose' mode is enabled.
 
111
-- @param command string: The command to be executed. No quoting/escaping
 
112
-- is applied.
 
113
-- @param ... Strings containing additional arguments, which will be quoted.
 
114
-- @return boolean: true if command succeeds (status code 0), false
 
115
-- otherwise.
 
116
function fs_lua.execute_quiet(command, ...)
 
117
   assert(type(command) == "string")
 
118
   if cfg.verbose then -- omit silencing output
 
119
      return fs.execute_string(quote_args(command, ...))
 
120
   else
 
121
      return fs.execute_string(fs.quiet(quote_args(command, ...)))
98
122
   end
99
 
   return fs.execute_string(command)
100
123
end
101
124
 
102
125
--- Check the MD5 checksum for a file.
103
126
-- @param file string: The file to be checked.
104
127
-- @param md5sum string: The string with the expected MD5 checksum.
 
128
-- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false + msg if not
105
129
-- or if it could not perform the check for any reason.
106
 
function check_md5(file, md5sum)
107
 
   file = normalize(file)
108
 
   local computed = fs.get_md5(file)
 
130
function fs_lua.check_md5(file, md5sum)
 
131
   file = dir.normalize(file)
 
132
   local computed, msg = fs.get_md5(file)
109
133
   if not computed then
110
 
      return false
 
134
      return false, msg
111
135
   end
112
136
   if computed:match("^"..md5sum) then
113
137
      return true
114
138
   else
115
 
      return false
116
 
   end
 
139
      return false, "Mismatch MD5 hash for file "..file
 
140
   end
 
141
end
 
142
 
 
143
--- List the contents of a directory.
 
144
-- @param at string or nil: directory to list (will be the current
 
145
-- directory if none is given).
 
146
-- @return table: an array of strings with the filenames representing
 
147
-- the contents of a directory.
 
148
function fs_lua.list_dir(at)
 
149
   local result = {}
 
150
   for file in fs.dir(at) do
 
151
      result[#result+1] = file
 
152
   end
 
153
   return result
 
154
end
 
155
 
 
156
--- Iterate over the contents of a directory.
 
157
-- @param at string or nil: directory to list (will be the current
 
158
-- directory if none is given).
 
159
-- @return function: an iterator function suitable for use with
 
160
-- the for statement.
 
161
function fs_lua.dir(at)
 
162
   if not at then
 
163
      at = fs.current_dir()
 
164
   end
 
165
   at = dir.normalize(at)
 
166
   if not fs.is_dir(at) then
 
167
      return function() end
 
168
   end
 
169
   return coroutine.wrap(function() fs.dir_iterator(at) end)
117
170
end
118
171
 
119
172
---------------------------------------------------------------------
128
180
-- @param cmd string: No quoting/escaping is applied to the command.
129
181
-- @return boolean: true if command succeeds (status code 0), false
130
182
-- otherwise.
131
 
function execute_string(cmd)
 
183
function fs_lua.execute_string(cmd)
132
184
   local code = os.execute(cmd)
133
 
   if code == 0 or code == true then
134
 
      return true
135
 
   else
136
 
      return false
137
 
   end
 
185
   return (code == 0 or code == true)
138
186
end
139
187
 
140
188
--- Obtain current directory.
141
189
-- Uses the module's internal dir stack.
142
190
-- @return string: the absolute pathname of the current directory.
143
 
function current_dir()
 
191
function fs_lua.current_dir()
144
192
   return lfs.currentdir()
145
193
end
146
194
 
149
197
-- semantics of chdir, as it does not handle errors the same way,
150
198
-- but works well for our purposes for now.
151
199
-- @param d string: The directory to switch to.
152
 
function change_dir(d)
 
200
function fs_lua.change_dir(d)
153
201
   table.insert(dir_stack, lfs.currentdir())
154
 
   d = normalize(d)
155
 
   lfs.chdir(d)
 
202
   d = dir.normalize(d)
 
203
   return lfs.chdir(d)
156
204
end
157
205
 
158
206
--- Change directory to root.
159
207
-- Allows leaving a directory (e.g. for deleting it) in
160
208
-- a crossplatform way.
161
 
function change_dir_to_root()
 
209
function fs_lua.change_dir_to_root()
162
210
   table.insert(dir_stack, lfs.currentdir())
163
211
   lfs.chdir("/") -- works on Windows too
164
212
end
165
213
 
166
214
--- Change working directory to the previous in the dir stack.
167
215
-- @return true if a pop ocurred, false if the stack was empty.
168
 
function pop_dir()
 
216
function fs_lua.pop_dir()
169
217
   local d = table.remove(dir_stack)
170
218
   if d then
171
219
      lfs.chdir(d)
176
224
end
177
225
 
178
226
--- Create a directory if it does not already exist.
 
227
-- If any of the higher levels in the path name do not exist
179
228
-- too, they are created as well.
180
229
-- @param directory string: pathname of directory to create.
181
 
function make_dir(directory)
 
230
-- @return boolean or (boolean, string): true on success or (false, error message) on failure.
 
231
function fs_lua.make_dir(directory)
182
232
   assert(type(directory) == "string")
183
 
   directory = normalize(directory)
 
233
   directory = dir.normalize(directory)
184
234
   local path = nil
185
235
   if directory:sub(2, 2) == ":" then
186
236
     path = directory:sub(1, 2)
196
244
      path = path and path .. dir.separator .. d or d
197
245
      local mode = lfs.attributes(path, "mode")
198
246
      if not mode then
199
 
         if not lfs.mkdir(path) then
200
 
            return false
 
247
         local ok, err = lfs.mkdir(path)
 
248
         if not ok then
 
249
            return false, err
201
250
         end
202
251
      elseif mode ~= "directory" then
203
 
         return false
 
252
         return false, path.." is not a directory"
204
253
      end
205
254
   end
206
255
   return true
210
259
-- Does not return errors (for example, if directory is not empty or
211
260
-- if already does not exist)
212
261
-- @param d string: pathname of directory to remove.
213
 
function remove_dir_if_empty(d)
 
262
function fs_lua.remove_dir_if_empty(d)
214
263
   assert(d)
215
 
   d = normalize(d)
 
264
   d = dir.normalize(d)
216
265
   lfs.rmdir(d)
217
266
end
218
267
 
220
269
-- Does not return errors (for example, if directory is not empty or
221
270
-- if already does not exist)
222
271
-- @param d string: pathname of directory to remove.
223
 
function remove_dir_tree_if_empty(d)
 
272
function fs_lua.remove_dir_tree_if_empty(d)
224
273
   assert(d)
225
 
   d = normalize(d)
 
274
   d = dir.normalize(d)
226
275
   for i=1,10 do
227
276
      lfs.rmdir(d)
228
277
      d = dir.dir_name(d)
236
285
-- or nil to use the source filename permissions
237
286
-- @return boolean or (boolean, string): true on success, false on failure,
238
287
-- plus an error message.
239
 
function copy(src, dest, perms)
 
288
function fs_lua.copy(src, dest, perms)
240
289
   assert(src and dest)
241
 
   src = normalize(src)
242
 
   dest = normalize(dest)
 
290
   src = dir.normalize(src)
 
291
   dest = dir.normalize(dest)
243
292
   local destmode = lfs.attributes(dest, "mode")
244
293
   if destmode == "directory" then
245
294
      dest = dir.path(dest, dir.base_name(src))
247
296
   if not perms then perms = fs.get_permissions(src) end
248
297
   local src_h, err = io.open(src, "rb")
249
298
   if not src_h then return nil, err end
250
 
   local dest_h, err = io.open(dest, "wb+")
 
299
   local dest_h, err = io.open(dest, "w+b")
251
300
   if not dest_h then src_h:close() return nil, err end
252
301
   while true do
253
302
      local block = src_h:read(8192)
273
322
      if not ok then return false end
274
323
   elseif srcmode == "directory" then
275
324
      local subdir = dir.path(dest, dir.base_name(src))
276
 
      fs.make_dir(subdir)
 
325
      local ok, err = fs.make_dir(subdir)
 
326
      if not ok then return nil, err end
277
327
      for file in lfs.dir(src) do
278
328
         if file ~= "." and file ~= ".." then
279
329
            local ok = recursive_copy(dir.path(src, file), subdir)
289
339
-- @param dest string: Pathname of destination
290
340
-- @return boolean or (boolean, string): true on success, false on failure,
291
341
-- plus an error message.
292
 
function copy_contents(src, dest)
 
342
function fs_lua.copy_contents(src, dest)
293
343
   assert(src and dest)
294
 
   src = normalize(src)
295
 
   dest = normalize(dest)
 
344
   src = dir.normalize(src)
 
345
   dest = dir.normalize(dest)
296
346
   assert(lfs.attributes(src, "mode") == "directory")
297
347
 
298
348
   for file in lfs.dir(src) do
312
362
-- @return boolean or (boolean, string): true on success,
313
363
-- or nil and an error message on failure.
314
364
local function recursive_delete(name)
315
 
   local mode = lfs.attributes(name, "mode")
316
 
 
317
 
   if mode == "file" then
318
 
      return os.remove(name)
319
 
   elseif mode == "directory" then
 
365
   local ok = os.remove(name)
 
366
   if ok then return true end
 
367
   local pok, ok, err = pcall(function()
320
368
      for file in lfs.dir(name) do
321
369
         if file ~= "." and file ~= ".." then
322
370
            local ok, err = recursive_delete(dir.path(name, file))
324
372
         end
325
373
      end
326
374
      local ok, err = lfs.rmdir(name)
327
 
      if not ok then return nil, err end
 
375
      return ok, (not ok) and err
 
376
   end)
 
377
   if pok then
 
378
      return ok, err
 
379
   else
 
380
      return pok, ok
328
381
   end
329
 
   return true
330
382
end
331
383
 
332
384
--- Delete a file or a directory and all its contents.
333
385
-- @param name string: Pathname of source
334
 
function delete(name)
335
 
   name = normalize(name)
336
 
   return recursive_delete(name) or false
 
386
-- @return nil
 
387
function fs_lua.delete(name)
 
388
   name = dir.normalize(name)
 
389
   recursive_delete(name)
337
390
end
338
391
 
339
 
--- List the contents of a directory.
340
 
function list_dir(at)
341
 
   assert(type(at) == "string" or not at)
342
 
   if not at then
343
 
      at = fs.current_dir()
344
 
   end
345
 
   at = normalize(at)
346
 
   if not fs.is_dir(at) then
347
 
      return {}
348
 
   end
349
 
   local result = {}
 
392
--- Internal implementation function for fs.dir.
 
393
-- Yields a filename on each iteration.
 
394
-- @param at string: directory to list
 
395
-- @return nil
 
396
function fs_lua.dir_iterator(at)
350
397
   for file in lfs.dir(at) do
351
398
      if file ~= "." and file ~= ".." then
352
 
         table.insert(result, file)
 
399
         coroutine.yield(file)
353
400
      end
354
401
   end
355
 
   return result
356
402
end
357
403
 
358
404
--- Implementation function for recursive find.
383
424
-- directory if none is given).
384
425
-- @return table: an array of strings with the filenames representing
385
426
-- the contents of a directory.
386
 
function find(at)
 
427
function fs_lua.find(at)
387
428
   assert(type(at) == "string" or not at)
388
429
   if not at then
389
430
      at = fs.current_dir()
390
431
   end
391
 
   at = normalize(at)
 
432
   at = dir.normalize(at)
392
433
   if not fs.is_dir(at) then
393
434
      return {}
394
435
   end
400
441
--- Test for existance of a file.
401
442
-- @param file string: filename to test
402
443
-- @return boolean: true if file exists, false otherwise.
403
 
function exists(file)
 
444
function fs_lua.exists(file)
404
445
   assert(file)
405
 
   file = normalize(file)
 
446
   file = dir.normalize(file)
406
447
   return type(lfs.attributes(file)) == "table"
407
448
end
408
449
 
409
450
--- Test is pathname is a directory.
410
451
-- @param file string: pathname to test
411
452
-- @return boolean: true if it is a directory, false otherwise.
412
 
function is_dir(file)
 
453
function fs_lua.is_dir(file)
413
454
   assert(file)
414
 
   file = normalize(file)
 
455
   file = dir.normalize(file)
415
456
   return lfs.attributes(file, "mode") == "directory"
416
457
end
417
458
 
418
459
--- Test is pathname is a regular file.
419
460
-- @param file string: pathname to test
420
461
-- @return boolean: true if it is a file, false otherwise.
421
 
function is_file(file)
 
462
function fs_lua.is_file(file)
422
463
   assert(file)
423
 
   file = normalize(file)
 
464
   file = dir.normalize(file)
424
465
   return lfs.attributes(file, "mode") == "file"
425
466
end
426
467
 
427
 
function set_time(file, time)
428
 
   file = normalize(file)
 
468
function fs_lua.set_time(file, time)
 
469
   file = dir.normalize(file)
429
470
   return lfs.touch(file, time)
430
471
end
431
472
 
437
478
 
438
479
if zip_ok then
439
480
 
440
 
function zip(zipfile, ...)
 
481
function fs_lua.zip(zipfile, ...)
441
482
   return lrzip.zip(zipfile, ...)
442
483
end
443
484
 
447
488
--- Uncompress files from a .zip archive.
448
489
-- @param zipfile string: pathname of .zip archive to be extracted.
449
490
-- @return boolean: true on success, false on failure.
450
 
function unzip(zipfile)
 
491
function fs_lua.unzip(zipfile)
451
492
   local zipfile, err = luazip.open(zipfile)
452
493
   if not zipfile then return nil, err end
453
494
   local files = zipfile:files()
454
495
   local file = files()
455
496
   repeat
456
497
      if file.filename:sub(#file.filename) == "/" then
457
 
         fs.make_dir(dir.path(fs.current_dir(), file.filename))
 
498
         local ok, err = fs.make_dir(dir.path(fs.current_dir(), file.filename))
 
499
         if not ok then return nil, err end
458
500
      else
 
501
         local base = dir.dir_name(file.filename)
 
502
         if base ~= "" then
 
503
            base = dir.path(fs.current_dir(), base)
 
504
            if not fs.is_dir(base) then
 
505
               local ok, err = fs.make_dir(base)
 
506
               if not ok then return nil, err end
 
507
            end
 
508
         end
459
509
         local rf, err = zipfile:open(file.filename)
460
510
         if not rf then zipfile:close(); return nil, err end
461
511
         local contents = rf:read("*a")
481
531
 
482
532
local ltn12 = require("ltn12")
483
533
local luasec_ok, https = pcall(require, "ssl.https")
 
534
 
484
535
local redirect_protocols = {
485
536
   http = http,
486
537
   https = luasec_ok and https,
487
538
}
488
539
 
489
 
local function http_request(url, http, loop_control)
 
540
local function request(url, method, http, loop_control)
490
541
   local result = {}
491
 
   local res, status, headers, err = http.request { url = url, proxy = cfg.proxy, redirect = false, sink = ltn12.sink.table(result) }
 
542
   
 
543
   local proxy = cfg.proxy
 
544
   if type(proxy) ~= "string" then proxy = nil end
 
545
   -- LuaSocket's http.request crashes when given URLs missing the scheme part.
 
546
   if proxy and not proxy:find("://") then
 
547
      proxy = "http://" .. proxy
 
548
   end
 
549
   
 
550
   if cfg.show_downloads then
 
551
      io.write(method.." "..url.." ...\n")
 
552
   end
 
553
   local dots = 0
 
554
   if cfg.connection_timeout and cfg.connection_timeout > 0 then
 
555
      http.TIMEOUT = cfg.connection_timeout
 
556
   end
 
557
   local res, status, headers, err = http.request {
 
558
      url = url,
 
559
      proxy = proxy,
 
560
      method = method,
 
561
      redirect = false,
 
562
      sink = ltn12.sink.table(result),
 
563
      step = cfg.show_downloads and function(...)
 
564
         io.write(".")
 
565
         io.flush()
 
566
         dots = dots + 1
 
567
         if dots == 70 then
 
568
            io.write("\n")
 
569
            dots = 0
 
570
         end
 
571
         return ltn12.pump.step(...)
 
572
      end,
 
573
      headers = {
 
574
         ["user-agent"] = cfg.user_agent.." via LuaSocket"
 
575
      },
 
576
   }
 
577
   if cfg.show_downloads then
 
578
      io.write("\n")
 
579
   end
492
580
   if not res then
493
581
      return nil, status
494
582
   elseif status == 301 or status == 302 then
502
590
               return nil, "Redirection loop -- broken URL?"
503
591
            end
504
592
            loop_control[url] = true
505
 
            return http_request(location, redirect_protocols[protocol], loop_control)
 
593
            return request(location, method, redirect_protocols[protocol], loop_control)
506
594
         else
507
 
            return nil, "URL redirected to unsupported protocol - install luasec to get HTTPS support."
 
595
            return nil, "URL redirected to unsupported protocol - install luasec to get HTTPS support.", "https"
508
596
         end
509
597
      end
510
598
      return nil, err
511
599
   elseif status ~= 200 then
512
600
      return nil, err
513
601
   else
 
602
      return result, status, headers, err
 
603
   end
 
604
end
 
605
 
 
606
local function http_request(url, http, cached)
 
607
   if cached then
 
608
      local tsfd = io.open(cached..".timestamp", "r")
 
609
      if tsfd then
 
610
         local timestamp = tsfd:read("*a")
 
611
         tsfd:close()
 
612
         local result, status, headers, err = request(url, "HEAD", http)
 
613
         if status == 200 and headers["last-modified"] == timestamp then
 
614
            return true
 
615
         end
 
616
         if not result then
 
617
            return nil, status, headers
 
618
         end
 
619
      end
 
620
   end
 
621
   local result, status, headers, err = request(url, "GET", http)
 
622
   if result then
 
623
      if cached and headers["last-modified"] then
 
624
         local tsfd = io.open(cached..".timestamp", "w")
 
625
         if tsfd then
 
626
            tsfd:write(headers["last-modified"])
 
627
            tsfd:close()
 
628
         end
 
629
      end
514
630
      return table.concat(result)
 
631
   else
 
632
      return nil, status, headers
515
633
   end
516
634
end
517
635
 
 
636
local downloader_warning = false
 
637
 
518
638
--- Download a remote file.
519
639
-- @param url string: URL to be fetched.
520
640
-- @param filename string or nil: this function attempts to detect the
521
641
-- resulting local filename of the remote file as the basename of the URL;
522
642
-- if that is not correct (due to a redirection, for example), the local
523
643
-- filename can be given explicitly as this second argument.
524
 
function download(url, filename)
 
644
-- @return (boolean, string): true and the filename on success,
 
645
-- false and the error message on failure.
 
646
function fs_lua.download(url, filename, cache)
525
647
   assert(type(url) == "string")
526
648
   assert(type(filename) == "string" or not filename)
527
649
 
528
 
   filename = dir.path(fs.current_dir(), filename or dir.base_name(url))
 
650
   filename = fs.absolute_name(filename or dir.base_name(url))
529
651
   
530
 
   local content, err
 
652
   local content, err, https_err
531
653
   if util.starts_with(url, "http:") then
532
 
      content, err = http_request(url, http)
 
654
      content, err, https_err = http_request(url, http, cache and filename)
533
655
   elseif util.starts_with(url, "ftp:") then
534
656
      content, err = ftp.get(url)
535
657
   elseif util.starts_with(url, "https:") then
536
658
      if luasec_ok then
537
 
         content, err = http_request(url, https)
 
659
         content, err = http_request(url, https, cache and filename)
538
660
      else
539
 
         err = "Unsupported protocol - install luasec to get HTTPS support."
 
661
         https_err = true
540
662
      end
541
663
   else
542
664
      err = "Unsupported protocol"
543
665
   end
 
666
   if https_err then
 
667
      if not downloader_warning then
 
668
         util.printerr("Warning: falling back to "..cfg.downloader.." - install luasec to get native HTTPS support")
 
669
         downloader_warning = true
 
670
      end
 
671
      return fs.use_downloader(url, filename, cache)
 
672
   end
 
673
   if cache and content == true then
 
674
      return true, filename
 
675
   end
544
676
   if not content then
545
677
      return false, tostring(err)
546
678
   end
549
680
   if not file then return false end
550
681
   file:write(content)
551
682
   file:close()
552
 
   return true
 
683
   return true, filename
 
684
end
 
685
 
 
686
else --...if socket_ok == false then
 
687
 
 
688
function fs_lua.download(url, filename, cache)
 
689
   return fs.use_downloader(url, filename, cache)
553
690
end
554
691
 
555
692
end
561
698
 
562
699
--- Get the MD5 checksum for a file.
563
700
-- @param file string: The file to be computed.
564
 
function get_md5(file)
 
701
-- @return string: The MD5 checksum or nil + error
 
702
function fs_lua.get_md5(file)
565
703
   file = fs.absolute_name(file)
566
 
   local file = io.open(file, "rb")
567
 
   if not file then return false end
568
 
   local computed = md5.sumhexa(file:read("*a"))
569
 
   file:close()
570
 
   return computed
 
704
   local file_handler = io.open(file, "rb")
 
705
   if not file_handler then return nil, "Failed to open file for reading: "..file end
 
706
   local computed = md5.sumhexa(file_handler:read("*a"))
 
707
   file_handler:close()
 
708
   if computed then return computed end
 
709
   return nil, "Failed to compute MD5 hash for file "..file
571
710
end
572
711
 
573
712
end
590
728
   ["7"] = "rwx",
591
729
}
592
730
 
593
 
function chmod(file, mode)
 
731
function fs_lua.chmod(file, mode)
594
732
   -- LuaPosix (as of 5.1.15) does not support octal notation...
595
733
   if mode:sub(1,1) == "0" then
596
734
      local new_mode = {}
603
741
   return err == 0
604
742
end
605
743
 
606
 
function get_permissions(file)
 
744
function fs_lua.get_permissions(file)
607
745
   return posix.stat(file, "mode")
608
746
end
609
747
 
616
754
--- Apply a patch.
617
755
-- @param patchname string: The filename of the patch.
618
756
-- @param patchdata string or nil: The actual patch as a string.
619
 
function apply_patch(patchname, patchdata)
 
757
function fs_lua.apply_patch(patchname, patchdata)
620
758
   local p, all_ok = patch.read_patch(patchname, patchdata)
621
759
   if not all_ok then
622
760
      return nil, "Failed reading patch "..patchname
631
769
-- @param dest string: Pathname of destination
632
770
-- @return boolean or (boolean, string): true on success, false on failure,
633
771
-- plus an error message.
634
 
function move(src, dest)
 
772
function fs_lua.move(src, dest)
635
773
   assert(src and dest)
636
774
   if fs.exists(dest) and not fs.is_dir(dest) then
637
775
      return false, "File already exists: "..dest
640
778
   if not ok then
641
779
      return false, err
642
780
   end
643
 
   ok = fs.delete(src)
644
 
   if not ok then
 
781
   fs.delete(src)
 
782
   if fs.exists(src) then
645
783
      return false, "Failed move: could not delete "..src.." after copy."
646
784
   end
647
785
   return true
652
790
-- @param flags table: the flags table passed to run() drivers.
653
791
-- @return boolean or (boolean, string): true on success, false on failure,
654
792
-- plus an error message.
655
 
function check_command_permissions(flags)
 
793
function fs_lua.check_command_permissions(flags)
656
794
   local root_dir = path.root_dir(cfg.rocks_dir)
657
795
   local ok = true
658
796
   local err = ""
659
 
   for _, dir in ipairs { cfg.rocks_dir, root_dir, dir.dir_name(root_dir) } do
 
797
   for _, dir in ipairs { cfg.rocks_dir, root_dir } do
660
798
      if fs.exists(dir) and not fs.is_writable(dir) then
661
799
         ok = false
662
800
         err = "Your user does not have write permissions in " .. dir
663
801
         break
664
802
      end
665
803
   end
 
804
   local root_parent = dir.dir_name(root_dir)
 
805
   if ok and not fs.exists(root_dir) and not fs.is_writable(root_parent) then
 
806
      ok = false
 
807
      err = root_dir.." does not exist and your user does not have write permissions in " .. root_parent
 
808
   end
666
809
   if ok then
667
810
      return true
668
811
   else
674
817
      return nil, err
675
818
   end
676
819
end
 
820
 
 
821
--- Check whether a file is a Lua script
 
822
-- When the file can be succesfully compiled by the configured
 
823
-- Lua interpreter, it's considered to be a valid Lua file.
 
824
-- @param name filename of file to check
 
825
-- @return boolean true, if it is a Lua script, false otherwise
 
826
function fs_lua.is_lua(name)
 
827
  name = name:gsub([[%\]],"/")   -- normalize on fw slash to prevent escaping issues
 
828
  local lua = fs.Q(dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter))  -- get lua interpreter configured
 
829
  -- execute on configured interpreter, might not be the same as the interpreter LR is run on
 
830
  local result = fs.execute_string(lua..[[ -e "if loadfile(']]..name..[[') then os.exit() else os.exit(1) end"]])
 
831
  return (result == true) 
 
832
end
 
833
 
 
834
return fs_lua