source_win_bat 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60ef7f12402da3cef3443161f97eef66b580182aa70c2ea20e2f936dc56f9cd3
4
- data.tar.gz: 70f32f56ddb5aa0bdded6e72084bb977af84658b4dcabf3cbdc3e288cdc104e4
3
+ metadata.gz: 124a161b8c1f79c0be6b1e9093929776da276aa2f6877477887153266929a075
4
+ data.tar.gz: 832bee8c90c33ba7b4cfbc157b7fbbd4b1727cdac4c6f6e3d2f6a099de9040c1
5
5
  SHA512:
6
- metadata.gz: 87b0dda50fb66fac4b6c691bebf72d6549561e48092ce04945e083a4dca332ad5530c4bcf91ff43f7d8759d415ffef4789e3ab89f178ab142e00231c1617e698
7
- data.tar.gz: 6aa26c86859b485d8033a01ddb5a72b73b5e07306a9b2326e762a63034e6ca11b377719de21694befaa6dd22d4e665b76a913dd87f4e7e97836f36c6ab48a4e6
6
+ metadata.gz: 8378f7c810a1570633f2f727b4c425f80410e0283b4cdc8cf15ef3ade5f4fa4d9b39c2dd384f72193c704384a459a435805dedea51648e7422b6200064d34be5
7
+ data.tar.gz: 59325860b1d3fc11828def95aa2735a4cff93142ce96da76c67c380828d4ef023f1929fe8ba73f40d1030351e506831be00007a67429c2f438ca92581c871315
@@ -4,64 +4,57 @@ require 'securerandom'
4
4
  require 'tmpdir'
5
5
  require_relative 'unixcompatenv'
6
6
 
7
+ class CaseSensitiveVariableError < StandardError
8
+ end
9
+
7
10
  class SourceWindowsBatch
8
11
 
9
- VERSION = "0.3.0"
12
+ VERSION = "0.4.0"
13
+
14
+ @args = nil
15
+ @codepage = nil
16
+ @file_enc_opts = {}
10
17
 
11
18
  def main(argv)
12
- options = parse_args!(argv)
19
+ @args = parse_args!(argv)
20
+ @args.merge!(parse_option_envs())
13
21
 
14
22
  unless [:cygwin, :msys, :wsl].include? UnixCompatEnv.compat_env
15
23
  raise "You're in an unsupported UNIX compatible environment"
16
24
  end
17
25
 
18
- env = prepare_env_vars
19
- env_tmp_file_in = mk_tmpname(".env")
20
- macro_tmp_file_in = mk_tmpname(".doskey")
21
- cwd_tmp_file_in = mk_tmpname(".cwd")
22
- win_cmd = argv[3..-1].map {|v| "#{v}"}.join(" ")
23
- win_cmd += " & call set SW_EXITSTATUS=%^ERRORLEVEL% "
24
- win_cmd = concat_envdump(win_cmd, env_tmp_file_in)
25
- win_cmd = concat_macrodump(win_cmd, macro_tmp_file_in)
26
- win_cmd = concat_cwddump(win_cmd, cwd_tmp_file_in)
27
- win_cmd += " & call exit %^SW_EXITSTATUS%"
28
- if options[:show_cmd]
29
- STDERR.puts "SW: " + win_cmd
30
- end
26
+ load_codepage()
27
+ win_cmd, outfiles, proc_env = make_envsync_cmd(@args[:win_cmd])
28
+
31
29
  Signal.trap(:INT, "SIG_IGN")
30
+
32
31
  if UnixCompatEnv.compat_env == :wsl
33
32
  # * Skip winpty, assuming the system's WSL supports ConPTY
