rubyyabt 0.0.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -36,6 +36,7 @@ OptionParser.new do |opts|
36
36
  opts.on("-g", "--gpg-key KEY", "Use KEY as GPG key for encryption and signing") { |v| options[:gpg_key] = v }
37
37
  opts.on("-h", "--gpg-pass PASS", "Password for GPG key") { |v| options[:gpg_pass] = v }
38
38
  opts.on("-H", "--gpg-passfile FILE", "Read password for GPG key from FILE") { |v| options[:gpg_pass] = File.read(v) }
39
+ opts.on("-l", "--log-file FILE", "Write log to FILE") { |v| options[:logfile] = v.to_s }
39
40
  opts.on("-c", "--cache-max-age DAYS", "Allow maximum of DAYS time for a positive cache hit. False hits are never cached.") { |v| options[:cache_max_age] = v.to_i *(24*60*60) }
40
41
  opts.on("", "--http-timeout SECS", "Require all http requests to complete within SECS seconds.") { |v| options[:http_timeout] = v.to_i }
41
42
  opts.on("-C", "--empty-cache", "Start with an empty cache instead of restoring the cache from saved data") { options[:empty_cache] = true }
@@ -32,20 +32,20 @@ class Backup
32
32
  @time = Time.now
33
33
  @time.utc
34
34
  @name = @time.strftime("%Y%m%dT%H%M%S") unless @name
35
- @cui.message("Initialized backup #{@name}")
35
+ @cui.message("Initialized backup #{@name}", true, true)
36
36
  end
37
37
 
38
38
  def backup!()
39
39
  $errors = 0 if not $errors
40
- @cui.message("Scanning for files...")
40
+ @cui.message("Scanning for files...", true, true)
41
41
  @cui.start
42
- @cui.message("Found #{@source.files.count.to_s} files and directories")
42
+ @cui.message("Found #{@source.files.count.to_s} files and directories", true, true)
43
43
  backup_meta = Array.new
44
44
  backup_meta << '[Backup]'
45
45
  cache_timer = Time.now
46
46
  @source.files.each { | f |
47
47
  if (Time.now - cache_timer) > 900 then
48
- @cui.message("Uploading cache data...")
48
+ @cui.message("Uploading cache data...", true, true)
49
49
  @target.upload_cache
50
50
  cache_timer = Time.now
51
51
  end
@@ -65,19 +65,19 @@ class Backup
65
65
  $errors += 1
66
66
  end
67
67
  }
68
- @cui.message("Waiting for last chunk to finish uploading...")
68
+ @cui.message("Waiting for last chunk to finish uploading...", true, true)
69
69
  @cui.error("DEBUG[#{Thread.current.inspect}]: Joining chunk threads") if $myDEBUG
70
70
  chunk = Chunk.new
71
71
  chunk.join
72
72
  @cui.stop
73
73
  @cui.update
74
- @cui.message("Finished backing up data. Now uploading metadata for backup #{@name}.")
74
+ @cui.message("Finished backing up data. Now uploading metadata for backup #{@name}.", true, true)
75
75
  backup_meta = backup_meta.join("\n")
76
76
  @cui.error("DEBUG[#{Thread.current.inspect}]: Writing backup info") if $myDEBUG
77
77
  @target.write(:backup, @name, backup_meta)
78
- @cui.message("Uploading cache data...")
78
+ @cui.message("Uploading cache data...", true, true)
79
79
  @target.upload_cache
80
- @cui.error("Errors: A total of #{$errors} errors have been encountered.") if $errors > 0
80
+ @cui.error("Errors: A total of #{$errors} errors have been encountered.", true, true) if $errors > 0
81
81
  return $errors
82
82
  end
83
83
 
@@ -10,8 +10,6 @@ require 'classes/Target'
10
10
  #noinspection RubyResolve
11
11
  require 'classes/cui'
12
12
 
13
- $myDEBUG = false unless $myDEBUG
14
-
15
13
  class Chunk
