XDCC-Fetch 1.386

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.
Files changed (78) hide show
  1. data/COPYING +23 -0
  2. data/XDCC-Fetch.rbw +54 -0
  3. data/doc/ark.png +0 -0
  4. data/doc/connect_established.png +0 -0
  5. data/doc/index.html +300 -0
  6. data/doc/mega_ark.png +0 -0
  7. data/doc/package.png +0 -0
  8. data/doc/package_unknown.png +0 -0
  9. data/doc/shot1.png +0 -0
  10. data/doc/shot1_mini.png +0 -0
  11. data/doc/shot2.png +0 -0
  12. data/doc/shot2_mini.png +0 -0
  13. data/doc/xdccfetch.css +155 -0
  14. data/icons/ark.png +0 -0
  15. data/icons/ark_big.png +0 -0
  16. data/icons/camera_test.png +0 -0
  17. data/icons/cancel.png +0 -0
  18. data/icons/connect_creating.png +0 -0
  19. data/icons/connect_established.png +0 -0
  20. data/icons/connect_failed.png +0 -0
  21. data/icons/connect_no.png +0 -0
  22. data/icons/edit_add.png +0 -0
  23. data/icons/edit_remove.png +0 -0
  24. data/icons/exit.png +0 -0
  25. data/icons/fileclose.png +0 -0
  26. data/icons/folder_inbox.png +0 -0
  27. data/icons/idea.png +0 -0
  28. data/icons/mega_ark.png +0 -0
  29. data/icons/messagebox_critical.png +0 -0
  30. data/icons/messagebox_info.png +0 -0
  31. data/icons/messagebox_warning.png +0 -0
  32. data/icons/messagebox_warning_small.png +0 -0
  33. data/icons/package.png +0 -0
  34. data/icons/package_favourite.png +0 -0
  35. data/icons/package_unknown.png +0 -0
  36. data/src/Console/Console_Parser.rb +71 -0
  37. data/src/Console/XDCC_Pack_Match_Template.rb +29 -0
  38. data/src/Console/xdcc-fetch.rb +123 -0
  39. data/src/GUI/About_Dialog.rb +50 -0
  40. data/src/GUI/Application_Builder.rb +280 -0
  41. data/src/GUI/Context_Menu.rb +81 -0
  42. data/src/GUI/Custom_Tabs.rb +60 -0
  43. data/src/GUI/Dialog_Box.rb +116 -0
  44. data/src/GUI/Download_Finished_Box.rb +41 -0
  45. data/src/GUI/Empty_Text_Field_Handler.rb +86 -0
  46. data/src/GUI/Gui_Logic.rb +629 -0
  47. data/src/GUI/Icon_Loader.rb +58 -0
  48. data/src/GUI/Main_Window.rb +227 -0
  49. data/src/GUI/Packet_Item.rb +171 -0
  50. data/src/GUI/Packet_List.rb +145 -0
  51. data/src/GUI/Speed_Widget.rb +101 -0
  52. data/src/GUI/Talk_Back.rb +118 -0
  53. data/src/GUI/Toggle_Button.rb +56 -0
  54. data/src/Network/CTCP_Handler.rb +61 -0
  55. data/src/Network/DCC_File.rb +323 -0
  56. data/src/Network/DCC_Parser.rb +71 -0
  57. data/src/Network/IPAddr_Ext.rb +76 -0
  58. data/src/Network/IRC_Message.rb +161 -0
  59. data/src/Network/IRC_Server.rb +273 -0
  60. data/src/Network/IRC_Server_Respond_Map.rb +223 -0
  61. data/src/Network/IRC_User.rb +58 -0
  62. data/src/Network/TCP_Connection.rb +168 -0
  63. data/src/Network/XDCC_Announcement.rb +120 -0
  64. data/src/Network/XDCC_Announcement_Storage.rb +167 -0
  65. data/src/Network/XDCC_Download_Handler.rb +412 -0
  66. data/src/Network/XDCC_Pack.rb +58 -0
  67. data/src/Network/XDCC_Parser.rb +253 -0
  68. data/src/Translations/README +61 -0
  69. data/src/Translations/check_translations +83 -0
  70. data/src/Translations/de.rb +140 -0
  71. data/src/Translations/en.rb +145 -0
  72. data/src/Utilities/Configuration.rb +91 -0
  73. data/src/Utilities/Events.rb +87 -0
  74. data/src/Utilities/Globals.rb +138 -0
  75. data/src/Utilities/PrettyException.rb +1091 -0
  76. data/src/Utilities/Recursive_Open_Struct.rb +159 -0
  77. data/src/Utilities/Timer.rb +71 -0
  78. metadata +135 -0
