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 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
- data, # data to parse
30
- space_name, # space name - for links parsing
31
- site_url, # [optional] global site url
32
- vcs_url # [optional] custom VCS url - f.ex. for github-hosted repos
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+\.\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.4.0') && check_version('lex','2.5.30')
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 "\tlex parser.l"
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
- ^[ \t]*\*\*\*[ ]+ { yylval.ivalue=3; return ULI; }
128
- ^[ \t]*\*\*[ ]+ { yylval.ivalue=2; return ULI; }
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]*###[ ]+ { yylval.ivalue=3; return OLI; }
132
- ^[ \t]*##[ ]+ { yylval.ivalue=2; return OLI; }
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
- #ifdef RUBY_VERSION
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
- #define REALLOC_N(ptr,type,n) ptr=realloc(ptr,sizeof(type)*n)
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 const* svalue;
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 /*BRBR*/
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
- ulist: ulitem {concat("</li>",5)}
126
- | ulist ulitem {concat("</li>",5)}
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 {concat("<h1 id=\"h-",10); process_header($1)}
178
- h2 : H2 {concat("<h2 id=\"h-",10); process_header($1)}
179
- h3 : H3 {concat("<h3 id=\"h-",10); process_header($1)}
180
- h4 : H4 {concat("<h4 id=\"h-",10); process_header($1)}
181
- h5 : H5 {concat("<h5 id=\"h-",10); process_header($1)}
182
- //ul : UL {concat("<ul>",4)}
183
- oli : OLI {process_oli($1)}
184
- uli : ULI {process_uli($1)}
185
- br : BR {concat("<br />",6)}
186
- // | BRBR {concat("<br /><br />",12)}
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
- process_uli(int level){
198
- if( level == list_level ){
199
- concat("<li>",4);
200
- } else if( level < list_level ){
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
- // if(level > list_level)
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
- process_oli(int level){
213
- if( level == list_level ){
214
- concat("<li>",4);
215
- } else if( level < list_level ){
216
- list_level = level;
217
- //unconcat("</li>");
218
- concat("</ol></li><li>",14);
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
- // if(level > list_level)
221
- list_level = level;
222
- unconcat("</li>");
223
- concat("<ol><li>",8);
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++) concat_raw_char(*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
- process_svn_link(const char*target, int numbered_repo){
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
- concat_escaped_char(int c){
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("&lt;",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*what){
536
- int l = strlen(what);
537
- if( bufptr-buf > l && strncmp(bufptr-l,what,l) == 0 ) bufptr -= l;
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) ){