source_win_bat 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 13570792e56f831dce6bdc6c882950bfa681bedeb3e06bc6ef6ad4ef06b7a853
4
+ data.tar.gz: 0a68d985177b5f751b7fd7ebdf06440cda0269ec03347b261615ac919a80da3b
5
+ SHA512:
6
+ metadata.gz: db9581bba734ea26bad073cce02811c74ff748037e99165a57f07c81dad7590537b9af04bb445db084e249e7d73fb6e3d500e36ce863eac5778c29373cedfbd9
7
+ data.tar.gz: 2e8c8d97f2285224a2608dafda5045c36ce67790c30d26a0b70e030f97ba439c20f49657ec6400ee06b16be0493c72ff214596ac15083ebe23057792498f6746
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2019 Takaya Saeki
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,132 @@
1
+ # SourceWinBat - Source a Windows Batch in Bash!
2
+
3
+ `sw`, or SourceWinBat is a CLI utility to run Windows batch files from WSL/MSYS2/Cygwin,
4
+ and sync the shell environments of Bash and the Windows batch files, including
5
+ * Environment variables
6
+ * Doskeys
7
+ * Working directories
8
+
9
+ By SourceWinBat, you can execute Windows initialization scripts in Bash as if you use `source`
10
+ command for initialization Bash scripts.
11
+ SourceWinBat helps you do your daily Windows work in your favorite UNIX-compatible environment.
12
+
13
+
14
+ ## Usage
15
+
16
+ After initalization, `sw` function is defined in your Bash environment.
17
+ Run your Windows batch files or Windows CLI commands by `sw`.
18
+
19
+ You can run a Windows batch file
20
+ ```console
21
+ $ cat winbat.bat
22
+ @echo off
23
+ set ENV1=bar! ; Environment variables will be synced
24
+ echo foo! ; Execute Windows echo commands
25
+ ver ; Execute Windows ver command, which outputs the OS version
26
+ $ sw winbat.bat # winbat.bat is executed
27
+ foo!
28
+
29
+ Microsoft Windows [Version 10.0.17763.195]
30
+ $ echo $ENV1 # ENV1 is synced!
31
+ bar!
32
+ ```
33
+
34
+ You can also run a Windows command directly.
35
+ ```console
36
+ $ sw ver
37
+
38
+ Microsoft Windows [Version 10.0.17763.195]
39
+ ```
40
+
41
+ ## Examples
42
+
43
+ ### Environment Syncing
44
+
45
+ SourceWinBat syncs environment variables, doskeys, and working directories of the
46
+ Bash environment with those of the Windows cmd environment where a batch file is executed.
47
+
48
+ #### 1. Environment variables
49
+ A batch file can see the exported environment variables of Bash.
50
+ Conversely, Bash has the environment variables defined by the batch file and Windows system
51
+ after the batch file is executed. `PATH` is properly converted.
52
+
53
+ ```console
54
+ $ export UNIXENV="An UNIX environment variable is imported!"
55
+ $ cat syncenv.bat
56
+ echo %UNIXENV%
57
+ set WINENV=A Windows environment variable is imported!
58
+ set PATH=C:\any\path;%PATH%
59
+ $ sw syncenv.bat # syncenv sees the value of $UNIXENV, which we defined in Bash!
60
+ An UNIX environment variable is imported!
61
+ $ echo $WINENV # Now we can see $WINENV, which is set in synenv.bat!!
62
+ A Windows environment variable is imported!
63
+ $ echo $PATH # PATH is converted to the path of WSL
64
+ /mnt/c/any/path:/usr/bin/:/bin:(other paths go on...)
65
+ ```
66
+
67
+ #### 2. Doskeys
68
+ SourceWinBat enables Bash to import doskeys from Windows Batch files as Bash functions.
69
+
70
+ ```console
71
+ $ cat syncdoskey.bat
72
+ doskey echo1stparam=echo $1
73
+ doskey echoallparams=echo $*
74
+ doskey verver=ver
75
+ $ sw syncdoskey.bat
76
+ $ echo1stparam 1st 2nd 3rd # echo1stparam is imported!
77
+ 1st
78
+ $ echo1stparam %OS% # "echo $1" is executed by cmd.exe, so %OS% is expanded
79
+ Windows_NT
80
+ $ echoallparams 1st 2nd 3rd
81
+ 1st 2nd 3rd
82
+ $ verver
83
+
84
+ Microsoft Windows [Version 10.0.17763.195]
85
+ ```
86
+
87
+ #### 3. Working directories
88
+ As `source` of built-in Bash command syncs working directories, SourceWinBat also syncs them.
89
+
90
+ ```console
91
+ $ cd ~
92
+ $ cat syncwd.bat
93
+ pushd C:\Windows
94
+ pushd C:\Windows\system32
95
+ cd C:\Windows\system32\drivers
96
+ $ sw syncwd.bat
97
+ $ pwd # The current directory of Bash is changed
98
+ /mnt/c/Windows/System32/drivers
99
+ $ dirs # The directory stack is synced with that of the batch file
100
+ /mnt/c/Windows/System32/ /mnt/c/Windows /home/nullpo
101
+ ```
102
+
103
+ ## Installation
104
+
105
+ SourceWinBat is written in Ruby. You can install it by Gem.
106
+ ```console
107
+ # gem install source_win_bat
108
+ ```
109
+
110
+ Execute the line below to add the initialization in your `.bashrc`.
111
+ ```console
112
+ $ echo 'eval "$(init_sw)"' >> ~/.bashrc
113
+ ```
114
+ After restarting Bash, you will be able to use `sw` in your shell.
115
+ Currently, SourceWinBat supports only Bash as a shell.
116
+
117
+ ## Requirements
118
+
119
+ ### 1. For WSL users
120
+
121
+ October update or later is required. SourceWinBat requires ConPTY API.
122
+
123
+ ### 2. For MSYS2 and Cygwin users
124
+
125
+ If you use MSYS2 and Cygwin with SourceWinBat, `winpty` command is required.
126
+ Clone it from its GitHub repository and build it from the source. The repository is https://github.com/rprichard/winpty .
127
+ For MSYS2 users, DO NOT install `winpty` via `pacman`. As of 2019/01/03, Pacman installs the latest released version, 0.4.3-1, but this version does not work anymore.
128
+
129
+ ## TODOs
130
+
131
+ * Support non-ascii characters in Cygwin and MSYS2. SourceWinBat already supports them in WSL.
132
+ * Support shell operators in doskey such as pipe.
@@ -0,0 +1,76 @@
1
+ require_relative './lib/unixcompatenv'
2
+
3
+ task default: %w[test]
4
+
5
+ task :test do
6
+
7
+ # Get all compatibility environments to test
8
+ all_compat_envs = [:wsl, :msys, :cygwin]
9
+ installed_compat_envs = UnixCompatEnv.detect_installed_compat_envs
10
+ ignored_compat_envs = (ENV['IGNORED_COMPATENV'] || '').split(',')
11
+ .map(&:chomp)
12
+ env_to_readablestr = {wsl: "WSL", msys: "MSYS2", cygwin: "Cygwin"}
13
+
14
+ compat_envs = {}
15
+ all_compat_envs.each do |env|
16
+ next if ignored_compat_envs.include?(env_to_readablestr[env])
17
+ if installed_compat_envs.has_key?(env)
18
+ compat_envs[env] = installed_compat_envs[env]
19
+ next
20
+ end
21
+ path_env_name = "#{env_to_readablestr[env].upcase}_BASH_PATH"
22
+ if ENV[path_env_name] and File.exists?(ENV[path_env_name])
23
+ compat_envs[env] = ENV[path_env_name]
24
+ next
25
+ end
26
+ STDERR.puts "#{env_to_readablestr[env]} is not found. Set #{path_env_name} or add #{env_to_readablestr[env]} to IGNORED_COMPATENV environment variable. (Comma sepearated values)"
27
+ exit 1
28
+ end
29
+
30
+ # Test
31
+ unix_double_quote = -> str {
32
+ '"' + str.gsub('\\') {|_| '\\\\'}.gsub('"') {|_| '\\"'}.gsub('$') {|_| '\\$'} + '"'
33
+ }
34
+ cmd_double_quote = -> str {
35
+ '"' + str.gsub('\\') {|_| '\\\\'}.gsub('"') {|_| '\\"'} + '"'
36
+ }
37
+ ok_results = {}
38
+ succeeded = true
39
+ tests_winpath = UnixCompatEnv.to_win_path(File.realpath("./test"))
40
+ lib_winpath = UnixCompatEnv.to_win_path(File.realpath("./lib"))
41
+ compat_envs.each do |env, path|
42
+ case env
43
+ when :wsl
44
+ convpath_win2compat = "wslpath"
45
+ when :msys, :cygwin
46
+ convpath_win2compat = "cygpath -u"
47
+ end
48
+ cd2test = "cd \"$(#{convpath_win2compat} '#{tests_winpath}')\"; "
49
+ rubylib = "RUBYLIB=\"$(#{convpath_win2compat} '#{lib_winpath}')\" "
50
+ prove = "prove -e /bin/bash -j4 test_*.bash; "
51
+
52
+ cmd_in_env = cd2test + rubylib + prove
53
+ if env == UnixCompatEnv.compat_env
54
+ cmd = "bash -lc #{unix_double_quote.call(cmd_in_env)}"
55
+ elsif [UnixCompatEnv.compat_env, env].include?(:wsl)
56
+ cmd = "#{path} -lc #{unix_double_quote.call(cmd_in_env)}"
57
+ else
58
+ # Cygwin and MSYS2 cannot launch each other's applications directly due to DLL conflict.
59
+ # Solve that by wrapping a command by cmd.exe.
60
+ cmd_env_launch = "#{UnixCompatEnv.to_win_path(path)} -lc #{cmd_double_quote.call(cmd_in_env)}"
61
+ cmd = "cmd.exe /C #{unix_double_quote.call(cmd_env_launch)}"
62
+ end
63
+ puts "\e[1m\e[33m===#{env_to_readablestr[env]}===\e[0m\e[22m"
64
+ sh cmd do |ok, _|
65
+ ok_results[env] = ok
66
+ succeeded &&= ok
67
+ end
68
+ end
69
+
70
+ puts "\e[1m\e[33m===Summary===\e[0m\e[22m"
71
+ compat_envs.each do |env, _|
72
+ puts "#{env_to_readablestr[env]}: #{ok_results[env] ? "PASSED" : "FAILED"}"
73
+ end
74
+
75
+ exit succeeded ? 0 : 1
76
+ end
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ puts <<BASH
4
+ #!/bin/bash
5
+
6
+ which winpty > /dev/null
7
+ if [[ `uname` != Linux && $? != 0 ]]; then
8
+ echo "winpty is required to run 'sw'. Please install winpty." >&2
9
+ echo "Please note that you have to build the latest winpty from the git repository since 4.3 has a fatal bug." >&2
10
+ else
11
+ sw () {
12
+ local arg
13
+ local tmp_env tmp_macro tmp_cwd
14
+ local exitstatus
15
+
16
+ for arg in "$@"; do
17
+ if [[ "$arg" = "--" ]]; then
18
+ shift 1
19
+ break
20
+ elif [[ "$arg" =~ ^-+.+ ]]; then
21
+ case "$arg" in
22
+ "--help" | "-h" )
23
+ cat <<EOS
24
+ sw, or SourceWinBat, is a utility to run Windows batch files from WSL /
25
+ MSYS2 / Cygwin and sync environment variables, and working directories
26
+ between batch files and their UNIX Bash shell.
27
+
28
+ Usage:
29
+ sw [ [sw_options] -- ] win_bat_file [args...]
30
+
31
+ Sw options:
32
+ -help --help Show this help message
33
+
34
+ Examples:
35
+ sw echo test
36
+ sw somebat.bat
37
+
38
+ EOS
39
+ return ;;
40
+ * )
41
+ echo "Unknown option '$1'" >&2
42
+ return 1 ;;
43
+ esac
44
+ shift 1
45
+ else
46
+ break
47
+ fi
48
+ done
49
+
50
+ tmp_env="$(mktemp)"
51
+ tmp_macro="$(mktemp)"
52
+ tmp_cwd="$(mktemp)"
53
+ ruby -W0 -e "require 'source_win_bat'; SourceWindowsBatch.new.main(ARGV)" "${tmp_env}" "${tmp_macro}" "${tmp_cwd}" "$@"
54
+ exitstatus=$?
55
+ if [[ -e "${tmp_env}" ]]; then
56
+ source "${tmp_env}"
57
+ fi
58
+ if [[ -e "${tmp_macro}" ]]; then
59
+ source "${tmp_macro}"
60
+ fi
61
+ if [[ -e "${tmp_cwd}" ]]; then
62
+ source "${tmp_cwd}"
63
+ fi
64
+
65
+ ruby -e "begin; File.delete('${tmp_env}', '${tmp_macro}', '${tmp_cwd}'); rescue Errno::ENOENT; end"
66
+ return $exitstatus
67
+ }
68
+ fi
69
+
70
+ BASH
@@ -0,0 +1,287 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'securerandom'
4
+ require 'tmpdir'
5
+ require_relative 'unixcompatenv'
6
+
7
+ class SourceWindowsBatch
8
+
9
+ VERSION = "0.1.0"
10
+
11
+ def main(argv)
12
+ if argv.length < 4 || argv[3].chomp.empty?
13
+ STDERR.puts <<-EOS
14
+ Usage: sw windows_cmd_or_batch [options_for_the_cmd]
15
+
16
+ Internal Ruby command Usage:
17
+ #{File.basename(__FILE__)} env_out macro_out cwd_out windows_cmd_or_batch [options_for_the_cmd]
18
+ EOS
19
+ exit
20
+ end
21
+
22
+ unless [:cygwin, :msys, :wsl].include? UnixCompatEnv.compat_env
23
+ raise "You're in an unsupported UNIX compatible environment"
24
+ end
25
+
26
+ env = prepare_env_vars
27
+ env_tmp_file_in = mk_tmpname(".env")
28
+ macro_tmp_file_in = mk_tmpname(".doskey")
29
+ cwd_tmp_file_in = mk_tmpname(".cwd")
30
+ win_cmd = argv[3..-1].map {|v| "#{v}"}.join(" ")
31
+ win_cmd += " & call set SW_EXITSTATUS=%^ERRORLEVEL% "
32
+ win_cmd = concat_envdump(win_cmd, env_tmp_file_in)
33
+ win_cmd = concat_macrodump(win_cmd, macro_tmp_file_in)
34
+ win_cmd = concat_cwddump(win_cmd, cwd_tmp_file_in)
35
+ win_cmd += " & call exit %^SW_EXITSTATUS%"
36
+ # puts win_cmd
37
+ Signal.trap(:INT, "SIG_IGN")
38
+ if UnixCompatEnv.compat_env == :wsl
39
+ # * Skip winpty, assuming the system's WSL supports ConPTY
40
+ # * Use an absolute path since EC overwrites PATH with Windows-style PATH in WSL
41
+ pid = Process.spawn(env,
42
+ UnixCompatEnv.to_compat_path('C:\\Windows\\System32\\cmd.exe'),
43
+ '/C', win_cmd, :in => 0, :out => 1, :err => 2)
44
+ elsif !STDOUT.isatty
45
+ pid = Process.spawn(env, 'cmd.exe', '/C', win_cmd, :in => 0, :out => 1, :err => 2)
46
+ else
47
+ pid = Process.spawn(env, 'winpty', '--', 'cmd.exe', '/C', win_cmd, :in => 0, :out => 1, :err => 2)
48
+ end
49
+ Signal.trap(:INT) do
50
+ Process.signal("-KILL", pid)
51
+ end
52
+ status = nil
53
+ loop do
54
+ _, status = Process.wait2(pid)
55
+ break if status.exited?
56
+ end
57
+
58
+ begin
59
+ codepage = detect_ansi_codepage
60
+ conv_setenv_stmts(env_tmp_file_in, argv[0], :bash, codepage)
61
+ conv_doskey_stmts(macro_tmp_file_in, argv[1], :bash, codepage)
62
+ gen_chdir_cmds(cwd_tmp_file_in, argv[2], :bash, codepage)
63
+ [env_tmp_file_in, macro_tmp_file_in, cwd_tmp_file_in].each do |f|
64
+ File.delete f
65
+ end
66
+ rescue Errno::ENOENT
67
+ # ignore
68
+ end
69
+
70
+ exit(status.exitstatus)
71
+ end
72
+
73
+ private
74
+
75
+ def detect_ansi_codepage
76
+ if !STDOUT.isatty && UnixCompatEnv.compat_env == :wsl
77
+ # cmd.exe seems to use UTF-8 when Stdout is redirected in WSL. TODO: Is it always fixed?
78
+ return "65001" # CP65001 is UTF-8
79
+ end
80
+
81
+ posh_cmd = <<-EOS
82
+ Get-WinSystemLocale | Select-Object Name, DisplayName,
83
+ @{ n='OEMCP'; e={ $_.TextInfo.OemCodePage } },
84
+ @{ n='ACP'; e={ $_.TextInfo.AnsiCodePage } }
85
+ EOS
86
+ posh_res = `powershell.exe "#{posh_cmd.gsub("$", "\\$")}"`
87
+ locale = posh_res.lines.select {|line| !(line =~ /^\s*$/)}[-1].chomp
88
+ ansi_cp = locale.split(" ")[-1]
89
+ ansi_cp
90
+ end
91
+
92
+ def serialize_wslenvs(wslenvs)
93
+ wslenvs.map {|varname, opt| "#{varname}#{opt.empty? ? "" : "/#{opt}"}"}.join(":")
94
+ end
95
+
96
+ def parse_wslenv(wslenv_str)
97
+ wslenvs = Hash[]
98
+ wslenv_str.split(":").each do |wslenvvar|
99
+ envvar_name, envvar_opt = wslenvvar.split('/')
100
+ wslenvs[envvar_name] = envvar_opt || ""
101
+ end
102
+ wslenvs
103
+ end
104
+
105
+ def prepare_env_vars
106
+ return {} if UnixCompatEnv.compat_env != :wsl
107
+
108
+ wslenvs = Hash[]
109
+ ENV.each do |envvar_name, _|
110
+ wslenvs[envvar_name] = ""
111
+ end
112
+ wslenvs.merge!(parse_wslenv(ENV['WSLENV']))
113
+ # We don't use '/l' option, but convert paths by ourselves instead.
114
+ # See the comment that starts with 'How PATH in WSLENV is handled'
115
+ wslenvs['PATH'] = ""
116
+ var_wslenv = serialize_wslenvs(wslenvs)
117
+
118
+ paths = []
119
+ ENV['PATH'].split(":").each do |path|
120
+ begin
121
+ rpath = File.realpath(path)
122
+ if rpath.start_with?(UnixCompatEnv.win_root_in_compat)
123
+ path = UnixCompatEnv.to_win_path(rpath)
124
+ end
125
+ rescue Errno::ENOENT
126
+ end
127
+ paths.push(path)
128
+ end
129
+ var_path = paths.join(';')
130
+
131
+ {"WSLENV" => var_wslenv, "PATH" => var_path}
132
+ end
133
+
134
+ def mk_tmpname(suffix)
135
+ "#{UnixCompatEnv.win_tmp_in_compat}#{SecureRandom.uuid + suffix}"
136
+ end
137
+
138
+ def concat_envdump(cmd, tmpfile)
139
+ cmd + " & set > #{dq_win_path(UnixCompatEnv.to_win_path(tmpfile))}"
140
+ end
141
+
142
+ def concat_macrodump(cmd, tmpfile)
143
+ #TODO: escape
144
+ cmd + " & doskey /macros > #{dq_win_path(UnixCompatEnv.to_win_path(tmpfile))}"
145
+ end
146
+
147
+ def concat_cwddump(cmd, tmpfile)
148
+ #TODO: escape
149
+ winpath = dq_win_path(UnixCompatEnv.to_win_path(tmpfile))
150
+ cmd + " & cd > #{winpath} & pushd >> #{winpath}"
151
+ end
152
+
153
+ def dq_win_path(str)
154
+ str.gsub(/\//, '\\')
155
+ .split("\\")
156
+ .map {|dir| dir.include?(" ") ? "\"#{dir}\"" : dir}
157
+ .join("\\")
158
+ end
159
+
160
+ def escape_singlequote(str)
161
+ str.gsub(/'/, '"\'"')
162
+ end
163
+
164
+ def conv_to_host_cmds(in_file, out_file, conv_method, env)
165
+ unless File.exist?(in_file)
166
+ return
167
+ end
168
+ File.open(out_file, "w") do |out|
169
+ File.open(in_file) do |f|
170
+ f.each_line do |line|
171
+ line.force_encoding("ASCII-8BIT")
172
+ converted = conv_method.call(line, env)
173
+ out.puts converted if converted
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+
180
+ def to_compat_pathlist(path, shell)
181
+ raise "Unsupporeted" unless shell == :bash
182
+ path.split(";")
183
+ .map {|p| UnixCompatEnv.to_compat_path(p)}
184
+ .join(":")
185
+ end
186
+
187
+ def to_env_stmt(set_stmt, shell)
188
+ raise "Unsupporeted" unless shell == :bash
189
+ var, val = /([^=]*)=(.*)$/.match(set_stmt)[1..2]
190
+
191
+ is_var_valid = /^[a-zA-Z_][_0-9a-zA-Z]*$/ =~ var
192
+ return nil unless is_var_valid
193
+
194
+ if var == "PATH" && UnixCompatEnv.compat_env != :wsl
195
+ val = to_compat_pathlist(val, :bash)
196
+ end
197
+
198
+ stmt = "export #{var}='#{escape_singlequote(val.chomp)}'"
199
+ return stmt if UnixCompatEnv.compat_env != :wsl
200
+
201
+ if var == "PATH"
202
+ stmt += "\nexport WSLENV=PATH/l:${WSLENV:-__EC_DUMMY_ENV}"
203
+ else
204
+ stmt += "\nexport WSLENV=#{var}:${WSLENV:-__EC_DUMMY_ENV}"
205
+ end
206
+ stmt
207
+ end
208
+
209
+ def conv_setenv_stmts(setenvfile, outfile, shell, codepage)
210
+ raise "Unsupporeted" if shell != :bash
211
+
212
+ File.open(outfile, "w") do |f_out|
213
+ envs = []
214
+ File.read(setenvfile, encoding: "CP#{codepage}:UTF-8").lines.each do |set_stmt|
215
+ var, val = /([^=]*)=(.*)$/.match(set_stmt)[1..2]
216
+
217
+ is_var_valid = /^[a-zA-Z_][_0-9a-zA-Z]*$/ =~ var
218
+ next if !is_var_valid
219
+
220
+ if var == "PATH"
221
+ val = to_compat_pathlist(val, shell)
222
+ end
223
+
224
+ envs.push(var)
225
+ f_out.puts("export #{var}='#{escape_singlequote(val.chomp)}'")
226
+ end
227
+ if UnixCompatEnv.compat_env == :wsl
228
+ # How PATH in WSLENV is handled:
229
+ # EC configures PATH's WSLENV flag as follows
230
+ # A. When EC internally launches a Windows bat file
231
+ # Set the PATH's flag to '' (nothing) since EC converts each Unix
232
+ # path to a corresponding Windows path.
233
+ # B. When EC syncs environment variables with the result of a bat file
234
+ # Leave the PATH's WSLENV flag as is
235
+ wslenvs = Hash[*envs.flat_map {|env| [env, ""]}]
236
+ wslenvs.delete('PATH')
237
+ wslenvs.merge!(parse_wslenv(ENV['WSLENV']))
238
+
239
+ if wslenvs.length > 0
240
+ f_out.puts("export WSLENV='#{serialize_wslenvs(wslenvs)}'")
241
+ end
242
+ end
243
+ end
244
+ end
245
+
246
+ def conv_doskey_stmts(doskeyfile, outfile, shell, codepage)
247
+ raise "Unsupporeted" unless shell == :bash
248
+
249
+ File.open(outfile, "w") do |f_out|
250
+ File.open(doskeyfile, encoding: "CP#{codepage}:UTF-8") do |f_in|
251
+ f_in.each_line do |doskey_stmt|
252
+ key, body = /([^=]*)=(.*)$/.match(doskey_stmt)[1..2]
253
+
254
+ is_key_valid = /^[a-zA-Z][0-9a-zA-Z]*$/ =~ key
255
+ return nil unless is_key_valid
256
+
257
+ body_substituted = escape_singlequote(body.chomp)
258
+ .gsub(/(?<param>\$[1-9]|\$\$|\$\*)/, '\'"\k<param>"\'')
259
+
260
+ f_out.puts <<-"EOS"
261
+ #{key} () {
262
+ sw '#{body_substituted}'
263
+ }
264
+ EOS
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ def gen_chdir_cmds(dirs, outfile, shell, codepage)
271
+ raise "Unsupporeted" unless shell == :bash
272
+ return unless File.exist?(dirs)
273
+
274
+ lines = File.read(dirs, encoding:"CP#{codepage}:UTF-8").lines.select {|line| !line.empty?}
275
+ cwd = lines[0]
276
+ dirs = lines[1..-1]
277
+
278
+ res = []
279
+ dirs.reverse.each do |dir|
280
+ res.push "cd '#{escape_singlequote(UnixCompatEnv.to_compat_path(dir.chomp))}'"
281
+ res.push "pushd . > /dev/null"
282
+ end
283
+ res.push "cd '#{escape_singlequote(UnixCompatEnv.to_compat_path(cwd.chomp))}'"
284
+ File.write(outfile, res.join("\n"))
285
+ end
286
+
287
+ end
@@ -0,0 +1,107 @@
1
+ require 'pathname'
2
+
3
+ module UnixCompatEnv
4
+
5
+ @@compat_env = nil
6
+ @@compat_root = nil
7
+ @@win_root = nil
8
+ @@win_tmp = nil
9
+
10
+ def self.detect_installed_compat_envs
11
+ res = {}
12
+
13
+ default_paths = {
14
+ wsl: "/c/Windows/System32/bash.exe",
15
+ msys: "/c/tools/msys64/usr/bin/bash.exe",
16
+ cygwin: "/c/tools/cygwin/bin/bash.exe"
17
+ }
18
+
19
+ default_paths.each do |env, path|
20
+ path = win_root_in_compat[0..-2] + path
21
+ if File.exists?(path)
22
+ res[env] = path
23
+ end
24
+ end
25
+
26
+ res
27
+ end
28
+
29
+ def self.compat_env
30
+ @@compat_env ||=
31
+ case RUBY_PLATFORM
32
+ when /msys/
33
+ :msys
34
+ when /linux/
35
+ :wsl
36
+ when /cygwin/
37
+ :cygwin
38
+ else
39
+ :win
40
+ end
41
+ end
42
+
43
+ def self.compat_root_in_win
44
+ return @@compat_root if @@compat_root || compat_env == :wsl # @@compat_root should be nil for :wsl
45
+ case compat_env
46
+ when :msys, :cygwin
47
+ path = `cygpath -w /`.chomp
48
+ if !path.end_with?("\\")
49
+ path += "\\"
50
+ end
51
+ @@compat_root = path
52
+ when :wsl
53
+ @@compat_root = nil
54
+ end
55
+ @@compat_root
56
+ end
57
+
58
+ def self.win_root_in_compat
59
+ return @@win_root if @@win_root
60
+ case compat_env
61
+ when :msys, :cygwin
62
+ root = `cygpath -u c:/`.chomp
63
+ when :wsl
64
+ root = `wslpath c:/`.chomp
65
+ end
66
+ raise "unexpected win root path" unless root.end_with?("c/")
67
+ @@win_root = root[0...-2]
68
+ end
69
+
70
+ def self.win_tmp_in_compat
71
+ return @@win_tmp if @@win_tmp
72
+ case compat_env
73
+ when :wsl, :cygwin
74
+ @@win_tmp = to_compat_path(`cmd.exe /C "echo %TEMP%"`.chomp) + '/'
75
+ when :msys
76
+ @@win_tmp = to_compat_path(ENV['temp'])
77
+ end
78
+ @@win_tmp
79
+ end
80
+
81
+ def self.to_win_path(path)
82
+ path = Pathname.new(path).cleanpath.to_s
83
+ raise "Abs path is expected: #{path}" if path[0] != "/"
84
+
85
+ if path.start_with?(win_root_in_compat)
86
+ drive = path[win_root_in_compat.length]
87
+ "#{drive.upcase}:\\" + (path[(win_root_in_compat.length + 2)..-1] || '').gsub('/', '\\')
88
+ elsif compat_env == :wsl
89
+ raise "A WSL path which cannot be accessed from Windows: #{path}"
90
+ else
91
+ # [0...-1] trims trailing '/'
92
+ compat_root_in_win[0...-1] + path.gsub('/', '\\')
93
+ end
94
+ end
95
+
96
+ def self.to_compat_path(path)
97
+ if !compat_root_in_win.nil? && path.start_with?(compat_root_in_win)
98
+ path = path[compat_root_in_win.length - 1 .. -1]
99
+ end
100
+ if /^[a-zA-Z]:/ =~ path
101
+ drive = path[0]
102
+ path = win_root_in_compat + drive.downcase + (path[2..-1] || "")
103
+ end
104
+ path.gsub('\\', '/')
105
+ end
106
+
107
+ end
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'source_win_bat'
3
+ s.version = '0.1.0'
4
+ s.date = '2019-01-05'
5
+ s.summary = "'source' Windows bat files in your UNIX compatible shell in Windows"
6
+ s.description = <<EOS
7
+ sw, or SourceWinBat, is a utility to run Windows batch files from WSL /
8
+ MSYS2 / Cygwin and sync environment variables, doskeys, and working
9
+ directories between batch files and their UNIX shell environments.
10
+ EOS
11
+ s.authors = ["Takaya Saeki"]
12
+ s.email = 'abc.tkys+pub@gmail.com'
13
+ s.files = `git ls-files`.split("\n")
14
+ s.executables = ["init_sw"]
15
+ s.homepage = 'http://rubygems.org/gems/source_win_bat'
16
+ s.license = 'MIT'
17
+ end
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ exit /B 42
@@ -0,0 +1,6 @@
1
+ @echo off
2
+
3
+ cd C:\
4
+ pushd C:\Windows
5
+ pushd C:\Windows\system32
6
+ cd C:\Windows\system32\drivers
@@ -0,0 +1,7 @@
1
+ @echo off
2
+
3
+ doskey foo=echo bar
4
+ doskey echo1stparam=echo $1
5
+ doskey echoallparams=echo $*
6
+ doskey verver=ver
7
+ REM other special characters are not supported currently
@@ -0,0 +1,6 @@
1
+ @echo off
2
+
3
+ SET WINENV1=FOO
4
+ SET WINENV2=BAR
5
+ SET PATH=C:;%PATH%
6
+
@@ -0,0 +1,24 @@
1
+ #!/bin/bash
2
+
3
+ eval "$("$(dirname "$(realpath "${BASH_SOURCE:-0}")")/../bin/init_sw")"
4
+ source ./tap.bash
5
+
6
+ to_unix_path () {
7
+ if [[ $(uname) = Linux ]]; then
8
+ wslpath "$@"
9
+ else
10
+ cygpath "$@"
11
+ fi
12
+ }
13
+
14
+ to_win_path () {
15
+ if [[ $(uname) = Linux ]]; then
16
+ wslpath -w "$@"
17
+ else
18
+ cygpath -w "$@"
19
+ fi
20
+ }
21
+
22
+ strip () {
23
+ sed -e 's/[ \r]*$//'
24
+ }
@@ -0,0 +1,37 @@
1
+ tap_tests () {
2
+ echo 1..$1
3
+ }
4
+
5
+ declare -i tap_test_counter=0
6
+ _tap_echo_result () {
7
+ local mes
8
+
9
+ tap_test_counter=tap_test_counter+1
10
+ if [[ -n $2 ]]; then
11
+ mes=" - $2"
12
+ else
13
+ mes=""
14
+ fi
15
+
16
+ if [[ $1 == 0 ]]; then
17
+ echo ok $tap_test_counter$mes
18
+ else
19
+ echo not ok $tap_test_counter$mes
20
+ fi
21
+
22
+ }
23
+
24
+ tap_ok () {
25
+ _tap_echo_result 0 "$1"
26
+ }
27
+
28
+ tap_notok() {
29
+ _tap_echo_result 1 "$1"
30
+ }
31
+
32
+ tap_okif() {
33
+ _tap_echo_result $1 "$2"
34
+ }
35
+
36
+
37
+
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+
3
+ source ./setup_test.bash
4
+ tap_tests 6
5
+
6
+ export jp=日本語
7
+ [[ $(sw echo %jp% | strip) = "日本語" ]]; tap_okif $?
8
+ sw echo %jp% > /dev/null # Re-import $jp from Windows
9
+ [[ $(echo $jp) = "日本語" ]]; tap_okif $? "Test a varible with Japanese value keeps its value after sw"
10
+ sw set jp2=あいうえお > /dev/null
11
+ [[ $(echo $jp2) = "あいうえお" ]]; tap_okif $?
12
+
13
+
14
+ tmp="$(to_unix_path "$(sw echo %TEMP% | strip)")/sw_日本語ディレクトリ"
15
+ rm -rf "$tmp"
16
+ mkdir "$tmp"
17
+ win_tmp="$(to_win_path "$tmp")"
18
+ sw cd "$win_tmp"
19
+ [[ `pwd` = "$tmp" ]]; tap_okif $?
20
+ [[ $(sw echo %jp% | strip) = "日本語" ]]; tap_okif $?
21
+ [[ $(sw cd | strip) = "$win_tmp" ]]; tap_okif $?
22
+ rm -rf "$tmp"
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+
3
+ source ./setup_test.bash
4
+ tap_tests 3
5
+
6
+ sw exit 0
7
+ [[ $? = 0 ]]; tap_okif $? "Test exitcode is propagated 1"
8
+
9
+ sw exit 42
10
+ [[ $? = 42 ]]; tap_okif $? "Test exitcode is propagated 2"
11
+
12
+ sw exit.cmd
13
+ [[ $? = 42 ]]; tap_okif $? "Test exitcode is propagated 3"
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ source ./setup_test.bash
4
+ tap_tests 1
5
+
6
+ expected="^test-test \r?"
7
+ [[ $(sw "echo test-test") =~ $expected ]]; tap_okif $? "Test the bug is fixed that sw misunderstoods hyphens in a commad as an option"
@@ -0,0 +1,10 @@
1
+ #!/bin/bash
2
+
3
+ source ./setup_test.bash
4
+ tap_tests 2
5
+
6
+ sw setcwd.cmd
7
+
8
+ expected="[cC]/Windows/System32/drivers /.*[cC]/Windows .*/[cC]\$"
9
+ [[ $(dirs) =~ $expected ]]; tap_okif $? "test if pushd works"
10
+ [[ $(pwd) =~ [cC]/Windows/System32/drivers$ ]]; tap_okif $? "test if cd works"
@@ -0,0 +1,16 @@
1
+ #!/bin/bash
2
+
3
+ source ./setup_test.bash
4
+ tap_tests 4
5
+
6
+ sw setdoskey.cmd
7
+
8
+ expected="bar \r?"
9
+ [[ $(foo) =~ $expected ]]; tap_okif $?
10
+ expected="foo \r?"
11
+ [[ $(echo1stparam foo bar) =~ $expected ]]; tap_okif $?
12
+ expected="foo bar \r?"
13
+ [[ $(echoallparams foo bar) =~ $expected ]]; tap_okif $?
14
+ expected="Microsoft Windows"
15
+ [[ $(verver) =~ $expected ]]; tap_okif $?
16
+
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+
3
+ source ./setup_test.bash
4
+ tap_tests 5
5
+
6
+ sw setenv.cmd
7
+
8
+ [[ "$WINENV1" = FOO ]]; tap_okif $? "Test a variable is imported from a bat file 1"
9
+ [[ "$WINENV2" = BAR ]]; tap_okif $? "Test a variable is imported from a bat file 2"
10
+ [[ "$PATH" =~ ^/[^:]*[cC]/?: ]]; tap_okif $? "Test PATH is imported from a bat file and converted to UNIX-style"
11
+ [[ $(sw echo %WINENV1%) =~ FOO ]]; tap_okif $? "Test a variable is exported to a bat file 1"
12
+ export UNIXENV1=BUZ
13
+ [[ $(sw echo %UNIXENV1%) =~ BUZ ]]; tap_okif $? "Test a variable is exported to a bat file 2"
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: source_win_bat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Takaya Saeki
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-01-05 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: "sw, or SourceWinBat, is a utility to run Windows batch files from WSL
14
+ /\nMSYS2 / Cygwin and sync environment variables, doskeys, and working \ndirectories
15
+ between batch files and their UNIX shell environments.\n"
16
+ email: abc.tkys+pub@gmail.com
17
+ executables:
18
+ - init_sw
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - LICENSE
23
+ - README.md
24
+ - Rakefile
25
+ - bin/init_sw
26
+ - lib/source_win_bat.rb
27
+ - lib/unixcompatenv.rb
28
+ - source_win_bat.gemspec
29
+ - test/exit.cmd
30
+ - test/setcwd.cmd
31
+ - test/setdoskey.cmd
32
+ - test/setenv.cmd
33
+ - test/setup_test.bash
34
+ - test/tap.bash
35
+ - test/test_ansicpchars.bash
36
+ - test/test_exit.bash
37
+ - test/test_optionparsebug.bash
38
+ - test/test_setcwd.bash
39
+ - test/test_setdoskey.bash
40
+ - test/test_setenv.bash
41
+ homepage: http://rubygems.org/gems/source_win_bat
42
+ licenses:
43
+ - MIT
44
+ metadata: {}
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project:
61
+ rubygems_version: 2.7.6
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: "'source' Windows bat files in your UNIX compatible shell in Windows"
65
+ test_files: []