breakout_parser 0.0.23 → 0.0.31
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.
- checksums.yaml +15 -0
- data/ChangeLog +19 -0
- data/README +23 -11
- data/ext/breakout_parser/extconf.rb +10 -5
- data/ext/breakout_parser/parser.h +27 -0
- data/ext/breakout_parser/parser.l +22 -6
- data/ext/breakout_parser/parser.y +294 -126
- data/ext/breakout_parser/ruby_ext.c +51 -22
- data/ext/breakout_parser/ruby_ext.h +2 -0
- data/spec/links_only_parser_spec.rb +916 -0
- data/spec/obj_proxy.rb +22 -0
- data/spec/parser_examples_spec.rb +115 -0
- data/spec/parser_spec.rb +146 -34
- data/spec/spec_helper.rb +1 -0
- metadata +89 -62
- data/ext/breakout_parser/lex.yy.c +0 -2996
- data/ext/breakout_parser/parser.tab.c +0 -2482
- data/ext/breakout_parser/parser.tab.h +0 -113
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YTU3NTk2NGQ1ZTEyMjFlOTFiYmI4NDY0ZDIzOWFkNzRiMTFiNTdiYQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OGMwYTA0YjBiOTI2MDZmODc2M2NkYjk2OTQzN2QzMWI0NTg4OTlmMQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MjIzYjUyMzE3ZTE3MWIxMDIxMzgzMmJhYWI1ZTk0ZmQyZGQ1YmU5NDFhMzdm
|
10
|
+
NWUxODM5MzhmZTg0YjEzODg5MGFkMWFiODE0ZDgzN2EyYTY5ZTk5MTBhMTA3
|
11
|
+
Zjk2NzJhYzM4YzI4NmM0ZGY3NzcwN2ZkZDczMTY1YjgyMzM0MmE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
Nzk1MGE5YTVlYzY4NDJjZTI4YTE0NjYxYmE4ZWRkYmRjNDJmOWExNzA5MzQ5
|
14
|
+
YjkwMDRkNTQxMWJlZDc1NGQ1ODJmYTc0ZjViZjRjZGYyYWM4NGMxYTQwZWNj
|
15
|
+
NzA5MzY0NDlmYzQ5MmY5ZjY2MjU0ZGIyMzk3ZjIwNDA2MDBmZTk=
|
data/ChangeLog
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
* Rollback code that leaks RAM for LI processing
|
2
|
+
* Fixed RAM leaks in process_snippet_link()
|
3
|
+
|
4
|
+
[ 0.0.30 ]
|
5
|
+
* added ability to chain unordered lists inside ordered lists and vice-versa
|
6
|
+
* added ability to insert ordered lists that use letters instead numbers (with '%' instead '#')
|
7
|
+
|
8
|
+
[ 0.0.29 ]
|
9
|
+
* fixed unescaped characters in URL links
|
10
|
+
|
11
|
+
[ 0.0.28 ]
|
12
|
+
* fixed bold or italic texts at the beginning or (un)ordered lists
|
13
|
+
|
14
|
+
[ 0.0.26 ]
|
15
|
+
* fixed list parser for multilevel lists
|
16
|
+
|
17
|
+
[ 0.0.24 ]
|
18
|
+
* Ruby 1.9.3 compatibility
|
19
|
+
|
1
20
|
[ 0.0.21 ]
|
2
21
|
* emit correct urls for external svn links
|
3
22
|
(https://www.assembla.com/spaces/breakout/tickets/8472)
|
data/README
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
Breakout Parser
|
2
|
-
===============
|
1
|
+
Breakout Parser
|
2
|
+
===============
|
3
3
|
|
4
4
|
- simplified Textile parser with some Assembla-specific features
|
5
5
|
- converts Textile into HTML
|
@@ -7,27 +7,39 @@ Breakout Parser
|
|
7
7
|
Build dependencies
|
8
8
|
==================
|
9
9
|
|
10
|
-
You will need Flex, Bison, GCC, and GNU Make to build this gem.
|
10
|
+
- You will need Flex, Bison, GCC, and GNU Make to build this gem.
|
11
|
+
|
12
|
+
Development
|
13
|
+
================
|
14
|
+
|
15
|
+
Compiling and testing
|
16
|
+
- just run 'rake' in the repos root dir
|
11
17
|
|
18
|
+
Just compiling
|
19
|
+
- just run 'rake build_ext'
|
20
|
+
|
21
|
+
Testing
|
22
|
+
- just run 'rspec' or 'rspec spec/'
|
12
23
|
|
13
24
|
Usage
|
14
25
|
===============
|
15
26
|
|
16
27
|
require 'breakout_parser'
|
17
|
-
puts BreakoutParser.parse("h1. xxx", "my_space")
|
28
|
+
puts BreakoutParser.parse("h1. xxx", "my_space")
|
18
29
|
# prints: <h1 id="h-xxx">xxx</h1>
|
19
30
|
|
20
|
-
puts BreakoutParser.parse_links_only("h1. http://xxx", "my_space")
|
31
|
+
puts BreakoutParser.parse_links_only("h1. http://xxx", "my_space")
|
21
32
|
# prints: h1. <a rel="nofollow" href="http://xxx">http://xxx</a>
|
22
33
|
|
23
|
-
|
24
|
-
|
25
34
|
Arguments
|
26
35
|
===============
|
27
36
|
|
28
37
|
BreakoutParser.parse(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
38
|
+
data, # data to parse
|
39
|
+
space_name, # space name - for links parsing
|
40
|
+
site_url, # [optional] global site url
|
41
|
+
vcs_url, # [optional] custom VCS url - f.ex. for github-hosted repos
|
42
|
+
absolute_urls, # [optional] boolean value that denotes if URLs have to be absolute
|
43
|
+
large_files_url, # [optional] URL used for large files (i.e. when using ADN/CDN)
|
44
|
+
data_attributes, # [optional] hash of data attributes that would be added to links
|
33
45
|
)
|
@@ -12,12 +12,17 @@ dir_config(extension_name)
|
|
12
12
|
if RUBY_PLATFORM['mswin32']
|
13
13
|
$defs << "-MT" # link statically - avoid usage of MSVCR90.dll
|
14
14
|
end
|
15
|
+
|
16
|
+
# Add RUBY_19 constant if it's Ruby 1.9
|
17
|
+
if RUBY_VERSION =~ /1.9/ then
|
18
|
+
$CPPFLAGS += " -DRUBY_19"
|
19
|
+
end
|
15
20
|
create_makefile(([extension_name]*2).join('/'))
|
16
21
|
|
17
22
|
def check_version name, need_ver
|
18
23
|
exe = find_executable name
|
19
24
|
return false unless exe
|
20
|
-
exe_ver = `#{exe} -V`[/\d+\.\d
|
25
|
+
exe_ver = `#{exe} -V`[/\d+\.\d+(\.\d+)?/]
|
21
26
|
unless exe_ver
|
22
27
|
puts "[?] cannot determine #{name} version"
|
23
28
|
return false
|
@@ -27,20 +32,20 @@ def check_version name, need_ver
|
|
27
32
|
r = (exe_ver[0] > need_ver[0]) || (
|
28
33
|
(exe_ver[0] == need_ver[0]) && (
|
29
34
|
(exe_ver[1] > need_ver[1]) ||
|
30
|
-
(exe_ver[1] == need_ver[1] && exe_ver[2] >= need_ver[2])
|
35
|
+
(exe_ver[1] == need_ver[1] && (exe_ver[2] || 0) >= need_ver[2])
|
31
36
|
)
|
32
37
|
)
|
33
38
|
puts "[-] wanted #{name} >= #{need_ver.join('.')}, but got only #{exe_ver.join('.')}" unless r
|
34
39
|
r
|
35
40
|
end
|
36
41
|
|
37
|
-
if !windows && File.exist?('Makefile') && check_version('bison','2.
|
42
|
+
if !windows && File.exist?('Makefile') && check_version('bison','2.3.0') && check_version('lex','2.5.30')
|
38
43
|
File.open('Makefile','a') do |f|
|
39
44
|
f.puts
|
40
45
|
f.puts "parser.tab.c parser.tab.h: parser.y"
|
41
|
-
f.puts "\tbison -d parser.y"
|
46
|
+
f.puts "\tbison -d $(srcdir)/parser.y"
|
42
47
|
f.puts
|
43
48
|
f.puts "lex.yy.c: parser.l"
|
44
|
-
f.puts "\
|
49
|
+
f.puts "\tflex $(srcdir)/parser.l"
|
45
50
|
end
|
46
51
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
void process_url(const char *url);
|
2
|
+
void process_email(const char *url);
|
3
|
+
void process_inline_code(const char *p);
|
4
|
+
void revert_bold();
|
5
|
+
void revert_italic();
|
6
|
+
void process_ticket_link(const char *ticket_id);
|
7
|
+
void process_svn_link(const char *target, int numbered_repo);
|
8
|
+
void process_git_link(const char *target, int numbered_repo);
|
9
|
+
void process_url_link(const char *target,const char *proto);
|
10
|
+
void process_wiki_link(const char *target);
|
11
|
+
void process_anchor_link(const char *target);
|
12
|
+
void process_file_link(const char *target);
|
13
|
+
void process_image_link(const char *target);
|
14
|
+
void process_snippet_link(const char *space_id_and_snippet_id);
|
15
|
+
void concat_escaped_char(int c);
|
16
|
+
void process_header(const char *title);
|
17
|
+
int closing_list_stack_push(const char * term);
|
18
|
+
const char * closing_list_stack_pop();
|
19
|
+
void close_opened_lists();
|
20
|
+
void process_list_item(int level, int type);
|
21
|
+
const char *unconcat(const char *term);
|
22
|
+
int is_ending_with(const char *term);
|
23
|
+
void concat_raw_char(int c);
|
24
|
+
int concat_custom_vcs_url(const char *rev);
|
25
|
+
void set_numeric_ticket_id(const char *ticket_id, char *numeric_ticket_id);
|
26
|
+
void process_data_attributes(char *ticket_id);
|
27
|
+
void add_data_attribute(char *name, char *value);
|
@@ -113,7 +113,7 @@ extern size_t in_buf_len;
|
|
113
113
|
[ \t]+@[^\r\n\xff@]+@/[ \t\r\n,.\xff] { yylval.svalue = yytext; return INLINE_CODE; }
|
114
114
|
|
115
115
|
^h[1-5]\.[ \t]+[^ \t\r\n][^\r\n]*/[\r\n\xff] {
|
116
|
-
yylval.svalue = yytext+4;
|
116
|
+
yylval.svalue = yytext+4;
|
117
117
|
switch(yytext[1]){
|
118
118
|
case '1': return H1;
|
119
119
|
case '2': return H2;
|
@@ -121,17 +121,31 @@ extern size_t in_buf_len;
|
|
121
121
|
case '4': return H4;
|
122
122
|
case '5': return H5;
|
123
123
|
}
|
124
|
-
return H1;
|
124
|
+
return H1;
|
125
125
|
}
|
126
126
|
|
127
|
-
|
128
|
-
^[ \t]
|
127
|
+
/* PS1: being nice to bold and italic markup in the beginning of LI items */
|
128
|
+
^[ \t]*\*{3}/[ ]+(?:\*|_) { yylval.ivalue=3; return ULI; /* PS1 */ }
|
129
|
+
^[ \t]*\*{3}[ ]+ { yylval.ivalue=3; return ULI; }
|
130
|
+
^[ \t]*\*{2}/[ ]+(?:\*|_) { yylval.ivalue=2; return ULI; /* PS1 */ }
|
131
|
+
^[ \t]*\*{2}[ ]+ { yylval.ivalue=2; return ULI; }
|
132
|
+
^[ \t]*\*/[ ]+(?:\*|_) { yylval.ivalue=1; return ULI; /* PS1 */ }
|
129
133
|
^[ \t]*\*[ ]+ { yylval.ivalue=1; return ULI; }
|
130
134
|
|
131
|
-
^[ \t]
|
132
|
-
^[ \t]
|
135
|
+
^[ \t]*#{3}/[ ]+(?:\*|_) { yylval.ivalue=3; return OLI; /* PS1 */ }
|
136
|
+
^[ \t]*#{3}[ ]+ { yylval.ivalue=3; return OLI; }
|
137
|
+
^[ \t]*#{2}/[ ]+(?:\*|_) { yylval.ivalue=2; return OLI; /* PS1 */ }
|
138
|
+
^[ \t]*#{2}[ ]+ { yylval.ivalue=2; return OLI; }
|
139
|
+
^[ \t]*#/[ ]+(?:\*|_) { yylval.ivalue=1; return OLI; /* PS1 */ }
|
133
140
|
^[ \t]*#[ ]+ { yylval.ivalue=1; return OLI; }
|
134
141
|
|
142
|
+
^[ \t]*%{3}/[ ]+(?:\*|_) { yylval.ivalue=3; return OLI2; /* PS1 */ }
|
143
|
+
^[ \t]*%{3}[ ]+ { yylval.ivalue=3; return OLI2; }
|
144
|
+
^[ \t]*%{2}/[ ]+(?:\*|_) { yylval.ivalue=2; return OLI2; /* PS1 */ }
|
145
|
+
^[ \t]*%{2}[ ]+ { yylval.ivalue=2; return OLI2; }
|
146
|
+
^[ \t]*%/[ ]+(?:\*|_) { yylval.ivalue=1; return OLI2; /* PS1 */ }
|
147
|
+
^[ \t]*%[ ]+ { yylval.ivalue=1; return OLI2; }
|
148
|
+
|
135
149
|
|
136
150
|
<INITIAL,_LINKS_ONLY,_BOLD,_ITALIC>{
|
137
151
|
(https?:\/\/|www\.)[^ \r\n<>"(){}*]+[^ \r\n<>"(){}*,.\[\]] { yylval.svalue = yytext; return URL; }
|
@@ -164,6 +178,8 @@ extern size_t in_buf_len;
|
|
164
178
|
\[\[file:[a-zA-Z0-9_.-]+(\|[^\[\]]+)?\]\] { yylval.svalue = yytext+7; return FILE_LINK; }
|
165
179
|
\[\[image:[a-zA-Z0-9_.-]+(\|[^\[\]]+)?\]\] { yylval.svalue = yytext+8; return IMAGE_LINK; }
|
166
180
|
|
181
|
+
\[\[snippet:[a-zA-Z0-9_.-]+:[0-9]+\]\] { yylval.svalue = yytext+10; return SNIPPET_LINK; }
|
182
|
+
|
167
183
|
^[ \t]+ ; /* skip spaces at line start */
|
168
184
|
[ \t]+ { yylval.ivalue = ' '; return T_CHAR; }
|
169
185
|
|
@@ -1,13 +1,16 @@
|
|
1
1
|
// vim:ts=4:sw=4:expandtab
|
2
|
-
%{
|
2
|
+
%{
|
3
3
|
#include <stdio.h>
|
4
4
|
#include <stdlib.h>
|
5
5
|
#include <string.h>
|
6
|
-
|
7
|
-
#
|
6
|
+
#include <time.h>
|
7
|
+
#include "parser.h"
|
8
8
|
#include "ruby.h"
|
9
|
+
|
10
|
+
#ifdef RUBY_19
|
11
|
+
#include "ruby/st.h"
|
9
12
|
#else
|
10
|
-
#
|
13
|
+
#include "st.h"
|
11
14
|
#endif
|
12
15
|
|
13
16
|
extern int yylex();
|
@@ -24,13 +27,20 @@ size_t space_name_len = 0;
|
|
24
27
|
const char *site_url = NULL;
|
25
28
|
size_t site_url_len = 0;
|
26
29
|
|
30
|
+
const char *large_files_url = NULL;
|
31
|
+
size_t large_files_url_len = 0;
|
32
|
+
|
33
|
+
VALUE meta_attributes;
|
34
|
+
|
27
35
|
extern VALUE vcs_url;
|
28
36
|
|
37
|
+
const char ** closing_list_stack = NULL;
|
38
|
+
int closing_list_stack_size = 0;
|
29
39
|
int list_level = 1;
|
30
40
|
int absolute_urls = 0;
|
31
41
|
|
32
42
|
#define CHECK_BUF_SIZE(len) \
|
33
|
-
if( (bufptr - buf + len + 1) >= bufsize ){ \
|
43
|
+
if( (size_t)(bufptr - buf + len + 1) >= bufsize ){ \
|
34
44
|
/*printf("[.] REALLOC oldsz=%d, newsz=%d\n",bufsize, (bufsize+((len > 0x1000) ? (len+0x1000) : 0x1000)));*/ \
|
35
45
|
char *oldbuf = buf; \
|
36
46
|
bufsize += (len > 0x1000) ? (len+0x1000) : 0x1000; \
|
@@ -38,18 +48,16 @@ int absolute_urls = 0;
|
|
38
48
|
bufptr = buf + (bufptr-oldbuf); \
|
39
49
|
}
|
40
50
|
|
41
|
-
concat(const char*what, size_t len){
|
42
|
-
// printf("[.] concat: w=\"%s\", l=%d\n",what,len);
|
51
|
+
void concat(const char*what, size_t len){
|
43
52
|
CHECK_BUF_SIZE(len);
|
44
53
|
memcpy(bufptr, what, len);
|
45
54
|
bufptr += len;
|
46
|
-
// printf("[.] concat OK\n");
|
47
55
|
}
|
48
56
|
|
49
57
|
// it's better to use concat(), especially if "what"'s length is always predefined
|
50
|
-
concat2(const char*what){
|
58
|
+
void concat2(const char * what) {
|
51
59
|
size_t len = strlen(what);
|
52
|
-
concat(what,len);
|
60
|
+
concat(what, len);
|
53
61
|
}
|
54
62
|
|
55
63
|
// better error reporting
|
@@ -70,13 +78,13 @@ void yyerror(const char *msg)
|
|
70
78
|
%union {
|
71
79
|
double dvalue;
|
72
80
|
int ivalue;
|
73
|
-
char
|
81
|
+
const char* svalue;
|
74
82
|
}
|
75
83
|
|
76
84
|
|
77
85
|
%token <ivalue> T_CHAR BOLD_START ITALIC_START
|
78
86
|
%token <ivalue> BOLD_ITALIC_START ITALIC_BOLD_START
|
79
|
-
%token <ivalue> ULI OLI
|
87
|
+
%token <ivalue> ULI OLI OLI2
|
80
88
|
%token <svalue> T_WORD TICKET_LINK LINK SVN_REVISION_LINK GIT_REVISION_LINK WIKI_LINK ANCHOR_LINK
|
81
89
|
%token <svalue> SVN_N_REVISION_LINK GIT_N_REVISION_LINK
|
82
90
|
%token <svalue> URL_WITH_PROTO_LINK URL_WITHOUT_PROTO_LINK
|
@@ -85,146 +93,197 @@ void yyerror(const char *msg)
|
|
85
93
|
%token <svalue> UL
|
86
94
|
%token <svalue> H1 H2 H3 H4 H5
|
87
95
|
%token <svalue> INLINE_CODE
|
88
|
-
%token SPACE BR
|
96
|
+
%token SPACE BR
|
89
97
|
%token PRE_CODE_START PRE_CODE_END PRE_START PRE_END CODE_START CODE_END
|
90
98
|
%token NOTEXTILE_START NOTEXTILE_END
|
91
99
|
%token BOLD_END ITALIC_END
|
92
100
|
%token REVERT_BOLD REVERT_ITALIC
|
101
|
+
%token <svalue> SNIPPET_LINK
|
93
102
|
|
94
103
|
//%type <dvalue> expression
|
95
104
|
//%type <dvalue> term
|
96
105
|
//%type <dvalue> varornum
|
97
106
|
%%
|
98
|
-
text :
|
107
|
+
text :
|
99
108
|
| textitem text
|
100
109
|
|
101
110
|
|
102
111
|
textitem: br
|
103
112
|
| words
|
104
|
-
| h1 {concat("</h1>",5)}
|
105
|
-
| h2 {concat("</h2>",5)}
|
106
|
-
| h3 {concat("</h3>",5)}
|
107
|
-
| h4 {concat("</h4>",5)}
|
108
|
-
| h5 {concat("</h5>",5)}
|
109
|
-
| {
|
110
|
-
list_level=1;
|
111
|
-
concat("<ul>",4)
|
112
|
-
} ulist {
|
113
|
-
concat("</ul>",5);
|
114
|
-
for(; list_level>1 && list_level<4; list_level--) concat("</li></ul>",10);
|
115
|
-
} textitem
|
116
|
-
| {
|
117
|
-
list_level=1;
|
118
|
-
concat("<ol>",4)
|
119
|
-
} olist {
|
120
|
-
concat("</ol>",5);
|
121
|
-
for(; list_level>1 && list_level<4; list_level--) concat("</li></ol>",10);
|
122
|
-
} textitem
|
113
|
+
| h1 {concat("</h1>",5);}
|
114
|
+
| h2 {concat("</h2>",5);}
|
115
|
+
| h3 {concat("</h3>",5);}
|
116
|
+
| h4 {concat("</h4>",5);}
|
117
|
+
| h5 {concat("</h5>",5);}
|
118
|
+
| listitem { concat("</li>", 5); close_opened_lists(); }
|
123
119
|
| code
|
124
120
|
|
125
|
-
|
126
|
-
|
121
|
+
listitem: ulitem
|
122
|
+
| olitem
|
123
|
+
| olitem2
|
127
124
|
|
128
125
|
ulitem: uli words
|
129
126
|
| uli words BR
|
130
127
|
|
131
|
-
olist: olitem {concat("</li>",5)}
|
132
|
-
| olist olitem {concat("</li>",5)}
|
133
|
-
|
134
128
|
olitem: oli words
|
135
129
|
| oli words BR
|
136
130
|
|
131
|
+
olitem2: oli2 words
|
132
|
+
| oli2 words BR
|
133
|
+
|
137
134
|
words: word
|
138
135
|
| word words
|
139
136
|
|
140
137
|
word : chars
|
141
138
|
| link
|
142
|
-
| T_WORD {concat2($1)} // TODO: somehow pass T_WORD's length here
|
143
|
-
| URL {process_url($1)}
|
144
|
-
| EMAIL {process_email($1)}
|
145
|
-
| BOLD_START {$1 ? concat(" <strong>",9) : concat("<strong>",8)}
|
146
|
-
| BOLD_END {concat("</strong>",9)}
|
147
|
-
| ITALIC_START {$1 ? concat(" <em>",5) : concat("<em>",4)}
|
148
|
-
| ITALIC_END {concat("</em>",5)}
|
149
|
-
| BOLD_ITALIC_START {$1 ? concat(" <strong><em>",13) : concat("<strong><em>",12)}
|
150
|
-
| ITALIC_BOLD_START {$1 ? concat(" <em><strong>",13) : concat("<em><strong>",12)}
|
151
|
-
| INLINE_CODE {process_inline_code($1)}
|
152
|
-
| REVERT_BOLD {revert_bold()}
|
153
|
-
| REVERT_ITALIC {revert_italic()}
|
154
|
-
|
155
|
-
link: TICKET_LINK {process_ticket_link($1)}
|
156
|
-
| SVN_REVISION_LINK {process_svn_link($1,0)}
|
157
|
-
| GIT_REVISION_LINK {process_git_link($1,0)}
|
158
|
-
| SVN_N_REVISION_LINK {process_svn_link($1,1)}
|
159
|
-
| GIT_N_REVISION_LINK {process_git_link($1,1)}
|
160
|
-
| URL_WITH_PROTO_LINK {process_url_link($1,NULL)}
|
161
|
-
| URL_WITHOUT_PROTO_LINK {process_url_link($1,"http://")}
|
162
|
-
| WIKI_LINK {process_wiki_link($1)}
|
163
|
-
| ANCHOR_LINK {process_anchor_link($1)}
|
164
|
-
| FILE_LINK {process_file_link($1)}
|
165
|
-
| IMAGE_LINK {process_image_link($1)}
|
139
|
+
| T_WORD {concat2($1);} // TODO: somehow pass T_WORD's length here
|
140
|
+
| URL {process_url($1);}
|
141
|
+
| EMAIL {process_email($1);}
|
142
|
+
| BOLD_START {$1 ? concat(" <strong>",9) : concat("<strong>",8);}
|
143
|
+
| BOLD_END {concat("</strong>",9);}
|
144
|
+
| ITALIC_START {$1 ? concat(" <em>",5) : concat("<em>",4);}
|
145
|
+
| ITALIC_END {concat("</em>",5);}
|
146
|
+
| BOLD_ITALIC_START {$1 ? concat(" <strong><em>",13) : concat("<strong><em>",12);}
|
147
|
+
| ITALIC_BOLD_START {$1 ? concat(" <em><strong>",13) : concat("<em><strong>",12);}
|
148
|
+
| INLINE_CODE {process_inline_code($1);}
|
149
|
+
| REVERT_BOLD {revert_bold();}
|
150
|
+
| REVERT_ITALIC {revert_italic();}
|
151
|
+
|
152
|
+
link: TICKET_LINK {process_ticket_link($1);}
|
153
|
+
| SVN_REVISION_LINK {process_svn_link($1,0);}
|
154
|
+
| GIT_REVISION_LINK {process_git_link($1,0);}
|
155
|
+
| SVN_N_REVISION_LINK {process_svn_link($1,1);}
|
156
|
+
| GIT_N_REVISION_LINK {process_git_link($1,1);}
|
157
|
+
| URL_WITH_PROTO_LINK {process_url_link($1,NULL);}
|
158
|
+
| URL_WITHOUT_PROTO_LINK {process_url_link($1,"http://");}
|
159
|
+
| WIKI_LINK {process_wiki_link($1);}
|
160
|
+
| ANCHOR_LINK {process_anchor_link($1);}
|
161
|
+
| FILE_LINK {process_file_link($1);}
|
162
|
+
| IMAGE_LINK {process_image_link($1);}
|
163
|
+
| SNIPPET_LINK {process_snippet_link($1);}
|
166
164
|
|
167
165
|
chars:
|
168
166
|
| char chars
|
169
167
|
|
170
|
-
char : T_CHAR {concat_escaped_char($1)}
|
168
|
+
char : T_CHAR {concat_escaped_char($1);}
|
171
169
|
|
172
170
|
//raw_chars:
|
173
171
|
// | raw_char raw_chars
|
174
172
|
|
175
173
|
//raw_char : T_CHAR {concat_raw_char($1)}
|
176
174
|
|
177
|
-
h1 : H1
|
178
|
-
h2 : H2
|
179
|
-
h3 : H3
|
180
|
-
h4 : H4
|
181
|
-
h5 : H5
|
182
|
-
|
183
|
-
oli : OLI
|
184
|
-
|
185
|
-
br : BR
|
186
|
-
|
187
|
-
|
188
|
-
code : PRE_CODE_START {concat("<pre><code>",11)} chars PRE_CODE_END {concat("</code></pre>",13)}
|
175
|
+
h1 : H1 {concat("<h1 id=\"h-",10); process_header($1);}
|
176
|
+
h2 : H2 {concat("<h2 id=\"h-",10); process_header($1);}
|
177
|
+
h3 : H3 {concat("<h3 id=\"h-",10); process_header($1);}
|
178
|
+
h4 : H4 {concat("<h4 id=\"h-",10); process_header($1);}
|
179
|
+
h5 : H5 {concat("<h5 id=\"h-",10); process_header($1);}
|
180
|
+
uli : ULI {process_list_item($1, 1);}
|
181
|
+
oli : OLI {process_list_item($1, 2);}
|
182
|
+
oli2 : OLI2 {process_list_item($1, 3);}
|
183
|
+
br : BR {concat("<br />",6);}
|
184
|
+
|
185
|
+
code : PRE_CODE_START {concat("<pre><code>",11);} chars PRE_CODE_END {concat("</code></pre>",13);}
|
189
186
|
| NOTEXTILE_START chars NOTEXTILE_END
|
190
|
-
| PRE_START {concat("<pre>",5)} chars PRE_END {concat("</pre>",6)}
|
191
|
-
| CODE_START {concat("<code>",6)} chars CODE_END {concat("</code>",7)}
|
187
|
+
| PRE_START {concat("<pre>",5);} chars PRE_END {concat("</pre>",6);}
|
188
|
+
| CODE_START {concat("<code>",6);} chars CODE_END {concat("</code>",7);}
|
192
189
|
|
193
190
|
//word : T_WORD { process_word($1); }
|
194
191
|
|
195
192
|
%%
|
196
193
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
list_level--;
|
202
|
-
//unconcat("</li>");
|
203
|
-
concat("</ul></li><li>",14);
|
194
|
+
int closing_list_stack_push(const char * term) {
|
195
|
+
closing_list_stack_size++;
|
196
|
+
if( closing_list_stack != NULL ) {
|
197
|
+
closing_list_stack = REALLOC_N(closing_list_stack, const char *, closing_list_stack_size);
|
204
198
|
} else {
|
205
|
-
|
206
|
-
list_level++;
|
207
|
-
unconcat("</li>");
|
208
|
-
concat("<ul><li>",8);
|
199
|
+
closing_list_stack = ALLOC(const char *);
|
209
200
|
}
|
201
|
+
*(closing_list_stack + closing_list_stack_size - 1) = (const char *) strdup(term);
|
202
|
+
xfree((void *) term);
|
203
|
+
return closing_list_stack_size;
|
210
204
|
}
|
211
205
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
206
|
+
const char * closing_list_stack_pop() {
|
207
|
+
const char *term = NULL;
|
208
|
+
if( !closing_list_stack_size ) return NULL;
|
209
|
+
term = *(closing_list_stack + --closing_list_stack_size);
|
210
|
+
REALLOC_N(closing_list_stack, const char *, closing_list_stack_size);
|
211
|
+
return term;
|
212
|
+
}
|
213
|
+
|
214
|
+
void close_opened_lists() {
|
215
|
+
const char *term = NULL;
|
216
|
+
while( ( term = closing_list_stack_pop() ) != NULL ) {
|
217
|
+
concat2(term);
|
218
|
+
xfree((void *) term);
|
219
|
+
}
|
220
|
+
}
|
221
|
+
|
222
|
+
void process_list_item(int level, int type) {
|
223
|
+
int insert_start_tag = 0, unconcat_steps, level_diff = list_level - level;
|
224
|
+
const char *removed_term = NULL;
|
225
|
+
char * start_tag = NULL, * closing_tag = NULL;
|
226
|
+
|
227
|
+
switch( type ) {
|
228
|
+
case 1:
|
229
|
+
start_tag = strdup("<ul>");
|
230
|
+
closing_tag = strdup("</ul>");
|
231
|
+
break;
|
232
|
+
case 2:
|
233
|
+
start_tag = strdup("<ol>");
|
234
|
+
closing_tag = strdup("</ol>");
|
235
|
+
break;
|
236
|
+
case 3:
|
237
|
+
start_tag = strdup("<ol class='letters'>");
|
238
|
+
closing_tag = strdup("</ol>");
|
239
|
+
break;
|
240
|
+
}
|
241
|
+
|
242
|
+
if( !is_ending_with("</ul>") && !is_ending_with("</ol>") ) {
|
243
|
+
insert_start_tag = 1;
|
244
|
+
list_level = 1;
|
245
|
+
level_diff = list_level - level;
|
246
|
+
if( level_diff < 0 ) {
|
247
|
+
level_diff = 0;
|
248
|
+
level = 1;
|
249
|
+
}
|
250
|
+
}
|
251
|
+
if( level_diff < -1 ) level = list_level + 1;
|
252
|
+
|
253
|
+
if( level_diff >= 0 ) {
|
254
|
+
unconcat_steps = level;
|
255
|
+
for( ; unconcat_steps; --unconcat_steps ) {
|
256
|
+
if( unconcat_steps > 1 ) {
|
257
|
+
removed_term = unconcat("</li></ul>");
|
258
|
+
if( removed_term == NULL ) removed_term = unconcat("</li></ol>");
|
259
|
+
} else {
|
260
|
+
removed_term = unconcat("</ul>");
|
261
|
+
if( removed_term == NULL ) removed_term = unconcat("</ol>");
|
262
|
+
if( removed_term != NULL ) {
|
263
|
+
xfree((void *) removed_term);
|
264
|
+
removed_term = NULL; // we don't want to push this closing tag into the stack in this case
|
265
|
+
}
|
266
|
+
}
|
267
|
+
if( removed_term != NULL ) closing_list_stack_push(removed_term);
|
268
|
+
}
|
269
|
+
if( insert_start_tag ) concat2(start_tag);
|
270
|
+
concat2("<li>");
|
219
271
|
} else {
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
272
|
+
unconcat_steps = level - 1;
|
273
|
+
for( ; unconcat_steps; --unconcat_steps ) {
|
274
|
+
if( ( removed_term = unconcat("</li></ul>") ) || ( removed_term = unconcat("</li></ol>") ) ) {
|
275
|
+
closing_list_stack_push(removed_term);
|
276
|
+
}
|
277
|
+
}
|
278
|
+
concat2(start_tag);
|
279
|
+
concat2("<li>");
|
224
280
|
}
|
281
|
+
list_level = level;
|
282
|
+
closing_list_stack_push(closing_tag);
|
283
|
+
xfree(start_tag);
|
225
284
|
}
|
226
285
|
|
227
|
-
concat_hex_char(char c){
|
286
|
+
void concat_hex_char(char c){
|
228
287
|
unsigned char d;
|
229
288
|
d = ((unsigned char)c)>>4;
|
230
289
|
concat_raw_char(d>9 ? ('a'+d-10) : '0'+d);
|
@@ -232,12 +291,12 @@ concat_hex_char(char c){
|
|
232
291
|
concat_raw_char(d>9 ? ('a'+d-10) : '0'+d);
|
233
292
|
}
|
234
293
|
|
235
|
-
need_hex_convert(const char*p, const char*pend){
|
294
|
+
int need_hex_convert(const char*p, const char*pend){
|
236
295
|
// scan for non alphanum chars first
|
237
296
|
for(; *p && p<=pend; p++){
|
238
|
-
if( *p == ' ' ||
|
239
|
-
*p == '_' ||
|
240
|
-
*p == '-' ||
|
297
|
+
if( *p == ' ' ||
|
298
|
+
*p == '_' ||
|
299
|
+
*p == '-' ||
|
241
300
|
*p == '.' ||
|
242
301
|
(*p >= '0' && *p <= '9') ||
|
243
302
|
(*p >= 'a' && *p <= 'z') ||
|
@@ -252,7 +311,7 @@ need_hex_convert(const char*p, const char*pend){
|
|
252
311
|
return 0;
|
253
312
|
}
|
254
313
|
|
255
|
-
process_inline_code(const char*p){
|
314
|
+
void process_inline_code(const char*p){
|
256
315
|
if( *p == ' ' || *p == 9 ){
|
257
316
|
concat_raw_char(' ');
|
258
317
|
while( *p == ' ' || *p == 9 ) p++;
|
@@ -266,7 +325,7 @@ process_inline_code(const char*p){
|
|
266
325
|
concat("</code>",7);
|
267
326
|
}
|
268
327
|
|
269
|
-
process_header(const char*title){
|
328
|
+
void process_header(const char*title){
|
270
329
|
const char*p,*pend;
|
271
330
|
|
272
331
|
// skip heading spaces
|
@@ -287,7 +346,7 @@ process_header(const char*title){
|
|
287
346
|
for(p = title; *p && p<=pend; p++) concat_escaped_char( *p );
|
288
347
|
}
|
289
348
|
|
290
|
-
process_link_tail(const char*text,const char*pend,const char*prepend){
|
349
|
+
void process_link_tail(const char*text,const char*pend,const char*prepend){
|
291
350
|
const char*p;
|
292
351
|
|
293
352
|
concat("\">",2);
|
@@ -311,7 +370,7 @@ process_link_tail(const char*text,const char*pend,const char*prepend){
|
|
311
370
|
concat("</a>",4);
|
312
371
|
}
|
313
372
|
|
314
|
-
process_anchor_link(const char*target){
|
373
|
+
void process_anchor_link(const char*target){
|
315
374
|
const char *p,*pend;
|
316
375
|
|
317
376
|
// skip tail
|
@@ -333,7 +392,7 @@ process_anchor_link(const char*target){
|
|
333
392
|
process_link_tail(target,NULL,"#");
|
334
393
|
}
|
335
394
|
|
336
|
-
process_url_link(const char*target,const char* proto){
|
395
|
+
void process_url_link(const char*target,const char* proto){
|
337
396
|
const char *c;
|
338
397
|
concat("<a rel=\"nofollow\" href=\"",24);
|
339
398
|
if(proto){
|
@@ -345,17 +404,23 @@ process_url_link(const char*target,const char* proto){
|
|
345
404
|
if(*(bufptr-1) == '/') bufptr--; // skip redundant '/'
|
346
405
|
}
|
347
406
|
}
|
348
|
-
for(c=target; *c && *c != ']' && *c != '|'; c++)
|
407
|
+
for(c=target; *c && *c != ']' && *c != '|'; c++) concat_escaped_char(*c);
|
349
408
|
process_link_tail(target,NULL,proto);
|
350
409
|
}
|
351
410
|
|
352
|
-
concat_site_url(){
|
411
|
+
void concat_site_url(){
|
353
412
|
if( site_url && site_url_len > 0 ){
|
354
413
|
concat(site_url, site_url_len);
|
355
414
|
}
|
356
415
|
}
|
357
416
|
|
358
|
-
|
417
|
+
void concat_large_files_url(){
|
418
|
+
if( large_files_url && large_files_url_len > 0 ){
|
419
|
+
concat(large_files_url, large_files_url_len);
|
420
|
+
}
|
421
|
+
}
|
422
|
+
|
423
|
+
void process_svn_link(const char*target, int numbered_repo){
|
359
424
|
const char *c;
|
360
425
|
// can use sprintf here.. but I think it's a way slower than raw concat
|
361
426
|
concat("<a href=\"",9);
|
@@ -397,7 +462,7 @@ int concat_custom_vcs_url(const char*rev){
|
|
397
462
|
return 0;
|
398
463
|
}
|
399
464
|
|
400
|
-
process_git_link(const char*target, int numbered_repo){
|
465
|
+
void process_git_link(const char*target, int numbered_repo){
|
401
466
|
const char *c;
|
402
467
|
// can use sprintf here.. but I think it's a way slower than raw concat
|
403
468
|
concat("<a href=\"",9);
|
@@ -418,7 +483,7 @@ process_git_link(const char*target, int numbered_repo){
|
|
418
483
|
process_link_tail(target,NULL,"revision:");
|
419
484
|
}
|
420
485
|
|
421
|
-
process_wiki_link(const char*target){
|
486
|
+
void process_wiki_link(const char*target){
|
422
487
|
const char *c;
|
423
488
|
// can use sprintf here.. but I think it's a way slower than raw concat
|
424
489
|
concat("<a class=\"wiki_link\" title=\"",28);
|
@@ -441,11 +506,12 @@ process_wiki_link(const char*target){
|
|
441
506
|
process_link_tail(target,NULL,NULL);
|
442
507
|
}
|
443
508
|
|
444
|
-
process_file_link(const char*target){
|
509
|
+
void process_file_link(const char*target){
|
445
510
|
const char *c;
|
446
511
|
// can use sprintf here.. but I think it's a way slower than raw concat
|
447
512
|
concat("<a href=\"",9);
|
448
513
|
concat_site_url();
|
514
|
+
concat_large_files_url();
|
449
515
|
concat("/spaces/",8);
|
450
516
|
concat(space_name,space_name_len);
|
451
517
|
concat("/documents/download/",20);
|
@@ -453,11 +519,12 @@ process_file_link(const char*target){
|
|
453
519
|
process_link_tail(target,NULL,"file:");
|
454
520
|
}
|
455
521
|
|
456
|
-
process_image_link(const char*target){
|
522
|
+
void process_image_link(const char*target){
|
457
523
|
const char *c, *p;
|
458
524
|
// can use sprintf here.. but I think it's a way slower than raw concat
|
459
525
|
concat("<img src=\"",10);
|
460
526
|
concat_site_url();
|
527
|
+
concat_large_files_url();
|
461
528
|
concat("/spaces/",8);
|
462
529
|
concat(space_name,space_name_len);
|
463
530
|
concat("/documents/download/",20);
|
@@ -471,7 +538,7 @@ process_image_link(const char*target){
|
|
471
538
|
concat("\" />",4);
|
472
539
|
}
|
473
540
|
|
474
|
-
process_ticket_link(const char*ticket_id){
|
541
|
+
void process_ticket_link(const char*ticket_id){
|
475
542
|
const char *c;
|
476
543
|
while(*ticket_id && (*ticket_id < '0' || *ticket_id > '9') ) ticket_id++;
|
477
544
|
// can use sprintf here.. but I think it's a way slower than raw concat
|
@@ -481,10 +548,102 @@ process_ticket_link(const char*ticket_id){
|
|
481
548
|
concat(space_name,space_name_len);
|
482
549
|
concat("/tickets/",9);
|
483
550
|
for(c=ticket_id; *c && *c>='0' && *c<='9'; c++) concat_raw_char(*c);
|
551
|
+
|
552
|
+
if (RHASH_SIZE(meta_attributes) > 0) {
|
553
|
+
char *numeric_ticket_id = ALLOC_N(char, strlen(ticket_id));
|
554
|
+
set_numeric_ticket_id(ticket_id, numeric_ticket_id);
|
555
|
+
|
556
|
+
process_data_attributes(numeric_ticket_id);
|
557
|
+
xfree(numeric_ticket_id);
|
558
|
+
}
|
559
|
+
|
484
560
|
process_link_tail(ticket_id,NULL,"#");
|
485
561
|
}
|
486
562
|
|
487
|
-
|
563
|
+
void process_snippet_link(const char *space_id_with_snippet_id) {
|
564
|
+
size_t len = strlen(space_id_with_snippet_id);
|
565
|
+
char *space_id = ALLOC_N(char, len);
|
566
|
+
char *snippet_id = ALLOC_N(char, len);
|
567
|
+
char *pch;
|
568
|
+
char *timeval = ALLOC_N(char, 20);
|
569
|
+
int i = 0;
|
570
|
+
pch = strtok((char *)space_id_with_snippet_id, ":]");
|
571
|
+
// Extract only first two tokens: space_id and snippet_id
|
572
|
+
while (i < 2) {
|
573
|
+
i == 0 ? strcpy(space_id, pch) : strcpy(snippet_id, pch);
|
574
|
+
pch = strtok(NULL, ":]");
|
575
|
+
i += 1;
|
576
|
+
}
|
577
|
+
|
578
|
+
concat("<script async=\"true\" id=\"snippet-", 33);
|
579
|
+
concat(snippet_id, strlen(snippet_id));
|
580
|
+
concat("\" src=\"", 7);
|
581
|
+
concat_site_url();
|
582
|
+
concat_large_files_url();
|
583
|
+
concat("/spaces/", 8);
|
584
|
+
concat2(space_id);
|
585
|
+
concat("/snippets/", 10);
|
586
|
+
concat2(snippet_id);
|
587
|
+
concat(".js?_=", 6);
|
588
|
+
sprintf(timeval, "%ld", (long)time(NULL));
|
589
|
+
concat2(timeval);
|
590
|
+
concat("\"></script>", 11);
|
591
|
+
xfree(timeval);
|
592
|
+
xfree(space_id);
|
593
|
+
xfree(snippet_id);
|
594
|
+
}
|
595
|
+
|
596
|
+
int iterate_attributes_hash(VALUE key, VALUE record, st_data_t arg) {
|
597
|
+
add_data_attribute(RSTRING_PTR(key), RSTRING_PTR(record));
|
598
|
+
return ST_CONTINUE;
|
599
|
+
}
|
600
|
+
|
601
|
+
// NOTE: Data attributes work for tickets only right now
|
602
|
+
// Nevertheless, it's really easy to extend this method to
|
603
|
+
// add data attributes to any desired object
|
604
|
+
void process_data_attributes(char *ticket_id) {
|
605
|
+
VALUE meta_attributes_hash;
|
606
|
+
|
607
|
+
if (st_lookup(RHASH_TBL(meta_attributes), rb_str_new2(ticket_id), &meta_attributes_hash) == 1) {
|
608
|
+
// We found something for that ticket id
|
609
|
+
if (TYPE(meta_attributes_hash) == T_HASH) {
|
610
|
+
int meta_attributes_len = RHASH_SIZE(meta_attributes_hash);
|
611
|
+
if (meta_attributes_len > 0) {
|
612
|
+
st_data_t result = 0;
|
613
|
+
concat("\"", 1); // Close href quotation mark
|
614
|
+
st_foreach(RHASH_TBL(meta_attributes_hash), iterate_attributes_hash, result);
|
615
|
+
unconcat("\""); // Remove final quotation mark as it will be added by process_link_tail
|
616
|
+
};
|
617
|
+
} else {
|
618
|
+
rb_raise(rb_eTypeError, "Expected instance of Hash, %0x given", TYPE(meta_attributes_hash));
|
619
|
+
}
|
620
|
+
}
|
621
|
+
}
|
622
|
+
|
623
|
+
// Add data attribute to link
|
624
|
+
void add_data_attribute(char *attribute_name, char *attribute_value) {
|
625
|
+
concat(" data-", 6);
|
626
|
+
concat2(attribute_name);
|
627
|
+
concat("=\"", 2);
|
628
|
+
concat2(attribute_value);
|
629
|
+
concat("\"", 1);
|
630
|
+
}
|
631
|
+
|
632
|
+
void set_numeric_ticket_id(const char *ticket_id, char *numeric_ticket_id) {
|
633
|
+
char *c, *mutable_ticket_id;
|
634
|
+
int i = 0;
|
635
|
+
|
636
|
+
mutable_ticket_id = ALLOC_N(char, strlen(ticket_id));
|
637
|
+
memcpy(mutable_ticket_id, ticket_id, strlen(ticket_id));
|
638
|
+
for (c = mutable_ticket_id; *c && *c >= '0' && *c <= '9'; c++) {
|
639
|
+
*(numeric_ticket_id + i) = *c;
|
640
|
+
i++;
|
641
|
+
}
|
642
|
+
xfree(mutable_ticket_id);
|
643
|
+
*(numeric_ticket_id + i) = '\0';
|
644
|
+
}
|
645
|
+
|
646
|
+
void concat_escaped_char(int c){
|
488
647
|
switch(c){
|
489
648
|
case '<':
|
490
649
|
concat("<",4);
|
@@ -505,12 +664,12 @@ concat_escaped_char(int c){
|
|
505
664
|
}
|
506
665
|
|
507
666
|
|
508
|
-
concat_raw_char(int c){
|
667
|
+
void concat_raw_char(int c){
|
509
668
|
CHECK_BUF_SIZE(1);
|
510
669
|
*bufptr++ = c;
|
511
670
|
}
|
512
671
|
|
513
|
-
process_url(const char*url){
|
672
|
+
void process_url(const char*url){
|
514
673
|
const char *p;
|
515
674
|
|
516
675
|
concat("<a rel=\"nofollow\" href=\"",24);
|
@@ -524,7 +683,7 @@ process_url(const char*url){
|
|
524
683
|
process_link_tail(url,NULL,NULL);
|
525
684
|
}
|
526
685
|
|
527
|
-
process_email(const char*url){
|
686
|
+
void process_email(const char*url){
|
528
687
|
const char *p;
|
529
688
|
|
530
689
|
concat("<a href=\"mailto:",16);
|
@@ -532,12 +691,21 @@ process_email(const char*url){
|
|
532
691
|
process_link_tail(url,NULL,NULL);
|
533
692
|
}
|
534
693
|
|
535
|
-
unconcat(const char*
|
536
|
-
int
|
537
|
-
if(
|
694
|
+
const char *unconcat(const char *term){
|
695
|
+
int str_size = is_ending_with(term);
|
696
|
+
if( str_size ) {
|
697
|
+
bufptr -= str_size;
|
698
|
+
return (const char *) strndup(term, str_size + 1);
|
699
|
+
}
|
700
|
+
return (const char *) NULL;
|
701
|
+
}
|
702
|
+
int is_ending_with(const char *term) {
|
703
|
+
int str_size = (int) strlen(term);
|
704
|
+
if( bufptr - buf > str_size && strncmp(bufptr - str_size, term, str_size) == 0 ) return str_size;
|
705
|
+
return 0;
|
538
706
|
}
|
539
707
|
|
540
|
-
revert_bold(){
|
708
|
+
void revert_bold(){
|
541
709
|
char *p;
|
542
710
|
for( p=bufptr-1; p >= (buf+7) ; p--){
|
543
711
|
if( 0 == strncmp(p-7, "<strong>", 8) ){
|
@@ -547,7 +715,7 @@ revert_bold(){
|
|
547
715
|
}
|
548
716
|
}
|
549
717
|
|
550
|
-
revert_italic(){
|
718
|
+
void revert_italic(){
|
551
719
|
char *p;
|
552
720
|
for( p=bufptr-1; p >= (buf+3) ; p--){
|
553
721
|
if( 0 == strncmp(p-3, "<em>", 4) ){
|