dtas 0.0.0 → 0.1.I

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Documentation/dtas-console.txt +15 -0
  4. data/Documentation/dtas-player_protocol.txt +5 -3
  5. data/Documentation/dtas-sourceedit.txt +14 -7
  6. data/GIT-VERSION-GEN +3 -3
  7. data/INSTALL +3 -3
  8. data/README +6 -5
  9. data/Rakefile +16 -7
  10. data/bin/dtas-console +48 -3
  11. data/bin/dtas-cueedit +1 -1
  12. data/bin/dtas-sinkedit +12 -28
  13. data/bin/dtas-sourceedit +15 -30
  14. data/lib/dtas.rb +1 -1
  15. data/lib/dtas/command.rb +0 -5
  16. data/lib/dtas/compat_onenine.rb +2 -2
  17. data/lib/dtas/disclaimer.rb +4 -3
  18. data/lib/dtas/edit_client.rb +48 -0
  19. data/lib/dtas/format.rb +2 -9
  20. data/lib/dtas/player.rb +64 -28
  21. data/lib/dtas/player/client_handler.rb +39 -20
  22. data/lib/dtas/process.rb +16 -15
  23. data/lib/dtas/replaygain.rb +19 -3
  24. data/lib/dtas/sink.rb +1 -2
  25. data/lib/dtas/source.rb +1 -141
  26. data/lib/dtas/source/av.rb +29 -0
  27. data/lib/dtas/source/av_ff_common.rb +127 -0
  28. data/lib/dtas/source/{command.rb → cmd.rb} +1 -1
  29. data/lib/dtas/source/ff.rb +30 -0
  30. data/lib/dtas/source/file.rb +94 -0
  31. data/lib/dtas/source/{mp3.rb → mp3gain.rb} +1 -1
  32. data/lib/dtas/source/sox.rb +114 -0
  33. data/lib/dtas/unix_client.rb +1 -9
  34. data/test/player_integration.rb +5 -17
  35. data/test/test_format.rb +10 -14
  36. data/test/test_format_change.rb +4 -8
  37. data/test/test_player_integration.rb +50 -62
  38. data/test/test_process.rb +33 -0
  39. data/test/test_rg_integration.rb +45 -35
  40. data/test/test_sink_pipe_size.rb +20 -0
  41. data/test/test_sink_tee_integration.rb +2 -4
  42. data/test/{test_source.rb → test_source_av.rb} +16 -16
  43. data/test/test_source_sox.rb +115 -0
  44. metadata +23 -12
  45. data/.rsync_doc +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a1e4e92e52c071f8b53fbf9d05c32a276ec9a83
4
- data.tar.gz: d5ab2c9d24f71ae9bd61e2c1b3b4b0ef4fb379ca
3
+ metadata.gz: e1f118957182ff09d25d1da7f0ae4fda9c867e21
4
+ data.tar.gz: a42b9ef9d642cd13bb32a24cf2571a315acb8842
5
5
  SHA512:
6
- metadata.gz: ec862253ef20e2ff0ca445d3795dad21cf1c800312f44443bbce66ea5ff5c51ebc43273db8d51141974b432b76b3a34316140e250b43a179e6d6f5bdcdfdf030
7
- data.tar.gz: ff364dc679cb1e7ea780316588d3af8f098ffe31b132eab165dae446059827f14ee8fda195d725bd2814049116ebe590662bcbb443cf8521fe69070ad1658179
6
+ metadata.gz: 2becf89b867d7183a554c00eed34cf1a8a5f468b051173e072894072b3711fb4c5df40cb2d551c7bf07501fe55e82ac2a3922f93a286ab889fb7cdb853c78974
7
+ data.tar.gz: 6898c1eb14d91ea620f306abec3620c42258ec3d964d40fdd382ec74590cc81b955012047dd8748d88989b9e62a25de3d35f71c175a0ae7985f9aeb084c084ee
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  *.log
8
8
  /man
9
9
  *.gem
10
+ *.gz
@@ -29,6 +29,9 @@ Key bindings are inspired partially by mplayer(1)
29
29
  - SPACE - toggle play/pause state of the playback
30
30
  - 'p'/'P' - decrease/increase timer resolution
31
31
  - BACKSPACE - seek to the beginning of the track
