jota 0.9.0 → 0.9.1

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.
@@ -1,5 +1,6 @@
1
1
 
2
- # $Id: helper.rb 296 2011-01-31 10:39:18Z dz $
2
+ require 'objspace'
3
+ require 'time'
3
4
 
4
5
  def escape_from(str)
5
6
  str.gsub!(/\n(>*)From /,"\n\\1>From ")
@@ -30,7 +31,16 @@ def expand_filename(str, dirname, filename)
30
31
  end
31
32
 
32
33
  def expand_tabs(str)
33
- return str.gsub(/([^\t]{8})|([^\t]*)\t/n){[$+].pack("A8")}
34
+ # see http://www.rootr.net/rubyfaq-9.html, Sec. 9.17
35
+
36
+ # The pack-A version does not work, because A expects a binary/ASCII
37
+ # string, not a Unicode string
38
+
39
+ #return str.gsub(/([^\t]{8})|([^\t]*)\t/n){[$+].pack("A8")}
40
+
41
+ s = str.clone
42
+ while s.sub!(/(^[^\t]*)\t(\t*)/){$1+' '*(8-$1.size%8+8*$2.size)};end
43
+ return s
34
44
  end
35
45
 
36
46
  def debug_file
@@ -48,7 +58,7 @@ def debug_file
48
58
  print_verbose "logging to '#{$debug_filename}' "+
49
59
  "not possible: #{msg}, using stdout"
50
60
  end
51
-
61
+
52
62
  end
53
63
  return $debug_file
54
64
  end
@@ -58,7 +68,7 @@ def print_verbose(msg)
58
68
  # do not write to $debug_file, this can be nil
59
69
  debug_file.puts "#{Process.pid}-1# #{msg}"
60
70
  debug_file.flush
61
- end
71
+ end
62
72
  end
63
73
 
64
74
  def print_debug(msg)
@@ -66,7 +76,7 @@ def print_debug(msg)
66
76
  # do not write to $debug_file, this can be nil
67
77
  debug_file.puts "#{Process.pid}-2# #{msg}"
68
78
  debug_file.flush
69
- end
79
+ end
70
80
  end
71
81
 
72
82
  def editor(str)
@@ -98,3 +108,31 @@ def editor(str)
98
108
 
99
109
  return lines
100
110
  end
111
+
112
+ # for debugging only, normally not referenced
113
+
114
+ class Class
115
+ def <=>(x)
116
+ self.to_s <=> x.to_s
117
+ end
118
+ end
119
+
120
+ def dump_objectspace
121
+ GC.start
122
+ sleep 60
123
+
124
+ count = Hash.new 0
125
+ ObjectSpace.each_object do |o|
126
+ count[o.class] += 1
127
+ end
128
+
129
+ File.open "objectspace-#{Time.now.iso8601}","w" do |f|
130
+ f.puts "ObjectSpace.memsize_of_all = #{ObjectSpace.memsize_of_all.to_s}"
131
+ f.puts "ps -o rss = #{`ps -o rss= -p #{Process.pid}`.to_i} kB"
132
+ f.puts
133
+
134
+ count.keys.sort.each do |k|
135
+ f.puts "#{k} #{count[k]}"
136
+ end
137
+ end
138
+ end
@@ -1,6 +1,4 @@
1
1
 
2
- # $Id: $
3
-
4
2
  require 'clip_array'
5
3
  require 'version'
6
4
 
@@ -61,5 +59,4 @@ def list
61
59
  @data.close
62
60
  end
63
61
 
64
-
65
62
  end # class Immediate
@@ -1,8 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # $Id: jota.rb 296 2011-01-31 10:39:18Z dz $
4
-
5
-
6
3
  require 'getoptlong'
7
4
  require 'helper'
8
5
 
@@ -184,7 +181,7 @@ def start_curses
184
181
  curses = JotaCurses.new
185
182
 
186
183
  if @filename then
187
- open_or_fail(curses,@filename)
184
+ open_or_fail(curses,@filename)
188
185
  if @regexp then
189
186
  goto_or_fail(curses,@regexp)
190
187
  end
@@ -235,7 +232,7 @@ def start_gui
235
232
  msgbox.secondary_text = "I'm terribly sorry."+