@@ -0,0 +1,1091 @@
1
+ ###################################################
2
+ # # # # # # # # # # # # # # # # # # # # # # # # # #
3
+ #
4
+ # = lib/PrettyException.rb
5
+ #
6
+ # PrettyPrint for Exceptions
7
+ #
8
+ # Author:: Dmitry V. Sabanin <sdmitry@lrn.ru>
9
+ # Revision:: $Date: 2004/12/20 05:11:50 $ $Revision: 1.16 $
10
+ # License:: LGPL
11
+ #
12
+ # Modified to use rdoc's templating by Michael Neumann (mneumann@ntecs.de).
13
+ #
14
+ # # # # # # # # # # # # # # # # # # # # # # # # # #
15
+ ###################################################
16
+
17
+ require 'rdoc/template'
18
+ require 'rbconfig'
19
+ require 'cgi'
20
+
21
+ ###################################################
22
+ # Web PrettyPrint for Exceptions
23
+ #
24
+ class PrettyException
25
+
26
+ attr_writer :message
27
+
28
+ ###################################################
29
+ def initialize(exception, tplpath = nil)
30
+ @tplpath = tplpath
31
+ @exception = exception
32
+ @message = nil
33
+ end
34
+ ###################################################
35
+
36
+ ###################################################
37
+ def build_template
38
+ tmpl =
39
+ if @tplpath and File.exists?(@tplpath)
40
+ File.read(@tplpath)
41
+ else
42
+ tplfile = File.readlines(__FILE__)
43
+ start = nil
44
+ tplfile.each_with_index do |line,idx|
45
+ if line =~ /^__END__\s*$/
46
+ start = idx + 1
47
+ break
48
+ end
49
+ end
50
+ tplfile[start..-1].join('')
51
+ end
52
+
53
+ return TemplatePage.new(tmpl)
54
+ end
55
+ ###################################################
56
+
57
+ ###################################################
58
+ def gen_source(file, line, mark_line = true)
59
+ source = File.readlines(file)
60
+ source = hilite_source(source)
61
+ line = line.to_i
62
+ if mark_line
63
+ show = 3
64
+ begin_from = line - show
65
+ end_at = line + show
66
+ begin_from = begin_from > 0 ? begin_from : 0
67
+ work_with = source[begin_from..end_at]
68
+ else
69
+ work_with = [ source[line-1] ]
70
+ end
71
+ work_with ||= []
72
+ res = []
73
+ work_with.each do |i, l|
74
+ l = ('<span class="hl_lineno">%.3d:</span>%s' % [i," "]) + l
75
+ if i == line and mark_line
76
+ buf = '<div class="current_line">' + l + '</div>'
77
+ else
78
+ buf = l + "\n"
79
+ end
80
+ res << buf
81
+ end
82
+ res.join
83
+ end
84
+ ###################################################
85
+
86
+ ###################################################
87
+ def hilite_source(src)
88
+ lx = LexerRuby::LexerOld.new
89
+ src.each do |sline|
90
+ lx.lex_line(sline)
91
+ end
92
+ res = []
93
+ lineno = 0
94
+ this_line = []
95
+ hc = false
96
+ heredoc_buf = nil
97
+ lx.result.each do |text, token|
98
+ if hc
99
+ lineno += 1
100
+ res << [lineno, this_line.join]
101
+ this_line = []
102
+ hc = false
103
+ end
104
+ if token != :heredoc and heredoc_buf and token != :any
105
+ heredoc = heredoc_buf.split(/\n/)
106
+ heredoc.each do |hd_line|
107
+ res << [lineno, ('<span class="hl_heredoc">%s</span>' % [hd_line.to_s])]
108
+ lineno += 1
109
+ end
110
+ this_line = []
111
+ heredoc_buf = nil
112
+ end
113
+ case token
114
+ when :keyword, :ident, :punct,
115
+ :comment, :ivar, :dot,
116
+ :string, :command, :number,
117
+ :gvar, :literal, :symbol
118
+ if token == :comment
119
+ hc = true
120
+ end
121
+ text = CGI.escapeHTML(text)
122
+ this_line << ('<span class="hl_%s">%s</span>' % [token.to_s, text.rstrip])
123
+ when :heredoc
124
+ text = CGI.escapeHTML(text)
125
+ heredoc_buf ||= ''
126
+ heredoc_buf << text
127
+ when :any
128
+ if text =~ /^\s*\n$/
129
+ lineno += 1
130
+ res << [lineno, this_line.join]
131
+ this_line = []
132
+ else
133
+ this_line << text
134
+ end
135
+ else
136
+ this_line << CGI.escapeHTML(text)
137
+ end
138
+ end
139
+ res << [lineno+1, this_line.join] if this_line.length > 0
140
+ res
141
+ end
142
+ ###################################################
143
+
144
+ ###################################################
145
+ def print
146
+ tpl = build_template
147
+ contents = {}
148
+ contents['message'] = CGI::escapeHTML(@exception.message).to_s
149
+ contents['exception'] = @exception.class.to_s
150
+ contents['time'] = Time.now.to_s
151
+ bt = []
152
+ @exception.backtrace.each_with_index do |str,idx|
153
+ file,text = str.scan(/^(.+):(.+)(?::(.+))?$/).flatten;
154
+ file,line = file.split(/:/)
155
+ unless line
156
+ line = text
157
+ text = nil
158
+ end
159
+ is_stdlib = false
160
+ paths = [Config::CONFIG['rubylibdir'], Config::CONFIG['sitedir']]
161
+ paths.each do |dir|
162
+ is_stdlib = true if (file =~ /#{Regexp::escape(dir)}/)
163
+ end
164
+ data = { 'file' => file, 'line' => line, 'text' => text, 'from_stdlib' => is_stdlib }
165
+ if idx == 0
166
+ data['source'] = gen_source(file, line)
167
+ else
168
+ data['source'] = gen_source(file, line)
169
+ end
170
+ data['iteration_id'] = idx.to_s
171
+ bt << data
172
+ end
173
+ contents['backtrace'] = bt
174
+ tpl.write_html_on(output='', contents)
175
+ output
176
+ end
177
+ ###################################################
178
+
179
+ alias_method :to_s, :print
180
+
181
+ end
182
+
183
+ class LexerBase
184
+ def initialize
185
+ @states = []
186
+ @result = []
187
+ @result_endofline = nil
188
+ end
189
+ attr_reader :states, :result, :result_endofline
190
+
191
+ def set_states(states)
192
+ @states = states
193
+ end
194
+ def set_result(result)
195
+ @result = result
196
+ end
197
+ def format(text, state_output)
198
+ @result << [text, state_output]
199
+ end
200
+ def format_end(state_output)
201
+ @result_endofline = state_output
202
+ end
203
+ def match(regexp, output)
204
+ m = regexp.match(@text)
205
+ return false unless m
206
+ txt = @text.slice!(0, m.end(0))
207
+ format(txt, output)
208
+ true
209
+ end
210
+ def lex_line(text)
211
+ raise "derived class #{self.class} must overload #lex_line."
212
+ end
213
+ def self.profile
214
+ lines = IO.readlines(__FILE__)
215
+ lexer = self.new
216
+ puts "profiling the #{self.inspect} lexer (this may take some time)"
217
+ require 'profiler'
218
+ Profiler__.start_profile
219
+ lines.each do |line|
220
+ lexer.set_states([])
221
+ lexer.set_result([])
222
+ lexer.lex_line(line)
223
+ end
224
+ Profiler__.print_profile(STDOUT)
225
+ end
226
+ def self.benchmark
227
+ n = 10000
228
+ puts "benchmarking the lexers (computing #{n} lines " +
229
+ "with GC disabled)"
230
+ require 'benchmark'
231
+ Benchmark.bm(20) do |b|
232
+ lexer = LexerRuby::LexerOld.new
233
+ #=begin
234
+ lines = IO.readlines(__FILE__)
235
+ GC.disable
236
+ b.report("#{lexer.class}") do
237
+ n.times do |i|
238
+ lexer.set_states([])
239
+ lexer.set_result([])
240
+ lexer.lex_line(lines[i%lines.size].clone)
241
+ end
242
+ end
243
+ #=begin
244
+ GC.enable
245
+ GC.start
246
+ lines = IO.readlines(__FILE__)
247
+ lexer = LexerRuby::LexerNew.new
248
+ GC.disable
249
+ b.report("#{lexer.class}") do
250
+ n.times do |i|
251
+ lexer.set_states([])
252
+ lexer.set_result([])
253
+ lexer.lex_line(lines[i%lines.size])
254
+ end
255
+ end
256
+ =begin
257
+ =end
258
+ GC.enable
259
+ GC.start
260
+ lines = IO.readlines(__FILE__)
261
+ lexer = LexerRuby::Lexer3.new
262
+ GC.disable
263
+ b.report("#{lexer.class}") do
264
+ n.times do |i|
265
+ lexer.set_states([])
266
+ lexer.set_result([])
267
+ lexer.lex_line(lines[i%lines.size])
268
+ end
269
+ end
270
+ GC.enable
271
+ end
272
+ end
273
+ end
274
+
275
+ module LexerText
276
+
277
+ class Lexer < LexerBase
278
+ RE_TAB = /\A\t+/
279
+ RE_NOTTAB = /\A[^\t]+/
280
+ def lex_line(text)
281
+ @text = text
282
+ until @text.empty?
283
+ if match(RE_TAB, :tab)
284
+ else
285
+ match(RE_NOTTAB, :text)
286
+ end
287
+ end
288
+ end
289
+ end # class Lexer
290
+
291
+ end # module LexerText
292
+
293
+ module LexerRuby
294
+
295
+ module State
296
+
297
+ class Base
298
+ end
299
+
300
+ class Heredoc < Base
301
+ def initialize(begin_tag, ignore_leading_spaces, interpolate=true)
302
+ @begin_tag = begin_tag
303
+ @ignore_leading_spaces = ignore_leading_spaces
304
+ @interpolate = interpolate
305
+ end
306
+ attr_reader :begin_tag, :ignore_leading_spaces, :interpolate
307
+ def ==(other)
308
+ (self.class == other.class) and
309
+ (@begin_tag == other.begin_tag) and
310
+ (@ignore_leading_spaces == other.ignore_leading_spaces) and
311
+ (@interpolate == other.interpolate)
312
+ end
313
+ end
314
+
315
+ class Comment < Base
316
+ def ==(other)
317
+ (self.class == other.class)
318
+ end
319
+ end
320
+
321
+ class Endoffile < Base
322
+ def ==(other)
323
+ (self.class == other.class)
324
+ end
325
+ end
326
+
327
+ end # module State
328
+
329
+ class NewerRubyLexer
330
+ RE_TOKENIZE = Regexp.new([
331
+ # TODO: @ivar
332
+ # TODO: @@cvar
333
+ # TODO: $gvar
334
+ # TODO: %literals %w(a b c) %Q|'"|
335
+ # TODO: :symbol
336
+ # TODO: ?x chars
337
+ # TODO: 0b01001 binary data
338
+ # TODO: 0x234af hex data
339
+ # TODO: keywords =begin defined?
340
+ # TODO: ruby puncturation .. ... && ||
341
+ # TODO: /regexp/
342
+ # TODO: __END__ tag
343
+ # TODO: illegal ruby heredoc which has space after end-tag
344
+ # TODO: illegal ruby puncturation &&& |||
345
+ # TODO: illegal ruby numbers 0x23Yab
346
+ '#.*', # # blah ## !?! comment
347
+ '<<-?[[:alpha:]]+', # <<HTML <<-XML heredoc
348
+ '<<-?\'[[:alpha:]]+\'', # <<'eof' <<-'X' heredoc single quoted
349
+ '<<-?"[[:alpha:]]+"', # <<"eof" <<-"X" heredoc double quoted
350
+ '\.[[:alpha:]][[:alnum:]_]*', # .method .dup2 method call
351
+ '[[:alpha:]][[:alnum:]_]*', # value pix2_3 identifier
352
+ '\'(?:[^\\\\]|\\\\.)*?\'', # '\'x\\' '' string single quoted
353
+ '"(?:[^\\\\]|\\\\.)*?"', # "ab" "" string double quoted
354
+ '\d+\.\d+', # 0.123 32.10 number as float
355
+ '\d+', # 42 999 number as integer
356
+ '.' # * + fallthrough
357
+ ].join('|'))
358
+ def initialize
359
+ @char_to_symbol_hash = {
360
+ " " => :space,
361
+ "\t" => :tab
362
+ }
363
+ @char_to_symbol_hash.default = :should_not_happen
364
+ end
365
+ def tokenize(string)
366
+ string.scan(RE_TOKENIZE)
367
+ end
368
+ def lex(string)
369
+ tokens = tokenize(string)
370
+ states = tokens.map do |token|
371
+ # TODO: by inserting 2 parentesises in the TOKENIZER
372
+ # then I can destinguish between good/bad tokens.
373
+ # 'ab'.scan(/(a)(b)/) do |(good, bad)|
374
+ case token
375
+ when /\A(?:"|')./
376
+ :string
377
+ when /\A<<-?(?:"|'|)[[:alpha:]]/
378
+ :heredoc
379
+ when /\A\.[[:alpha:]]/
380
+ :method
381
+ when /\A[[:alpha:]]/
382
+ :ident
383
+ when /\A#/
384
+ :comment
385
+ when /\A[[:punct:]]/
386
+ :punct
387
+ when /\A[[:digit:]]/
388
+ :number
389
+ else
390
+ @char_to_symbol_hash[token]
391
+ end
392
+ end
393
+ [tokens, states]
394
+ end
395
+ end
396
+
397
+ class Lexer3 < LexerBase
398
+ def initialize
399
+ @rl = NewerRubyLexer.new
400
+ super
401
+ end
402
+ def what_to_output(hash)
403
+ @rl.what_to_output(hash)
404
+ end
405
+ def lex_line(text)
406
+ tokens, states = @rl.lex(text)
407
+ tokens.each_with_index do |token, index|
408
+ format(token, states[index])
409
+ end
410
+ true
411
+ end
412
+ end
413
+
414
+
415
+ class RubyLexer
416
+ def initialize
417
+ @result = []
418
+ end
419
+ attr_reader :result
420
+ PUNCT = ['(', ')'] +
421
+ %w(=== == =~ => = != !~ !) +
422
+ %w(<< <=> <= < >= >) +
423
+ %w({ } [ ]) +
424
+ %w(:: : ... ..) +
425
+ %w(+= + -= - ** * / %) +
426
+ %w(|| | && &) +
427
+ %w(, ;)
428
+ RE_NUMBER = /\d[\d\.]*/
429
+ RE_PUNCT = Regexp.new('(?:' +
430
+ PUNCT.map{|i| Regexp.escape(i)}.join('|') + ')')
431
+ RE_IDENT = /[[:alpha:]][\w\!]*/
432
+ RE_STRING = /".*?"|'.*?'/
433
+ RE_COMMENT = /#.*/
434
+ RE_COMMAND = /\.[[:alnum:]_]*[[:alnum:]_\?\!]/
435
+ RE_SPACE = /\x20+/
436
+ RE_NEWLINE = /\n+/
437
+ RE_TABS = /\t+/
438
+ def scan_index(string, regexp, prio, symbol)
439
+ string.scan(regexp) do
440
+ @result << [$~.begin(0), prio, $~.end(0), symbol]
441
+ end
442
+ end
443
+ def lex(string)
444
+ scan_index(string, RE_COMMENT, 0, :comment)
445
+ scan_index(string, RE_STRING, 1, :string)
446
+ scan_index(string, RE_COMMAND, 2, :command)
447
+ scan_index(string, RE_IDENT, 3, :ident)
448
+ scan_index(string, RE_NUMBER, 4, :number)
449
+ scan_index(string, RE_PUNCT, 5, :punct)
450
+ scan_index(string, RE_SPACE, 6, :space)
451
+ scan_index(string, RE_NEWLINE, 7, :newline)
452
+ scan_index(string, RE_TABS, 8, :tabs)
453
+ end
454
+ def result
455
+ # primary key = string begin position
456
+ # secondary key = priority
457
+ @result.sort!
458
+ # collect the highest precedens data
459
+ ary = [0]
460
+ @result.each do |i1, prio, i2, symbol|
461
+ next if ary.last > i1 # discard low precedens data
462
+ if ary.last < i1
463
+ ary << :any
464
+ ary << i1
465
+ end
466
+ ary << symbol
467
+ ary << i2
468
+ end
469
+ #p ary
470
+ ary
471
+ end
472
+ def self.mk(*symbols)
473
+ symbols.each do |symbol|
474
+ class_eval %{
475
+ def #{symbol.to_s}
476
+ res = []
477
+ @result.each do |i1, prio, i2, sym|
478
+ if sym == :#{symbol.to_s}
479
+ res << [i1, i2]
480
+ end
481
+ end
482
+ res
483
+ end
484
+ }
485
+ end
486
+ end
487
+ mk :number, :ident, :string, :punct
488
+ mk :comment, :command, :space, :newline, :tabs
489
+ end
490
+
491
+ class LexerNew < LexerBase
492
+ def lex_line(text)
493
+ #puts("-"*40)
494
+ #puts "text=#{text.inspect}"
495
+ rl = RubyLexer.new
496
+ rl.lex(text)
497
+ res = rl.result
498
+ #puts "res=#{res.inspect}"
499
+ i1 = res.shift
500
+ while res.size > 1
501
+ symbol = res.shift
502
+ i2 = res.shift
503
+ format(text[i1, (i2-i1)], symbol)
504
+ i1 = i2
505
+ end
506
+ #puts "result=#{@result.inspect}"
507
+ true
508
+ end
509
+ end
510
+
511
+ class LexerOld < LexerBase
512
+ RE_COMMENT = /^#.*/m
513
+
514
+ RE_TAB = /^\t+/
515
+
516
+ RE_SPACE = /^\x20+/
517
+
518
+ KEYWORDS = %w(alias and begin BEGIN break case class) +
519
+ %w(defined? def do else elsif end END ensure for if loop) +
520
+ %w(module next nil not or raise redo require rescue) +
521
+ %w(retry return self super then true false undef) +
522
+ %w(unless until yield when while)
523
+ RE_KEYWORD = Regexp.new(
524
+ '\A(?:' +
525
+ KEYWORDS.map{|txt|Regexp.escape(txt)}.join('|') +
526
+ ')(?!\w)'
527
+ )
528
+
529
+ RE_SYMBOL = /\A:[[:alpha:]_][[:alnum:]_]*/
530
+
531
+ RE_STRING = /^("|\')(?:[^\\]|\\.)*?\1/
532
+ RE_STRING_INTERPOL = /\A ((?:[^\\]|\\.)*?) (\#\{ .*? \}) /x
533
+ # TODO: interpolated code can nest (hint: recursion is necessary)
534
+ def match_string
535
+ m = RE_STRING.match(@text)
536
+ return false unless m
537
+ txt = @text.slice!(0, m.end(0))
538
+ if m[1] == '\''
539
+ format(txt, :string)
540
+ return true
541
+ end
542
+ # double quoted strings may contain interpolated code
543
+ until txt.empty?
544
+ m = RE_STRING_INTERPOL.match(txt)
545
+ unless m
546
+ format(txt, :string)
547
+ break
548
+ end
549
+ format(m[1], :string) unless m[1].empty?
550
+ format(m[2], :string1)
551
+ txt.slice!(0, m.end(0))
552
+ end
553
+ true
554
+ end
555
+
556
+ RE_REGEXP = /\A\/(.*?[^\\])?\//
557
+
558
+ RE_IVAR = /\A@[[:alnum:]_]+/
559
+
560
+ RE_DOT = /\A\.[[:alnum:]_]*[[:alnum:]_\?\!]/
561
+
562
+ RE_IDENTIFIER = /\A(?:[[:alnum:]_]+|\S+)/
563
+
564
+ RE_NUMBER = Regexp.new(
565
+ '\A(?:' + [
566
+ '0x[_a-fA-F0-9]+',
567
+ '0b[_01]+',
568
+ '\d[0-9_]*(?:\.[0-9_]*)?',
569
+ '\?.'
570
+ ].join('|') +
571
+ ')'
572
+ )
573
+
574
+ PUNCT = ['(', ')'] +
575
+ %w(=== == =~ => = != !~ !) +
576
+ %w(<< <=> <= < >= >) +
577
+ %w({ } [ ]) +
578
+ %w(:: : ... ..) +
579
+ %w(+= + -= - ** * / %) +
580
+ %w(|| | && &) +
581
+ %w(, ;)
582
+ RE_PUNCT = Regexp.new(
583
+ '\A(?:' +
584
+ PUNCT.map{|txt|Regexp.escape(txt)}.join('|') +
585
+ ')'
586
+ )
587
+
588
+ VAR_GLOBALS = %q(_~*$!@/\\;,.=:<>"-&`'+1234567890).split(//)
589
+ RE_GVAR = Regexp.new(
590
+ '\A\$(?:' +
591
+ VAR_GLOBALS.map{|txt|Regexp.escape(txt)}.join('|') +
592
+ '|[[:alnum:]_]+' +
593
+ ')'
594
+ )
595
+
596
+ # TODO: deal with multiline literals
597
+ RE_LITERAL = Regexp.new(
598
+ '\A%[Qqwrx]?(?:' + [
599
+ '\(.*?\)', # TODO: must count pairs
600
+ '\{.*?\}', # TODO: must count pairs
601
+ '\<.*?\>', # TODO: must count pairs
602
+ '\[.*?\]', # TODO: must count pairs
603
+ '([^\(\{\<\[]).*?\1'
604
+ ].join('|') + ')'
605
+ )
606
+
607
+ RE_BEGIN = /\A=begin$\n?\x20*\z/ # eat tailing space
608
+ def match_comment_begin
609
+ m = RE_BEGIN.match(@text)
610
+ return false unless m
611
+ #puts "comments"
612
+ @states << State::Comment.new
613
+ txt = @text.slice!(0, m.end(0))
614
+ format(txt, :mcomment)
615
+ format_end(:mcomment_end)
616
+ true
617
+ end
618
+ RE_HEREDOC = /\A<<(-)?('|"|)(\w+)\2/
619
+ def match_heredoc_begin
620
+ m = RE_HEREDOC.match(@text)
621
+ return false unless m
622
+ ignore_leading_space = (m[1] != nil)
623
+ interpolate = (m[2] != "'")
624
+ begin_pattern = m[3]
625
+ @states << State::Heredoc.new(
626
+ begin_pattern,
627
+ ignore_leading_space,
628
+ interpolate
629
+ )
630
+ txt = @text.slice!(0, m.end(0))
631
+ format(txt, :heredoc)
632
+ true
633
+ end
634
+ RE_END = /\A__END__$\n?\x20*\z/ # eat tailing space
635
+ def match_endoffile
636
+ m = RE_END.match(@text)
637
+ return false unless m
638
+ #puts "propagate __END__"
639
+ @states << State::Endoffile.new
640
+ txt = @text.slice!(0, m.end(0))
641
+ format(txt, :endoffile)
642
+ format_end(:endoffile_end)
643
+ true
644
+ end
645
+ def lex_line_normal(text)
646
+ @text = text
647
+ return if match_comment_begin
648
+ return if match_endoffile
649
+ until @text.empty?
650
+ if match(RE_COMMENT, :comment)
651
+ format_end(:comment_end)
652
+ elsif match(RE_REGEXP, :regexp)
653
+ elsif match_heredoc_begin
654
+ elsif match(RE_LITERAL, :literal)
655
+ elsif match(RE_KEYWORD, :keyword)
656
+ elsif match(RE_SYMBOL, :symbol)
657
+ elsif match(RE_PUNCT, :punct)
658
+ elsif match(RE_GVAR, :gvar)
659
+ elsif match_string
660
+ elsif match(RE_NUMBER, :number)
661
+ elsif match(RE_IVAR, :ivar)
662
+ elsif match(RE_DOT, :dot)
663
+ elsif match(RE_IDENTIFIER, :ident)
664
+ elsif match(RE_TAB, :tab)
665
+ elsif match(RE_SPACE, :space)
666
+ else
667
+ #@text.slice!(0, 1)
668
+ txt = @text.slice!(0, 1)
669
+ format(txt, :any)
670
+ end
671
+ end
672
+ end
673
+ def match_heredoc_end(regexp)
674
+ m = regexp.match(@text)
675
+ return false unless m
676
+ #puts "end of heredoc"
677
+ @states.shift
678
+ txt = @text.slice!(0, m.end(0))
679
+ format(txt, :heredoc)
680
+ format_end(:heredoc_end2)
681
+ true
682
+ end
683
+ def lex_line_heredoc(text)
684
+ # TODO: color interpolated code #{code}
685
+ @text = text
686
+ format_end(:heredoc_end)
687
+ hd_end = nil
688
+ state = @states[0]
689
+ hd_end = /\A#{state.begin_tag}$\n?\x20*\z/
690
+ return if match_heredoc_end(hd_end)
691
+ # continue lexing
692
+ ign_lead_spc = state.ignore_leading_spaces
693
+ until @text.empty?
694
+ if match(RE_TAB, :heredoc_tab)
695
+ elsif ign_lead_spc and match_heredoc_end(hd_end)
696
+ else
697
+ txt = @text.slice!(0, 1)
698
+ format(txt, :heredoc)
699
+ end
700
+ end
701
+ end
702
+ def match_comment_end
703
+ m = /\A\=end\b.*?$\n?\x20*\z/.match(@text)
704
+ return false unless m
705
+ #puts "comment end"
706
+ @states.shift
707
+ txt = @text.slice!(0, m.end(0))
708
+ format(txt, :mcomment)
709
+ true
710
+ end
711
+ def lex_line_comment(text)
712
+ @text = text
713
+ format_end(:mcomment_end)
714
+ return if match_comment_end
715
+ until @text.empty?
716
+ if match(RE_TAB, :mcomment_tab)
717
+ else
718
+ txt = @text.slice!(0, 1)
719
+ format(txt, :mcomment)
720
+ end
721
+ end
722
+ end
723
+ def lex_line_endoffile(text)
724
+ @text = text
725
+ format_end(:endoffile_end)
726
+ until @text.empty?
727
+ if match(RE_TAB, :endoffile_tab)
728
+ else
729
+ txt = @text.slice!(0, 1)
730
+ format(txt, :endoffile)
731
+ end
732
+ end
733
+ end
734
+ def lex_line(text)
735
+ if @states.empty?
736
+ return lex_line_normal(text)
737
+ end
738
+ state = @states[0]
739
+ case state
740
+ when State::Heredoc: lex_line_heredoc(text)
741
+ when State::Comment: lex_line_comment(text)
742
+ when State::Endoffile: lex_line_endoffile(text)
743
+ else
744
+ raise "unknown state #{state.class}"
745
+ end
746
+ end
747
+ end
748
+
749
+ # TODO: make the new lexer work!
750
+ Lexer = LexerOld # slow
751
+ #Lexer = LexerNew # slow
752
+ #Lexer = Lexer3 # fastest
753
+
754
+ end # module LexerRuby
755
+
756
+ __END__
757
+ <html>
758
+ <head>
759
+ <title>Oops!</title>
760
+ <style>
761
+ .data {
762
+ border-style: dotted;
763
+ padding: 4px; }
764
+ .trace_header {
765
+ border-style: dotted;
766
+ border-width: thin;
767
+ background-color: #CCCCCC;
768
+ text-align: center; }
769
+ .normal_trace_entry {
770
+ border-style: dotted;
771
+ border-width: thin;
772
+ text-align: center;
773
+ padding: 6px; }
774
+ .stdlib_trace_entry {
775
+ border-style: dotted;
776
+ border-width: thin;
777
+ text-align: right;
778
+ padding: 6px; }
779
+ .source {
780
+ width: 100%;
781
+ background-color: #F4F4F4;
782
+ display: none;
783
+ }
784
+ span.hl_lineno {
785
+ font-weight: bold;
786
+ }
787
+ pre {
788
+ width: 80%;
789
+ padding: 0px;
790
+ margin: 0px;
791
+ }
792
+ div.current_line {
793
+ color: red;
794
+ background-color: #F4DADA;
795
+ }
796
+ span.hl_keyword {
797
+ font-weight: bold;
798
+ }
799
+ span.hl_punct {
800
+ font-weight: bold;
801
+ color: darkblue;
802
+ }
803
+ span.hl_ident {
804
+ }
805
+ span.hl_command {
806
+ font-weight: bold;
807
+ }
808
+ span.hl_number {
809
+ color: darkgreen;
810
+ }
811
+ span.hl_string {
812
+ color: darkgreen;
813
+ }
814
+ span.hl_comment {
815
+ color: grey;
816
+ }
817
+ span.hl_ivar {
818
+ font-weight: bold;
819
+ color: darkred;
820
+ }
821
+ span.hl_dot {
822
+ font-weight: bold;
823
+ }
824
+ span.hl_literal {
825
+ color: green;
826
+ }
827
+ span.hl_gvar {
828
+ font-weight: bold;
829
+ }
830
+ span.hl_symbol {
831
+ color: blue;
832
+ }
833
+ span.hl_regexp {
834
+ color: green;
835
+ }
836
+ tr {
837
+ background-color: white;
838
+ }
839
+ </style>
840
+ <script type="text/javascript" language="javascript">
841
+ function toggleCode( id ) {
842
+ if ( document.getElementById )
843
+ elem = document.getElementById( id );
844
+ else if ( document.all )
845
+ elem = eval( "document.all." + id );
846
+ else
847
+ return false;
848
+
849
+ elemStyle = elem.style;
850
+
851
+ if ( elemStyle.display != "block" ) {
852
+ elemStyle.display = "block"
853
+ } else {
854
+ elemStyle.display = "none"
855
+ }
856
+
857
+ return true;
858
+ }
859
+
860
+ var isDOM = (typeof(document.getElementsByTagName) != 'undefined'
861
+ && typeof(document.createElement) != 'undefined')
862
+ ? 1 : 0;
863
+ var isIE4 = (typeof(document.all) != 'undefined'
864
+ && parseInt(navigator.appVersion) >= 4)
865
+ ? 1 : 0;
866
+ var isNS4 = (typeof(document.layers) != 'undefined')
867
+ ? 1 : 0;
868
+ var capable = (isDOM || isIE4 || isNS4)
869
+ ? 1 : 0;
870
+ // Uggly fix for Opera and Konqueror 2.2 that are half DOM compliant
871
+ if (capable) {
872
+ if (typeof(window.opera) != 'undefined') {
873
+ var browserName = ' ' + navigator.userAgent.toLowerCase();
874
+ if ((browserName.indexOf('konqueror 7') == 0)) {
875
+ capable = 0;
876
+ }
877
+ } else if (typeof(navigator.userAgent) != 'undefined') {
878
+ var browserName = ' ' + navigator.userAgent.toLowerCase();
879
+ if ((browserName.indexOf('konqueror') > 0) && (browserName.indexOf('konqueror/3') == 0)) {
880
+ capable = 0;
881
+ }
882
+ } // end if... else if...
883
+ } // end if
884
+
885
+ /**
886
+ * This array is used to remember mark status of rows in browse mode
887
+ */
888
+ var marked_row = new Array;
889
+
890
+
891
+ /**
892
+ * Sets/unsets the pointer and marker in browse mode
893
+ *
894
+ * @param object the table row
895
+ * @param integer the row number
896
+ * @param string the action calling this script (over, out or click)
897
+ * @param string the default background color
898
+ * @param string the color to use for mouseover
899
+ * @param string the color to use for marking a row
900
+ *
901
+ * @return boolean whether pointer is set or not
902
+ */
903
+ function setPointer(theRow, theRowNum, theAction, theDefaultColor, thePointerColor, theMarkColor)
904
+ {
905
+ var theCells = null;
906
+
907
+ // 1. Pointer and mark feature are disabled or the browser can't get the
908
+ // row -> exits
909
+ if ((thePointerColor == '' && theMarkColor == '')
910
+ || typeof(theRow.style) == 'undefined') {
911
+ return false;
912
+ }
913
+
914
+ // 2. Gets the current row and exits if the browser can't get it
915
+ if (typeof(document.getElementsByTagName) != 'undefined') {
916
+ theCells = theRow.getElementsByTagName('td');
917
+ }
918
+ else if (typeof(theRow.cells) != 'undefined') {
919
+ theCells = theRow.cells;
920
+ }
921
+ else {
922
+ return false;
923
+ }
924
+
925
+ // 3. Gets the current color...
926
+ var rowCellsCnt = theCells.length;
927
+ var domDetect = null;
928
+ var currentColor = null;
929
+ var newColor = null;
930
+ // 3.1 ... with DOM compatible browsers except Opera that does not return
931
+ // valid values with "getAttribute"
932
+ if (typeof(window.opera) == 'undefined'
933
+ && typeof(theCells[0].getAttribute) != 'undefined') {
934
+ currentColor = theCells[0].getAttribute('bgcolor');
935
+ domDetect = true;
936
+ }
937
+ // 3.2 ... with other browsers
938
+ else {
939
+ currentColor = theCells[0].style.backgroundColor;
940
+ domDetect = false;
941
+ } // end 3
942
+
943
+ // 3.3 ... Opera changes colors set via HTML to rgb(r,g,b) format so fix it
944
+ if (currentColor.indexOf("rgb") >= 0)
945
+ {
946
+ var rgbStr = currentColor.slice(currentColor.indexOf('(') + 1,
947
+ currentColor.indexOf(')'));
948
+ var rgbValues = rgbStr.split(",");
949
+ currentColor = "#";
950
+ var hexChars = "0123456789ABCDEF";
951
+ for (var i = 0; i < 3; i++)
952
+ {
953
+ var v = rgbValues[i].valueOf();
954
+ currentColor += hexChars.charAt(v/16) + hexChars.charAt(v%16);
955
+ }
956
+ }
957
+
958
+ // 4. Defines the new color
959
+ // 4.1 Current color is the default one
960
+ if (currentColor == ''
961
+ || currentColor.toLowerCase() == theDefaultColor.toLowerCase()) {
962
+ if (theAction == 'over' && thePointerColor != '') {
963
+ newColor = thePointerColor;
964
+ }
965
+ else if (theAction == 'click' && theMarkColor != '') {
966
+ newColor = theMarkColor;
967
+ marked_row[theRowNum] = true;
968
+ // Garvin: deactivated onclick marking of the checkbox because it's also executed
969
+ // when an action (like edit/delete) on a single item is performed. Then the checkbox
970
+ // would get deactived, even though we need it activated. Maybe there is a way
971
+ // to detect if the row was clicked, and not an item therein...
972
+ // document.getElementById('id_rows_to_delete' + theRowNum).checked = true;
973
+ }
974
+ }
975
+ // 4.1.2 Current color is the pointer one
976
+ else if (currentColor.toLowerCase() == thePointerColor.toLowerCase()
977
+ && (typeof(marked_row[theRowNum]) == 'undefined' || !marked_row[theRowNum])) {
978
+ if (theAction == 'out') {
979
+ newColor = theDefaultColor;
980
+ }
981
+ else if (theAction == 'click' && theMarkColor != '') {
982
+ newColor = theMarkColor;
983
+ marked_row[theRowNum] = true;
984
+ // document.getElementById('id_rows_to_delete' + theRowNum).checked = true;
985
+ }
986
+ }
987
+ // 4.1.3 Current color is the marker one
988
+ else if (currentColor.toLowerCase() == theMarkColor.toLowerCase()) {
989
+ if (theAction == 'click') {
990
+ newColor = (thePointerColor != '')
991
+ ? thePointerColor
992
+ : theDefaultColor;
993
+ marked_row[theRowNum] = (typeof(marked_row[theRowNum]) == 'undefined' || !marked_row[theRowNum])
994
+ ? true
995
+ : null;
996
+ // document.getElementById('id_rows_to_delete' + theRowNum).checked = false;
997
+ }
998
+ } // end 4
999
+
1000
+ // 5. Sets the new color...
1001
+ if (newColor) {
1002
+ var c = null;
1003
+ // 5.1 ... with DOM compatible browsers except Opera
1004
+ if (domDetect) {
1005
+ for (c = 0; c < rowCellsCnt; c++) {
1006
+ theCells[c].setAttribute('bgcolor', newColor, 0);
1007
+ } // end for
1008
+ }
1009
+ // 5.2 ... with other browsers
1010
+ else {
1011
+ for (c = 0; c < rowCellsCnt; c++) {
1012
+ theCells[c].style.backgroundColor = newColor;
1013
+ }
1014
+ }
1015
+ } // end 5
1016
+
1017
+ return true;
1018
+ } // end of the 'setPointer()' function
1019
+
1020
+
1021
+ /**
1022
+ * getElement
1023
+ */
1024
+ function getElement(e,f){
1025
+ if(document.layers){
1026
+ f=(f)?f:self;
1027
+ if(f.document.layers[e]) {
1028
+ return f.document.layers[e];
1029
+ }
1030
+ for(W=0;i<f.document.layers.length;W++) {
1031
+ return(getElement(e,fdocument.layers[W]));
1032
+ }
1033
+ }
1034
+ if(document.all) {
1035
+ return document.all[e];
1036
+ }
1037
+ return document.getElementById(e);
1038
+ }
1039
+ </script>
1040
+ </head>
1041
+ <body bgcolor="white">
1042
+ <table cellspacing="4" width ="80%" align="center">
1043
+ <tr>
1044
+ <td colspan="3" align="left" class="normal_trace_entry">
1045
+ <p class="data" style="font-size: large; margin: 0px; border-color: red;"><b>Exception raised!</b><br />
1046
+ <b>%exception%</b>: <b>%message%</b><br />
1047
+ Time: <b>%time%</b>
1048
+ </p>
1049
+ </td>
1050
+ </tr>
1051
+ <tr>
1052
+ <td class="trace_header"><b>File</b></td>
1053
+ <td class="trace_header"><b>Line</b></td>
1054
+ <td class="trace_header"><b>Info</b></td>
1055
+ </tr>
1056
+ START:backtrace
1057
+ IFNOT:from_stdlib
1058
+ <tr bgcolor="white" onmouseover="setPointer(this, %iteration_id%, 'over', 'white', '#CCFFCC', '#FFB2B2');" onmouseout="setPointer(this, %iteration_id%, 'out', 'white', '#CCFFCC', '#FFB2B2');" onmousedown="toggleCode('src%iteration_id%'); setPointer(this, %iteration_id%, 'click', 'white', '#CCFFCC', '#FFB2B2');">
1059
+ IF:text
1060
+ <td bgcolor="white" class="normal_trace_entry">%file%</td>
1061
+ <td bgcolor="white" class="normal_trace_entry">%line%</td>
1062
+ <td bgcolor="white" class="normal_trace_entry">%text%</td>
1063
+ ENDIF:text
1064
+ IFNOT:text
1065
+ <td bgcolor="white" class="normal_trace_entry">%file%</td>
1066
+ <td bgcolor="white" class="normal_trace_entry" colspan="2">%line%</td>
1067
+ ENDIF:text
1068
+ IF:source
1069
+ </tr>
1070
+ <tr>
1071
+ <td colspan="3"><div id="src%iteration_id%" class="source"><tt><pre>%source%</pre></tt></div></td>
1072
+ ENDIF:source
1073
+ ENDIF:from_stdlib
1074
+ IF:from_stdlib
1075
+ <tr bgcolor="white" onmouseover="setPointer(this, %iteration_id%, 'over', 'white', '#CCFFCC', '#FFB2B2');" onmouseout="setPointer(this, %iteration_id%, 'out', 'white', '#CCFFCC', '#FFB2B2');" onmousedown="setPointer(this, %iteration_id%, 'click', 'white', '#CCFFCC', '#FFB2B2');">
1076
+ IF:text
1077
+ <td bgcolor="white" class="stdlib_trace_entry">%file%</td>
1078
+ <td bgcolor="white" class="stdlib_trace_entry">%line%</td>
1079
+ <td bgcolor="white" class="stdlib_trace_entry">%text%</td>
1080
+ ENDIF:text
1081
+ IFNOT:text
1082
+ <td bgcolor="white" class="stdlib_trace_entry">%file%</td>
1083
+ <td bgcolor="white" class="stdlib_trace_entry" colspan="2">%line%</td>
1084
+ ENDIF:text
1085
+ ENDIF:from_stdlib
1086
+ </tr>
1087
+
1088
+ END:backtrace
1089
+ </table>
1090
+ </body>
1091
+ </html>