16
14
  attr_reader :data, :length
17
15
  @@upload_mutex = Mutex.new
@@ -27,16 +25,12 @@ class Chunk
27
25
  end
28
26
 
29
27
  def join()
30
- @cui.error("DEBUG[#{Thread.current.inspect}]: Joining chunk thread") if $myDEBUG
31
28
  @@upload_thread.join if @@upload_thread
32
29
  end
33
30
 
34
31
  def set_data(data)
35
- @cui.error("DEBUG[#{Thread.current.inspect}: Entered chunk set_data") if $myDEBUG
36
32
  @data = data
37
- @cui.error("DEBUG[#{Thread.current.inspect}: Set data") if $myDEBUG
38
33
  @length = @data.length
39
- @cui.error("DEBUG[#{Thread.current.inspect}: Calculated length") if $myDEBUG
40
34
  @md5 = nil
41
35
  @md5 = Digest::MD5.hexdigest(@data)
42
36
  @sha256 = nil
@@ -64,26 +58,20 @@ class Chunk
64
58
  end
65
59
 
66
60
  def backup!()
67
- @cui.error("DEBUG[#{Thread.current.inspect}]: Locking chunk upload mutex in thread #{Thread.current.inspect}") if $myDEBUG
68
61
  @@upload_mutex.synchronize {
69
- @cui.error("DEBUG[#{Thread.current.inspect}]: Got lock of chunk upload mutex in thread #{Thread.current.inspect}") if $myDEBUG
70
- @cui.error("DEBUG[#{Thread.current.inspect}]: Trying to join earlier upload threads if any (@@uploadThread: #{@@upload_thread.inspect})") if $myDEBUG
71
62
  @@upload_thread.join if @@upload_thread
72
63
  @@upload_thread = Thread.new {
73
64
  target = Target.instance # Get a target object, then ...
74
- @cui.error("DEBUG[#{Thread.current.inspect}]: Checking if target chunk exists (#{sha256}, #{md5})") if $myDEBUG
75
65
  if not target.exists?(:chunk, "#{sha256}.#{md5}") then
76
- @cui.error("Uploading chunk #{sha256}.#{md5}...") if $myDEBUG
77
66
  target.write(:chunk, "#{sha256}.#{md5}", @data)
67
+ @cui.message("Chunk #{sha256}.#{md5} (#{@data.length} bytes) uploaded.", true, false)
78
68
  end
79
- @cui.error("DEBUG[#{Thread.current.inspect}]: Upload chunk thread finished (thread id: #{Thread.current.inspect})") if $myDEBUG
80
69
  }
81
- @cui.error("DEBUG[#{Thread.current.inspect}]: New upload thread: @@upload_thread = #{@@upload_thread.inspect}") if $myDEBUG
82
70
  }
83
71
  end
84
72
 
85
73
  def restore!()
86
- @cui.message("Downloading chunk #{sha256}.#{md5}...")
74
+ @cui.message("Downloading chunk #{sha256}.#{md5}...", true, true)
87
75
  target = Target.instance
88
76
  @data = target.read(:chunk, "#{sha256}.#{md5}")
89
77
  @length = @data.length
@@ -92,9 +80,9 @@ class Chunk
92
80
  @md5 = nil
93
81
  @sha256 = nil
94
82
  if (expected_md5 == md5) and (expected_sha256 == sha256) then
95
- @cui.message("chunk verified")
83
+ @cui.message("Chunk verified", true, true)
96
84
  else
97
- @cui.message("checksum error in chunk #{sha256}.#{md5}")
85
+ @cui.error("checksum error in chunk #{sha256}.#{md5}", true, true)
98
86
  raise 'checksum error'
99
87
  end
100
88
  end
@@ -8,8 +8,6 @@ require 'thread'
8
8
  #noinspection RubyResolve
9
9
  require 'classes/proxy_http_cache_hash'
10
10
 