236
233
  "\n\nDetails:\n#{e.class}: #{e.message}"
237
234
  msgbox.add_button("Crash",Gtk::Dialog::RESPONSE_OK)
238
-
235
+
239
236
  msgbox.run do | response |
240
237
  case response
241
238
  when Gtk::Dialog::RESPONSE_OK
@@ -285,7 +282,7 @@ def run
285
282
  start_immediate
286
283
  else
287
284
  begin
288
- # this 'require' must be after the command line is parsed,
285
+ # this 'require' must be after the command line is parsed,
289
286
  # because 'require libglade2' parses the commandline too.
290
287
  require 'gui'
291
288
  gui_ok = true
@@ -1,6 +1,4 @@
1
1
 
2
- # $Id: jota_curses.rb 296 2011-01-31 10:39:18Z dz $
3
-
4
2
  require "curses"
5
3
  require 'tempfile'
6
4
 
@@ -40,7 +38,7 @@ def set_terminal
40
38
  @columns = Curses.stdscr.maxx
41
39
  @lines = Curses.stdscr.maxy
42
40
  print_debug "setting terminal: lines=#{@lines}, columns=#{@columns}"
43
- end
41
+ end
44
42
 
45
43
  def initialize
46
44
  @data = nil
@@ -58,7 +56,7 @@ def initialize
58
56
  @editor_running = false
59
57
 
60
58
  default_focus
61
- end
59
+ end
62
60
 
63
61
  def finish
64
62
  Curses.close_screen
@@ -80,7 +78,7 @@ def open(filename)
80
78
  @data.current_index = 0
81
79
  @top_win_first = 0
82
80
  return @data
83
- end
81
+ end
84
82
 
85
83
  def clip_array
86
84
  return @data
@@ -101,7 +99,7 @@ def create(filename)
101
99
  @data.current_index = 0
102
100
  @top_win_first = 0
103
101
  draw_bars
104
- end
102
+ end
105
103
 
106
104
 
107
105
  def delete
@@ -121,7 +119,7 @@ def delete
121
119
  end
122
120
 
123
121
  @data.delete
124
- end
122
+ end
125
123
 
126
124
  def run
127
125
  Curses.init_screen
@@ -158,7 +156,7 @@ def run
158
156
  @bot_win_lines = draw_bottom_window
159
157
  handle_key
160
158
  end
161
- end
159
+ end
162
160
 
163
161
 
164
162
 
@@ -168,7 +166,7 @@ end
168
166
 
169
167
  def get_bot_win_height
170
168
  rows = @lines - @top_win_height - 3
171
-
169
+
172
170
  # some error checking if the terminal window is made too small
173
171
  if rows < 3 then
174
172
  # first try to adjust the windows
@@ -189,7 +187,7 @@ def get_bot_win_height
189
187
  else
190
188
  return rows
191
189
  end
192
- end
190
+ end
193
191
 
194
192
  private
195
193
 
@@ -232,7 +230,7 @@ def draw_bars
232
230
  @bot_bar.addstr("h=help, q=quit")
233
231
  end
234
232
  @bot_bar.refresh
235
- end
233
+ end
236
234
 
237
235
  def draw_top_window
238
236
  @top_win.clear
@@ -244,13 +242,13 @@ def draw_top_window
244
242
  marker = " "
245
243
  end
246
244
  if i < @data.length then
