rinku 1.1.0 → 1.2.0
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.
- 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
|
-
|