32
+ - 9/0 - decrease/increase ReplayGain preamp
33
+ - 'f'/'F' - decrease/increase ReplayGain fallback_gain value
34
+ - 'r'/'R' - cycle forward/backwards through ReplayGain modes
32
35
  - Ctrl-C - exit dtas-console
33
36
 
34
37
  # ENVIRONMENT
@@ -36,6 +39,18 @@ Key bindings are inspired partially by mplayer(1)
36
39
  DTAS_PLAYER_SOCK - the path to the dtas-player control socket.
37
40
  This defaults to ~/.dtas/player.sock
38
41
 
42
+ # CAVEATS
43
+
44
+ Increasing timer resolution increases the number of CPU wakeups and
45
+ power consumption. This defeats the efforts of dtas-player
46
+ configurations which use large buffers (especially in the sink) to
47
+ reduce wakeups and power consumption.
48
+
49
+ In rare cases (or poorly-configured systems), increased wakeups from
50
+ dtas-console will also increase contention with the sound card,
51
+ negatively affecting audio quality even if CPU utilization is not
52
+ a problem.
53
+
39
54
  # SEE ALSO
40
55
 
41
56
  dtas-player(1), dtas-player_protocol(7), dtas-ctl(1), dtas-sinkedit(1),
@@ -8,7 +8,7 @@ dtas-player_protocol - protocol for controling dtas-player
8
8
  # DESCRIPTION
9
9
 
10
10
  This gives a specification of the dtas-player protocol over a local Unix
11
- SOCK_SEQPACKET or SOCK_DGRAM socket. The dtas-player protocol should NOT be
11
+ SOCK_SEQPACKET socket. The dtas-player protocol should NOT be
12
12
  considered stable at this point and compatibility will break.
13
13
 
