rinku 1.0.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/COPYING +13 -0
- data/README.markdown +87 -0
- data/Rakefile +71 -0
- data/VERSION +1 -0
- data/ext/rinku/autolink.c +239 -0
- data/ext/rinku/autolink.h +39 -0
- data/ext/rinku/buffer.c +323 -0
- data/ext/rinku/buffer.h +154 -0
- data/ext/rinku/extconf.rb +4 -0
- data/ext/rinku/html_autolink.c +221 -0
- data/ext/rinku/rinku.c +86 -0
- data/lib/rinku.rb +41 -0
- data/rinku.gemspec +34 -0
- data/test/autolink_test.rb +135 -0
- metadata +80 -0
data/ext/rinku/buffer.c
ADDED
@@ -0,0 +1,323 @@
|
|
1
|
+
/* buffer.c - automatic buffer structure */
|
2
|
+
|
3
|
+
/*
|
4
|
+
* Copyright (c) 2008, Natacha Porté
|
5
|
+
*
|
6
|
+
* Permission to use, copy, modify, and distribute this software for any
|
7
|
+
* purpose with or without fee is hereby granted, provided that the above
|
8
|
+
* copyright notice and this permission notice appear in all copies.
|
9
|
+
*
|
10
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
11
|
+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
12
|
+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
13
|
+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
14
|
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
15
|
+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
16
|
+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
17
|
+
*/
|
18
|
+
|
19
|
+
/*
|
20
|
+
* COMPILE TIME OPTIONS
|
21
|
+
*
|
22
|
+
* BUFFER_STATS • if defined, stats are kept about memory usage
|
23
|
+
*/
|
24
|
+
|
25
|
+
#define BUFFER_STDARG
|
26
|
+
#define BUFFER_MAX_ALLOC_SIZE (1024 * 1024 * 16) //16mb
|
27
|
+
|
28
|
+
#include "buffer.h"
|
29
|
+
|
30
|
+
#include <stdio.h>
|
31
|
+
#include <stdlib.h>
|
32
|
+
#include <string.h>
|
33
|
+
|
34
|
+
|
35
|
+
/********************
|
36
|
+
* GLOBAL VARIABLES *
|
37
|
+
********************/
|
38
|
+
|
39
|
+
#ifdef BUFFER_STATS
|
40
|
+
long buffer_stat_nb = 0;
|
41
|
+
size_t buffer_stat_alloc_bytes = 0;
|
42
|
+
#endif
|
43
|
+
|
44
|
+
|
45
|
+
/***************************
|
46
|
+
* STATIC HELPER FUNCTIONS *
|
47
|
+
***************************/
|
48
|
+
|
49
|
+
/* lower • retruns the lower-case variant of the input char */
|
50
|
+
static char
|
51
|
+
lower(char c) {
|
52
|
+
return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; }
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
/********************
|
57
|
+
* BUFFER FUNCTIONS *
|
58
|
+
********************/
|
59
|
+
|
60
|
+
/* bufcasecmp • case-insensitive buffer comparison */
|
61
|
+
int
|
62
|
+
bufcasecmp(const struct buf *a, const struct buf *b) {
|
63
|
+
size_t i = 0;
|
64
|
+
size_t cmplen;
|
65
|
+
if (a == b) return 0;
|
66
|
+
if (!a) return -1; else if (!b) return 1;
|
67
|
+
cmplen = (a->size < b->size) ? a->size : b->size;
|
68
|
+
while (i < cmplen && lower(a->data[i]) == lower(b->data[i])) ++i;
|
69
|
+
if (i < a->size) {
|
70
|
+
if (i < b->size) return lower(a->data[i]) - lower(b->data[i]);
|
71
|
+
else return 1; }
|
72
|
+
else { if (i < b->size) return -1;
|
73
|
+
else return 0; } }
|
74
|
+
|
75
|
+
|
76
|
+
/* bufcmp • case-sensitive buffer comparison */
|
77
|
+
int
|
78
|
+
bufcmp(const struct buf *a, const struct buf *b) {
|
79
|
+
size_t i = 0;
|
80
|
+
size_t cmplen;
|
81
|
+
if (a == b) return 0;
|
82
|
+
if (!a) return -1; else if (!b) return 1;
|
83
|
+
cmplen = (a->size < b->size) ? a->size : b->size;
|
84
|
+
while (i < cmplen && a->data[i] == b->data[i]) ++i;
|
85
|
+
if (i < a->size) {
|
86
|
+
if (i < b->size) return a->data[i] - b->data[i];
|
87
|
+
else return 1; }
|
88
|
+
else { if (i < b->size) return -1;
|
89
|
+
else return 0; } }
|
90
|
+
|
91
|
+
|
92
|
+
/* bufcmps • case-sensitive comparison of a string to a buffer */
|
93
|
+
int
|
94
|
+
bufcmps(const struct buf *a, const char *b) {
|
95
|
+
const size_t len = strlen(b);
|
96
|
+
size_t cmplen = len;
|
97
|
+
int r;
|
98
|
+
if (!a || !a->size) return b ? 0 : -1;
|
99
|
+
if (len < a->size) cmplen = a->size;
|
100
|
+
r = strncmp(a->data, b, cmplen);
|
101
|
+
if (r) return r;
|
102
|
+
else if (a->size == len) return 0;
|
103
|
+
else if (a->size < len) return -1;
|
104
|
+
else return 1; }
|
105
|
+
|
106
|
+
int
|
107
|
+
bufprefix(const struct buf *buf, const char *prefix)
|
108
|
+
{
|
109
|
+
size_t i;
|
110
|
+
|
111
|
+
for (i = 0; i < buf->size; ++i) {
|
112
|
+
if (prefix[i] == 0)
|
113
|
+
return 0;
|
114
|
+
|
115
|
+
if (buf->data[i] != prefix[i])
|
116
|
+
return buf->data[i] - prefix[i];
|
117
|
+
}
|
118
|
+
|
119
|
+
return 0;
|
120
|
+
}
|
121
|
+
|
122
|
+
|
123
|
+
/* bufdup • buffer duplication */
|
124
|
+
struct buf *
|
125
|
+
bufdup(const struct buf *src, size_t dupunit) {
|
126
|
+
size_t blocks;
|
127
|
+
struct buf *ret;
|
128
|
+
if (src == 0) return 0;
|
129
|
+
ret = malloc(sizeof (struct buf));
|
130
|
+
if (ret == 0) return 0;
|
131
|
+
ret->unit = dupunit;
|
132
|
+
ret->size = src->size;
|
133
|
+
ret->ref = 1;
|
134
|
+
if (!src->size) {
|
135
|
+
ret->asize = 0;
|
136
|
+
ret->data = 0;
|
137
|
+
return ret; }
|
138
|
+
blocks = (src->size + dupunit - 1) / dupunit;
|
139
|
+
ret->asize = blocks * dupunit;
|
140
|
+
ret->data = malloc(ret->asize);
|
141
|
+
if (ret->data == 0) {
|
142
|
+
free(ret);
|
143
|
+
return 0; }
|
144
|
+
memcpy(ret->data, src->data, src->size);
|
145
|
+
#ifdef BUFFER_STATS
|
146
|
+
buffer_stat_nb += 1;
|
147
|
+
buffer_stat_alloc_bytes += ret->asize;
|
148
|
+
#endif
|
149
|
+
return ret; }
|
150
|
+
|
151
|
+
/* bufgrow • increasing the allocated size to the given value */
|
152
|
+
int
|
153
|
+
bufgrow(struct buf *buf, size_t neosz) {
|
154
|
+
size_t neoasz;
|
155
|
+
void *neodata;
|
156
|
+
if (!buf || !buf->unit || neosz > BUFFER_MAX_ALLOC_SIZE) return 0;
|
157
|
+
if (buf->asize >= neosz) return 1;
|
158
|
+
neoasz = buf->asize + buf->unit;
|
159
|
+
while (neoasz < neosz) neoasz += buf->unit;
|
160
|
+
neodata = realloc(buf->data, neoasz);
|
161
|
+
if (!neodata) return 0;
|
162
|
+
#ifdef BUFFER_STATS
|
163
|
+
buffer_stat_alloc_bytes += (neoasz - buf->asize);
|
164
|
+
#endif
|
165
|
+
buf->data = neodata;
|
166
|
+
buf->asize = neoasz;
|
167
|
+
return 1; }
|
168
|
+
|
169
|
+
|
170
|
+
/* bufnew • allocation of a new buffer */
|
171
|
+
struct buf *
|
172
|
+
bufnew(size_t unit) {
|
173
|
+
struct buf *ret;
|
174
|
+
ret = malloc(sizeof (struct buf));
|
175
|
+
if (ret) {
|
176
|
+
#ifdef BUFFER_STATS
|
177
|
+
buffer_stat_nb += 1;
|
178
|
+
#endif
|
179
|
+
ret->data = 0;
|
180
|
+
ret->size = ret->asize = 0;
|
181
|
+
ret->ref = 1;
|
182
|
+
ret->unit = unit; }
|
183
|
+
return ret; }
|
184
|
+
|
185
|
+
|
186
|
+
/* bufnullterm • NUL-termination of the string array (making a C-string) */
|
187
|
+
void
|
188
|
+
bufnullterm(struct buf *buf) {
|
189
|
+
if (!buf || !buf->unit) return;
|
190
|
+
if (buf->size < buf->asize && buf->data[buf->size] == 0) return;
|
191
|
+
if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1))
|
192
|
+
buf->data[buf->size] = 0; }
|
193
|
+
|
194
|
+
|
195
|
+
/* bufprintf • formatted printing to a buffer */
|
196
|
+
void
|
197
|
+
bufprintf(struct buf *buf, const char *fmt, ...) {
|
198
|
+
va_list ap;
|
199
|
+
if (!buf || !buf->unit) return;
|
200
|
+
va_start(ap, fmt);
|
201
|
+
vbufprintf(buf, fmt, ap);
|
202
|
+
va_end(ap); }
|
203
|
+
|
204
|
+
|
205
|
+
/* bufput • appends raw data to a buffer */
|
206
|
+
void
|
207
|
+
bufput(struct buf *buf, const void *data, size_t len) {
|
208
|
+
if (!buf) return;
|
209
|
+
if (buf->size + len > buf->asize && !bufgrow(buf, buf->size + len))
|
210
|
+
return;
|
211
|
+
memcpy(buf->data + buf->size, data, len);
|
212
|
+
buf->size += len; }
|
213
|
+
|
214
|
+
|
215
|
+
/* bufputs • appends a NUL-terminated string to a buffer */
|
216
|
+
void
|
217
|
+
bufputs(struct buf *buf, const char *str) {
|
218
|
+
bufput(buf, str, strlen (str)); }
|
219
|
+
|
220
|
+
|
221
|
+
/* bufputc • appends a single char to a buffer */
|
222
|
+
void
|
223
|
+
bufputc(struct buf *buf, char c) {
|
224
|
+
if (!buf) return;
|
225
|
+
if (buf->size + 1 > buf->asize && !bufgrow(buf, buf->size + 1))
|
226
|
+
return;
|
227
|
+
buf->data[buf->size] = c;
|
228
|
+
buf->size += 1; }
|
229
|
+
|
230
|
+
|
231
|
+
/* bufrelease • decrease the reference count and free the buffer if needed */
|
232
|
+
void
|
233
|
+
bufrelease(struct buf *buf) {
|
234
|
+
if (!buf) return;
|
235
|
+
buf->ref -= 1;
|
236
|
+
if (buf->ref == 0) {
|
237
|
+
#ifdef BUFFER_STATS
|
238
|
+
buffer_stat_nb -= 1;
|
239
|
+
buffer_stat_alloc_bytes -= buf->asize;
|
240
|
+
#endif
|
241
|
+
free(buf->data);
|
242
|
+
free(buf); } }
|
243
|
+
|
244
|
+
|
245
|
+
/* bufreset • frees internal data of the buffer */
|
246
|
+
void
|
247
|
+
bufreset(struct buf *buf) {
|
248
|
+
if (!buf) return;
|
249
|
+
#ifdef BUFFER_STATS
|
250
|
+
buffer_stat_alloc_bytes -= buf->asize;
|
251
|
+
#endif
|
252
|
+
free(buf->data);
|
253
|
+
buf->data = 0;
|
254
|
+
buf->size = buf->asize = 0; }
|
255
|
+
|
256
|
+
|
257
|
+
/* bufset • safely assigns a buffer to another */
|
258
|
+
void
|
259
|
+
bufset(struct buf **dest, struct buf *src) {
|
260
|
+
if (src) {
|
261
|
+
if (!src->asize) src = bufdup(src, 1);
|
262
|
+
else src->ref += 1; }
|
263
|
+
bufrelease(*dest);
|
264
|
+
*dest = src; }
|
265
|
+
|
266
|
+
|
267
|
+
/* bufslurp • removes a given number of bytes from the head of the array */
|
268
|
+
void
|
269
|
+
bufslurp(struct buf *buf, size_t len) {
|
270
|
+
if (!buf || !buf->unit || len <= 0) return;
|
271
|
+
if (len >= buf->size) {
|
272
|
+
buf->size = 0;
|
273
|
+
return; }
|
274
|
+
buf->size -= len;
|
275
|
+
memmove(buf->data, buf->data + len, buf->size); }
|
276
|
+
|
277
|
+
|
278
|
+
/* buftoi • converts the numbers at the beginning of the buf into an int */
|
279
|
+
int
|
280
|
+
buftoi(struct buf *buf, size_t offset_i, size_t *offset_o) {
|
281
|
+
int r = 0, neg = 0;
|
282
|
+
size_t i = offset_i;
|
283
|
+
if (!buf || !buf->size) return 0;
|
284
|
+
if (buf->data[i] == '+') i += 1;
|
285
|
+
else if (buf->data[i] == '-') {
|
286
|
+
neg = 1;
|
287
|
+
i += 1; }
|
288
|
+
while (i < buf->size && buf->data[i] >= '0' && buf->data[i] <= '9') {
|
289
|
+
r = (r * 10) + buf->data[i] - '0';
|
290
|
+
i += 1; }
|
291
|
+
if (offset_o) *offset_o = i;
|
292
|
+
return neg ? -r : r; }
|
293
|
+
|
294
|
+
|
295
|
+
|
296
|
+
/* vbufprintf • stdarg variant of formatted printing into a buffer */
|
297
|
+
void
|
298
|
+
vbufprintf(struct buf *buf, const char *fmt, va_list ap) {
|
299
|
+
int n;
|
300
|
+
va_list ap_save;
|
301
|
+
if (buf == 0
|
302
|
+
|| (buf->size >= buf->asize && !bufgrow (buf, buf->size + 1)))
|
303
|
+
return;
|
304
|
+
|
305
|
+
va_copy(ap_save, ap);
|
306
|
+
n = vsnprintf(buf->data + buf->size, buf->asize - buf->size, fmt, ap);
|
307
|
+
|
308
|
+
if (n < 0 || (size_t)n >= buf->asize - buf->size) {
|
309
|
+
size_t new_size = (n > 0) ? n : buf->size;
|
310
|
+
if (!bufgrow (buf, buf->size + new_size + 1))
|
311
|
+
return;
|
312
|
+
|
313
|
+
n = vsnprintf(buf->data + buf->size, buf->asize - buf->size, fmt, ap_save);
|
314
|
+
}
|
315
|
+
va_end(ap_save);
|
316
|
+
|
317
|
+
if (n < 0)
|
318
|
+
return;
|
319
|
+
|
320
|
+
buf->size += n;
|
321
|
+
}
|
322
|
+
|
323
|
+
/* vim: set filetype=c: */
|
data/ext/rinku/buffer.h
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
/* buffer.h - automatic buffer structure */
|
2
|
+
|
3
|
+
/*
|
4
|
+
* Copyright (c) 2008, Natacha Porté
|
5
|
+
*
|
6
|
+
* Permission to use, copy, modify, and distribute this software for any
|
7
|
+
* purpose with or without fee is hereby granted, provided that the above
|
8
|
+
* copyright notice and this permission notice appear in all copies.
|
9
|
+
*
|
10
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
11
|
+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
12
|
+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
13
|
+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
14
|
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
15
|
+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
16
|
+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
17
|
+
*/
|
18
|
+
|
19
|
+
#ifndef LITHIUM_BUFFER_H
|
20
|
+
#define LITHIUM_BUFFER_H
|
21
|
+
|
22
|
+
#include <stddef.h>
|
23
|
+
|
24
|
+
#if defined(_MSC_VER)
|
25
|
+
#define __attribute__(x)
|
26
|
+
#define inline
|
27
|
+
#define strncasecmp _strnicmp
|
28
|
+
#define snprintf _snprintf
|
29
|
+
#define va_copy(d,s) ((d) = (s))
|
30
|
+
#endif
|
31
|
+
|
32
|
+
/********************
|
33
|
+
* TYPE DEFINITIONS *
|
34
|
+
********************/
|
35
|
+
|
36
|
+
/* struct buf • character array buffer */
|
37
|
+
struct buf {
|
38
|
+
char * data; /* actual character data */
|
39
|
+
size_t size; /* size of the string */
|
40
|
+
size_t asize; /* allocated size (0 = volatile buffer) */
|
41
|
+
size_t unit; /* reallocation unit size (0 = read-only buffer) */
|
42
|
+
int ref; }; /* reference count */
|
43
|
+
|
44
|
+
/**********
|
45
|
+
* MACROS *
|
46
|
+
**********/
|
47
|
+
|
48
|
+
#define STRLEN(x) (sizeof(x) - 1)
|
49
|
+
|
50
|
+
/* CONST_BUF • global buffer from a string litteral */
|
51
|
+
#define CONST_BUF(name, string) \
|
52
|
+
static struct buf name = { string, sizeof string -1, sizeof string }
|
53
|
+
|
54
|
+
|
55
|
+
/* VOLATILE_BUF • macro for creating a volatile buffer on the stack */
|
56
|
+
#define VOLATILE_BUF(name, strname) \
|
57
|
+
struct buf name = { strname, strlen(strname) }
|
58
|
+
|
59
|
+
|
60
|
+
/* BUFPUTSL • optimized bufputs of a string litteral */
|
61
|
+
#define BUFPUTSL(output, litteral) \
|
62
|
+
bufput(output, litteral, sizeof litteral - 1)
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
/********************
|
67
|
+
* BUFFER FUNCTIONS *
|
68
|
+
********************/
|
69
|
+
|
70
|
+
/* bufcasecmp • case-insensitive buffer comparison */
|
71
|
+
int
|
72
|
+
bufcasecmp(const struct buf *, const struct buf *);
|
73
|
+
|
74
|
+
/* bufcmp • case-sensitive buffer comparison */
|
75
|
+
int
|
76
|
+
bufcmp(const struct buf *, const struct buf *);
|
77
|
+
|
78
|
+
/* bufcmps • case-sensitive comparison of a string to a buffer */
|
79
|
+
int
|
80
|
+
bufcmps(const struct buf *, const char *);
|
81
|
+
|
82
|
+
/* bufprefix * compare the beginning of a buffer with a string */
|
83
|
+
int
|
84
|
+
bufprefix(const struct buf *buf, const char *prefix);
|
85
|
+
|
86
|
+
/* bufdup • buffer duplication */
|
87
|
+
struct buf *
|
88
|
+
bufdup(const struct buf *, size_t)
|
89
|
+
__attribute__ ((malloc));
|
90
|
+
|
91
|
+
/* bufgrow • increasing the allocated size to the given value */
|
92
|
+
int
|
93
|
+
bufgrow(struct buf *, size_t);
|
94
|
+
|
95
|
+
/* bufnew • allocation of a new buffer */
|
96
|
+
struct buf *
|
97
|
+
bufnew(size_t)
|
98
|
+
__attribute__ ((malloc));
|
99
|
+
|
100
|
+
/* bufnullterm • NUL-termination of the string array (making a C-string) */
|
101
|
+
void
|
102
|
+
bufnullterm(struct buf *);
|
103
|
+
|
104
|
+
/* bufprintf • formatted printing to a buffer */
|
105
|
+
void
|
106
|
+
bufprintf(struct buf *, const char *, ...)
|
107
|
+
__attribute__ ((format (printf, 2, 3)));
|
108
|
+
|
109
|
+
/* bufput • appends raw data to a buffer */
|
110
|
+
void
|
111
|
+
bufput(struct buf *, const void*, size_t);
|
112
|
+
|
113
|
+
/* bufputs • appends a NUL-terminated string to a buffer */
|
114
|
+
void
|
115
|
+
bufputs(struct buf *, const char*);
|
116
|
+
|
117
|
+
/* bufputc • appends a single char to a buffer */
|
118
|
+
void
|
119
|
+
bufputc(struct buf *, char);
|
120
|
+
|
121
|
+
/* bufrelease • decrease the reference count and free the buffer if needed */
|
122
|
+
void
|
123
|
+
bufrelease(struct buf *);
|
124
|
+
|
125
|
+
/* bufreset • frees internal data of the buffer */
|
126
|
+
void
|
127
|
+
bufreset(struct buf *);
|
128
|
+
|
129
|
+
/* bufset • safely assigns a buffer to another */
|
130
|
+
void
|
131
|
+
bufset(struct buf **, struct buf *);
|
132
|
+
|
133
|
+
/* bufslurp • removes a given number of bytes from the head of the array */
|
134
|
+
void
|
135
|
+
bufslurp(struct buf *, size_t);
|
136
|
+
|
137
|
+
/* buftoi • converts the numbers at the beginning of the buf into an int */
|
138
|
+
int
|
139
|
+
buftoi(struct buf *, size_t, size_t *);
|
140
|
+
|
141
|
+
|
142
|
+
|
143
|
+
#ifdef BUFFER_STDARG
|
144
|
+
#include <stdarg.h>
|
145
|
+
|
146
|
+
/* vbufprintf • stdarg variant of formatted printing into a buffer */
|
147
|
+
void
|
148
|
+
vbufprintf(struct buf *, const char*, va_list);
|
149
|
+
|
150
|
+
#endif /* def BUFFER_STDARG */
|
151
|
+
|
152
|
+
#endif /* ndef LITHIUM_BUFFER_H */
|
153
|
+
|
154
|
+
/* vim: set filetype=c: */
|
@@ -0,0 +1,221 @@
|
|
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
|
+
skip_tags(struct buf *ob, const 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
|
+
void
|
109
|
+
upshtml_autolink(
|
110
|
+
struct buf *ob,
|
111
|
+
struct buf *text,
|
112
|
+
unsigned int flags,
|
113
|
+
const char *link_attr,
|
114
|
+
void (*link_text_cb)(struct buf *ob, const struct buf *link, void *payload),
|
115
|
+
void *payload)
|
116
|
+
{
|
117
|
+
size_t i, end;
|
118
|
+
struct buf *link = bufnew(16);
|
119
|
+
const char *active_chars;
|
120
|
+
|
121
|
+
if (!text || text->size == 0)
|
122
|
+
return;
|
123
|
+
|
124
|
+
switch (flags) {
|
125
|
+
case AUTOLINK_EMAILS:
|
126
|
+
active_chars = "<@";
|
127
|
+
break;
|
128
|
+
|
129
|
+
case AUTOLINK_URLS:
|
130
|
+
active_chars = "<w:";
|
131
|
+
|
132
|
+
case AUTOLINK_ALL:
|
133
|
+
active_chars = "<@w:";
|
134
|
+
break;
|
135
|
+
|
136
|
+
default:
|
137
|
+
return;
|
138
|
+
}
|
139
|
+
|
140
|
+
if (link_text_cb == NULL)
|
141
|
+
link_text_cb = &autolink_escape_cb;
|
142
|
+
|
143
|
+
bufgrow(ob, text->size);
|
144
|
+
|
145
|
+
i = end = 0;
|
146
|
+
|
147
|
+
while (i < text->size) {
|
148
|
+
size_t rewind;
|
149
|
+
|
150
|
+
while (end < text->size && strchr(active_chars, text->data[end]) == NULL)
|
151
|
+
end++;
|
152
|
+
|
153
|
+
bufput(ob, text->data + i, end - i);
|
154
|
+
|
155
|
+
if (end >= text->size)
|
156
|
+
break;
|
157
|
+
|
158
|
+
i = end;
|
159
|
+
link->size = 0;
|
160
|
+
|
161
|
+
switch (text->data[i]) {
|
162
|
+
case '@':
|
163
|
+
end = ups_autolink__email(&rewind, link, text->data + i, i, text->size - i);
|
164
|
+
if (end > 0) {
|
165
|
+
ob->size -= rewind;
|
166
|
+
BUFPUTSL(ob, "<a");
|
167
|
+
if (link_attr) bufputs(ob, link_attr);
|
168
|
+
BUFPUTSL(ob, " href=\"mailto:");
|
169
|
+
bufput(ob, link->data, link->size);
|
170
|
+
BUFPUTSL(ob, "\">");
|
171
|
+
link_text_cb(ob, link, payload);
|
172
|
+
BUFPUTSL(ob, "</a>");
|
173
|
+
}
|
174
|
+
break;
|
175
|
+
|
176
|
+
case 'w':
|
177
|
+
end = ups_autolink__www(&rewind, link, text->data + i, i, text->size - i);
|
178
|
+
if (end > 0) {
|
179
|
+
BUFPUTSL(ob, "<a");
|
180
|
+
if (link_attr) bufputs(ob, link_attr);
|
181
|
+
BUFPUTSL(ob, " href=\"http://");
|
182
|
+
bufput(ob, link->data, link->size);
|
183
|
+
BUFPUTSL(ob, "\">");
|
184
|
+
link_text_cb(ob, link, payload);
|
185
|
+
BUFPUTSL(ob, "</a>");
|
186
|
+
}
|
187
|
+
break;
|
188
|
+
|
189
|
+
case ':':
|
190
|
+
end = ups_autolink__url(&rewind, link, text->data + i, i, text->size - i);
|
191
|
+
if (end > 0) {
|
192
|
+
ob->size -= rewind;
|
193
|
+
BUFPUTSL(ob, "<a");
|
194
|
+
if (link_attr) bufputs(ob, link_attr);
|
195
|
+
BUFPUTSL(ob, " href=\"");
|
196
|
+
bufput(ob, link->data, link->size);
|
197
|
+
BUFPUTSL(ob, "\">");
|
198
|
+
link_text_cb(ob, link, payload);
|
199
|
+
BUFPUTSL(ob, "</a>");
|
200
|
+
}
|
201
|
+
break;
|
202
|
+
|
203
|
+
case '<':
|
204
|
+
end = skip_tags(ob, text->data + i, text->size - i);
|
205
|
+
break;
|
206
|
+
|
207
|
+
default:
|
208
|
+
end = 0;
|
209
|
+
break;
|
210
|
+
}
|
211
|
+
|
212
|
+
if (!end)
|
213
|
+
end = i + 1;
|
214
|
+
else {
|
215
|
+
i += end;
|
216
|
+
end = i;
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
|