breakout_parser 0.0.23 → 0.0.31
Sign up to get free protection for your applications and to get access to all the features.
- 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) ){
|