247
- @top_win.addstr("%s %s\n" %
245
+ @top_win.addstr("%s %s\n" %
248
246
  [marker, @data[i].title![@left_margin_top..@left_margin_top+@columns-4]])
249
247
  end
250
248
  end
251
249
  end
252
250
  @top_win.refresh
253
- end
251
+ end
254
252
 
255
253
  def draw_bottom_window
256
254
  print_debug "drawing text in #{@mode} mode"
@@ -285,7 +283,7 @@ def draw_bottom_window
285
283
  @bot_win.refresh
286
284
 
287
285
  return result
288
- end
286
+ end
289
287
 
290
288
  def help_text
291
289
  return <<-EOT
@@ -313,14 +311,14 @@ Jota #{Version::STRING}, (C) #{Version::YEARS} #{Version::AUTHOR} <#{Version::AU
313
311
  EDITOR #{ENV["EDITOR"]}
314
312
  SHELL #{ENV["SHELL"]}
315
313
  EOT
316
- end
314
+ end
317
315
 
318
316
  def redraw_all
319
317
  print_debug "redrawing"
320
318
  draw_bars
321
319
  draw_top_window
322
320
  @bot_win_lines = draw_bottom_window
323
- end
321
+ end
324
322
 
325
323
  def set_windows
326
324
  bot_win_height = get_bot_win_height
@@ -352,7 +350,7 @@ def set_windows
352
350
  @bot_bar.keypad = true
353
351
 
354
352
  redraw_all
355
- end
353
+ end
356
354
 
357
355
  def app_quit
358
356
  if @data then
@@ -365,7 +363,7 @@ def app_quit
365
363
  finish
366
364
  exit
367
365
  end
368
- end
366
+ end
369
367
 
370
368
  def call_editor(text)
371
369
  Curses.close_screen
@@ -387,7 +385,7 @@ def handle_key
387
385
  @bot_bar.move(0,79)
388
386
  @bot_bar.refresh
389
387
  # Ruby 1.8: getch return alway a Fixnum
390
- # Ruby 1.9: getch return a String if char is printable (between " "
388
+ # Ruby 1.9: getch return a String if char is printable (between " "
391
389
  # and "~"), Fixnum otherwise
392
390
  begin
393
391
  ch = @bot_bar.getch
@@ -492,7 +490,7 @@ def handle_key
492
490
  if command == "" then
493
491
  command = ENV["SHELL"]
494
492
  end
495
-
493
+
496
494
  Curses.stdscr.clear
497
495
  Curses.close_screen
498
496
  puts "\nJota executing '#{command}'"
@@ -561,7 +559,7 @@ def handle_key
561
559
 
562
560
  adjust_top_win_first
563
561
  adjust_bot_win_first
564
- end
562
+ end
565
563
 
566
564
  def adjust_top_win_first
567
565
  return if not @data
@@ -570,7 +568,7 @@ def adjust_top_win_first
570
568
  elsif @data.current_index > @top_win_first+@top_win_height-1 then
571
569
  @top_win_first = @data.current_index-@top_win_height+1
572
570
  end
573
- end
571
+ end
574
572
 
575
573
  def adjust_bot_win_first
576
574
  return if not @data
@@ -579,13 +577,13 @@ def adjust_bot_win_first
579
577
  elsif @bot_win_lines and @bot_win_first > @bot_win_lines-1 then
580
578
  @bot_win_first = @bot_win_lines-1
581
579
  end
582
- end
580
+ end
583
581
 
584
582
  def message(str)
585
583
  @bot_bar.clear
586
584
  @bot_bar.addstr(str)
587
585
  @bot_bar.refresh
588
- end
586
+ end
589
587
 
590
588
  def prompt(str,type=:string)
591
589
  @bot_bar.clear
@@ -608,7 +606,7 @@ def prompt(str,type=:string)
608
606
  Curses.noecho
609
607
  draw_bars
610
608
  return result
611
- end
609
+ end
612
610
 
613
611
  def prompt_filename(str)
614
612
  loop do
@@ -618,7 +616,7 @@ def prompt_filename(str)
618
616
  @bot_win.addstr File.expand_path(".")
619
617
  @bot_win.addstr(":\n")
620
618
  ent = (Dir.entries ".").sort
621
- ent.each do | f |
619
+ ent.each do | f |
622
620
  next if f =~ /^\./
623
621
  @bot_win.addstr(f)
624
622
  if File.directory? f then
@@ -631,14 +629,14 @@ def prompt_filename(str)
631
629
  return filename
632
630
  end
633
631
  end
634
- end
632
+ end
635
633
 
636
634
  def error(msg)
637
635
  @bot_bar.clear
638
636
  @bot_bar.addstr("ERROR: ")
639
637
  @bot_bar.addstr(msg.to_s)
640
638
  @bot_bar.refresh
641
- end
639
+ end
642
640
 
643
641
  def really?(prefstr,promptstr)
644
642
  if @data.nil? then
@@ -656,6 +654,6 @@ def really?(prefstr,promptstr)
656
654
  else
657
655
  return false
658
656
  end
659
- end
657
+ end
660
658
 
661
659
  end # class
@@ -0,0 +1,258 @@
1
+
2
+
3
+ # from page.rb
4
+
5
+ # def Page.from_mbox(str)
6
+ # # "From_" line already removed
7
+ # cl = Page.new
8
+ # in_header = true
9
+ # charset = "UTF-8"
10
+ # str.each_line do | line |
11
+ # line.encode!(charset)
12
+ # if in_header then
13
+ # if line == "\n" then
14
+ # in_header = false
15
+ # next
16
+ # end
17
+ # #(key, value) = line.split(/\s*:\s*/,2)
18
+ # # stricter pattern to preserve leading spaces
19
+ # (key, value) = line.split(/\s*: /,2)
20
+ # value.chomp!
21
+ # case key.downcase
22
+ # when "flags", "x-flags"
23
+ # cl.wrap = value.downcase.include?("w")
24
+ # when "subject"
25
+ # cl.title = value
26
+ # when "created-at", "date"
27
+ # cl.created = Time.parse(value)
28
+ # when "content-type"
29
+ # case value
30
+ # when "text/plain"
31
+ # cl.type = :text
32
+ # when "application/x-jota-pref"
33
+ # cl.type = :pref
34
+ # else
35
+ # cl.type = :text # safe fallback type
36
+ # end
37
+ # end
38
+ # else
39
+ # cl.data << line
40
+ # end
41
+ # end
42
+ # return cl
43
+ # end
44
+
45
+ # def to_mbox
46
+ # str = ""
47
+ # charset = "UTF-8"
48
+
49
+ # str << "Created-At: %s\n" % @created.ctime
50
+
51
+ # str << "Subject: %s\n" % @title
52
+
53
+ # case @type
54
+ # when :text
55
+ # str << "Content-Type: text/plain; charset=#{charset}\n"
56
+ # when :pref
57
+ # str << "Content-Type: application/x-jota-pref\n"
58
+ # end
59
+
60
+ # str << "Flags: "
61
+ # str << "w" if @wrap
62
+ # str << "\n"
63
+
64
+ # if type == :pref then
65
+ # str << "Originating-Client: %s %s\n" %
66
+ # [Jota::Version::PROGRAM_NAME, Jota::Version::STRING]
67
+ # end
68
+
69
+ # str << "\n"
70
+
71
+ # str << @data
72
+
73
+ # return str
74
+ # end
75
+
76
+
77
+ # from book.rb
78
+
79
+ # def Book.read_mbox(file)
80
+ # ca = self.new
81
+ # first_line = true
82
+ # str = ""
83
+ # i = 1
84
+
85
+ # Log.debug "reading pages, mbox format"
86
+
87
+ # if file.eof? or file.readline =~ /^From / then
88
+ # # valid
89
+ # else
90
+ # raise Jota::AppError, "Not an Mbox file"
91
+ # end
92
+
93
+ # file.each_line do | line |
94
+ # line.chomp!
95
+ # if line =~ /^From / then
96
+ # if not first_line then
97
+ # page = Jota::Page.from_mbox(unescape_from(str))
98
+ # if page.type == :pref then
99
+ # ca.pref = Jota::Preferences.read(page.data)
100
+ # Log.debug " #{i}: Preferences"
101
+ # else
102
+ # ca << page
103
+ # Log.debug " #{i}: '#{page.title}'"
104
+ # end
105
+ # i += 1
106
+ # end
107
+ # str = ""
108
+ # else
109
+ # str << line << "\n"
110
+ # end
111
+ # first_line = false
112
+ # end
113
+
114
+ # if str != "" then
115
+ # page = Jota::Page.from_mbox(unescape_from(str))
116
+ # if page.type == :pref then
117
+ # ca.pref = Jota::Preferences.read(page.data)
118
+ # Log.debug " #{i}: Preferences"
119
+ # else
120
+ # ca << page
121
+ # Log.debug " #{i}: '#{page.title}'"
122
+ # end
123
+ # end
124
+
125
+ # ca.current_index = ca.length - 1
126
+ # return ca
127
+ # end
128
+
129
+ # def write_mbox(file)
130
+ # Log.debug "writing pages, mbox format"
131
+ # i = 1
132
+
133
+ # # Pref page first
134
+ # pc = Jota::Page.new
135
+ # pc.type = :pref
136
+ # pc.title = "Preferences"
137
+ # pc.data = @pref.write
138
+
139
+ # ([pc]+self).each do | page |
140
+ # file.print "From %s@localhost %s\n" %
141
+ # [Jota::Version::PROGRAM_NAMELC, Time.now.ctime]
142
+ # file.print(escape_from(page.to_mbox.chomp))
143
+ # file.print("\n")
144
+ # Log.debug " #{i}: '#{page.title}'"
145
+ # i += 1
146
+ # end
147
+ # end
148
+
149
+ require 'log'
150
+ require 'tmail' # gem install tmail, needs ruby > 1.9.1; does not work with
151
+ # ruby-2.1.1 "has a non escaped non ASCII character in non
152
+ # ASCII-8BIT script (RegexpError)"
153
+
154
+ # ... since everybody should now use the new gem "mail".
155
+ # https://rubygems.org/gems/mail
156
+
157
+ #http://www.rubydoc.info/gems/mbox
158
+
159
+ mb =Mbox.open "mbox.jota"
160
+ mb.each do |x|
161
+ x.to_s
162
+ end
163
+
164
+ require 'yaml'
165
+
166
+
167
+ module Jota
168
+
169
+ class Storage; end
170
+
171
+ class MboxStorage < Storage
172
+
173
+ attr_reader :book, :filename, :is_rw
174
+
175
+ def initialize book, filename=nil
176
+ @book = book
177
+ @filename = File.expand_path filename
178
+ @is_rw = true
179
+ end
180
+
181
+ def self.load filename, mode = :rw
182
+ Log.info "loading Mbox file '#{filename}' with mode '#{mode}'"
183
+ array = []
184
+ pref = nil
185
+ TMail::UNIXMbox.new(filename,nil,true).each_port do |port|
186
+ mail = TMail::Mail.new(port)
187
+ Log.debug "MboxStorage.load: '#{mail.inspect}'"
188
+ type = parse_type(mail.content_type)
189
+ data = mail.body
190
+ hash = {
191
+ "created" => Date.parse(mail["created-at"]),
192
+ "data" => data,
193
+ "title" => mail.subject,
194
+ "type" => type,
195
+ "wrap" => mail["flags"].downcase.include?("w"),
196
+ }
197
+ if type == :pref then
198
+ pref = YAML.load data
199
+ else
200
+ page = Page.from_hash hash
201
+ array << page
202
+ end
203
+ end
204
+
205
+ storage = self.new (Book.from_array(array, pref)), filename
206
+ return storage
207
+ end
208
+
209
+ def save_as filename
210
+ return if ! @book
211
+ Log.info "saving Mbox file '#{filename}'"
212
+ # mailbox = UNIXMbox.new(filename,nil,true)
213
+ array = @book.map{|page|page.to_hash}
214
+
215
+ File.open(@filename, "w") do |file|
216
+ # preferences
217
+ mail = TMail::Mail.new
218
+ mail["created-at"] = Time.now.to_s
219
+ mail["flags"] = ""
220
+ mail.subject = "Preferences"
221
+ mail.body = YAML.dump @book.pref
222
+ mail.set_content_type "application", "x-jota-pref"
223
+ Log.debug "MboxStorage.save_as: pref '#{mail.inspect}'"
224
+ file.print mail.encoded
225
+
226
+ # pages
227
+ array.each do |hash|
228
+ mail = TMail::Mail.new
229
+ mail["created-at"] = hash["created"].to_s
230
+ mail["flags"] = hash["wrap"]? "w" : "" # the only flag for now
231
+ mail.subject = hash["title"]
232
+ mail.body = hash["data"]
233
+ mail.set_content_type "text", "plain" # all non-:pref pages are :text for now
234
+ Log.debug "MboxStorage.save_as: page '#{mail.inspect}'"
235
+ file.print mail.encoded
236
+ end
237
+ end
238
+ end
239
+
240
+ def save
241
+ save_as @filename if @filename
242
+ end
243
+
244
+ private
245
+
246
+ def parse_type content_type
247
+ case content_type
248
+ when "text/plain"
249
+ return :text
250
+ when "application/x-jota-pref"
251
+ return :pref
252
+ else
253
+ return :text
254
+ end
255
+ end
256
+
257
+ end # class
258
+ end # module