11
- $myDEBUG = false if not $myDEBUG
12
- $myVERBOSE = false if not $myVERBOSE
13
11
  $options = nil unless $options
14
12
 
15
13
  class ProxyHTTP
@@ -60,48 +58,43 @@ class ProxyHTTP
60
58
  @cui.error("Cache is not compatible. Starting with an empty cache.")
61
59
  @cache = ProxyHTTPCache_Hash.new
62
60
  end
61
+ @cui.message("Successfully restored cache with a size of #{cache_data.length} bytes.", true, true)
63
62
  rescue Exception # Just start out with an empty cache
64
63
  @cache = ProxyHTTPCache_Hash.new
64
+ @cui.message("Unable to restore cache. Starting with an empty cache.", true, true)
65
65
  end
66
66
 
67
67
  end
68
68
 
69
69
  #noinspection RubyScope
70
70
  def request(http_request, timeout = 300, tries = 20)
71
- @cui.error("DEBUG[#{Thread.current.inspect}]: Trying to lock ProxyHTTP request mutex in thread #{Thread.current.inspect}") if $myDEBUG
72
71
  @mutex.synchronize {
73
- @cui.error("DEBUG[#{Thread.current.inspect}]: Got ProxyHTTP request mutex in thread #{Thread.current.inspect}") if $myDEBUG
74
72
  response = ''
75
73
  while tries > 0 do
76
- @cui.error("DEBUG[#{Thread.current.inspect}]: Got #{tries} tries left for request #{http_request.inspect} #{http_request.path}, timeout #{timeout}") if $myDEBUG
77
74
  begin
78
75
  Timeout::timeout(timeout) {
79
- @cui.error("DEBUG[#{Thread.current.inspect}]: Sending request #{http_request.inspect} for #{http_request.path}....") if $myDEBUG
80
76
  response = @http.request(http_request)
81
77
  }
82
78
  rescue Timeout::Error
83
79
  tries -= 1
84
- @cui.error("Timeout during request. #{tries} tries left")
80
+ @cui.error("Timeout during request. #{tries} tries left", true, true)
85
81
  retry if tries > 0
86
82
  raise
87
83
  rescue Exception => ex
88
- @cui.error("Caught exception #{ex} with message #{ex.message} during request. #{tries} tries left") if $myVERBOSE
84
+ @cui.error("Caught exception #{ex} with message #{ex.message} during request. #{tries} tries left", true, false)
89
85
  tries -= 1
90
86
  retry if tries > 0
91
87
  raise
92
88
  end
93
89
  code = response.code.to_i
94
- @cui.error("DEBUG[#{Thread.current.inspect}]: Response code: #{code}") if $myDEBUG
95
90
  return response if (code >= 200) and (code < 300)
96
91
  if (code >= 400) and (code < 500) # No retries for these errors...
97
92
  # Yeehaw! we need an exception for code 401 since humyo sometimes replies with it with correct authorization
98
93
  tries = 0 if (code != 401)
99
94
  end
100
95
  tries -= 1
101
- @cui.error("DEBUG[#{Thread.current.inspect}]: #{tries} tries left for this request. Retrying after sleep if > 0") if $myDEBUG
102
96
  sleep(rand) if tries > 0 # Sleep up to one second before retrying
103
97
  end
104
- @cui.error("DEBUG[#{Thread.current.inspect}]: Raising exception for failed request: #{response.code} #{response.message}") if $myDEBUG
105
98
  raise 'HTTP request failed: ' + response.code + ' ' + response.message
106
99
  }
107
100
  end
@@ -118,7 +111,6 @@ class ProxyHTTP
118
111
  get = Net::HTTP::Get.new(uri.request_uri)
119
112
  get.initialize_http_header({"User-Agent" => $options[:user_agent]})
120
113
  get.basic_auth($options[:username], $options[:password])
121
- @cui.error("DEBUG[#{Thread.current.inspect}]: Reading from #{uri.to_s}") if $myDEBUG
122
114
  begin
