source_win_bat 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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