rinku 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -2
- data/ext/rinku/autolink.h +0 -9
- data/ext/rinku/rinku.c +227 -18
- data/lib/rails_rinku.rb +13 -27
- data/lib/rinku.rb +3 -2
- data/rinku.gemspec +1 -2
- data/test/autolink_test.rb +1 -1
- metadata +7 -6
- data/ext/rinku/html_autolink.c +0 -226
data/Rakefile
CHANGED
@@ -56,8 +56,7 @@ task :gather => 'upskirt/src/markdown.h' do |t|
|
|
56
56
|
files =
|
57
57
|
FileList[
|
58
58
|
'upskirt/src/{buffer,autolink}.h',
|
59
|
-
'upskirt/src/{buffer,autolink}.c'
|
60
|
-
'upskirt/html/html_autolink.c'
|
59
|
+
'upskirt/src/{buffer,autolink}.c'
|
61
60
|
]
|
62
61
|
cp files, 'ext/rinku/',
|
63
62
|
:preserve => true,
|
data/ext/rinku/autolink.h
CHANGED
@@ -19,15 +19,6 @@
|
|
19
19
|
|
20
20
|
#include "buffer.h"
|
21
21
|
|
22
|
-
typedef enum {
|
23
|
-
AUTOLINK_URLS = (1 << 0),
|
24
|
-
AUTOLINK_EMAILS = (1 << 1),
|
25
|
-
AUTOLINK_ALL = AUTOLINK_URLS|AUTOLINK_EMAILS,
|
26
|
-
AUTOLINK_SANITIZE_NAMES = (1 << 2),
|
27
|
-
AUTOLINK_SANITIZE_URLS = (1 << 3),
|
28
|
-
AUTOLINK_SANITIZE = AUTOLINK_SANITIZE_NAMES|AUTOLINK_SANITIZE_URLS,
|
29
|
-
} autolink_mode;
|
30
|
-
|
31
22
|
extern size_t
|
32
23
|
ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
|
33
24
|
|
data/ext/rinku/rinku.c
CHANGED
@@ -27,17 +27,223 @@
|
|
27
27
|
#include "autolink.h"
|
28
28
|
#include "buffer.h"
|
29
29
|
|
30
|
-
|
30
|
+
#include <string.h>
|
31
|
+
#include <stdlib.h>
|
32
|
+
#include <stdio.h>
|
33
|
+
#include <ctype.h>
|
34
|
+
|
35
|
+
static VALUE rb_mRinku;
|
36
|
+
|
37
|
+
typedef enum {
|
38
|
+
AUTOLINK_URLS = (1 << 0),
|
39
|
+
AUTOLINK_EMAILS = (1 << 1),
|
40
|
+
AUTOLINK_ALL = AUTOLINK_URLS|AUTOLINK_EMAILS
|
41
|
+
} autolink_mode;
|
42
|
+
|
43
|
+
typedef size_t (*autolink_parse_cb)(size_t *rewind, struct buf *, char *, size_t, size_t);
|
44
|
+
|
45
|
+
typedef enum {
|
46
|
+
AUTOLINK_ACTION_NONE = 0,
|
47
|
+
AUTOLINK_ACTION_WWW,
|
48
|
+
AUTOLINK_ACTION_EMAIL,
|
49
|
+
AUTOLINK_ACTION_URL,
|
50
|
+
AUTOLINK_ACTION_SKIP_TAG
|
51
|
+
} autolink_action;
|
52
|
+
|
53
|
+
static autolink_parse_cb g_callbacks[] = {
|
54
|
+
NULL,
|
55
|
+
ups_autolink__www, /* 1 */
|
56
|
+
ups_autolink__email,/* 2 */
|
57
|
+
ups_autolink__url, /* 3 */
|
58
|
+
};
|
59
|
+
|
60
|
+
static const char *g_hrefs[] = {
|
61
|
+
NULL,
|
62
|
+
"<a href=\"http://",
|
63
|
+
"<a href=\"mailto:",
|
64
|
+
"<a href=\"",
|
65
|
+
};
|
66
|
+
|
67
|
+
static void
|
68
|
+
autolink_escape_cb(struct buf *ob, const struct buf *text, void *unused)
|
69
|
+
{
|
70
|
+
size_t i = 0, org;
|
71
|
+
|
72
|
+
while (i < text->size) {
|
73
|
+
org = i;
|
74
|
+
|
75
|
+
while (i < text->size &&
|
76
|
+
text->data[i] != '<' &&
|
77
|
+
text->data[i] != '>' &&
|
78
|
+
text->data[i] != '&' &&
|
79
|
+
text->data[i] != '"')
|
80
|
+
i++;
|
81
|
+
|
82
|
+
if (i > org)
|
83
|
+
bufput(ob, text->data + org, i - org);
|
84
|
+
|
85
|
+
if (i >= text->size)
|
86
|
+
break;
|
87
|
+
|
88
|
+
switch (text->data[i]) {
|
89
|
+
case '<': BUFPUTSL(ob, "<"); break;
|
90
|
+
case '>': BUFPUTSL(ob, ">"); break;
|
91
|
+
case '&': BUFPUTSL(ob, "&"); break;
|
92
|
+
case '"': BUFPUTSL(ob, """); break;
|
93
|
+
default: bufputc(ob, text->data[i]); break;
|
94
|
+
}
|
95
|
+
|
96
|
+
i++;
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
static inline int
|
101
|
+
is_closing_a(const char *tag, size_t size)
|
102
|
+
{
|
103
|
+
size_t i;
|
104
|
+
|
105
|
+
if (tag[0] != '<' || size < STRLEN("</a>") || tag[1] != '/')
|
106
|
+
return 0;
|
107
|
+
|
108
|
+
i = 2;
|
109
|
+
|
110
|
+
while (i < size && isspace(tag[i]))
|
111
|
+
i++;
|
112
|
+
|
113
|
+
if (i == size || tag[i] != 'a')
|
114
|
+
return 0;
|
115
|
+
|
116
|
+
i++;
|
117
|
+
|
118
|
+
while (i < size && isspace(tag[i]))
|
119
|
+
i++;
|
120
|
+
|
121
|
+
if (i == size || tag[i] != '>')
|
122
|
+
return 0;
|
123
|
+
|
124
|
+
return i;
|
125
|
+
}
|
126
|
+
|
127
|
+
static size_t
|
128
|
+
autolink__skip_tag(struct buf *ob, char *text, size_t size)
|
129
|
+
{
|
130
|
+
size_t i = 0;
|
131
|
+
|
132
|
+
while (i < size && text[i] != '>')
|
133
|
+
i++;
|
134
|
+
|
135
|
+
if (size > 3 && text[1] == 'a' && isspace(text[2])) {
|
136
|
+
while (i < size) {
|
137
|
+
size_t tag_len = is_closing_a(text + i, size - i);
|
138
|
+
if (tag_len) {
|
139
|
+
i += tag_len;
|
140
|
+
break;
|
141
|
+
}
|
142
|
+
i++;
|
143
|
+
}
|
144
|
+
}
|
31
145
|
|
32
|
-
|
33
|
-
|
146
|
+
return i + 1;
|
147
|
+
}
|
148
|
+
|
149
|
+
int
|
150
|
+
rinku_autolink(
|
34
151
|
struct buf *ob,
|
35
152
|
struct buf *text,
|
36
153
|
unsigned int flags,
|
37
154
|
const char *link_attr,
|
38
155
|
void (*link_text_cb)(struct buf *ob, const struct buf *link, void *payload),
|
39
|
-
void *payload)
|
156
|
+
void *payload)
|
157
|
+
{
|
158
|
+
size_t i, end;
|
159
|
+
struct buf *link = bufnew(16);
|
160
|
+
char active_chars[256];
|
161
|
+
void (*link_url_cb)(struct buf *, const struct buf *, void *);
|
162
|
+
int link_count = 0;
|
163
|
+
|
164
|
+
if (!text || text->size == 0)
|
165
|
+
return;
|
166
|
+
|
167
|
+
memset(active_chars, 0x0, sizeof(active_chars));
|
168
|
+
|
169
|
+
active_chars['<'] = AUTOLINK_ACTION_SKIP_TAG;
|
170
|
+
|
171
|
+
if (flags & AUTOLINK_EMAILS)
|
172
|
+
active_chars['@'] = AUTOLINK_ACTION_EMAIL;
|
173
|
+
|
174
|
+
if (flags & AUTOLINK_URLS) {
|
175
|
+
active_chars['w'] = AUTOLINK_ACTION_WWW;
|
176
|
+
active_chars['W'] = AUTOLINK_ACTION_WWW;
|
177
|
+
active_chars[':'] = AUTOLINK_ACTION_URL;
|
178
|
+
}
|
179
|
+
|
180
|
+
if (link_text_cb == NULL)
|
181
|
+
link_text_cb = &autolink_escape_cb;
|
182
|
+
|
183
|
+
if (link_attr != NULL) {
|
184
|
+
while (isspace(*link_attr))
|
185
|
+
link_attr++;
|
186
|
+
}
|
187
|
+
|
188
|
+
bufgrow(ob, text->size);
|
189
|
+
|
190
|
+
i = end = 0;
|
191
|
+
|
192
|
+
while (i < text->size) {
|
193
|
+
size_t rewind, link_end;
|
194
|
+
char action;
|
195
|
+
|
196
|
+
while (end < text->size && (action = active_chars[(int)text->data[end]]) == 0)
|
197
|
+
end++;
|
198
|
+
|
199
|
+
if (end == text->size) {
|
200
|
+
if (link_count > 0)
|
201
|
+
bufput(ob, text->data + i, end - i);
|
202
|
+
break;
|
203
|
+
}
|
40
204
|
|
205
|
+
if (action == AUTOLINK_ACTION_SKIP_TAG) {
|
206
|
+
end += autolink__skip_tag(ob, text->data + end, text->size - end);
|
207
|
+
continue;
|
208
|
+
}
|
209
|
+
|
210
|
+
link->size = 0;
|
211
|
+
link_end = g_callbacks[(int)action](&rewind, link, text->data + end, end, text->size - end);
|
212
|
+
|
213
|
+
/* print the link */
|
214
|
+
if (link_end > 0) {
|
215
|
+
bufput(ob, text->data + i, end - i - rewind);
|
216
|
+
|
217
|
+
bufputs(ob, g_hrefs[(int)action]);
|
218
|
+
autolink_escape_cb(ob, link, NULL);
|
219
|
+
|
220
|
+
if (link_attr) {
|
221
|
+
BUFPUTSL(ob, "\" ");
|
222
|
+
bufputs(ob, link_attr);
|
223
|
+
bufputc(ob, '>');
|
224
|
+
} else {
|
225
|
+
BUFPUTSL(ob, "\">");
|
226
|
+
}
|
227
|
+
|
228
|
+
link_text_cb(ob, link, payload);
|
229
|
+
BUFPUTSL(ob, "</a>");
|
230
|
+
|
231
|
+
link_count++;
|
232
|
+
i = end + link_end;
|
233
|
+
end = i;
|
234
|
+
} else {
|
235
|
+
end = end + 1;
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
bufrelease(link);
|
240
|
+
return link_count;
|
241
|
+
}
|
242
|
+
|
243
|
+
|
244
|
+
/**
|
245
|
+
* Ruby code
|
246
|
+
*/
|
41
247
|
static void
|
42
248
|
autolink_callback(struct buf *link_text, const struct buf *link, void *block)
|
43
249
|
{
|
@@ -97,7 +303,7 @@ rb_rinku_autolink(int argc, VALUE *argv, VALUE self)
|
|
97
303
|
{
|
98
304
|
VALUE result, rb_text, rb_mode, rb_html, rb_block;
|
99
305
|
struct buf input_buf = {0, 0, 0, 0, 0}, *output_buf;
|
100
|
-
int link_mode;
|
306
|
+
int link_mode, count;
|
101
307
|
const char *link_attr = NULL;
|
102
308
|
ID mode_sym;
|
103
309
|
|
@@ -119,7 +325,7 @@ rb_rinku_autolink(int argc, VALUE *argv, VALUE self)
|
|
119
325
|
|
120
326
|
input_buf.data = RSTRING_PTR(rb_text);
|
121
327
|
input_buf.size = RSTRING_LEN(rb_text);
|
122
|
-
output_buf = bufnew(
|
328
|
+
output_buf = bufnew(32);
|
123
329
|
|
124
330
|
if (mode_sym == rb_intern("all"))
|
125
331
|
link_mode = AUTOLINK_ALL;
|
@@ -131,26 +337,29 @@ rb_rinku_autolink(int argc, VALUE *argv, VALUE self)
|
|
131
337
|
rb_raise(rb_eTypeError,
|
132
338
|
"Invalid linking mode (possible values are :all, :urls, :email_addresses)");
|
133
339
|
|
134
|
-
|
340
|
+
count = rinku_autolink(
|
341
|
+
output_buf,
|
342
|
+
&input_buf,
|
343
|
+
link_mode,
|
344
|
+
link_attr,
|
345
|
+
RTEST(rb_block) ? &autolink_callback : NULL,
|
346
|
+
(void*)rb_block);
|
135
347
|
|
136
|
-
if (
|
137
|
-
|
138
|
-
else
|
139
|
-
|
348
|
+
if (count == 0)
|
349
|
+
result = rb_text;
|
350
|
+
else {
|
351
|
+
result = rb_str_new(output_buf->data, output_buf->size);
|
352
|
+
rb_enc_copy(result, rb_text);
|
353
|
+
}
|
140
354
|
|
141
|
-
result = rb_str_new(output_buf->data, output_buf->size);
|
142
355
|
bufrelease(output_buf);
|
143
|
-
|
144
|
-
/* force the input encoding */
|
145
|
-
rb_enc_copy(result, rb_text);
|
146
|
-
|
147
356
|
return result;
|
148
357
|
}
|
149
358
|
|
150
359
|
void Init_rinku()
|
151
360
|
{
|
152
|
-
|
153
|
-
|
361
|
+
rb_mRinku = rb_define_module("Rinku");
|
362
|
+
rb_define_method(rb_mRinku, "auto_link", rb_rinku_autolink, -1);
|
154
363
|
}
|
155
364
|
|
156
365
|
|
data/lib/rails_rinku.rb
CHANGED
@@ -1,35 +1,21 @@
|
|
1
1
|
require 'rinku'
|
2
2
|
|
3
3
|
module RailsRinku
|
4
|
-
|
5
|
-
|
6
|
-
ActiveSupport.on_load(:action_view) do
|
7
|
-
require 'active_support/core_ext/object/blank'
|
8
|
-
require 'active_support/core_ext/array/extract_options'
|
9
|
-
require 'active_support/core_ext/hash/reverse_merge'
|
10
|
-
require 'active_support/core_ext/hash/keys'
|
4
|
+
def auto_link(text, *args, &block)
|
5
|
+
return ''.html_safe if text.blank?
|
11
6
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
return ''.html_safe if text.blank?
|
17
|
-
|
18
|
-
options = args.size == 2 ? {} : args.extract_options!
|
19
|
-
unless args.empty?
|
20
|
-
options[:link] = args[0] || :all
|
21
|
-
options[:html] = args[1] || {}
|
22
|
-
end
|
23
|
-
options.reverse_merge!(:link => :all, :html => {})
|
24
|
-
text = sanitize(text) unless options[:sanitize] == false
|
25
|
-
|
26
|
-
Rinku.auto_link(text, options[:link], tag_options(options[:html]), &block)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
7
|
+
options = args.size == 2 ? {} : args.extract_options!
|
8
|
+
unless args.empty?
|
9
|
+
options[:link] = args[0] || :all
|
10
|
+
options[:html] = args[1] || {}
|
32
11
|
end
|
12
|
+
options.reverse_merge!(:link => :all, :html => {})
|
13
|
+
text = sanitize(text) unless options[:sanitize] == false
|
14
|
+
|
15
|
+
Rinku.auto_link(text, options[:link], tag_options(options[:html]), &block)
|
33
16
|
end
|
34
17
|
end
|
35
18
|
|
19
|
+
module ActionView::Helpers::TextHelper
|
20
|
+
include RailsRinku
|
21
|
+
end
|
data/lib/rinku.rb
CHANGED
data/rinku.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'rinku'
|
3
|
-
s.version = '1.
|
3
|
+
s.version = '1.2.0'
|
4
4
|
s.summary = "Mostly autolinking"
|
5
5
|
s.description = <<-EOF
|
6
6
|
A fast and very smart autolinking library that
|
@@ -19,7 +19,6 @@ Gem::Specification.new do |s|
|
|
19
19
|
ext/rinku/autolink.h
|
20
20
|
ext/rinku/buffer.c
|
21
21
|
ext/rinku/buffer.h
|
22
|
-
ext/rinku/html_autolink.c
|
23
22
|
ext/rinku/extconf.rb
|
24
23
|
lib/rinku.rb
|
25
24
|
lib/rails_rinku.rb
|
data/test/autolink_test.rb
CHANGED
@@ -28,7 +28,7 @@ class RedcarpetAutolinkTest < Test::Unit::TestCase
|
|
28
28
|
assert_equal "{link: #{link3_result}}", Rinku.auto_link("{link: #{link3_raw}}")
|
29
29
|
end
|
30
30
|
|
31
|
-
def test_auto_link_with_multiple_trailing_punctuations
|
31
|
+
def test_auto_link_with_multiple_trailing_punctuations
|
32
32
|
url = "http://youtube.com"
|
33
33
|
url_result = generate_result(url)
|
34
34
|
assert_equal url_result, Rinku.auto_link(url)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rinku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 1.
|
10
|
+
version: 1.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- "Vicent Mart\xC3\xAD"
|
@@ -15,7 +15,8 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-06-11 00:00:00
|
18
|
+
date: 2011-06-11 00:00:00 +02:00
|
19
|
+
default_executable:
|
19
20
|
dependencies: []
|
20
21
|
|
21
22
|
description: " A fast and very smart autolinking library that\n acts as a drop-in replacement for Rails `auto_link`\n"
|
@@ -35,12 +36,12 @@ files:
|
|
35
36
|
- ext/rinku/autolink.h
|
36
37
|
- ext/rinku/buffer.c
|
37
38
|
- ext/rinku/buffer.h
|
38
|
-
- ext/rinku/html_autolink.c
|
39
39
|
- ext/rinku/extconf.rb
|
40
40
|
- lib/rinku.rb
|
41
41
|
- lib/rails_rinku.rb
|
42
42
|
- rinku.gemspec
|
43
43
|
- test/autolink_test.rb
|
44
|
+
has_rdoc: true
|
44
45
|
homepage: http://github.com/tanoku/rinku
|
45
46
|
licenses: []
|
46
47
|
|
@@ -70,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
71
|
requirements: []
|
71
72
|
|
72
73
|
rubyforge_project:
|
73
|
-
rubygems_version: 1.
|
74
|
+
rubygems_version: 1.6.2
|
74
75
|
signing_key:
|
75
76
|
specification_version: 3
|
76
77
|
summary: Mostly autolinking
|
data/ext/rinku/html_autolink.c
DELETED
@@ -1,226 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* Copyright (c) 2011, Vicent Marti
|
3
|
-
*
|
4
|
-
* Permission to use, copy, modify, and distribute this software for any
|
5
|
-
* purpose with or without fee is hereby granted, provided that the above
|
6
|
-
* copyright notice and this permission notice appear in all copies.
|
7
|
-
*
|
8
|
-
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
9
|
-
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
10
|
-
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
11
|
-
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
12
|
-
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
13
|
-
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
14
|
-
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
15
|
-
*/
|
16
|
-
|
17
|
-
#include "autolink.h"
|
18
|
-
#include "buffer.h"
|
19
|
-
|
20
|
-
#include <string.h>
|
21
|
-
#include <stdlib.h>
|
22
|
-
#include <stdio.h>
|
23
|
-
#include <ctype.h>
|
24
|
-
|
25
|
-
static void
|
26
|
-
autolink_escape_cb(struct buf *ob, const struct buf *text, void *unused)
|
27
|
-
{
|
28
|
-
size_t i = 0, org;
|
29
|
-
|
30
|
-
while (i < text->size) {
|
31
|
-
org = i;
|
32
|
-
|
33
|
-
while (i < text->size &&
|
34
|
-
text->data[i] != '<' &&
|
35
|
-
text->data[i] != '>' &&
|
36
|
-
text->data[i] != '&' &&
|
37
|
-
text->data[i] != '"')
|
38
|
-
i++;
|
39
|
-
|
40
|
-
if (i > org)
|
41
|
-
bufput(ob, text->data + org, i - org);
|
42
|
-
|
43
|
-
if (i >= text->size)
|
44
|
-
break;
|
45
|
-
|
46
|
-
switch (text->data[i]) {
|
47
|
-
case '<': BUFPUTSL(ob, "<"); break;
|
48
|
-
case '>': BUFPUTSL(ob, ">"); break;
|
49
|
-
case '&': BUFPUTSL(ob, "&"); break;
|
50
|
-
case '"': BUFPUTSL(ob, """); break;
|
51
|
-
default: bufputc(ob, text->data[i]); break;
|
52
|
-
}
|
53
|
-
|
54
|
-
i++;
|
55
|
-
}
|
56
|
-
}
|
57
|
-
|
58
|
-
static inline int
|
59
|
-
is_closing_a(const char *tag, size_t size)
|
60
|
-
{
|
61
|
-
size_t i;
|
62
|
-
|
63
|
-
if (tag[0] != '<' || size < STRLEN("</a>") || tag[1] != '/')
|
64
|
-
return 0;
|
65
|
-
|
66
|
-
i = 2;
|
67
|
-
|
68
|
-
while (i < size && isspace(tag[i]))
|
69
|
-
i++;
|
70
|
-
|
71
|
-
if (i == size || tag[i] != 'a')
|
72
|
-
return 0;
|
73
|
-
|
74
|
-
i++;
|
75
|
-
|
76
|
-
while (i < size && isspace(tag[i]))
|
77
|
-
i++;
|
78
|
-
|
79
|
-
if (i == size || tag[i] != '>')
|
80
|
-
return 0;
|
81
|
-
|
82
|
-
return i;
|
83
|
-
}
|
84
|
-
|
85
|
-
static size_t
|
86
|
-
autolink__skip_tag(struct buf *ob, char *text, size_t size)
|
87
|
-
{
|
88
|
-
size_t i = 0;
|
89
|
-
|
90
|
-
while (i < size && text[i] != '>')
|
91
|
-
i++;
|
92
|
-
|
93
|
-
if (size > 3 && text[1] == 'a' && isspace(text[2])) {
|
94
|
-
while (i < size) {
|
95
|
-
size_t tag_len = is_closing_a(text + i, size - i);
|
96
|
-
if (tag_len) {
|
97
|
-
i += tag_len;
|
98
|
-
break;
|
99
|
-
}
|
100
|
-
i++;
|
101
|
-
}
|
102
|
-
}
|
103
|
-
|
104
|
-
bufput(ob, text, i + 1);
|
105
|
-
return i + 1;
|
106
|
-
}
|
107
|
-
|
108
|
-
typedef size_t (*autolink_parse_cb)(size_t *rewind, struct buf *, char *, size_t, size_t);
|
109
|
-
|
110
|
-
typedef enum {
|
111
|
-
AUTOLINK_ACTION_NONE = 0,
|
112
|
-
AUTOLINK_ACTION_WWW,
|
113
|
-
AUTOLINK_ACTION_EMAIL,
|
114
|
-
AUTOLINK_ACTION_URL,
|
115
|
-
AUTOLINK_ACTION_SKIP_TAG
|
116
|
-
} autolink_action;
|
117
|
-
|
118
|
-
static autolink_parse_cb g_callbacks[] = {
|
119
|
-
NULL,
|
120
|
-
ups_autolink__www, /* 1 */
|
121
|
-
ups_autolink__email,/* 2 */
|
122
|
-
ups_autolink__url, /* 3 */
|
123
|
-
};
|
124
|
-
|
125
|
-
static const char *g_hrefs[] = {
|
126
|
-
NULL,
|
127
|
-
"<a href=\"http://",
|
128
|
-
"<a href=\"mailto:",
|
129
|
-
"<a href=\"",
|
130
|
-
};
|
131
|
-
|
132
|
-
void
|
133
|
-
upshtml_autolink(
|
134
|
-
struct buf *ob,
|
135
|
-
struct buf *text,
|
136
|
-
unsigned int flags,
|
137
|
-
const char *link_attr,
|
138
|
-
void (*link_text_cb)(struct buf *ob, const struct buf *link, void *payload),
|
139
|
-
void *payload)
|
140
|
-
{
|
141
|
-
size_t i, end;
|
142
|
-
struct buf *link = bufnew(16);
|
143
|
-
char active_chars[256];
|
144
|
-
void (*link_url_cb)(struct buf *, const struct buf *, void *);
|
145
|
-
|
146
|
-
if (!text || text->size == 0)
|
147
|
-
return;
|
148
|
-
|
149
|
-
memset(active_chars, 0x0, sizeof(active_chars));
|
150
|
-
|
151
|
-
active_chars['<'] = AUTOLINK_ACTION_SKIP_TAG;
|
152
|
-
|
153
|
-
if (flags & AUTOLINK_EMAILS)
|
154
|
-
active_chars['@'] = AUTOLINK_ACTION_EMAIL;
|
155
|
-
|
156
|
-
if (flags & AUTOLINK_URLS) {
|
157
|
-
active_chars['w'] = AUTOLINK_ACTION_WWW;
|
158
|
-
active_chars['W'] = AUTOLINK_ACTION_WWW;
|
159
|
-
active_chars[':'] = AUTOLINK_ACTION_URL;
|
160
|
-
}
|
161
|
-
|
162
|
-
if (link_text_cb == NULL)
|
163
|
-
link_text_cb = &autolink_escape_cb;
|
164
|
-
|
165
|
-
if (link_attr != NULL) {
|
166
|
-
while (isspace(*link_attr))
|
167
|
-
link_attr++;
|
168
|
-
}
|
169
|
-
|
170
|
-
bufgrow(ob, text->size);
|
171
|
-
|
172
|
-
i = end = 0;
|
173
|
-
|
174
|
-
while (i < text->size) {
|
175
|
-
size_t rewind;
|
176
|
-
char action;
|
177
|
-
|
178
|
-
while (end < text->size && (action = active_chars[(int)text->data[end]]) == 0)
|
179
|
-
end++;
|
180
|
-
|
181
|
-
bufput(ob, text->data + i, end - i);
|
182
|
-
|
183
|
-
if (end >= text->size)
|
184
|
-
break;
|
185
|
-
|
186
|
-
i = end;
|
187
|
-
end = 0;
|
188
|
-
link->size = 0;
|
189
|
-
|
190
|
-
if (action == AUTOLINK_ACTION_SKIP_TAG) {
|
191
|
-
end = autolink__skip_tag(ob, text->data + i, text->size - i);
|
192
|
-
} else {
|
193
|
-
end = g_callbacks[(int)action](&rewind, link, text->data + i, i, text->size - i);
|
194
|
-
|
195
|
-
/* print the link */
|
196
|
-
if (end > 0) {
|
197
|
-
ob->size -= rewind;
|
198
|
-
|
199
|
-
bufputs(ob, g_hrefs[(int)action]);
|
200
|
-
autolink_escape_cb(ob, link, NULL);
|
201
|
-
|
202
|
-
if (link_attr) {
|
203
|
-
BUFPUTSL(ob, "\" ");
|
204
|
-
bufputs(ob, link_attr);
|
205
|
-
bufputc(ob, '>');
|
206
|
-
} else {
|
207
|
-
BUFPUTSL(ob, "\">");
|
208
|
-
}
|
209
|
-
|
210
|
-
link_text_cb(ob, link, payload);
|
211
|
-
BUFPUTSL(ob, "</a>");
|
212
|
-
}
|
213
|
-
}
|
214
|
-
|
215
|
-
if (!end)
|
216
|
-
end = i + 1;
|
217
|
-
else {
|
218
|
-
i += end;
|
219
|
-
end = i;
|
220
|
-
}
|
221
|
-
}
|
222
|
-
|
223
|
-
bufrelease(link);
|
224
|
-
}
|
225
|
-
|
226
|
-
|