123
115
  response = request(get, $options[:http_timeout])
124
116
  rescue Exception => ex
@@ -142,22 +134,19 @@ class ProxyHTTP
142
134
  put.initialize_http_header({"User-Agent" => $options[:user_agent], "Content-Type" => "application/octet-stream"})
143
135
  put.basic_auth($options[:username], $options[:password])
144
136
  put.body = data
145
- @cui.error("DEBUG[#{Thread.current.inspect}]: Writing to #{uri.to_s}") if $myDEBUG
146
137
  begin
147
138
  response = request(put, 180)
148
139
  rescue RuntimeError => ex
149
140
  if ex.message == "HTTP request failed: 409 Conflict" then
150
- @cui.error("DEBUG[#{Thread.current.inspect}]: Caught a 409 Conflict error") if $myDEBUG
151
141
  if (subdir_type != type) then
152
142
  begin
153
- @cui.error("DEBUG[#{Thread.current.inspect}]: Trying to create the directory #{@url.merge(@types[type] + '/').to_s}") if $myDEBUG
154
143
  mkdir(@url.merge(@types[type] + '/'))
155
144
  rescue RuntimeError => rt
156
145
  @cui.error("DEBUG[#{Thread.current.inspect}]: Caught exception during mkdir: #{rt.message}") if $myDEBUG
157
146
  raise if rt.message[0..23] != "HTTP request failed: 405"
158
147
  end
159
148
  end
160
- @cui.error("DEBUG[#{Thread.current.inspect}]: Trying to create the directory #{@url.merge(@types[type] + '/').merge(subdir).to_s}") if $myDEBUG
149
+ @cui.message("Trying to create the directory #{@url.merge(@types[type] + '/').merge(subdir).to_s}", true, false)
161
150
  mkdir(@url.merge(@types[type] + '/').merge(subdir))
162
151
  end
163
152
  retry
@@ -166,7 +155,6 @@ class ProxyHTTP
166
155
  raise
167
156
  else
168
157
  @cache.add(type, file) if @@valid_caches.include?(type)
169
- @cui.error("DEBUG[#{Thread.current.inspect}]: Added #{file} to cache list for #{subdir_type}") if $myDEBUG
170
158
  return true
171
159
  end
172
160
  end
@@ -175,7 +163,7 @@ class ProxyHTTP
175
163
  mkcol = Net::HTTP::Mkcol.new(uri.request_uri)
176
164
  mkcol.initialize_http_header({"User-Agent" => $options[:user_agent]})
177
165
  mkcol.basic_auth($options[:username], $options[:password])
178
- @cui.error("DEBUG[#{Thread.current.inspect}]: Trying to create directory #{uri.to_s}") if $myDEBUG
166
+ @cui.message("Trying to create directory #{uri.to_s}", true, false)
179
167
  begin
180
168
  request(mkcol)
181
169
  rescue Exception => ex
@@ -11,8 +11,6 @@ require 'digest/md5'
11
11
  require 'digest/sha2'
12
12
  require 'time'
13
13
 
14
- $myDEBUG = false if not $myDEBUG
15
- $myVERBOSE = false if not $myVERBOSE
16
14
  $options = nil unless $options
17
15
 
18
16
  class SMGFile
@@ -69,33 +67,23 @@ class SMGFile
69
67
  size = 0
70
68
  md5hashing = Digest::MD5.new
71
69
  sha256hashing = Digest::SHA2.new(256)
72
- @cui.message("DEBUG[#{Thread.current.inspect}: Created hashing objects") if $myDEBUG
73
70
  chunks = ''
71
+ gc_counter = -1 # Initialize a counter for the garbage collection
74
72
  while !fd.eof?
75
- @cui.message("DEBUG[#{Thread.current.inspect}: Checked for EOF") if $myDEBUG
73
+ GC.start if ((gc_counter += 1) % 30) == 0 # Run the garbage collection every now and then
76
74
  chunk = Chunk.new
