pidgin2adium 3.0.1 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +22 -0
- data/.gitignore +7 -0
- data/{History.txt → ChangeLog} +11 -0
- data/Gemfile +1 -9
- data/README.rdoc +38 -39
- data/Rakefile +4 -2
- data/VERSION +1 -1
- data/bin/pidgin2adium +63 -54
- data/ext/balance_tags_c/balance_tags_c.c +161 -161
- data/lib/pidgin2adium.rb +97 -97
- data/lib/pidgin2adium/balance_tags.rb +2 -2
- data/lib/pidgin2adium/basic_parser.rb +412 -0
- data/lib/pidgin2adium/html_log_parser.rb +125 -0
- data/lib/pidgin2adium/log_converter.rb +12 -13
- data/lib/pidgin2adium/log_file.rb +1 -1
- data/lib/pidgin2adium/log_parser.rb +3 -618
- data/lib/pidgin2adium/message.rb +97 -0
- data/lib/pidgin2adium/text_log_parser.rb +39 -0
- data/pidgin2adium.gemspec +31 -9
- data/spec/balance_tags_c_extn_spec.rb +47 -0
- data/spec/basic_parser_spec.rb +217 -0
- data/spec/html_log_parser_spec.rb +150 -0
- data/spec/log_converter_spec.rb +48 -0
- data/spec/log_file_spec.rb +168 -0
- data/spec/logfiles/2006-12-21.223606.txt +3 -0
- data/spec/logfiles/2008-01-15.071445-0500PST.htm +5 -0
- data/spec/logfiles/2008-01-15.071445-0500PST.html +5 -0
- data/spec/pidgin2adium_spec.rb +248 -3
- data/spec/spec_helper.rb +69 -16
- data/spec/test-output/README.md +1 -0
- data/spec/test-output/html_log_output.xml +6 -0
- data/spec/test-output/text_log_output.xml +4 -0
- data/spec/text_log_parser_spec.rb +42 -0
- data/tasks/extconf/balance_tags_c.rake +5 -1
- metadata +40 -26
- data/bin/pidgin2adium_profiler +0 -1
- data/tasks/build_profiler.rake +0 -49
data/.autotest
CHANGED
@@ -1,4 +1,26 @@
|
|
1
|
+
require 'autotest/growl'
|
2
|
+
require 'autotest/fsevent'
|
3
|
+
|
1
4
|
Autotest.add_hook :initialize do |at|
|
5
|
+
# Modifying ext/balance_tags_c.c triggers spec/balance_tags_c_extn_spec.rb
|
6
|
+
at.add_mapping(/ext\/.*\/(.*)\.[ch]/) do |_, m|
|
7
|
+
["spec/#{m[1]}_extn_spec.rb"]
|
8
|
+
end
|
9
|
+
|
10
|
+
at.add_mapping(%r{lib/pidgin2adium.rb}) do |_, m|
|
11
|
+
["spec/pidgin2adium_spec.rb"]
|
12
|
+
end
|
13
|
+
|
14
|
+
at.add_mapping(%r{lib/pidgin2adium/(.+_log_parser).rb}) do |_, m|
|
15
|
+
["spec/#{m[1]}_spec.rb"]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Re-run spec files when they change
|
19
|
+
at.add_mapping(%r{^spec/.+_spec.rb$}) do |filename, _|
|
20
|
+
filename
|
21
|
+
end
|
22
|
+
|
23
|
+
at.libs = %w{. lib spec}
|
2
24
|
end
|
3
25
|
|
4
26
|
Autotest.add_hook :run_command do |at|
|
data/.gitignore
CHANGED
data/{History.txt → ChangeLog}
RENAMED
@@ -1,3 +1,14 @@
|
|
1
|
+
=== 3.1.0 / 2010-08-13
|
2
|
+
* Compatible with Ruby 1.9!
|
3
|
+
- removed dependency on "parsedate" library, which 1.9 doesn't have
|
4
|
+
* log_parser.rb has been split into separate files (1 per class, more or less)
|
5
|
+
- "require pidgin2adium/log_parser" will still pull in all of the split-up
|
6
|
+
classes
|
7
|
+
* balance_tags_c extension really does work now
|
8
|
+
* Cleans up more junk from Pidgin logfiles when parsing
|
9
|
+
* Bugfixes and more graceful handling of error states
|
10
|
+
* Fully tested (except bin/pidgin2adium, which remains tricky)
|
11
|
+
|
1
12
|
=== 3.0.1 / 2010-08-07
|
2
13
|
Bugfix release:
|
3
14
|
* balance_tags_c.c: Use rb_eval_string instead of rb_reg_regcomp to avoid
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -43,47 +43,46 @@ The library style allows you to parse a log file and get back a LogFile[link:cla
|
|
43
43
|
require 'pidgin2adium'
|
44
44
|
logfile = Pidgin2Adium.parse("/path/to/log/file.html", "gabe,gbw,gabeb-w")
|
45
45
|
if logfile == false
|
46
|
-
|
46
|
+
puts "Oh no! Could not parse!"
|
47
47
|
else
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# Only StatusMessage has status
|
61
|
-
puts "Status: #{message.status}"
|
62
|
-
end
|
63
|
-
# Prints out the message in Adium log format
|
64
|
-
puts message.to_s
|
48
|
+
logfile.each do |message|
|
49
|
+
# Every Message subclass has sender, time, and buddy_alias
|
50
|
+
puts "Sender's screen name: #{message.sender}"
|
51
|
+
puts "Time message was sent: #{message.time}"
|
52
|
+
puts "Sender's alias: #{message.buddy_alias}"
|
53
|
+
if message.respond_to?(:body)
|
54
|
+
puts "Message body: #{message.body}"
|
55
|
+
if message.respond_to?(:event) # Pidgin2Adium::Event class
|
56
|
+
puts "Event type: #{message.event_type}"
|
57
|
+
end
|
58
|
+
elsif message.respond_to?(:status) # Pidgin2Adium::StatusMessage
|
59
|
+
puts "Status: #{message.status}"
|
65
60
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
61
|
+
# Prints out the message in Adium log format
|
62
|
+
puts message.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
success = logfile.write_out()
|
66
|
+
# To overwrite file if it exists:
|
67
|
+
# logfile.write_out(overwrite = true)
|
68
|
+
# To specify your own output dir (default = Pidgin2Adium::ADIUM_LOG_DIR):
|
69
|
+
# logfile.write_out(false, output_dir = my_dir)
|
70
|
+
# Or combine them:
|
71
|
+
# logfile.write_out(true, my_dir)
|
72
|
+
if success == false
|
73
|
+
puts "An error occurred!"
|
74
|
+
elsif success == Pidgin2Adium::FILE_EXISTS
|
75
|
+
# Not returned if overwrite set to true
|
76
|
+
puts "File already exists."
|
77
|
+
else
|
78
|
+
puts "Successfully wrote out log file!"
|
79
|
+
puts "Path to output file: #{success}"
|
80
|
+
end
|
81
|
+
# This deletes search indexes so Adium re-indexes the new chat logs.
|
82
|
+
# It is not automatically called after log_file.write_out()
|
83
|
+
# Call it after converting all the logs, since it takes up a bit of
|
84
|
+
# processing power.
|
85
|
+
Pidgin2Adium.delete_search_indexes()
|
87
86
|
end
|
88
87
|
|
89
88
|
===Example 2 (using library)
|
data/Rakefile
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
|
-
require 'bundler'
|
4
3
|
|
5
4
|
begin
|
6
5
|
require 'jeweler'
|
@@ -12,7 +11,9 @@ begin
|
|
12
11
|
gem.homepage = "http://github.com/gabebw/pidgin2adium"
|
13
12
|
gem.authors = ["Gabe Berke-Williams"]
|
14
13
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
-
gem.
|
14
|
+
gem.add_development_dependency(%q<bundler>, [">= 0.9.26"])
|
15
|
+
gem.add_development_dependency(%q<jeweler>, [">= 0"])
|
16
|
+
gem.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
16
17
|
end
|
17
18
|
Jeweler::GemcutterTasks.new
|
18
19
|
rescue LoadError
|
@@ -52,6 +53,7 @@ rescue Gem::LoadError
|
|
52
53
|
end
|
53
54
|
|
54
55
|
task :spec => :check_dependencies
|
56
|
+
task :spec => "extconf:compile"
|
55
57
|
|
56
58
|
task :default => :spec
|
57
59
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.0
|
1
|
+
3.1.0
|
data/bin/pidgin2adium
CHANGED
@@ -21,76 +21,85 @@ Written by Gabe Berke-Williams (gbw@rubyforge.org)
|
|
21
21
|
EOL
|
22
22
|
|
23
23
|
options = {}
|
24
|
+
|
24
25
|
oparser = OptionParser.new do |opts|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
26
|
+
opts.banner = "Usage: #{File.basename($0)} [options]"
|
27
|
+
opts.on('-i', '--in=IN_DIR', String, 'Directory where pidgin logs are stored') do |i|
|
28
|
+
options[:in] = i
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on('-a alias1,alias2', "--aliases alias1,alias2",
|
32
|
+
"A comma-separated list of your alias(es)",
|
33
|
+
"so this script knows which person in a chat",
|
34
|
+
"is you. Whitespace and case do not matter.") do |aliases|
|
35
|
+
options[:aliases] = aliases
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on('-o', '--out OUT_DIR',
|
39
|
+
'The top-level directory under which to',
|
40
|
+
'store the logs (each in its own folder',
|
41
|
+
'by screen name).',
|
42
|
+
"Defaults to: #{Pidgin2Adium::ADIUM_LOG_DIR}") do |out|
|
43
|
+
options[:output_dir] = out
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on('-f', '--force', 'If this is set, then logs in the Adium log',
|
47
|
+
'folder that have the same name as converted',
|
48
|
+
'logs will be overwritten.') do |f|
|
49
|
+
options[:force] = f
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on("-v", "--version", "Show version information") do
|
53
|
+
puts version
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
58
|
+
puts version
|
59
|
+
puts opts
|
60
|
+
exit
|
61
|
+
end
|
56
62
|
end
|
63
|
+
|
57
64
|
begin
|
58
|
-
|
65
|
+
oparser.parse!
|
59
66
|
rescue => bang
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
if bang.class == OptionParser::MissingArgument
|
68
|
+
# No argument provided for a switch that requires an argument.
|
69
|
+
puts '"%s" requires an argument.' % bang.args[0]
|
70
|
+
exit 1
|
71
|
+
elsif bang.class == OptionParser::InvalidOption
|
72
|
+
# Provided a switch that we don't handle.
|
73
|
+
puts '"%s" is not a valid switch.' % bang.args[0]
|
74
|
+
elsif bang.class == OptionParser::NeedlessArgument
|
75
|
+
# Raised when argument provided for a switch that doesn't take an argument.
|
76
|
+
puts bang.message
|
77
|
+
end
|
71
78
|
end
|
72
79
|
|
73
80
|
need_opts = false
|
74
81
|
required_opts = [[:i, :in], [:a, :aliases]]
|
82
|
+
|
75
83
|
required_opts.each do |short, long|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
84
|
+
if options.has_key?(long)
|
85
|
+
next
|
86
|
+
else
|
87
|
+
need_opts = true
|
88
|
+
puts "Required option -#{short}/--#{long} missing."
|
89
|
+
end
|
82
90
|
end
|
91
|
+
|
83
92
|
if need_opts
|
84
|
-
|
85
|
-
|
93
|
+
puts oparser.to_s
|
94
|
+
exit 1
|
86
95
|
end
|
87
96
|
|
88
97
|
extra_opts = {:overwrite => options[:force]}
|
89
98
|
if options.has_key?(:output_dir)
|
90
|
-
|
99
|
+
extra_opts[:output_dir] = options[:output_dir]
|
91
100
|
end
|
92
101
|
|
93
102
|
log_converter = Pidgin2Adium::LogConverter.new(options[:in],
|
94
|
-
|
95
|
-
|
103
|
+
options[:aliases],
|
104
|
+
extra_opts)
|
96
105
|
log_converter.start
|
@@ -28,171 +28,171 @@ static VALUE mP2A;
|
|
28
28
|
* Balances tags of _text_. Returns modified text.
|
29
29
|
*/
|
30
30
|
VALUE balance_tags_c(VALUE mod, VALUE text){
|
31
|
-
|
32
|
-
|
31
|
+
if( TYPE(text) != T_STRING ){
|
32
|
+
rb_raise(rb_eArgError, "bad argument to balance_tags_c, String only please.");
|
33
|
+
}
|
34
|
+
VALUE tagstack = rb_ary_new2(1);
|
35
|
+
int stacksize = 0;
|
36
|
+
VALUE tagqueue = rb_str_new2("");
|
37
|
+
VALUE ZERO = INT2FIX(0),
|
38
|
+
ONE = INT2FIX(1);
|
39
|
+
VALUE newtext = rb_str_new2("");
|
40
|
+
// Known single-entity/self-closing tags
|
41
|
+
VALUE single_tags = rb_ary_new3(5,
|
42
|
+
rb_str_new2("br"),
|
43
|
+
rb_str_new2("hr"),
|
44
|
+
rb_str_new2("img"),
|
45
|
+
rb_str_new2("input"),
|
46
|
+
rb_str_new2("meta"));
|
47
|
+
// Tags that can be immediately nested within themselves
|
48
|
+
VALUE nestable_tags = rb_ary_new3(4,
|
49
|
+
rb_str_new2("blockquote"),
|
50
|
+
rb_str_new2("div"),
|
51
|
+
rb_str_new2("span"),
|
52
|
+
rb_str_new2("font"));
|
53
|
+
// 1: tagname, with possible leading "/"
|
54
|
+
// 2: attributes
|
55
|
+
VALUE tag_regex = rb_eval_string("/<(\\/?\\w*)\\s*([^>]*)>/");
|
56
|
+
VALUE pos; // position in text
|
57
|
+
VALUE match;
|
58
|
+
VALUE tag;
|
59
|
+
VALUE attributes;
|
60
|
+
VALUE t; // loop variable when iterating over tagstack at end of while loop
|
61
|
+
int matchlen;
|
62
|
+
int done = 0;
|
63
|
+
int j, k, i; // loop counters
|
64
|
+
|
65
|
+
// WP bug fix for comments - in case you REALLY meant to type '< !--'
|
66
|
+
rb_funcall(text, rb_intern("gsub!"), 2,
|
67
|
+
rb_str_new2("< !--"),
|
68
|
+
rb_str_new2("< !--"));
|
69
|
+
|
70
|
+
// WP bug fix for LOVE <3 (and other situations with '<' before a number)
|
71
|
+
rb_funcall(text,rb_intern("gsub!"), 2,
|
72
|
+
rb_eval_string("/<([0-9]{1})/"),
|
73
|
+
rb_str_new2("<\\1"));
|
74
|
+
|
75
|
+
pos = rb_funcall(text, rb_intern("=~"), 1, tag_regex);
|
76
|
+
done = (pos == Qnil);
|
77
|
+
while ( ! done ){
|
78
|
+
rb_str_concat(newtext, tagqueue);
|
79
|
+
match = rb_funcall(text, rb_intern("match"), 1, tag_regex);
|
80
|
+
tag = rb_funcall(rb_reg_nth_match(1, match), rb_intern("downcase"), 0);
|
81
|
+
attributes = rb_reg_nth_match(2, match);
|
82
|
+
|
83
|
+
matchlen = NUM2INT(rb_funcall(rb_reg_nth_match(0, match), rb_intern("size"), 0));
|
84
|
+
|
85
|
+
// clear the shifter
|
86
|
+
tagqueue = rb_str_new2("");
|
87
|
+
// Pop or Push
|
88
|
+
if (0 == rb_str_cmp(rb_str_substr(tag, 0, 1), rb_str_new2("/"))){ // End Tag
|
89
|
+
rb_funcall(tag, rb_intern("slice!"), 2, ZERO, ONE);
|
90
|
+
// if too many closing tags
|
91
|
+
if(stacksize <= 0){
|
92
|
+
tag = rb_str_new2("");
|
93
|
+
//or close to be safe: tag = '/' << tag
|
94
|
+
} else if (0 == rb_str_cmp(RARRAY_PTR(tagstack)[stacksize - 1], tag)){
|
95
|
+
// found closing tag
|
96
|
+
// if stacktop value == tag close value then pop
|
97
|
+
// Close Tag
|
98
|
+
tag = rb_str_append(rb_str_new2("</"), tag);
|
99
|
+
rb_str_concat(tag, rb_str_new2(">"));
|
100
|
+
// Pop
|
101
|
+
rb_ary_pop(tagstack);
|
102
|
+
stacksize--;
|
103
|
+
} else { // closing tag not at top, search for it
|
104
|
+
for(j=stacksize-1; j>=0; j--){
|
105
|
+
if(0 == rb_str_cmp(RARRAY_PTR(tagstack)[j], tag) ){
|
106
|
+
// add tag to tagqueue
|
107
|
+
for(k = stacksize-1;k>=j;k--){
|
108
|
+
rb_str_concat(tagqueue, rb_str_new2("</"));
|
109
|
+
rb_str_concat(tagqueue, rb_ary_pop(tagstack));
|
110
|
+
rb_str_concat(tagqueue, rb_str_new2(">"));
|
111
|
+
stacksize--;
|
112
|
+
}
|
113
|
+
break;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
tag = rb_str_new2("");
|
117
|
+
}
|
118
|
+
} else {
|
119
|
+
// Begin Tag
|
120
|
+
|
121
|
+
// Tag Cleaning
|
122
|
+
|
123
|
+
if( ( RSTRING_LEN(attributes) > 0 && // test length before rb_str_substr
|
124
|
+
(0 == rb_str_cmp(rb_str_substr(attributes, -1, 1), rb_str_new2("/"))) ) ||
|
125
|
+
(0 == rb_str_cmp(tag, rb_str_new2(""))) ){
|
126
|
+
// If: self-closing or '', don't do anything.
|
127
|
+
} else if ( rb_ary_includes(single_tags, tag) ) {
|
128
|
+
// ElseIf: it's a known single-entity tag but it doesn't close itself, do so
|
129
|
+
rb_str_concat(attributes, rb_str_new2("/"));
|
130
|
+
} else {
|
131
|
+
// Push the tag onto the stack
|
132
|
+
// If the top of the stack is the same as the tag we want
|
133
|
+
// to push, close previous tag
|
134
|
+
if ( (stacksize > 0) &&
|
135
|
+
(Qfalse == rb_ary_includes(nestable_tags, tag)) &&
|
136
|
+
(0 == rb_str_cmp(rb_ary_entry(tagstack, stacksize - 1), tag))){
|
137
|
+
tagqueue = rb_str_new2("</");
|
138
|
+
rb_str_concat(tagqueue, rb_ary_pop(tagstack));
|
139
|
+
rb_str_concat(tagqueue, rb_str_new2(">"));
|
140
|
+
stacksize--;
|
141
|
+
}
|
142
|
+
rb_ary_push(tagstack, tag);
|
143
|
+
stacksize++;
|
144
|
+
}
|
145
|
+
|
146
|
+
// Attributes
|
147
|
+
if( 0 != rb_str_cmp(attributes, rb_str_new2("")) ){
|
148
|
+
attributes = rb_str_plus(rb_str_new2(" "), attributes);
|
149
|
+
}
|
150
|
+
tag = rb_str_plus(rb_str_new2("<"), tag);
|
151
|
+
rb_str_concat(tag, attributes);
|
152
|
+
rb_str_concat(tag, rb_str_new2(">"));
|
153
|
+
//If already queuing a close tag, then put this tag on, too
|
154
|
+
if( RSTRING_LEN(tagqueue) > 0 ){
|
155
|
+
rb_str_concat(tagqueue, tag);
|
156
|
+
tag = rb_str_new2("");
|
157
|
+
}
|
33
158
|
}
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
VALUE newtext = rb_str_new2("");
|
40
|
-
// Known single-entity/self-closing tags
|
41
|
-
VALUE single_tags = rb_ary_new3(5,
|
42
|
-
rb_str_new2("br"),
|
43
|
-
rb_str_new2("hr"),
|
44
|
-
rb_str_new2("img"),
|
45
|
-
rb_str_new2("input"),
|
46
|
-
rb_str_new2("meta"));
|
47
|
-
// Tags that can be immediately nested within themselves
|
48
|
-
VALUE nestable_tags = rb_ary_new3(4,
|
49
|
-
rb_str_new2("blockquote"),
|
50
|
-
rb_str_new2("div"),
|
51
|
-
rb_str_new2("span"),
|
52
|
-
rb_str_new2("font"));
|
53
|
-
// 1: tagname, with possible leading "/"
|
54
|
-
// 2: attributes
|
55
|
-
VALUE tag_regex = rb_eval_string("/<(\\/?\\w*)\\s*([^>]*)>/");
|
56
|
-
VALUE pos; // position in text
|
57
|
-
VALUE match;
|
58
|
-
VALUE tag;
|
59
|
-
VALUE attributes;
|
60
|
-
VALUE t; // loop variable when iterating over tagstack at end of while loop
|
61
|
-
int matchlen;
|
62
|
-
int done = 0;
|
63
|
-
int j, k, i; // loop counters
|
64
|
-
|
65
|
-
// WP bug fix for comments - in case you REALLY meant to type '< !--'
|
66
|
-
rb_funcall(text, rb_intern("gsub!"), 2,
|
67
|
-
rb_str_new2("< !--"),
|
68
|
-
rb_str_new2("< !--"));
|
69
|
-
|
70
|
-
// WP bug fix for LOVE <3 (and other situations with '<' before a number)
|
71
|
-
rb_funcall(text,rb_intern("gsub!"), 2,
|
72
|
-
rb_eval_string("/<([0-9]{1})/"),
|
73
|
-
rb_str_new2("<\\1"));
|
74
|
-
|
159
|
+
rb_str_concat(newtext,
|
160
|
+
rb_str_plus(rb_str_substr(text, 0, NUM2INT(pos)), tag));
|
161
|
+
text = rb_str_substr(text,
|
162
|
+
NUM2INT(pos)+matchlen,
|
163
|
+
RSTRING_LEN(text) - (NUM2INT(pos)+matchlen));
|
75
164
|
pos = rb_funcall(text, rb_intern("=~"), 1, tag_regex);
|
76
165
|
done = (pos == Qnil);
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
for(j=stacksize-1; j>=0; j--){
|
105
|
-
if(0 == rb_str_cmp(RARRAY_PTR(tagstack)[j], tag) ){
|
106
|
-
// add tag to tagqueue
|
107
|
-
for(k = stacksize-1;k>=j;k--){
|
108
|
-
rb_str_concat(tagqueue, rb_str_new2("</"));
|
109
|
-
rb_str_concat(tagqueue, rb_ary_pop(tagstack));
|
110
|
-
rb_str_concat(tagqueue, rb_str_new2(">"));
|
111
|
-
stacksize--;
|
112
|
-
}
|
113
|
-
break;
|
114
|
-
}
|
115
|
-
}
|
116
|
-
tag = rb_str_new2("");
|
117
|
-
}
|
118
|
-
} else {
|
119
|
-
// Begin Tag
|
120
|
-
|
121
|
-
// Tag Cleaning
|
122
|
-
|
123
|
-
if( ( RSTRING_LEN(attributes) > 0 && // test length before rb_str_substr
|
124
|
-
(0 == rb_str_cmp(rb_str_substr(attributes, -1, 1), rb_str_new2("/"))) ) ||
|
125
|
-
(0 == rb_str_cmp(tag, rb_str_new2(""))) ){
|
126
|
-
// If: self-closing or '', don't do anything.
|
127
|
-
} else if ( rb_ary_includes(single_tags, tag) ) {
|
128
|
-
// ElseIf: it's a known single-entity tag but it doesn't close itself, do so
|
129
|
-
rb_str_concat(attributes, rb_str_new2("/"));
|
130
|
-
} else {
|
131
|
-
// Push the tag onto the stack
|
132
|
-
// If the top of the stack is the same as the tag we want
|
133
|
-
// to push, close previous tag
|
134
|
-
if ( (stacksize > 0) &&
|
135
|
-
(Qfalse == rb_ary_includes(nestable_tags, tag)) &&
|
136
|
-
(0 == rb_str_cmp(rb_ary_entry(tagstack, stacksize - 1), tag))){
|
137
|
-
tagqueue = rb_str_new2("</");
|
138
|
-
rb_str_concat(tagqueue, rb_ary_pop(tagstack));
|
139
|
-
rb_str_concat(tagqueue, rb_str_new2(">"));
|
140
|
-
stacksize--;
|
141
|
-
}
|
142
|
-
rb_ary_push(tagstack, tag);
|
143
|
-
stacksize++;
|
144
|
-
}
|
145
|
-
|
146
|
-
// Attributes
|
147
|
-
if( 0 != rb_str_cmp(attributes, rb_str_new2("")) ){
|
148
|
-
attributes = rb_str_plus(rb_str_new2(" "), attributes);
|
149
|
-
}
|
150
|
-
tag = rb_str_plus(rb_str_new2("<"), tag);
|
151
|
-
rb_str_concat(tag, attributes);
|
152
|
-
rb_str_concat(tag, rb_str_new2(">"));
|
153
|
-
//If already queuing a close tag, then put this tag on, too
|
154
|
-
if( RSTRING_LEN(tagqueue) > 0 ){
|
155
|
-
rb_str_concat(tagqueue, tag);
|
156
|
-
tag = rb_str_new2("");
|
157
|
-
}
|
158
|
-
}
|
159
|
-
rb_str_concat(newtext,
|
160
|
-
rb_str_plus(rb_str_substr(text, 0, pos-1), tag));
|
161
|
-
text = rb_str_substr(text,
|
162
|
-
NUM2INT(pos)+matchlen,
|
163
|
-
RSTRING_LEN(text) - (NUM2INT(pos)+matchlen));
|
164
|
-
pos = rb_funcall(text, rb_intern("=~"), 1, tag_regex);
|
165
|
-
done = (pos == Qnil);
|
166
|
-
}
|
167
|
-
|
168
|
-
// Clear Tag Queue
|
169
|
-
rb_str_concat(newtext, tagqueue);
|
170
|
-
|
171
|
-
// Add Remaining text
|
172
|
-
rb_str_concat(newtext, text);
|
173
|
-
|
174
|
-
i = NUM2INT(rb_funcall(tagstack, rb_intern("length"), 0)) - 1;
|
175
|
-
// Empty Stack
|
176
|
-
for(; i >= 0; i--){
|
177
|
-
// Add remaining tags to close
|
178
|
-
t = RARRAY_PTR(tagstack)[i];
|
179
|
-
rb_str_concat(newtext, rb_str_new2("</"));
|
180
|
-
rb_str_concat(newtext, t);
|
181
|
-
rb_str_concat(newtext, rb_str_new2(">"));
|
182
|
-
}
|
183
|
-
|
184
|
-
// WP fix for the bug with HTML comments
|
185
|
-
rb_funcall(newtext, rb_intern("gsub!"), 2,
|
186
|
-
rb_str_new2("< !--"),
|
187
|
-
rb_str_new2("<!--"));
|
188
|
-
rb_funcall(newtext, rb_intern("gsub!"), 2,
|
189
|
-
rb_str_new2("< !--"),
|
190
|
-
rb_str_new2("< !--"));
|
191
|
-
|
192
|
-
return newtext;
|
166
|
+
}
|
167
|
+
|
168
|
+
// Clear Tag Queue
|
169
|
+
rb_str_concat(newtext, tagqueue);
|
170
|
+
|
171
|
+
// Add Remaining text
|
172
|
+
rb_str_concat(newtext, text);
|
173
|
+
|
174
|
+
i = NUM2INT(rb_funcall(tagstack, rb_intern("length"), 0)) - 1;
|
175
|
+
// Empty Stack
|
176
|
+
for(; i >= 0; i--){
|
177
|
+
// Add remaining tags to close
|
178
|
+
t = RARRAY_PTR(tagstack)[i];
|
179
|
+
rb_str_concat(newtext, rb_str_new2("</"));
|
180
|
+
rb_str_concat(newtext, t);
|
181
|
+
rb_str_concat(newtext, rb_str_new2(">"));
|
182
|
+
}
|
183
|
+
|
184
|
+
// WP fix for the bug with HTML comments
|
185
|
+
rb_funcall(newtext, rb_intern("gsub!"), 2,
|
186
|
+
rb_str_new2("< !--"),
|
187
|
+
rb_str_new2("<!--"));
|
188
|
+
rb_funcall(newtext, rb_intern("gsub!"), 2,
|
189
|
+
rb_str_new2("< !--"),
|
190
|
+
rb_str_new2("< !--"));
|
191
|
+
|
192
|
+
return newtext;
|
193
193
|
}
|
194
194
|
|
195
195
|
void Init_balance_tags_c(){
|
196
|
-
|
197
|
-
|
196
|
+
mP2A = rb_define_module("Pidgin2Adium");
|
197
|
+
rb_define_module_function(mP2A, "balance_tags_c", balance_tags_c, 1);
|
198
198
|
}
|