aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--install.bat166
-rw-r--r--src/luarocks/cfg.lua2
-rw-r--r--src/luarocks/fs.lua26
-rw-r--r--src/luarocks/fs/lua.lua25
-rw-r--r--src/luarocks/fs/unix/tools.lua7
-rw-r--r--src/luarocks/fs/win32.lua39
-rw-r--r--src/luarocks/fs/win32/tools.lua61
7 files changed, 238 insertions, 88 deletions
diff --git a/install.bat b/install.bat
index a4257be6..43a2b34f 100644
--- a/install.bat
+++ b/install.bat
@@ -6,8 +6,8 @@ local vars = {}
6 6
7vars.PREFIX = [[C:\LuaRocks]] 7vars.PREFIX = [[C:\LuaRocks]]
8vars.VERSION = "2.1" 8vars.VERSION = "2.1"
9vars.SYSCONFDIR = [[C:\LuaRocks]] 9vars.SYSCONFDIR = nil
10vars.ROCKS_TREE = [[C:\LuaRocks]] 10vars.ROCKS_TREE = nil
11vars.SCRIPTS_DIR = nil 11vars.SCRIPTS_DIR = nil
12vars.LUA_INTERPRETER = nil 12vars.LUA_INTERPRETER = nil
13vars.LUA_PREFIX = nil 13vars.LUA_PREFIX = nil
@@ -27,6 +27,7 @@ local FORCE_CONFIG = false
27local INSTALL_LUA = false 27local INSTALL_LUA = false
28local USE_MINGW = false 28local USE_MINGW = false
29local REGISTRY = false 29local REGISTRY = false
30local NOADMIN = false
30 31
31--- 32---
32-- Some helpers 33-- Some helpers
@@ -82,6 +83,25 @@ local function mkdir (dir)
82 return exec([[.\bin\bin\mkdir -p "]]..dir..[[" >NUL]]) 83 return exec([[.\bin\bin\mkdir -p "]]..dir..[[" >NUL]])
83end 84end
84 85
86-- does the current user have admin priviledges ( = elevated)
87local function permission()
88 return exec("net session >nul 2>&1") -- fails if not admin
89end
90
91-- rename file (full path) to backup (name only), appending number if required
92-- returns the new name (name only)
93local function backup(filename, backupname)
94 local path = filename:match("(.+)%\\.-$").."\\"
95 local nname = backupname
96 local i = 0
97 while exists(path..nname) do
98 i = i + 1
99 nname = backupname..tostring(i)
100 end
101 exec([[REN "]]..filename..[[" "]]..nname..[[" > NUL]])
102 return nname
103end
104
85-- interpolate string with values from 'vars' table 105-- interpolate string with values from 'vars' table
86local function S (tmpl) 106local function S (tmpl)
87 return (tmpl:gsub('%$([%a_][%w_]*)', vars)) 107 return (tmpl:gsub('%$([%a_][%w_]*)', vars))
@@ -136,6 +156,11 @@ Other options:
136/F Remove installation directory if it already exists. 156/F Remove installation directory if it already exists.
137/R Load registry information to register '.rockspec' 157/R Load registry information to register '.rockspec'
138 extension with LuaRocks commands (right-click). 158 extension with LuaRocks commands (right-click).
159/NOADMIN The installer requires admin priviledges. If not
160 available it will elevate a new process. Use this
161 switch to prevent elevation, but make sure the
162 destination paths are all accessible for the current
163 user.
139 164
140]]) 165]])
141end 166end
@@ -151,8 +176,6 @@ local function parse_options(args)
151 os.exit(0) 176 os.exit(0)
152 elseif name == "/P" then 177 elseif name == "/P" then
153 vars.PREFIX = option.value 178 vars.PREFIX = option.value
154 vars.SYSCONFDIR = option.value
155 vars.ROCKS_TREE = option.value
156 P_SET = true 179 P_SET = true
157 elseif name == "/CONFIG" then 180 elseif name == "/CONFIG" then
158 vars.SYSCONFDIR = option.value 181 vars.SYSCONFDIR = option.value
@@ -180,6 +203,8 @@ local function parse_options(args)
180 FORCE = true 203 FORCE = true
181 elseif name == "/R" then 204 elseif name == "/R" then
182 REGISTRY = true 205 REGISTRY = true
206 elseif name == "/NOADMIN" then
207 NOADMIN = true
183 else 208 else
184 die("Unrecognized option: " .. name) 209 die("Unrecognized option: " .. name)
185 end 210 end
@@ -376,7 +401,7 @@ local function look_for_lua_install ()
376 then 401 then
377 if get_runtime() then 402 if get_runtime() then
378 print("Runtime check completed, now testing interpreter...") 403 print("Runtime check completed, now testing interpreter...")
379 if exec(S[[$LUA_BINDIR\$LUA_INTERPRETER -v 2>NUL]]) then 404 if exec(S[["$LUA_BINDIR\$LUA_INTERPRETER" -v 2>NUL]]) then
380 print(" Ok") 405 print(" Ok")
381 return true 406 return true
382 end 407 end
@@ -397,7 +422,7 @@ local function look_for_lua_install ()
397 print("Headers found, checking runtime to use...") 422 print("Headers found, checking runtime to use...")
398 if get_runtime() then 423 if get_runtime() then
399 print("Runtime check completed, now testing interpreter...") 424 print("Runtime check completed, now testing interpreter...")
400 if exec(S[[$LUA_BINDIR\$LUA_INTERPRETER -v 2>NUL]]) then 425 if exec(S[["$LUA_BINDIR\$LUA_INTERPRETER" -v 2>NUL]]) then
401 print(" Ok") 426 print(" Ok")
402 return true 427 return true
403 end 428 end
@@ -411,7 +436,11 @@ local function look_for_lua_install ()
411 return false 436 return false
412end 437end
413 438
414--- 439
440-- ***********************************************************
441-- Installer script start
442-- ***********************************************************
443
415-- Poor man's command-line parsing 444-- Poor man's command-line parsing
416local config = {} 445local config = {}
417local with_arg = { -- options followed by an argument, others are flags 446local with_arg = { -- options followed by an argument, others are flags
@@ -427,6 +456,7 @@ local with_arg = { -- options followed by an argument, others are flags
427} 456}
428-- reconstruct argument values with spaces and double quotes 457-- reconstruct argument values with spaces and double quotes
429-- this will be damaged by the batch construction at the start of this file 458-- this will be damaged by the batch construction at the start of this file
459local oarg = arg -- retain old table
430if #arg > 0 then 460if #arg > 0 then
431 farg = table.concat(arg, " ") .. " " 461 farg = table.concat(arg, " ") .. " "
432 arg = {} 462 arg = {}
@@ -448,7 +478,10 @@ if #arg > 0 then
448 while farg:sub(1,1) == " " do farg = farg:sub(2,-1) end -- remove prefix spaces 478 while farg:sub(1,1) == " " do farg = farg:sub(2,-1) end -- remove prefix spaces
449 end 479 end
450end 480end
481for k,v in pairs(oarg) do if k < 1 then arg[k] = v end end -- copy 0 and negative indexes
482oarg = nil
451 483
484-- build config option table with name and value elements
452local i = 1 485local i = 1
453while i <= #arg do 486while i <= #arg do
454 local opt = arg[i] 487 local opt = arg[i]
@@ -470,6 +503,36 @@ print(S"LuaRocks $VERSION.x installer.\n")
470parse_options(config) 503parse_options(config)
471check_flags() 504check_flags()
472 505
506if not permission() then
507 if not NOADMIN then
508 -- must elevate the process with admin priviledges
509 if not exec("PowerShell /? >NUL 2>&1") then
510 -- powershell is not available, so error out
511 die("No administrative priviledges detected and cannot auto-elevate. Please run with admin priviledges or use the /NOADMIN switch")
512 end
513 print("Need admin priviledges, now elevating a new process to continue installing...")
514 local runner = os.getenv("TEMP").."\\".."LuaRocks_Installer.bat"
515 local f = io.open(runner, "w")
516 f:write("@echo off\n")
517 f:write("CHDIR /D "..arg[0]:match("(.+)%\\.-$").."\n") -- return to current dir, elevation changes current path
518 f:write('"'..arg[-1]..'" "'..table.concat(arg, '" "', 0)..'"\n')
519 f:write("ECHO Press any key to close this window...\n")
520 f:write("PAUSE > NUL\n")
521 f:write('DEL "'..runner..'"') -- temp batch file deletes itself
522 f:close()
523 -- run the created temp batch file in elevated mode
524 exec("PowerShell -Command (New-Object -com 'Shell.Application').ShellExecute('"..runner.."', '', '', 'runas')\n")
525 print("Now exiting unpriviledged installer")
526 os.exit() -- exit here, the newly created elevated process will do the installing
527 else
528 print("Attempting to install without admin priviledges...")
529 end
530else
531 print("Admin priviledges available for installing")
532end
533
534vars.SYSCONFDIR = vars.SYSCONFDIR or vars.PREFIX
535vars.ROCKS_TREE = vars.ROCKS_TREE or vars.PREFIX
473vars.FULL_PREFIX = S"$PREFIX\\$VERSION" 536vars.FULL_PREFIX = S"$PREFIX\\$VERSION"
474vars.BINDIR = vars.FULL_PREFIX 537vars.BINDIR = vars.FULL_PREFIX
475vars.LIBDIR = vars.FULL_PREFIX 538vars.LIBDIR = vars.FULL_PREFIX
@@ -599,8 +662,13 @@ else
599end 662end
600 663
601 664
665-- ***********************************************************
666-- Configure LuaRocks
667-- ***********************************************************
668
602print() 669print()
603print("Configuring LuaRocks...") 670print("Configuring LuaRocks...")
671
604-- Create a site-config file 672-- Create a site-config file
605if exists(S[[$LUADIR\luarocks\site_config.lua]]) then 673if exists(S[[$LUADIR\luarocks\site_config.lua]]) then
606 exec(S[[RENAME "$LUADIR\luarocks\site_config.lua" site_config.lua.bak]]) 674 exec(S[[RENAME "$LUADIR\luarocks\site_config.lua" site_config.lua.bak]])
@@ -644,50 +712,56 @@ vars.CONFIG_FILE = vars.SYSCONFDIR.."\\config.lua"
644if not exists(vars.SYSCONFDIR) then 712if not exists(vars.SYSCONFDIR) then
645 mkdir(vars.SYSCONFDIR) 713 mkdir(vars.SYSCONFDIR)
646end 714end
647if not exists(vars.CONFIG_FILE) then 715if exists(vars.CONFIG_FILE) then
648 local f = io.open(vars.CONFIG_FILE, "w") 716 local nname = backup(vars.CONFIG_FILE, "config.bak")
649 f:write([=[ 717 print("***************")
718 print(S"*** WARNING *** LuaRocks config file already exists: '$CONFIG_FILE'. The old file has been renamed to '"..nname.."'")
719 print("***************")
720end
721local f = io.open(vars.CONFIG_FILE, "w")
722f:write([=[
650rocks_servers = { 723rocks_servers = {
651 [[http://luarocks.org/repositories/rocks]] 724 [[http://luarocks.org/repositories/rocks]]
652} 725}
653rocks_trees = { 726rocks_trees = {
654]=]) 727]=])
655 if FORCE_CONFIG then 728if FORCE_CONFIG then
656 f:write(" home..[[/luarocks]],\n") 729 f:write(" home..[[/luarocks]],\n")
657 end 730end
658 f:write(S" [[$ROCKS_TREE]]\n") 731f:write(S" [[$ROCKS_TREE]]\n")
659 f:write("}\n") 732f:write("}\n")
660 if vars.SCRIPTS_DIR then 733if vars.SCRIPTS_DIR then
661 f:write(S"scripts_dir=[[$SCRIPTS_DIR]]\n") 734 f:write(S"scripts_dir=[[$SCRIPTS_DIR]]\n")
662 end 735end
663 f:write("variables = {\n") 736f:write("variables = {\n")
664 if USE_MINGW and vars.LUA_RUNTIME == "MSVCRT" then 737if USE_MINGW and vars.LUA_RUNTIME == "MSVCRT" then
665 f:write(" MSVCRT = 'm', -- make MinGW use MSVCRT.DLL as runtime\n") 738 f:write(" MSVCRT = 'm', -- make MinGW use MSVCRT.DLL as runtime\n")
666 else
667 f:write(" MSVCRT = '"..vars.LUA_RUNTIME.."',\n")
668 end
669 f:write(S" LUALIB = '$LUA_LIBNAME'\n")
670 f:write("}\n")
671 f:close()
672 print(S"Created LuaRocks config file: $CONFIG_FILE")
673else 739else
674 print(S"LuaRocks config file already exists: $CONFIG_FILE") 740 f:write(" MSVCRT = '"..vars.LUA_RUNTIME.."',\n")
675end 741end
742f:write(S" LUALIB = '$LUA_LIBNAME'\n")
743f:write("}\n")
744f:write("verbose = false -- set to 'true' to enable verbose output\n")
745f:close()
746
747print(S"Created LuaRocks config file: $CONFIG_FILE")
748
676 749
677print() 750print()
678print("Creating rocktrees...") 751print("Creating rocktrees...")
679if not exists(vars.ROCKS_TREE) then 752if not exists(vars.ROCKS_TREE) then
680 mkdir(vars.ROCKS_TREE) 753 mkdir(vars.ROCKS_TREE)
681 print(S[[Created rocktree: "$ROCKS_TREE"]]) 754 print(S[[Created system rocktree : "$ROCKS_TREE"]])
682else 755else
683 print(S[[Rocktree exists: "$ROCKS_TREE"]]) 756 print(S[[System rocktree exists : "$ROCKS_TREE"]])
684end 757end
685local APPDATA = os.getenv("APPDATA") 758
686if not exists(APPDATA.."\\luarocks") then 759vars.LOCAL_TREE = os.getenv("APPDATA")..[[\LuaRocks]]
687 mkdir(APPDATA.."\\luarocks") 760if not exists(vars.LOCAL_TREE) then
688 print([[Created rocktree: "]]..APPDATA..[[\luarocks"]]) 761 mkdir(vars.LOCAL_TREE)
762 print(S[[Created local user rocktree: "$LOCAL_TREE"]])
689else 763else
690 print([[Rocktree exists: "]]..APPDATA..[[\luarocks"]]) 764 print(S[[Local user rocktree exists : "$LOCAL_TREE"]])
691end 765end
692 766
693-- Load registry information 767-- Load registry information
@@ -712,12 +786,24 @@ exec( S[[del "$FULL_PREFIX\pe-parser.lua" > nul]] )
712-- *********************************************************** 786-- ***********************************************************
713 787
714print(S[[ 788print(S[[
789
715*** LuaRocks is installed! *** 790*** LuaRocks is installed! ***
716 791
717 You may want to add the following elements to your paths; 792You may want to add the following elements to your paths;
718PATH : $LUA_BINDIR;$FULL_PREFIX 793Lua interpreter;
719LUA_PATH : $ROCKS_TREE\share\lua\$LUA_VERSION\?.lua;$ROCKS_TREE\share\lua\$LUA_VERSION\?\init.lua 794 PATH : $LUA_BINDIR
720LUA_CPATH: $ROCKS_TREE\lib\lua\$LUA_VERSION\?.dll 795 PATHEXT : .LUA
796LuaRocks;
797 PATH : $FULL_PREFIX
798 LUA_PATH : $FULL_PREFIX\lua\?.lua;$FULL_PREFIX\lua\?\init.lua
799Local user rocktree (Note: %APPDATA% is user dependent);
800 PATH : %APPDATA%\LuaRocks\bin
801 LUA_PATH : %APPDATA%\LuaRocks\share\lua\$LUA_VERSION\?.lua;%APPDATA%\LuaRocks\share\lua\$LUA_VERSION\?\init.lua
802 LUA_CPATH: %APPDATA%\LuaRocks\lib\lua\$LUA_VERSION\?.dll
803System rocktree
804 PATH : $ROCKS_TREE\bin
805 LUA_PATH : $ROCKS_TREE\share\lua\$LUA_VERSION\?.lua;$ROCKS_TREE\share\lua\$LUA_VERSION\?\init.lua
806 LUA_CPATH: $ROCKS_TREE\lib\lua\$LUA_VERSION\?.dll
721 807
722]]) 808]])
723os.exit(0) 809os.exit(0)
diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua
index e3d924fb..f66ad1d9 100644
--- a/src/luarocks/cfg.lua
+++ b/src/luarocks/cfg.lua
@@ -322,7 +322,7 @@ if detected.windows then
322 local localappdata = os.getenv("LOCALAPPDATA") 322 local localappdata = os.getenv("LOCALAPPDATA")
323 if not localappdata then 323 if not localappdata then
324 -- for Windows versions below Vista 324 -- for Windows versions below Vista
325 localappdata = os.getenv("USER_PROFILE").."/Local Settings/Application Data" 325 localappdata = os.getenv("USERPROFILE").."/Local Settings/Application Data"
326 end 326 end
327 defaults.local_cache = localappdata.."/LuaRocks/Cache" 327 defaults.local_cache = localappdata.."/LuaRocks/Cache"
328end 328end
diff --git a/src/luarocks/fs.lua b/src/luarocks/fs.lua
index 467b1943..2d799da2 100644
--- a/src/luarocks/fs.lua
+++ b/src/luarocks/fs.lua
@@ -11,6 +11,32 @@ module("luarocks.fs", package.seeall)
11 11
12local cfg = require("luarocks.cfg") 12local cfg = require("luarocks.cfg")
13 13
14local pack = table.pack or function(...) return { n = select("#", ...), ... } end
15local unpack = table.unpack or unpack
16
17if cfg.verbose then -- patch io.popen and os.execute to display commands in verbose mode
18 old_popen = io.popen
19 io.popen = function(one, two)
20 if two == nil then
21 print("\nio.popen: ", one)
22 else
23 print("\nio.popen: ", one, "Mode:", two)
24 end
25 return old_popen(one, two)
26 end
27
28 old_exec = os.execute
29 os.execute = function(cmd)
30 print("\nos.execute: ", cmd)
31 local code = pack(old_exec(cmd))
32 print("Results: "..tostring(code.n))
33 for i = 1,code.n do
34 print(" "..tostring(i).." ("..type(code[i]).."): "..tostring(code[i]))
35 end
36 return unpack(code, 1, code.n)
37 end
38end
39
14local function load_fns(fs_table) 40local function load_fns(fs_table)
15 for name, fn in pairs(fs_table) do 41 for name, fn in pairs(fs_table) do
16 if not _M[name] then 42 if not _M[name] then
diff --git a/src/luarocks/fs/lua.lua b/src/luarocks/fs/lua.lua
index d477d88f..5f7d6c37 100644
--- a/src/luarocks/fs/lua.lua
+++ b/src/luarocks/fs/lua.lua
@@ -106,31 +106,36 @@ end
106 106
107--- Run the given command, quoting its arguments, silencing its output. 107--- Run the given command, quoting its arguments, silencing its output.
108-- The command is executed in the current directory in the dir stack. 108-- The command is executed in the current directory in the dir stack.
109-- Silencing is omitted if 'verbose' mode is enabled.
109-- @param command string: The command to be executed. No quoting/escaping 110-- @param command string: The command to be executed. No quoting/escaping
110-- is applied. 111-- is applied.
111-- @param ... Strings containing additional arguments, which are quoted. 112-- @param ... Strings containing additional arguments, which will be quoted.
112-- @return boolean: true if command succeeds (status code 0), false 113-- @return boolean: true if command succeeds (status code 0), false
113-- otherwise. 114-- otherwise.
114function execute_quiet(command, ...) 115function execute_quiet(command, ...)
115 assert(type(command) == "string") 116 assert(type(command) == "string")
116 return fs.execute_string(fs.quiet(quote_args(command, ...))) 117 if cfg.verbose then -- omit silencing output
118 return fs.execute_string(quote_args(command, ...))
119 else
120 return fs.execute_string(fs.quiet(quote_args(command, ...)))
121 end
117end 122end
118 123
119--- Check the MD5 checksum for a file. 124--- Check the MD5 checksum for a file.
120-- @param file string: The file to be checked. 125-- @param file string: The file to be checked.
121-- @param md5sum string: The string with the expected MD5 checksum. 126-- @param md5sum string: The string with the expected MD5 checksum.
122-- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false if not 127-- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false + msg if not
123-- or if it could not perform the check for any reason. 128-- or if it could not perform the check for any reason.
124function check_md5(file, md5sum) 129function check_md5(file, md5sum)
125 file = dir.normalize(file) 130 file = dir.normalize(file)
126 local computed = fs.get_md5(file) 131 local computed, msg = fs.get_md5(file)
127 if not computed then 132 if not computed then
128 return false 133 return false, msg
129 end 134 end
130 if computed:match("^"..md5sum) then 135 if computed:match("^"..md5sum) then
131 return true 136 return true
132 else 137 else
133 return false 138 return false, "Mismatch MD5 hash for file "..file
134 end 139 end
135end 140end
136 141
@@ -146,7 +151,6 @@ if lfs_ok then
146-- @return boolean: true if command succeeds (status code 0), false 151-- @return boolean: true if command succeeds (status code 0), false
147-- otherwise. 152-- otherwise.
148function execute_string(cmd) 153function execute_string(cmd)
149 if cfg.verbose then print("Executing: "..cmd) end
150 local code = os.execute(cmd) 154 local code = os.execute(cmd)
151 return (code == 0 or code == true) 155 return (code == 0 or code == true)
152end 156end
@@ -644,14 +648,15 @@ if md5_ok then
644 648
645--- Get the MD5 checksum for a file. 649--- Get the MD5 checksum for a file.
646-- @param file string: The file to be computed. 650-- @param file string: The file to be computed.
647-- @return string: The MD5 checksum 651-- @return string: The MD5 checksum or nil + error
648function get_md5(file) 652function get_md5(file)
649 file = fs.absolute_name(file) 653 file = fs.absolute_name(file)
650 local file = io.open(file, "rb") 654 local file = io.open(file, "rb")
651 if not file then return false end 655 if not file then return nil, "Failed to compute MD5 hash for file "..file end
652 local computed = md5.sumhexa(file:read("*a")) 656 local computed = md5.sumhexa(file:read("*a"))
653 file:close() 657 file:close()
654 return computed 658 if computed then return computed end
659 return nil, "Failed to compute MD5 hash for file "..file
655end 660end
656 661
657end 662end
diff --git a/src/luarocks/fs/unix/tools.lua b/src/luarocks/fs/unix/tools.lua
index e3468ab4..c857b093 100644
--- a/src/luarocks/fs/unix/tools.lua
+++ b/src/luarocks/fs/unix/tools.lua
@@ -328,8 +328,11 @@ function get_md5(file)
328 local pipe = io.popen(cmd.." "..fs.absolute_name(file)) 328 local pipe = io.popen(cmd.." "..fs.absolute_name(file))
329 local computed = pipe:read("*a") 329 local computed = pipe:read("*a")
330 pipe:close() 330 pipe:close()
331 if not computed then return nil end 331 if computed then
332 return computed:match("("..("%x"):rep(32)..")") 332 computed = computed:match("("..("%x"):rep(32)..")")
333 end
334 if computed then return computed end
335 return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file))
333end 336end
334 337
335function get_permissions(filename) 338function get_permissions(filename)
diff --git a/src/luarocks/fs/win32.lua b/src/luarocks/fs/win32.lua
index 0280b3f0..a3f0663b 100644
--- a/src/luarocks/fs/win32.lua
+++ b/src/luarocks/fs/win32.lua
@@ -9,6 +9,15 @@ local cfg = require("luarocks.cfg")
9local dir = require("luarocks.dir") 9local dir = require("luarocks.dir")
10local util = require("luarocks.util") 10local util = require("luarocks.util")
11 11
12-- Monkey patch io.popen and os.execute to make sure quoting
13-- works as expected.
14-- See http://lua-users.org/lists/lua-l/2013-11/msg00367.html
15local _prefix = "type NUL && "
16local _popen, _execute = io.popen, os.execute
17io.popen = function(cmd, ...) return _popen(_prefix..cmd, ...) end
18os.execute = function(cmd, ...) return _execute(_prefix..cmd, ...) end
19
20
12--- Annotate command string for quiet execution. 21--- Annotate command string for quiet execution.
13-- @param cmd string: A command-line string. 22-- @param cmd string: A command-line string.
14-- @return string: The command-line, with silencing annotation. 23-- @return string: The command-line, with silencing annotation.
@@ -173,3 +182,33 @@ function replace_file(old_file, new_file)
173 return os.rename(new_file, old_file) 182 return os.rename(new_file, old_file)
174end 183end
175 184
185--- Test is file/dir is writable.
186-- Warning: testing if a file/dir is writable does not guarantee
187-- that it will remain writable and therefore it is no replacement
188-- for checking the result of subsequent operations.
189-- @param file string: filename to test
190-- @return boolean: true if file exists, false otherwise.
191function is_writable(file)
192 assert(file)
193 file = dir.normalize(file)
194 local result
195 local tmpname = 'tmpluarockstestwritable.deleteme'
196 if fs.is_dir(file) then
197 local file2 = dir.path(file, tmpname)
198 local fh = io.open(file2, 'wb')
199 result = fh ~= nil
200 if fh then fh:close() end
201 if result then
202 -- the above test might give a false positive when writing to
203 -- c:\program files\ because of VirtualStore redirection on Vista and up
204 -- So check whether it's really there
205 result = fs.exists(file2)
206 end
207 os.remove(file2)
208 else
209 local fh = io.open(file, 'r+b')
210 result = fh ~= nil
211 if fh then fh:close() end
212 end
213 return result
214end
diff --git a/src/luarocks/fs/win32/tools.lua b/src/luarocks/fs/win32/tools.lua
index 979aaff4..0ec46314 100644
--- a/src/luarocks/fs/win32/tools.lua
+++ b/src/luarocks/fs/win32/tools.lua
@@ -12,10 +12,6 @@ local dir_stack = {}
12 12
13local vars = cfg.variables 13local vars = cfg.variables
14 14
15local function pack(...)
16 return { n = select("#", ...), ... }
17end
18
19--- Strip the last extension of a filename. 15--- Strip the last extension of a filename.
20-- Example: "foo.tar.gz" becomes "foo.tar". 16-- Example: "foo.tar.gz" becomes "foo.tar".
21-- If filename has no dots, returns it unchanged. 17-- If filename has no dots, returns it unchanged.
@@ -60,16 +56,8 @@ end
60-- otherwise. 56-- otherwise.
61function execute_string(cmd) 57function execute_string(cmd)
62 cmd = command_at(fs.current_dir(), cmd) 58 cmd = command_at(fs.current_dir(), cmd)
63 if cfg.verbose then print("Executing: "..tostring(cmd)) end 59 local code = os.execute(cmd)
64 local code = pack(os.execute(cmd)) 60 if code == 0 or code == true then
65 if cfg.verbose then
66 print("Results: "..tostring(code.n))
67 for i = 1,code.n do
68 print(" "..tostring(i).." ("..type(code[i]).."): "..tostring(code[i]))
69 end
70 print()
71 end
72 if code[1] == 0 or code[1] == true then
73 return true 61 return true
74 else 62 else
75 return false 63 return false
@@ -112,7 +100,7 @@ end
112function make_dir(directory) 100function make_dir(directory)
113 assert(directory) 101 assert(directory)
114 directory = dir.normalize(directory) 102 directory = dir.normalize(directory)
115 fs.execute_quiet(vars.MKDIR.." -p ", directory) 103 fs.execute_quiet(fs.Q(vars.MKDIR).." -p ", directory)
116 if not fs.is_dir(directory) then 104 if not fs.is_dir(directory) then
117 return false, "failed making directory "..directory 105 return false, "failed making directory "..directory
118 end 106 end
@@ -125,7 +113,7 @@ end
125-- @param directory string: pathname of directory to remove. 113-- @param directory string: pathname of directory to remove.
126function remove_dir_if_empty(directory) 114function remove_dir_if_empty(directory)
127 assert(directory) 115 assert(directory)
128 fs.execute_quiet(vars.RMDIR, directory) 116 fs.execute_quiet(fs.Q(vars.RMDIR), directory)
129end 117end
130 118
131--- Remove a directory if it is empty. 119--- Remove a directory if it is empty.
@@ -134,7 +122,7 @@ end
134-- @param directory string: pathname of directory to remove. 122-- @param directory string: pathname of directory to remove.
135function remove_dir_tree_if_empty(directory) 123function remove_dir_tree_if_empty(directory)
136 assert(directory) 124 assert(directory)
137 fs.execute_quiet(vars.RMDIR, directory) 125 fs.execute_quiet(fs.Q(vars.RMDIR), directory)
138end 126end
139 127
140--- Copy a file. 128--- Copy a file.
@@ -145,7 +133,7 @@ end
145function copy(src, dest) 133function copy(src, dest)
146 assert(src and dest) 134 assert(src and dest)
147 if dest:match("[/\\]$") then dest = dest:sub(1, -2) end 135 if dest:match("[/\\]$") then dest = dest:sub(1, -2) end
148 local ok = fs.execute(vars.CP, src, dest) 136 local ok = fs.execute(fs.Q(vars.CP), src, dest)
149 if ok then 137 if ok then
150 return true 138 return true
151 else 139 else
@@ -160,7 +148,7 @@ end
160-- plus an error message. 148-- plus an error message.
161function copy_contents(src, dest) 149function copy_contents(src, dest)
162 assert(src and dest) 150 assert(src and dest)
163 if fs.execute_quiet(vars.CP.." -dR "..src.."\\*.* "..fs.Q(dest)) then 151 if fs.execute_quiet(fs.Q(vars.CP).." -dR "..src.."\\*.* "..fs.Q(dest)) then
164 return true 152 return true
165 else 153 else
166 return false, "Failed copying "..src.." to "..dest 154 return false, "Failed copying "..src.." to "..dest
@@ -191,7 +179,7 @@ function list_dir(at)
191 return {} 179 return {}
192 end 180 end
193 local result = {} 181 local result = {}
194 local pipe = io.popen(command_at(at, vars.LS)) 182 local pipe = io.popen(command_at(at, fs.Q(vars.LS)))
195 for file in pipe:lines() do 183 for file in pipe:lines() do
196 table.insert(result, file) 184 table.insert(result, file)
197 end 185 end
@@ -214,7 +202,7 @@ function find(at)
214 return {} 202 return {}
215 end 203 end
216 local result = {} 204 local result = {}
217 local pipe = io.popen(command_at(at, vars.FIND.." 2> NUL")) 205 local pipe = io.popen(command_at(at, fs.Q(vars.FIND).." 2> NUL"))
218 for file in pipe:lines() do 206 for file in pipe:lines() do
219 -- Windows find is a bit different 207 -- Windows find is a bit different
220 local first_two = file:sub(1,2) 208 local first_two = file:sub(1,2)
@@ -233,7 +221,7 @@ end
233-- additional arguments. 221-- additional arguments.
234-- @return boolean: true on success, false on failure. 222-- @return boolean: true on success, false on failure.
235function zip(zipfile, ...) 223function zip(zipfile, ...)
236 return fs.execute_quiet(vars.SEVENZ.." -aoa a -tzip", zipfile, ...) 224 return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa a -tzip", zipfile, ...)
237end 225end
238 226
239--- Uncompress files from a .zip archive. 227--- Uncompress files from a .zip archive.
@@ -241,7 +229,7 @@ end
241-- @return boolean: true on success, false on failure. 229-- @return boolean: true on success, false on failure.
242function unzip(zipfile) 230function unzip(zipfile)
243 assert(zipfile) 231 assert(zipfile)
244 return fs.execute_quiet(vars.SEVENZ.." -aoa x", zipfile) 232 return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa x", zipfile)
245end 233end
246 234
247--- Test is pathname is a directory. 235--- Test is pathname is a directory.
@@ -257,7 +245,7 @@ end
257-- @return boolean: true if it is a regular file, false otherwise. 245-- @return boolean: true if it is a regular file, false otherwise.
258function is_file(file) 246function is_file(file)
259 assert(file) 247 assert(file)
260 return fs.execute(vars.TEST.." -f", file) 248 return fs.execute(fs.Q(vars.TEST).." -f", file)
261end 249end
262 250
263--- Download a remote file. 251--- Download a remote file.
@@ -276,7 +264,7 @@ function download(url, filename, cache)
276 264
277 local ok 265 local ok
278 if cfg.downloader == "wget" then 266 if cfg.downloader == "wget" then
279 local wget_cmd = vars.WGET.." --no-check-certificate --no-cache --user-agent=\""..cfg.user_agent.." via wget\" --quiet " 267 local wget_cmd = fs.Q(vars.WGET).." --no-check-certificate --no-cache --user-agent=\""..cfg.user_agent.." via wget\" --quiet "
280 if cache then 268 if cache then
281 -- --timestamping is incompatible with --output-document, 269 -- --timestamping is incompatible with --output-document,
282 -- but that's not a problem for our use cases. 270 -- but that's not a problem for our use cases.
@@ -289,7 +277,7 @@ function download(url, filename, cache)
289 ok = fs.execute(wget_cmd..fs.Q(url).." 2> NUL 1> NUL") 277 ok = fs.execute(wget_cmd..fs.Q(url).." 2> NUL 1> NUL")
290 end 278 end
291 elseif cfg.downloader == "curl" then 279 elseif cfg.downloader == "curl" then
292 ok = fs.execute_string(vars.CURL.." -L --user-agent \""..cfg.user_agent.." via curl\" "..fs.Q(url).." 2> NUL 1> "..fs.Q(filename)) 280 ok = fs.execute_string(fs.Q(vars.CURL).." -L --user-agent \""..cfg.user_agent.." via curl\" "..fs.Q(url).." 2> NUL 1> "..fs.Q(filename))
293 end 281 end
294 if ok then 282 if ok then
295 return true, filename 283 return true, filename
@@ -302,7 +290,7 @@ end
302-- @param archive string: Filename of archive. 290-- @param archive string: Filename of archive.
303-- @return boolean : success status 291-- @return boolean : success status
304local function gunzip(archive) 292local function gunzip(archive)
305 return fs.execute_quiet(vars.SEVENZ.." -aoa x", archive) 293 return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa x", archive)
306end 294end
307 295
308--- Unpack an archive. 296--- Unpack an archive.
@@ -314,7 +302,7 @@ function unpack_archive(archive)
314 assert(type(archive) == "string") 302 assert(type(archive) == "string")
315 303
316 local ok 304 local ok
317 local sevenzx = vars.SEVENZ.." -aoa x" 305 local sevenzx = fs.Q(vars.SEVENZ).." -aoa x"
318 if archive:match("%.tar%.gz$") then 306 if archive:match("%.tar%.gz$") then
319 ok = gunzip(archive) 307 ok = gunzip(archive)
320 if ok then 308 if ok then
@@ -346,22 +334,25 @@ function unpack_archive(archive)
346end 334end
347 335
348local md5_cmd = { 336local md5_cmd = {
349 md5sum = vars.MD5SUM, 337 md5sum = fs.Q(vars.MD5SUM),
350 openssl = vars.OPENSSL.." md5", 338 openssl = fs.Q(vars.OPENSSL).." md5",
351 md5 = vars.MD5, 339 md5 = fs.Q(vars.MD5),
352} 340}
353 341
354--- Get the MD5 checksum for a file. 342--- Get the MD5 checksum for a file.
355-- @param file string: The file to be computed. 343-- @param file string: The file to be computed.
356-- @return string: The MD5 checksum 344-- @return string: The MD5 checksum or nil + message
357function get_md5(file) 345function get_md5(file)
358 local cmd = md5_cmd[cfg.md5checker] 346 local cmd = md5_cmd[cfg.md5checker]
359 if not cmd then return nil end 347 if not cmd then return nil end
360 local pipe = io.popen(cmd.." "..fs.absolute_name(file)) 348 local pipe = io.popen(cmd.." "..fs.Q(fs.absolute_name(file)))
361 local computed = pipe:read("*a") 349 local computed = pipe:read("*a")
362 pipe:close() 350 pipe:close()
363 if not computed then return nil end 351 if computed then
364 return computed:match("("..("%x"):rep(32)..")") 352 computed = computed:match("("..("%x"):rep(32)..")")
353 end
354 if computed then return computed end
355 return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file))
365end 356end
366 357
367--- Test for existance of a file. 358--- Test for existance of a file.