77
- @cui.message("DEBUG[#{Thread.current.inspect}: Created new chunk") if $myDEBUG
78
75
  data = fd.read($options[:chunk_size])
79
- @cui.message("DEBUG[#{Thread.current.inspect}: Read data") if $myDEBUG
80
76
  chunk.set_data(data)
81
- @cui.message("DEBUG[#{Thread.current.inspect}: Saved data in chunk") if $myDEBUG
82
77
  md5hashing << data
83
- @cui.message("DEBUG[#{Thread.current.inspect}: hashed for MD5") if $myDEBUG
84
78
  sha256hashing << data
85
- @cui.message("DEBUG[#{Thread.current.inspect}: hashed for sha256") if $myDEBUG
86
79
  size += data.length
87
- @cui.message("DEBUG[#{Thread.current.inspect}: Updated read size") if $myDEBUG
88
80
  chunk.backup! # Upload this chunk
89
- @cui.message("DEBUG[#{Thread.current.inspect}: backed up chunk") if $myDEBUG
90
81
  chunks += chunk.sha256 + '.' + chunk.md5 + "\n"
91
82
  @cui.finished_size_add(data.length)
92
83
  end
93
84
  fd.close()
94
- @cui.message("DEBUG[#{Thread.current.inspect}: Closed FD") if $myDEBUG
95
85
  md5 = md5hashing.hexdigest
96
- @cui.message("DEBUG[#{Thread.current.inspect}: Got MD5") if $myDEBUG
97
86
  sha256 = sha256hashing.hexdigest
98
- @cui.message("DEBUG[#{Thread.current.inspect}: Got sha256") if $myDEBUG
99
87
  # Build the metadata file
100
88
  @metadata = "[stat]\n"
101
89
  @metadata += "filename = " + @filename + "\n"
@@ -112,14 +100,12 @@ class SMGFile
112
100
  @metadata += "\n"
113
101
  @metadata += "[chunks]\n"
114
102
  @metadata += chunks
115
- @cui.message("Size mismatch: expected #{expected_size} but found #{size}!") if expected_size != size
103
+ @cui.message("Size mismatch: expected #{expected_size} but found #{size}!", true, true) if expected_size != size
116
104
  @size = size
117
105
  @mtime = mtime
118
- @cui.message("DEBUG[#{Thread.current.inspect}: Checking if file already exists on server") if $myDEBUG
119
106
  return true if @target.exists?(:file, "#{meta_sha256}.#{meta_md5}") # if this chunk already exists, no need to upload it
120
- @cui.message("Uploading file metadata #{@filename}: #{meta_sha256}.#{meta_md5}...") if $myDEBUG
107
+ @cui.message("Uploading file metadata #{@filename}: #{meta_sha256}.#{meta_md5}...", true, true)
121
108
  @target.write(:file, "#{meta_sha256}.#{meta_md5}", @metadata)
122
- @cui.message("DEBUG[#{Thread.current.inspect}: Uploaded file metadata") if $myDEBUG
123
109
  end
124
110
 
125
111
  def backup_dir()
@@ -141,12 +127,11 @@ class SMGFile
141
127
  @size = 0
142
128
  @mtime = mtime
143
129
  return true if @target.exists?(:file, "#{meta_sha256}.#{meta_md5}") # if this chunk already exists, no need to upload it
144
- @cui.message("Uploading directory metadata #{@filename}: #{meta_sha256}.#{meta_md5}...") if $myVERBOSE
130
+ @cui.message("Uploading directory metadata #{@filename}: #{meta_sha256}.#{meta_md5}...", true, true)
145
131
  @target.write(:file, "#{meta_sha256}.#{meta_md5}", @metadata)
146
132
  end
147
133
 
148
134
  def backup_link()
149
- @cui.message("Backing up link #{@filename}") if $myVERBOSE
150
135
  stat = @source.lstat(@filename)