14
14
  Inspiration is taken from MPRIS and MPRIS 2.0 specifications (e.g.
@@ -33,6 +33,7 @@ dtas-sinkedit(1), and dtas-enq(1) also implement this protocol.
33
33
  - ENVVALUE - must be a suitable environment variable (setenv(3))
34
34
  - COMMAND, this may be quoted string passed to sh -c "",
35
35
  variable/argument expansion will be performed by the shell
36
+ - SOURCENAME - "sox" or "av", more backends may be supported in the future
36
37
  - FILENAME - an expanded pathname relative to / is recommended since
37
38
  dtas-player and the client may run in different directories
38
39
 
@@ -167,14 +168,15 @@ For little-endian machines, $ECAFMT defaults to: -fs32_le,2,44100
167
168
  + nonblock=BOOLEAN - drop audio data to avoid holding back other sinks
168
169
  + pipe_size=UNSIGNED - set the size of the pipe for the sink (Linux-only)
169
170
 
170
- * source cat - dump the current source command and env in YAML
171
+ * source SOURCENAME cat - dump the current source command and env in YAML
171
172
 
172
- * source ed SOURCEARGS - edit the source (decoder) command and environment.
173
+ * source ed SOURCENAME SOURCEARGS - edit the source parameters.
173
174
  This changes here will immediately restart the source process.
174
175
  See the code for dtas-sourceedit(1) for an example of using this.
175
176
  + command=COMMAND - change the command-line used to decode audio
176
177
  + env.ENVNAME=ENVVALUE - set ENVNAME to ENVVALUE for the source process
177
178
  + env#ENVNAME - unset ENVNAME in the source process (only)
179
+ + tryorder=INTEGER - lower values are tried first
178
180
 
179
181
  * watch - adds the client to the passive watch list for notifications.
180
182
  It is recommended clients issue no further commands and open
@@ -3,23 +3,30 @@
3
3
 
4
4
  # NAME
5
5
 
6
- dtas-sourceedit - edit the command and environment of the decoder
6
+ dtas-sourceedit - edit parameters of a source decoder
7
7
 
8
8
  # SYNOPSYS
9
9
 
10
- dtas-sourceedit
10
+ dtas-sourceedit {sox | av | ff}
11
11
 
12
12
  # DESCRIPTION
13
13
 
14
- dtas-sourceedit spawns an editor to allow editing of a sink as a YAML file.
15
- See dtas-player_protocol(7) for details on SINKARGS.
14
+ dtas-sourceedit spawns an editor to allow editing of a source as a YAML file.
15
+ See dtas-player_protocol(7) for details on SOURCEARGS.
16
16
 
17
17
  # EXAMPLES
18
18
 
19
- Invoking dtas-sourceedit will spawn your favorite text editor on a
20
- given SINKNAME:
19
+ Invoking dtas-sourceedit will spawn your favorite text editor on "sox":
21
20
 
22
- $ dtas-sourceedit
21
+ $ dtas-sourceedit sox
22
+
23
+ To change the way dtas-player calls avconv (part of libav):
24
+
25
+ $ dtas-sourceedit av
26
+
27
+ To change the way dtas-player calls ffmpeg (lightly-tested):
28
+
29
+ $ dtas-sourceedit ff
23
30
 
24
31
  # ENVIRONMENT
25
32
 
@@ -3,7 +3,7 @@
3
3
  # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
4
  CONSTANT = "DTAS::VERSION"
5
5
  RVF = "lib/dtas/version.rb"
6
- DEF_VER = "v0.0.0"
6
+ DEF_VER = "v0.1.I"
7
7
  vn = DEF_VER
8
8
 
9
9
  # First see if there is a version file (included in release tarballs),
@@ -22,9 +22,9 @@ if File.exist?(".git")
22
22
  end
23
23
 
24
24
  vn = vn.sub!(/\Av/, "")
25
- new_ruby_version = "#{CONSTANT} = '#{vn}'\n"
25
+ new_ruby_version = "#{CONSTANT} = '#{vn}' # :nodoc:\n"
26
26
  cur_ruby_version = File.read(RVF) rescue nil
27
27
  if new_ruby_version != cur_ruby_version
28
- File.open(RVF, "w") { |fp| fp.write("# :enddoc:\n#{new_ruby_version}") }
28
+ File.open(RVF, "w") { |fp| fp.write(new_ruby_version) }
29
29
  end
30
30
  puts vn if $0 == __FILE__
data/INSTALL CHANGED
@@ -40,10 +40,10 @@ Debian users can install sox, mp3gain, and flac dependencies easily:
40
40
 
41
41
  Grab the latest tarball from our HTTP site:
42
42
 
43
- http://dtas.80x24.org/2013/dtas-0.0.0.tar.gz
43
+ http://dtas.80x24.org/2013/dtas-0.1.I.tar.gz
44
44
 
45
- $ tar zxvf dtas-0.0.0.tar.gz
46
- $ cd dtas-0.0.0
45
+ $ tar zxvf dtas-0.1.I.tar.gz
46
+ $ cd dtas-0.1.I
47
47
  $ ruby setup.rb
48
48
 
49
49
  GNU/Linux users may optionally install "io_splice" and
data/README CHANGED
@@ -40,17 +40,18 @@ Users of dtas-player will also be interested in the following scripts:
40
40
  * dtas-xdelay - alternative sink for dtas-player
41
41
 
42
42
  All scripts have some documentation in the Documentation/ directory or
43
- manpages distributed with the gem. dtas exposes no public APIs outside
44
- of command-line and YAML text. dtas is aimed at users familiar with the
45
- *nix command-line and editing text files. Familiarity with the Ruby
46
- programming language is absolutely NOT required.
43
+ manpages distributed with the gem. Documentation is also available on
44
+ http://dtas.80x24.org/$COMMAND.txt in plain-text form.
45
+
46
+ dtas exposes no public APIs outside of command-line and YAML text. dtas is
47
+ aimed at users familiar with the *nix command-line and editing text files.
48
+ Familiarity with the Ruby programming language is absolutely NOT required.
47
49
 
48
50
  Coming:
49
51
 
50
52
  * MPRIS/MPRIS 2.0 bridge for partial dtas-player control
51
53
  * tracklist support in dtas-player (maybe?)
52
54
  * whatever command-line tools come to mind...
53
- * native ffmpeg/avconv/gst support in dtas-player
54
55
  * better error handling, many bugfixes, etc...
55
56
  * better documentation
56
57
 
data/Rakefile CHANGED
@@ -59,9 +59,20 @@ h = Hoe.spec('dtas') do |p|
59
59
  end
60
60
 
61
61
  task :rsync_docs do
62
- dest = "80x24.org:/srv/dtas/"
63
- system("rsync --chmod=Fugo=r --files-from=.rsync_doc -av ./ #{dest}")
64
- system("rsync --chmod=Fugo=r -av ./Documentation/*.txt #{dest}")
62
+ dest = ENV["RSYNC_DEST"] || "80x24.org:/srv/dtas/"
63
+ top = %w(INSTALL NEWS README COPYING)
64
+ files = []
65
+ Dir['Documentation/*.txt'].to_a.concat(top).each do |txt|
66
+ gz = "#{txt}.gz"
67
+ tmp = "#{gz}.#$$"
68
+ sh("gzip -9 < #{txt} > #{tmp}")
69
+ st = File.stat(txt)
70
+ File.utime(st.atime, st.mtime, tmp) # make nginx gzip_static happy
71
+ File.rename(tmp, gz)
72
+ files << txt
73
+ files << gz
74
+ end
75
+ sh("rsync --chmod=Fugo=r -av #{files.join(' ')} #{dest}")
65
76
  end
66
77
 
67
78
  task :coverage do
@@ -81,8 +92,7 @@ task tarball: "pkg/#{base}" do
81
92
  Dir.chdir("pkg") do
82
93
  tgz = "#{base}.tar.gz"
83
94
  tmp = "#{tmp}.#$$"
84
- cmd = "tar cf - #{base} | gzip -9 > #{tmp}"
85
- system(cmd) or abort "#{cmd}: #$?"
95
+ sh "tar cf - #{base} | gzip -9 > #{tmp}"
86
96
  File.rename(tmp, tgz)
87
97
  end
88
98
  end
@@ -90,8 +100,7 @@ end
90
100
  task dist: [ :tarball, :package ] do
91
101
  Dir.chdir("pkg") do
92
102
  %w(dtas-linux dtas-mpris).each do |gem|
93
- cmd = "gem build ../#{gem}.gemspec"
94
- system(cmd) or abort "#{cmd}: #$?"
103
+ sh "gem build ../#{gem}.gemspec"
95
104
  end
96
105
  end
97
106
  end
@@ -5,6 +5,7 @@
5
5
  #
6
6
  # Note: no idea what I'm doing, especially w.r.t. curses
7
7
  require 'dtas/unix_client'
8
+ require 'dtas/rg_state'
8
9
  require 'curses'
9
10
  require 'yaml'
10
11
 
@@ -14,6 +15,14 @@ c = DTAS::UNIXClient.new
14
15
  cur = YAML.load(c.req('current'))
15
16
  readable = [ w, $stdin ]
16
17
 
18
+ # current rg mode
19
+ rg_mode = DTAS::RGState::RG_MODE.keys.unshift("off")
20
+ if (rg = cur["rg"]) && (rg = rg["mode"])
21
+ rg_mode_i = rg_mode.index(cur["rg"]["mode"])
22
+ else
23
+ rg_mode_i = 0
24
+ end
25
+
17
26
  def update_tfmt(prec)
18
27
  prec == 0 ? '%H:%M:%S' : "%H:%M:%S.%#{prec}N"
19
28
  end
@@ -69,6 +78,24 @@ def show_events(lineno, screen, events)
69
78
  end
70
79
  end
71
80
 
81
+ def fmt_to_s(f)
82
+ r = [ f['rate'], f['channels'], f['type'], f['bits'] ]
83
+ r.compact!
84
+ r.join(',')
85
+ end
86
+
87
+ def rg_string(rg, current)
88
+ rv = "rg mode=#{rg['mode']||'off'}"
89
+ defaults = DTAS::RGState::RG_DEFAULT
90
+ # don't show things that are too rare
91
+ %w(preamp fallback_gain).each do |param|
92
+ val = rg[param] || defaults[param]
93
+ rv << " #{param}=#{val}"
94
+ end
95
+ env = current && current["env"] and rv << " / RGFX='#{env['RGFX']}'"
96
+ rv
97
+ end
98
+
72
99
  begin
73
100
  Curses.init_screen
74
101
  Curses.nonl
@@ -79,6 +106,7 @@ begin
79
106
  screen.keypad(true)
80
107
  loop do
81
108
  lineno = -1
109
+ pfmt = cur['format']
82
110
  if current = cur['current']
83
111
  Curses.setpos(lineno += 1, 0)
84
112
  Curses.clrtoeol
@@ -89,13 +117,16 @@ begin
89
117
  rate = current_format['rate'].to_f
90
118
  elapsed += nr / rate
91
119
  total = " [#{Time.at(current['samples'] / rate).strftime(tfmt)}]"
120
+ fmt = "(#{fmt_to_s(current_format)} > #{fmt_to_s(pfmt)})"
92
121
  else
93
122
  total = ""
123
+ fmt = fmt_to_s(pfmt)
124
+ fmt = "(#{fmt} > #{fmt})"
94
125
  end
95
126
 
96
127
  Curses.setpos(lineno += 1, 0)
97
128
  Curses.clrtoeol
98
- Curses.addstr("#{Time.at(elapsed).strftime(tfmt)}#{total}")
129
+ Curses.addstr("#{Time.at(elapsed).strftime(tfmt)}#{total} #{fmt}")
99
130
  else
100
131
  Curses.setpos(lineno += 1, 0)
101
132
  Curses.clrtoeol
@@ -104,6 +135,11 @@ begin
104
135
  Curses.clrtoeol
105
136
  end
106
137
 
138
+ rgs = rg_string(cur["rg"] || {}, current)
139
+ Curses.setpos(lineno += 1, 0)
140
+ Curses.clrtoeol
141
+ Curses.addstr(rgs)
142
+
107
143
  show_events(lineno, screen, events)
108
144
 
109
145
  Curses.refresh # draw and wait
@@ -119,8 +155,8 @@ begin
119
155
  when $stdin
120
156
  # keybindings taken from mplayer / vi
121
157
  case key = Curses.getch
122
- when "j" then c.req_ok("seek +5")
123
- when "k" then c.req_ok("seek -5")
158
+ when "j" then c.req_ok("seek -5")
159
+ when "k" then c.req_ok("seek +5")
124
160
  when Curses::KEY_DOWN then c.req_ok("seek -60")
125
161
  when Curses::KEY_UP then c.req_ok("seek +60")
126
162
  when Curses::KEY_LEFT then c.req_ok("seek -10")
@@ -129,8 +165,17 @@ begin
129
165
  # yes, some of us have long audio files
130
166
  when Curses::KEY_PPAGE then c.req_ok("seek +600")
131
167
  when Curses::KEY_NPAGE then c.req_ok("seek -600")
168
+ when "9" then c.req_ok("rg preamp-=1")
169
+ when "0" then c.req_ok("rg preamp+=1")
170
+ when "F" then c.req_ok("rg fallback_gain+=1")
171
+ when "f" then c.req_ok("rg fallback_gain-=1")
132
172
  when " "
133
173
  c.req("play_pause")
174
+ when "r" # cycle through replaygain modes
175
+ rg_mode_i >= 1 and c.req_ok("rg mode=#{rg_mode[rg_mode_i -= 1]}")
176
+ when "R"
177
+ rg_mode_i < (rg_mode.size - 1) and
178
+ c.req_ok("rg mode=#{rg_mode[rg_mode_i += 1]}")
134
179
  when "p" # lower precision of time display
135
180
  if prec_nr >= 1
136
181
  prec_nr -= 1
@@ -5,7 +5,7 @@
5
5
  require 'tempfile'
6
6
  require 'shellwords'
7
7
  usage = "Usage: #$0 FILENAME"
8
- editor = ENV["VISUAL"] || ENV["EDITOR"]
8
+ editor = ENV["VISUAL"] || ENV["EDITOR"] || "vi"
9
9
  ARGV.size > 0 or abort usage
10
10
 
11
11
  def err_msg(cmd, status)
@@ -2,20 +2,16 @@
2
2
  # -*- encoding: binary -*-
3
3
  # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
4
4
  # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
5
- require 'dtas/unix_client'
6
- require 'dtas/disclaimer'
7
- require 'tempfile'
8
- require 'yaml'
9
- editor = ENV["VISUAL"] || ENV["EDITOR"]
10
- c = DTAS::UNIXClient.new
11
- usage = "#$0 SINKNAME"
5
+ require 'dtas/edit_client'
6
+ include DTAS::EditClient
7
+ c = client_socket
8
+ sinks = c.req('sink ls') || "(unknown)"
9
+ usage = "Usage: #{DTAS_PROGNAME} SINKNAME\n" \
10
+ "available SINKNAME values: #{sinks}"
12
11
  ARGV.size == 1 or abort usage
13
12
  name = ARGV[0]
14
13
 
15
- tmp = Tempfile.new(%w(dtas-sinkedit .yml))
16
- tmp.sync = true
17
- tmp.binmode
18
-
14
+ tmp = tmpyaml
19
15
  buf = c.req(%W(sink cat #{name}))
20
16
  abort(buf) if buf =~ /\AERR/
21
17
  orig = YAML.load(buf)
@@ -27,32 +23,20 @@ tmp.rewind
27
23
  sink = YAML.load(tmp.read)
28
24
 
29
25
  cmd = %W(sink ed #{name})
30
- if env = sink["env"]
31
- env.each do |k,v|
32
- cmd << (v.nil? ? "env##{k}" : "env.#{k}=#{v}")
33
- end
34
- end
35
-
36
- # remove deleted env
37
- if orig_env = orig["env"]
38
- env ||= {}
39
- deleted_keys = orig_env.keys - env.keys
40
- deleted_keys.each { |k| cmd << "env##{k}" }
41
- end
26
+ update_cmd_env(cmd, orig, sink)
42
27
 
28
+ # both of these default to false
43
29
  %w(nonblock active).each do |field|
44
- if sink.key?(field)
45
- cmd << "#{field}=#{sink[field] ? 'true' : 'false'}"
46
- end
30
+ cmd << "#{field}=#{sink[field] ? 'true' : 'false'}"
47
31
  end
48
32
 
49
33
  %w(prio pipe_size).each do |field|
50
34
  value = sink[field] and cmd << "#{field}=#{value}"
51
35
  end
52
36
 
37
+ # nil OK
53
38
  %w(command).each do |field|
54
- value = sink[field]
55
- cmd << "#{field}=#{value}"
39
+ cmd << "#{field}=#{sink[field]}"
56
40
  end
57
41
 
58
42
  c.req_ok(cmd)
@@ -2,21 +2,17 @@
2
2
  # -*- encoding: binary -*-
3
3
  # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
4
4
  # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
5
- require 'tempfile'
6
- require 'yaml'
7
- require 'dtas/unix_client'
8
- require 'dtas/disclaimer'
9
- editor = ENV["VISUAL"] || ENV["EDITOR"]
10
- c = DTAS::UNIXClient.new
11
- usage = $0
12
- ARGV.size == 0 or abort usage
13
- name = ARGV[0]
5
+ require 'dtas/edit_client'
6
+ include DTAS::EditClient
7
+ c = client_socket
8
+ sources = c.req('source ls') || "(unknown)"
9
+ usage = "Usage: #{DTAS_PROGNAME} SOURCENAME\n" \
10
+ "available SOURCENAME values: #{sources}"
11
+ ARGV.size <= 1 or abort usage
12
+ name = ARGV[0] || "sox"
14
13
 
15
- tmp = Tempfile.new(%w(dtas-sourceedit .yml))
16
- tmp.sync = true
17
- tmp.binmode
18
-
19
- buf = c.req(%W(source cat))
14
+ tmp = tmpyaml
15
+ buf = c.req(%W(source cat #{name}))
20
16
  abort(buf) if buf =~ /\AERR/
21
17
  orig = YAML.load(buf)
22
18
 
@@ -26,23 +22,12 @@ system(cmd) or abort "#{cmd} failed: #$?"
26
22
  tmp.rewind
27
23
  source = YAML.load(tmp.read)
28
24
 
29
- cmd = %W(source ed)
30
- if env = source["env"]
31
- env.each do |k,v|
32
- cmd << (v.nil? ? "env##{k}" : "env.#{k}=#{v}")
33
- end
34
- end
35
-
36
- # remove deleted env
37
- if orig_env = orig["env"]
38
- env ||= {}
39
- deleted_keys = orig_env.keys - env.keys
40
- deleted_keys.each { |k| cmd << "env##{k}" }
41
- end
25
+ cmd = %W(source ed #{name})
26
+ update_cmd_env(cmd, orig, source)
42
27
 
43
- %w(command).each do |field|
44
- value = source[field]
45
- cmd << "#{field}=#{value}"
28
+ # nil OK
29
+ %w(tryorder command).each do |field|
30
+ cmd << "#{field}=#{source[field]}"
46
31
  end
47
32
 
48
33
  c.req_ok(cmd)