34
- # * Use an absolute path since EC overwrites PATH with Windows-style PATH in WSL
35
- pid = Process.spawn(env,
33
+ # * Use an absolute path since SWB overwrites PATH with Windows-style PATH in WSL
34
+ pid = Process.spawn(proc_env,
36
35
  UnixCompatEnv.to_compat_path('C:\\Windows\\System32\\cmd.exe'),
37
36
  '/C', win_cmd, :in => 0, :out => 1, :err => 2)
38
37
  elsif !STDOUT.isatty
39
- pid = Process.spawn(env, 'cmd.exe', '/C', win_cmd, :in => 0, :out => 1, :err => 2)
38
+ pid = Process.spawn(proc_env, 'cmd.exe', '/C', win_cmd, :in => 0, :out => 1, :err => 2)
40
39
  else
41
- pid = Process.spawn(env, 'winpty', '--', 'cmd.exe', '/C', win_cmd, :in => 0, :out => 1, :err => 2)
40
+ pid = Process.spawn(prov_env, 'winpty', '--', 'cmd.exe', '/C', win_cmd, :in => 0, :out => 1, :err => 2)
42
41
  end
42
+
43
43
  Signal.trap(:INT) do
44
44
  Process.signal("-KILL", pid)
45
45
  end
46
+
46
47
  status = nil
47
48
  loop do
48
49
  _, status = Process.wait2(pid)
49
50
  break if status.exited?
50
51
  end
51
52
 
52
- begin
53
- codepage = detect_ansi_codepage
54
- conv_setenv_stmts(env_tmp_file_in, argv[0], :bash, codepage)
55
- conv_doskey_stmts(macro_tmp_file_in, argv[1], :bash, codepage)
56
- gen_chdir_cmds(cwd_tmp_file_in, argv[2], :bash, codepage)
57
- if !options[:preserve_dump]
58
- [env_tmp_file_in, macro_tmp_file_in, cwd_tmp_file_in].each do |f|
59
- File.delete f
60
- end
61
- end
62
- rescue Errno::ENOENT
63
- # ignore
64
- end
53
+ conv_setenv_stmts(outfiles[:env_windump_file], @args[:env_sync_file])
54
+ conv_doskey_stmts(outfiles[:macro_windump_file], @args[:macro_sync_file])
55
+ gen_chdir_cmds(outfiles[:cwd_windump_file], @args[:cwd_sync_file])
56
+
57
+ delete_tmpfiles(outfiles)
65
58
 
66
59
  exit(status.exitstatus)
67
60
  end
@@ -69,19 +62,19 @@ class SourceWindowsBatch
69
62
  private
70
63
 
71
64
  def parse_args!(argv)
72
- options = {}
65
+ args = {}
73
66
  while argv.length > 0 && argv[0].start_with?("-")
74
67
  arg = argv.shift
75
68
  case arg
76
69
  when "--"
77
70
  next
78
- when "--show-cmd"
79
- options[:show_cmd] = true
71
+ when "--show-tmpfiles"
72
+ args[:show_tmpfiles] = true
80
73
  when "--preserve-dump"
81
- options[:preserve_dump] = true
74
+ args[:preserve_dump] = true
82
75
  when "--debug"
83
- options[:show_cmd] = true
84
- options[:preserve_dump] = true
76
+ args[:preserve_dump] = true
77
+ args[:show_tmpfiles] = true
85
78
  when "--help", "-h"
86
79
  puts help
87
80
  exit
@@ -98,65 +91,39 @@ class SourceWindowsBatch
98
91
  STDERR.puts help
99
92
  exit 1
100
93
  end
94
+
95
+ args[:env_sync_file] = argv[0]
96
+ args[:macro_sync_file] = argv[1]
97
+ args[:cwd_sync_file] = argv[2]
98
+ args[:win_cmd] = argv[3..-1].join(" ")
101
99
 
102
- options
100
+ args
103
101
  end
104
102
 
105
- def help
106
- <<EOS
107
- sw, or SourceWinBat, is a utility to run Windows batch files from WSL /
108
- MSYS2 / Cygwin and sync environment variables, and working directories
109
- between batch files and their UNIX Bash shell.
110
-
111
- Usage:
112
- sw [ [sw_options] -- ] win_bat_file [args...]
113
-
114
- Sw options:
115
- -h --help Show this help message
116
- -v --version Show the version information
117
- --preserve-dump Preserve the environment dump files of cmd.exe for
118
- debugging
119
- --show-cmd Show the command executed in cmd.exe for debugging
120
- --debug Enable '--preserve-dump' and '--show-cmd' options
121
-
122
- Examples:
123
- sw echo test
124
- sw somebat.bat
125
-
126
- You can control some behavior of SourceWinBat by defining following environment
127
- variables.
128
-
129
- Blacklisting and Whitelisting Environment Variable Not to be Synced:
130
-
131
- SWB_BLACKLIST Define comma-separated environment variable names with
132
- regular expressions. All environment variables included
133
- in this list will not be synced by SourceWinBat.
134
-
135
- SWB_WHITELIST Define variable names in the same manner as that of
136
- SWB_BLACKLIST. All environment variables that are NOT
137
- included in the list will NOT be synced by SourceWinBat.
138
-
139
- Examples:
140
-
141
- export SWB_BLACKLIST="foo:bar:baz_.*"
142
-
143
- "foo", "bar", and any variables name of which start with "baz_" will not
144
- be synced
145
-
146
- export SWB_BLACKLIST="sync_taboo"
147
- export SWB_WHITELIST="sync_.*"
103
+ def parse_option_envs()
104
+ options = {}
105
+ if ENV["SWB_DEBUG"] == "1"
106
+ options[:show_tmpfiles] = true
107
+ options[:preserve_dump] = true
108
+ end
148
109
 
149
- Only variables name of which start with "sync_" will be synced,
150
- except "sync_taboo".
151
- EOS
110
+ options
152
111
  end
153
112
 
154
- def detect_ansi_codepage
113
+ def detect_codepage
155
114
  if !STDOUT.isatty && UnixCompatEnv.compat_env == :wsl
156
- # cmd.exe seems to use UTF-8 when Stdout is redirected in WSL. TODO: Is it always fixed?
115
+ # cmd.exe seems to use UTF-8 when Stdout is redirected in WSL.
116
+ # TODO: Is it always fixed?
157
117
  return "65001" # CP65001 is UTF-8
158
118
  end
159
119
 
120
+ return ENV['SWB_CODEPAGE_CACHE'] if ENV['SWB_CODEPAGE_CACHE']
121
+
122
+ # You cannot detect the codepage by chcp because
123
+ # 1. chcp always retuns 65001 if it's not in a tty
124
+ # 2. you cannot get the output of a windows exe by Ruby's PTY module
125
+ # for some reason.
126
+ # So, we use powershell instead here.
160
127
  posh_cmd = <<-EOS
161
128
  Get-WinSystemLocale | Select-Object Name, DisplayName,
162
129
  @{ n='OEMCP'; e={ $_.TextInfo.OemCodePage } },
@@ -165,9 +132,22 @@ EOS
165
132
  posh_res = `powershell.exe "#{posh_cmd.gsub("$", "\\$")}"`
166
133
  locale = posh_res.lines.select {|line| !(line =~ /^\s*$/)}[-1].chomp
167
134
  ansi_cp = locale.split(" ")[-1]
135
+
136
+ ENV['SWB_CODEPAGE_CACHE'] = ansi_cp
137
+
168
138
  ansi_cp
169
139
  end
170
140
 
141
+ def load_codepage
142
+ @codepage = detect_codepage()
143
+ @file_enc_opts = {
144
+ invalid: :replace,
145
+ undef: :replace,
146
+ replace: "?",
147
+ encoding: "CP#{@codepage}:UTF-8"
148
+ }
149
+ end
150
+
171
151
  def serialize_wslenvs(wslenvs)
172
152
  wslenvs.map {|varname, opt| "#{varname}#{opt.empty? ? "" : "/#{opt}"}"}.join(":")
173
153
  end
@@ -197,22 +177,72 @@ EOS
197
177
  false
198
178
  end
199
179
 
180
+ def detect_diffcase_vars(var_hash)
181
+ same_names = Hash[]
182
+ var_hash.each do |name, val|
183
+ same_names[name.upcase] ||= []
184
+ same_names[name.upcase].push(name)
185
+ end
186
+ same_names.select! {|name, val| val.length > 1}
187
+
188
+ same_names
189
+ end
190
+
191
+ ###
192
+ # Handling of environment variable in WSL is tricky due to WSLENV's strange behavior.
193
+ # SWB has several rules to pass UNIX environment variables to Windows.
194
+ # 1. Normally, pass a variable by running initialization batch script before
195
+ # executing the target user batch file. The initialization script contains
196
+ # `set` statements generated by SWB.
197
+ #
198
+ # 2. If a variable contains characters that need escaping, such as '|', '>',
199
+ # pass it by WSLENV.
200
+ # Why not passing all variables by WSLENV?
201
+ # a. It's because WSLENV has strange bahavior about case-sensitiveness.
202
+ # WSLENV syncs variables in a case-sensitive manner, even though Windows
203
+ # apps handle environment variables do not.
204
+ # For example, PATH variable is sometimes 'Path' in Windows. So, WSLENV fails
205
+ # to sync Path with Unix's PATH. Syncing by running initialization batch script
206
+ # in Windows environment can avoid this.
207
+ # b. However, I couldn't find a complete way to escape a value of a variable.
208
+ # Without escaping, set statements gets crazy like `set hoge=foo | bar`, when
209
+ # variable `hoge` has a value of `foo | bar`. So, we cannot fully depend on
210
+ # initialization batch script. That's why SWB uses combination of initialization
211
+ # script and WSLENV.
212
+ #
213
+ # 3. If a variable exists in WSLENV in the first place, sync it by WSLENV.
214
+ # A user may set some flags in WSLENV.
215
+ #
216
+ # Additionally, after completing execution of the target user batch file, if SWB
217
+ # finds some variables the names of which differ only in case, SWB shows warning.
218
+ #
219
+ # Handling of environment variable in MSYS2 and Cygwin is simple.
220
+ # They sync environment variables without any effort.
221
+ ###
200
222
  def prepare_env_vars
201
- return {} if UnixCompatEnv.compat_env != :wsl
223
+ env = Hash[ENV]
224
+ return [{}, env] if UnixCompatEnv.compat_env != :wsl
202
225
 
203
- wslenvs = Hash[]
204
- ENV.each do |envvar_name, _|
226
+ chars_to_escape = /[><|&^%]/
227
+ vars = Hash[]
228
+ wslenvs = parse_wslenv(ENV['WSLENV'] || "")
229
+ ENV.each do |envvar_name, val|
205
230
  next if whitelist_block?(envvar_name) || blacklist_block?(envvar_name)
206
- wslenvs[envvar_name] = ""
231
+ next if wslenvs.has_key?(envvar_name)
232
+ if chars_to_escape =~ val
233
+ wslenvs[envvar_name] = ""
234
+ else
235
+ vars[envvar_name] = val
236
+ end
207
237
  end
208
- wslenvs.merge!(parse_wslenv(ENV['WSLENV'])) if ENV['WSLENV']
209
- # We don't use '/l' option, but convert paths by ourselves instead.
210
- # See the comment that starts with 'How PATH in WSLENV is handled'
211
- wslenvs['PATH'] = ""
212
- var_wslenv = serialize_wslenvs(wslenvs)
238
+ # We don't use WSLENV for Path, but convert paths by ourselves instead.
239
+ # So, set it to empty. WSLENV is restored to the original value in Windows
240
+ # environment by the initialization script
241
+ wslenvs['PATH'] = "" if wslenvs['PATH']
242
+ env['WSLENV'] = serialize_wslenvs(wslenvs) if !wslenvs.empty?
213
243
 
214
244
  paths = []
215
- ENV['PATH'].split(":").each do |path|
245
+ ENV['PATH'].split(':').each do |path|
216
246
  begin
217
247
  rpath = File.realpath(path)
218
248
  if rpath.start_with?(UnixCompatEnv.win_root_in_compat)
@@ -222,28 +252,92 @@ EOS
222
252
  end
223
253
  paths.push(path)
224
254
  end
225
- var_path = paths.join(';')
255
+ vars['PATH'] = paths.join(';')
256
+
257
+ if !(same_names = detect_diffcase_vars(vars.merge(wslenvs))).empty?
258
+ error_mes = <<-EOS
259
+ SWB Error:
260
+ You have environment variables the names of which differ only in case.
261
+ SWB cannot preserve and restore them due to case insensitiveness of Windows.
262
+ Please undefine them or add either to SWB_BLACKLIST to prevent ambiguity.
263
+ Ambiguous variables:
264
+ EOS
265
+ same_names.each do |_, vals|
266
+ error_mes += " - " + vals.join(", ") + "\n"
267
+ end
268
+ raise CaseSensitiveVariableError.new(error_mes)
269
+ end
226
270
 
227
- {"WSLENV" => var_wslenv, "PATH" => var_path}
271
+ [vars, env]
228
272
  end
229
273
 
230
274
  def mk_tmpname(suffix)
231
275
  "#{UnixCompatEnv.win_tmp_in_compat}#{SecureRandom.uuid + suffix}"
232
276
  end
233
277
 
234
- def concat_envdump(cmd, tmpfile)
235
- cmd + " & set > #{dq_win_path(UnixCompatEnv.to_win_path(tmpfile))}"
278
+ def make_envsync_cmd(cmd)
279
+ files = {}
280
+
281
+ begin
282
+ wrapper_batch_file = mk_tmpname(".cmd")
283
+ File.write(wrapper_batch_file, "@" + cmd)
284
+ files[:wrapper_batch_file] = wrapper_batch_file
285
+
286
+ statements = ["@call " + dq_win_path(UnixCompatEnv.to_win_path(wrapper_batch_file))]
287
+
288
+ statements.push("@set SWB_EXITSTATUS=%errorlevel%")
289
+ proc_env = concat_env_init!(statements, files)
290
+ concat_env_dump!(statements, files)
291
+ concat_macro_dump!(statements, files)
292
+ concat_cwd_dump!(statements, files)
293
+ statements.push("@exit %SWB_EXITSTATUS%")
294
+
295
+ internal_command_file = mk_tmpname(".cmd")
296
+ File.write(internal_command_file, statements.join("\r\n"))
297
+ files[:internal_command_file] = internal_command_file
298
+ internal_command = "@" + dq_win_path(UnixCompatEnv.to_win_path(internal_command_file))
299
+
300
+ [internal_command, files, proc_env]
301
+ rescue CaseSensitiveVariableError => e
302
+ STDERR.puts e.message
303
+ delete_tmpfiles(files)
304
+ exit(1)
305
+ end
236
306
  end
237
307
 
238
- def concat_macrodump(cmd, tmpfile)
308
+ def concat_env_init!(statements, outfiles)
309
+ vars_to_sync, proc_env = prepare_env_vars
310
+
311
+ env_init_file = mk_tmpname(".cmd")
312
+ File.write(env_init_file,
313
+ vars_to_sync.map {|var, val| "@set #{var}=#{val}"}.join("\r\n"),
314
+ opt=@file_enc_opts)
315
+ outfiles[:env_init_file] = env_init_file
316
+
317
+ statements.unshift("@call " + dq_win_path(UnixCompatEnv.to_win_path(env_init_file)))
318
+
319
+ proc_env
320
+ end
321
+
322
+ def concat_env_dump!(statements, outfiles)
323
+ env_windump_file = mk_tmpname(".env")
324
+ statements.push("@set > #{dq_win_path(UnixCompatEnv.to_win_path(env_windump_file))}")
325
+ outfiles[:env_windump_file] = env_windump_file
326
+ end
327
+
328
+ def concat_macro_dump!(statements, outfiles)
329
+ macro_windump_file = mk_tmpname(".doskey")
239
330
  #TODO: escape
240
- cmd + " & doskey /macros > #{dq_win_path(UnixCompatEnv.to_win_path(tmpfile))}"
331
+ statements.push("@doskey /macros > #{dq_win_path(UnixCompatEnv.to_win_path(macro_windump_file))}")
332
+ outfiles[:macro_windump_file] = macro_windump_file
241
333
  end
242
334
 
243
- def concat_cwddump(cmd, tmpfile)
335
+ def concat_cwd_dump!(statements, outfiles)
336
+ cwd_windump_file = mk_tmpname(".cwd")
244
337
  #TODO: escape
245
- winpath = dq_win_path(UnixCompatEnv.to_win_path(tmpfile))
246
- cmd + " & cd > #{winpath} & pushd >> #{winpath}"
338
+ winpath = dq_win_path(UnixCompatEnv.to_win_path(cwd_windump_file))
339
+ statements.push("@ cd > #{winpath} & pushd >> #{winpath}")
340
+ outfiles[:cwd_windump_file] = cwd_windump_file
247
341
  end
248
342
 
249
343
  def dq_win_path(str)
@@ -257,78 +351,65 @@ EOS
257
351
  str.gsub(/'/, '"\'"')
258
352
  end
259
353
 
260
- def to_compat_pathlist(path, shell)
261
- raise "Unsupporeted" unless shell == :bash
354
+ def to_compat_pathlist(path)
262
355
  path.split(";")
263
356
  .map {|p| UnixCompatEnv.to_compat_path(p)}
264
357
  .join(":")
265
358
  end
266
359
 
267
- def to_env_stmt(set_stmt, shell)
268
- raise "Unsupporeted" unless shell == :bash
269
- var, val = /([^=]*)=(.*)$/.match(set_stmt)[1..2]
270
-
271
- is_var_valid = /^[a-zA-Z_][_0-9a-zA-Z]*$/ =~ var
272
- return nil unless is_var_valid
273
-
274
- if var == "PATH" && UnixCompatEnv.compat_env != :wsl
275
- val = to_compat_pathlist(val, :bash)
276
- end
277
-
278
- stmt = "export #{var}='#{escape_singlequote(val.chomp)}'"
279
- return stmt if UnixCompatEnv.compat_env != :wsl
280
-
281
- if var == "PATH"
282
- stmt += "\nexport WSLENV=PATH/l:${WSLENV:-__EC_DUMMY_ENV}"
283
- else
284
- stmt += "\nexport WSLENV=#{var}:${WSLENV:-__EC_DUMMY_ENV}"
285
- end
286
- stmt
287
- end
288
-
289
- def conv_setenv_stmts(setenvfile, outfile, shell, codepage)
290
- raise "Unsupporeted" if shell != :bash
360
+ def conv_setenv_stmts(setenvfile, outfile)
361
+ return if !File.exist?(setenvfile)
291
362
 
363
+ vars = Hash[]
364
+ envs_casemap = Hash[ENV.keys.map {|k| [k.upcase, k]}]
292
365
  File.open(outfile, "w") do |f_out|
293
- envs = []
294
- File.read(setenvfile, encoding: "CP#{codepage}:UTF-8").lines.each do |set_stmt|
295
- var, val = /([^=]*)=(.*)$/.match(set_stmt)[1..2]
366
+ File.read(setenvfile, opt=@file_enc_opts).lines.each do |set_stmt|
367
+ var, val = /([^=]*)=(.*)$/.match(set_stmt)[1..2]
296
368
 
297
369
  is_var_valid = /^[a-zA-Z_][_0-9a-zA-Z]*$/ =~ var
298
370
  next if !is_var_valid
299
371
  next if whitelist_block?(var) || blacklist_block?(var)
372
+ vars[var] = val
300
373
 
301
- if var == "PATH"
302
- val = to_compat_pathlist(val, shell)
374
+ if var.upcase == "PATH"
375
+ val = to_compat_pathlist(val)
303
376
  end
304
377
 
305
- envs.push(var)
378
+ var = envs_casemap[var.upcase] || var
306
379
  f_out.puts("export #{var}='#{escape_singlequote(val.chomp)}'")
307
380
  end
308
- if UnixCompatEnv.compat_env == :wsl
309
- # How PATH in WSLENV is handled:
310
- # EC configures PATH's WSLENV flag as follows
311
- # A. When EC internally launches a Windows bat file
312
- # Set the PATH's flag to '' (nothing) since EC converts each Unix
313
- # path to a corresponding Windows path.
314
- # B. When EC syncs environment variables with the result of a bat file
315
- # Leave the PATH's WSLENV flag as is
316
- wslenvs = Hash[*envs.flat_map {|env| [env, ""]}]
317
- wslenvs.delete('PATH')
318
- wslenvs.merge!(parse_wslenv(ENV['WSLENV'])) if ENV['WSLENV']
319
-
320
- if wslenvs.length > 0
321
- f_out.puts("export WSLENV='#{serialize_wslenvs(wslenvs)}'")
322
- end
381
+ end
382
+
383
+ if !(same_names = detect_diffcase_vars(vars)).empty?
384
+ STDERR.puts <<-EOS
385
+ SWB Warning:
386
+ You've synced the environment variables the names of which differ only
387
+ in case. That means one of the following.
388
+ 1. You define a variable in your WSLENV, and your Windows environment
389
+ has another variable the name of which differ only in case.
390
+ 2. SWB synced a variable by WSLENV, and your Windows command defined
391
+ another variable the name of which differ only in case from that of
392
+ the variable SWB synced.
393
+ SWB normally syncs variables by initialization script. However, if
394
+ a variable's value contains special character to be escaped, SWB
395
+ syncs it by WSLENV instead. That is the case here.
396
+
397
+ To solve this warning, please undefine those variables, or add them, except
398
+ one variable, to SWB_BLACKLIST to prevent ambiguity.
399
+ Ambiguous variables:
400
+ EOS
401
+ same_names.each do |_, vals|
402
+ STDERR.puts(" - " + vals.join(", "))
323
403
  end
324
404
  end
405
+
325
406
  end
326
407
 
327
- def conv_doskey_stmts(doskeyfile, outfile, shell, codepage)
328
- raise "Unsupporeted" unless shell == :bash
408
+ def conv_doskey_stmts(doskeyfile, outfile)
409
+ return if !File.exist?(doskeyfile)
329
410
 
330
411
  File.open(outfile, "w") do |f_out|
331
- File.open(doskeyfile, encoding: "CP#{codepage}:UTF-8") do |f_in|
412
+ File.open(doskeyfile, opt=@file_enc_opts) do |f_in|
332
413
  f_in.each_line do |doskey_stmt|
333
414
  key, body = /([^=]*)=(.*)$/.match(doskey_stmt)[1..2]
334
415
 
@@ -348,11 +429,10 @@ EOS
348
429
  end
349
430
  end
350
431
 
351
- def gen_chdir_cmds(dirs, outfile, shell, codepage)
352
- raise "Unsupporeted" unless shell == :bash
353
- return unless File.exist?(dirs)
432
+ def gen_chdir_cmds(dirs, outfile)
433
+ return if !File.exist?(dirs)
354
434
 
355
- lines = File.read(dirs, encoding:"CP#{codepage}:UTF-8").lines.select {|line| !line.empty?}
435
+ lines = File.read(dirs, opt=@file_enc_opts).lines.select {|line| !line.empty?}
356
436
  cwd = lines[0]
357
437
  dirs = lines[1..-1]
358
438
 
@@ -365,4 +445,91 @@ EOS
365
445
  File.write(outfile, res.join("\n"))
366
446
  end
367
447
 
448
+ def log_file_content(file_type, filename)
449
+ STDERR.puts("=== begin: #{file_type} ===\n")
450
+ if File.exist?(filename)
451
+ STDERR.puts("=== - CP#{@codepage}:'#{filename}' ===\n")
452
+ STDERR.puts(File.read(filename, opt=@file_enc_opts))
453
+ else
454
+ STDERR.puts("This file doesn't exist.")
455
+ STDERR.puts("Maybe Windows command terminated by exit command")
456
+ end
457
+ STDERR.puts("=== end: #{file_type} ===\n")
458
+ end
459
+
460
+ def delete_tmpfiles(tmpfiles)
461
+ tmpfiles.each do |k, f|
462
+ if @args[:show_tmpfiles]
463
+ log_file_content(k, f)
464
+ end
465
+ if !@args[:preserve_dump]
466
+ File.delete(f) if File.exist?(f)
467
+ end
468
+ end
469
+ end
470
+
471
+ def help
472
+ <<EOS
473
+ sw, or SourceWinBat, is a utility to run Windows batch files from WSL /
474
+ MSYS2 / Cygwin and sync environment variables, and working directories
475
+ between batch files and their UNIX Bash shell.
476
+
477
+ Usage:
478
+ sw [ [sw_options] -- ] win_bat_file [args...]
479
+
480
+ Sw options:
481
+ -h --help Show this help message
482
+ -v --version Show the version information
483
+ --preserve-dump Preserve the environment dump files of cmd.exe for
484
+ debugging
485
+ --show-tmpfiles Show the contents of the temporary files such as
486
+ the environment dump files
487
+ --debug Enable '--preserve-dump', '--show-tmpfiles' options
488
+
489
+ Examples:
490
+ sw echo test
491
+ sw somebat.bat
492
+
493
+ You can control some behavior of SourceWinBat by defining following environment
494
+ variables.
495
+
496
+ Blacklisting and Whitelisting Environment Variable Not to be Synced:
497
+
498
+ SWB_BLACKLIST Define comma-separated environment variable names with
499
+ regular expressions. All environment variables included
500
+ in this list will not be synced by SourceWinBat.
501
+
502
+ SWB_WHITELIST Define variable names in the same manner as that of
503
+ SWB_BLACKLIST. All environment variables that are NOT
504
+ included in the list will NOT be synced by SourceWinBat.
505
+
506
+ Examples:
507
+
508
+ export SWB_BLACKLIST="foo:bar:baz_.*"
509
+
510
+ "foo", "bar", and any variables name of which start with "baz_" will not
511
+ be synced
512
+
513
+ export SWB_BLACKLIST="sync_taboo"
514
+ export SWB_WHITELIST="sync_.*"
515
+
516
+ Only variables name of which start with "sync_" will be synced,
517
+ except "sync_taboo".
518
+
519
+ ## Several things to keep in mind:
520
+
521
+ 1. SourceWinBat executes the given Windows command in "Batch file mode".
522
+
523
+ Windows cmd.exe has a few different behaviors in the interactive
524
+ "command line mode" and the "batch file mode". For example, expansion
525
+ result of an empty variable, or variable expansion in for command.
526
+ SourceWinBat executes the given command always in the batch file mode.
527
+
528
+ 2. `exit` command prevents SourceWinBat from synciny environment variables.
529
+
530
+ If you can fix the batch file you run, please replace `exit` with `exit /B`
531
+
532
+ EOS
533
+ end
534
+
368
535
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'source_win_bat'
3
- s.version = '0.3.0'
3
+ s.version = '0.4.0'
4
4
  s.date = '2019-02-06'
5
5
  s.summary = "'source' Windows bat files in your UNIX compatible shell in Windows"
6
6
  s.description = <<EOS
@@ -0,0 +1,5 @@
1
+ @echo off
2
+
3
+ set DiffCase1=BAR
4
+ set DIFFCASE2=BAR
5
+ set DiffCase3=BAR
@@ -0,0 +1,26 @@
1
+ #!/bin/bash
2
+
3
+ source ./setup_test.bash
4
+ tap_tests 12
5
+
6
+ export DIFFCASE1=FOO
7
+ [[ $(sw echo %DiffCase1%) =~ FOO ]]; tap_okif $? "Test a variable of different cases can be read in Windows 1"
8
+ export DiffCase2=FOO
9
+ [[ $(sw echo %DIFFCASE2%) =~ FOO ]]; tap_okif $? "Test a variable of different cases can be read in Windows 2"
10
+ [[ -z "$DIFFCASE3" ]]; tap_okif $? "Test a variable is undefined 1"
11
+ [[ -z "$DiffCase3" ]]; tap_okif $? "Test a variable is undefined 2"
12
+
13
+ sw set_diff_case_env.cmd
14
+
15
+ [[ "$DIFFCASE1" =~ BAR ]]; tap_okif $? "Test a variable of different cases is modified in Windows 1"
16
+ [[ -z "$DiffCase1" ]]; tap_okif $? "Test a variable of different cases merges 1"
17
+ [[ "$DiffCase2" =~ BAR ]]; tap_okif $? "Test a variable of different cases is modified in Windows 2"
18
+ [[ "$DiffCase3" =~ BAR ]]; tap_okif $? "Test a variable of different cases is modified in Windows 3"
19
+ [[ -z "$DIFFCASE3" ]]; tap_okif $? "Test a variable is undefined 3"
20
+
21
+ unset DiffCase3
22
+ export DIFFCASE3=BAZ
23
+ [[ $(sw echo %DiffCase3%) =~ BAZ ]]; tap_okif $? "Test a variable of different cases merges 1"
24
+ [[ $(echo $DIFFCASE3) =~ BAZ ]]; tap_okif $? "Test a variable of different cases merges 2"
25
+ [[ -z "$DiffCase3" ]]; tap_okif $? "Test a variable of different cases merges 3"
26
+
@@ -3,5 +3,5 @@
3
3
  source ./setup_test.bash
4
4
  tap_tests 1
5
5
 
6
- expected="^test-test \r?"
6
+ expected="^test-test"
7
7
  [[ $(sw "echo test-test") =~ $expected ]]; tap_okif $? "Test the bug is fixed that sw misunderstoods hyphens in a commad as an option"
@@ -5,11 +5,11 @@ tap_tests 4
5
5
 
6
6
  sw setdoskey.cmd
7
7
 
8
- expected="bar \r?"
8
+ expected="bar"
9
9
  [[ $(foo) =~ $expected ]]; tap_okif $?
10
- expected="foo \r?"
10
+ expected="foo"
11
11
  [[ $(echo1stparam foo bar) =~ $expected ]]; tap_okif $?
12
- expected="foo bar \r?"
12
+ expected="foo bar"
13
13
  [[ $(echoallparams foo bar) =~ $expected ]]; tap_okif $?
14
14
  expected="Microsoft Windows"
15
15
  [[ $(verver) =~ $expected ]]; tap_okif $?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: source_win_bat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takaya Saeki
@@ -25,15 +25,16 @@ files:
25
25
  - bin/init_sw
26
26
  - lib/source_win_bat.rb
27
27
  - lib/unixcompatenv.rb
28
- - source_win_bat-0.1.0.gem
29
28
  - source_win_bat.gemspec
30
29
  - test/exit_42.cmd
30
+ - test/set_diff_case_env.cmd
31
31
  - test/setcwd.cmd
32
32
  - test/setdoskey.cmd
33
33
  - test/setenv.cmd
34
34
  - test/setup_test.bash
35
35
  - test/tap.bash
36
36
  - test/test_ansicpchars.bash
37
+ - test/test_diffrent_case_envvar_bug.bash
37
38
  - test/test_exit.bash
38
39
  - test/test_optionparsebug.bash
39
40
  - test/test_setcwd.bash
Binary file