151
136
  ftype = stat.ftype
152
137
  mtime = stat.mtime.utc.rfc2822
@@ -164,7 +149,7 @@ class SMGFile
164
149
  @size = 0
165
150
  @mtime = mtime
166
151
  return true if @target.exists?(:file, "#{meta_sha256}.#{meta_md5}") # if this chunk already exists, no need to upload it
167
- @cui.message("Uploading file metadata #{@filename}: #{meta_sha256}.#{meta_md5}...") if $myVERBOSE
152
+ @cui.message("Uploading file metadata #{@filename}: #{meta_sha256}.#{meta_md5}...", true, true)
168
153
  @target.write(:file, "#{meta_sha256}.#{meta_md5}", @metadata)
169
154
  end
170
155
 
@@ -218,9 +203,9 @@ class SMGFile
218
203
  md5 = md5hashing.hexdigest
219
204
  sha256 = sha256hashing.hexdigest
220
205
  if (@md5 = md5) and (@sha256 = sha256) then
221
- @cui.message("File verified")
206
+ @cui.message("File verified", true, true)
222
207
  else
223
- @cui.error("Checksum error in file #{@filename}: #{sha256}.#{md5}")
208
+ @cui.error("Checksum error in file #{@filename}: #{sha256}.#{md5}", true, true)
224
209
  end
225
210
  end
226
211
 
@@ -43,16 +43,15 @@ class Target
43
43
  else
44
44
  raise "No idea how to connect to #{$options[:target]}"
45
45
  end
46
- @cui.message("Initialized proxy as #{@proxy.class}.") if $myVERBOSE
46
+ @cui.message("Initialized proxy as #{@proxy.class}.", true, true)
47
47
  begin
48
48
  if @proxy.caching? then
49
49
  if not $options[:empty_cache] then
50
- @cui.message("Trying to restore cache...") if $myVERBOSE
50
+ @cui.message('Trying to restore cache...', true, false)
51
51
  compressed_cache_data = read(:cache, "flist")
52
52
  begin
53
53
  # Check if the data is compressed and uncompress the data
54
54
  cache_data = Zlib::Inflate.inflate(compressed_cache_data)
55
- @cui.message("Uncompressed cache data from #{compressed_cache_data.length} bytes to #{cache_data.length} bytes.")
56
55
  rescue Exception
57
56
  # Uncompress failed, so the data was probably uncompressed from the beginning.
58
57
  cache_data = compressed_cache_data
@@ -102,15 +101,15 @@ class Target
102
101
  rescue Exception
103
102
  # Seems like compression does not work. We'll just use the uncompressed data then.
104
103
  compressed_cache_data = cache_data
105
- @cui.message('Unable to compress the cache data for upload. Using uncompressed data.')
104
+ @cui.message('Unable to compress the cache data for upload. Using uncompressed data.', true, false)
106
105
  end
107
- @cui.error("DEBUG[#{Thread.current.inspect}]: Cache has a length of #{compressed_cache_data.length}. Uploading...") if $myDEBUG
106
+ @cui.message("Cache has a length of #{compressed_cache_data.length}. Uploading...", true, false)
108
107
  write(:cache, 'flist', compressed_cache_data)
109
108
  end
110
109
  end
111
110
 
112
111
  def export_key()
113
- @cui.error("DEBUG[#{Thread.current.inspect}]: Exporting keys...") if $myDEBUG
112
+ @cui.message("Exporting keys...", true, true)
114
113
  IO.popen('"' + $options[:gpg_bin] + '" ' + '--export-ownertrust', 'w+') do | gpg |
115
114
  gpg.close_write()
116
115
  trust = gpg.read()
@@ -2,6 +2,7 @@ require 'singleton'
2
2
  #noinspection RubyResolve
3
3
  require 'thread'
4
4
 
5
+ $options = nil unless $options
5
6
 
6
7
  # Usage of stdout and stderr:
7
8
  # stdout will be used for normal status messages like the status bar.
@@ -29,6 +30,7 @@ class Cui
29
30
  @changed = false
30
31
  @active = false
31
32
  @exists = true
33
+ @logfile = nil
32
34
  end
33
35
  def exists(e)
34
36
  @exists = e
@@ -66,6 +68,7 @@ class Cui
66
68
  @last_file_name = @current_file_name
67
69
  @current_file_name = file.to_str
68
70
  update
71
+ write_log("File: #{file.to_s} (#{@finished_files + 1}/#{@total_files}; #{(@finished_size/1024/1024).to_i.to_s} MB/#{(@total_size/1024/1024).to_i.to_s} MB)")
69
72
  }
70
73
  end
71
74
  def start()
@@ -108,34 +111,40 @@ class Cui
108
111
  $stdout.print(backspace + line)
109
112
  @changed = false
110
113
  end
111
- def message(text)
114
+ def message(text, logging = false, write_stdout = true)
112
115
  @mutex.synchronize {
113
- case @active
114
- when true
115
- backspace = ("\b" * @last_length)
116
- text += " " * (@last_length - text.length) if (@last_length - text.length) > 0
117
- @last_length = 0
118
- $stdout.print(backspace + text + "\n")
119
- update
120
- when false
121
- # If there's no regular update, we can just write out a new line
122
- $stdout.print("\n" + text + "\n")
116
+ write_log(text) if logging
117
+ if write_stdout then
118
+ case @active
119
+ when true
120
+ backspace = ("\b" * @last_length)
121
+ text += " " * (@last_length - text.length) if (@last_length - text.length) > 0
122
+ @last_length = 0
123
+ $stdout.print(backspace + text + "\n")
124
+ update
125
+ when false
126
+ # If there's no regular update, we can just write out a new line
127
+ $stdout.print("\n" + text + "\n")
128
+ end
123
129
  end
124
130
  }
125
131
  end
126
- def error(text)
132
+ def error(text, logging = true, write_stderr = true)
127
133
  @mutex.synchronize {
128
- case @active
129
- when true
130
- backspace = ("\b" * @last_length) # Calculate the amount of backspaces required
131
- spaces = ""
132
- spaces = " " * (@last_length - text.length) if (@last_length - text.length) > 0
133
- $stderr.print(backspace + text + spaces + "\n")
134
- @last_length = 0
135
- update
136
- when false
137
- # If there's no regular update, we can just write out a new line
138
- $stdout.print("\n" + text + "\n")
134
+ write_log(text) if logging
135
+ if write_stderr then
136
+ case @active
137
+ when true
138
+ backspace = ("\b" * @last_length) # Calculate the amount of backspaces required
139
+ spaces = ""
140
+ spaces = " " * (@last_length - text.length) if (@last_length - text.length) > 0
141
+ $stderr.print(backspace + text + spaces + "\n")
142
+ @last_length = 0
143
+ update
144
+ when false
145
+ # If there's no regular update, we can just write out a new line
146
+ $stdout.print("\n" + text + "\n")
147
+ end
139
148
  end
140
149
  }
141
150
  end
@@ -145,4 +154,12 @@ class Cui
145
154
  @update_thread.join
146
155
  @update_thread = nil
147
156
  end
157
+ def write_log(text)
158
+ return if $options[:logfile].nil?
159
+ @logfile = File.new($options[:logfile], "w:utf-8") if @logfile.nil?
160
+ date_time = Time.now.strftime('%Y-%m-%d %H:%M:%S')
161
+ text = date_time + " " + text + "\n"
162
+ @logfile.write(text)
163
+ @logfile.flush
164
+ end
148
165
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 0
8
- - 5
9
- version: 0.0.5
7
+ - 1
8
+ - 6
9
+ version: 0.1.6
10
10
  platform: ruby
11
11
  authors:
12
12
  - Daniel Frank