mathematical 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/Rakefile +2 -2
- data/ext/mathematical/extconf.rb +2 -0
- data/ext/mathematical/mtex2MML/src/color_definitions.c +45 -0
- data/ext/mathematical/mtex2MML/src/deps/uthash/utarray.h +232 -0
- data/ext/mathematical/mtex2MML/src/deps/uthash/uthash.h +958 -0
- data/ext/mathematical/mtex2MML/src/deps/uthash/utlist.h +757 -0
- data/ext/mathematical/mtex2MML/src/deps/uthash/utstring.h +393 -0
- data/ext/mathematical/mtex2MML/src/lex.yy.c +4642 -3467
- data/ext/mathematical/mtex2MML/src/mtex2MML.h +31 -31
- data/ext/mathematical/mtex2MML/src/mtex2MML_ruby.c +2299 -0
- data/ext/mathematical/mtex2MML/src/parse_extras.c +223 -224
- data/ext/mathematical/mtex2MML/src/parse_extras.h +59 -58
- data/ext/mathematical/mtex2MML/src/string_extras.c +17 -194
- data/ext/mathematical/mtex2MML/src/string_extras.h +10 -28
- data/ext/mathematical/mtex2MML/src/y.tab.c +3622 -3063
- data/ext/mathematical/mtex2MML/src/y.tab.h +440 -336
- data/lib/mathematical/corrections.rb +0 -18
- data/lib/mathematical/version.rb +1 -1
- data/test/mathematical/corrections_test.rb +0 -40
- data/test/mathematical/fixtures/performance/big_file.text +8 -20
- data/test/mathematical/maliciousness_test.rb +12 -2
- data/test/mathematical/mathjax_test.rb +16 -0
- metadata +10 -4
- data/ext/mathematical/mtex2MML/src/stack.c +0 -80
- data/ext/mathematical/mtex2MML/src/stack.h +0 -101
@@ -1,281 +1,229 @@
|
|
1
1
|
#include <stdio.h>
|
2
2
|
#include <stdlib.h>
|
3
3
|
#include <string.h>
|
4
|
+
|
4
5
|
#include "parse_extras.h"
|
5
6
|
#include "string_extras.h"
|
6
|
-
#include "stack.h"
|
7
|
-
|
8
|
-
#ifdef FLIP_OFFSET_VAL
|
9
|
-
#define OFFSET_VAL 0
|
10
|
-
#else
|
11
|
-
#define OFFSET_VAL 1
|
12
|
-
#endif
|
13
|
-
|
14
|
-
void initSymbolDataArray(symbolDataArray *a, size_t initialSize)
|
15
|
-
{
|
16
|
-
// Allocate initial space
|
17
|
-
a->array = (symbolData *)malloc(initialSize * sizeof(symbolData));
|
18
|
-
|
19
|
-
a->used = 0; // no elements used
|
20
|
-
a->size = initialSize; // available number of elements
|
21
|
-
|
22
|
-
// Initialize all elements of the array at once: they are contiguous
|
23
|
-
memset(&a->array[0], 0, sizeof(symbolData) * initialSize);
|
24
|
-
}
|
25
7
|
|
26
|
-
void
|
8
|
+
void env_replacements(UT_array **environment_data_stack, const char *environment)
|
27
9
|
{
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
// Initialize the last/new elements of the reallocated array
|
32
|
-
memset(&a->array[a->used],0, sizeof(symbolData) * (a->size - a->used));
|
33
|
-
}
|
34
|
-
|
35
|
-
a->array[a->used].attribute = (char*)malloc(strlen(element.attribute) + 1);
|
36
|
-
strcpy(a->array[a->used].attribute, element.attribute);
|
37
|
-
|
38
|
-
a->array[a->used].offset_pos = element.offset_pos;
|
39
|
-
|
40
|
-
a->used++;
|
41
|
-
}
|
42
|
-
|
43
|
-
void sortSymbolDataArray(symbolDataArray *a) {
|
44
|
-
int i, j, n = a->used;
|
45
|
-
|
46
|
-
for(i = 1; i < n; i++) {
|
47
|
-
for(j = 0; j < n - i; j++) {
|
48
|
-
if(a->array[j].offset_pos < a->array[j+1].offset_pos) {
|
49
|
-
symbolData temp = a->array[j];
|
50
|
-
a->array[j] = a->array[j+1];
|
51
|
-
a->array[j+1] = temp;
|
52
|
-
}
|
53
|
-
}
|
54
|
-
}
|
55
|
-
}
|
56
|
-
|
57
|
-
void deleteSymbolDataArray(symbolDataArray *a)
|
58
|
-
{
|
59
|
-
int i;
|
60
|
-
|
61
|
-
// Free all name variables of each array element first
|
62
|
-
for(i = 0; i <a->used; i++)
|
63
|
-
{
|
64
|
-
free(a->array[i].attribute);
|
65
|
-
a->array[i].attribute = NULL;
|
66
|
-
}
|
67
|
-
|
68
|
-
// Now free the array
|
69
|
-
free(a->array);
|
70
|
-
a->array = NULL;
|
71
|
-
|
72
|
-
a->used = 0;
|
73
|
-
a->size = 0;
|
74
|
-
}
|
75
|
-
|
76
|
-
char * env_replacements(const char *string) {
|
77
|
-
stackT array_stack;
|
78
|
-
stackElementT stack_item, last_stack_item;
|
10
|
+
UT_array *array_stack;
|
11
|
+
UT_array *row_spacing_stack;
|
12
|
+
UT_array *rowlines_stack;
|
79
13
|
|
80
14
|
char *tok = NULL, *at_top = NULL;
|
81
|
-
char *new_environment = dupe_string(string), *dupe_str = dupe_string(string);
|
82
|
-
char *line = strtok(dupe_str, "\n");
|
83
|
-
char *attr_rowlines = "", *attr_rowspacing = "", *em_str, *temp = "";
|
84
15
|
|
16
|
+
char *temp = "", **last_stack_item;
|
17
|
+
char *a, *em_str;
|
85
18
|
const char *from = "\\begin", *until = "\\end", *hline = "\\hline", *hdashline = "\\hdashline",
|
86
|
-
|
87
|
-
|
19
|
+
*line_separator = "\\\\",
|
20
|
+
*em_pattern_begin = "\\[", *em_pattern_end = "]",
|
21
|
+
*is_smallmatrix = NULL, *is_gathered = NULL;
|
88
22
|
|
89
|
-
int
|
90
|
-
symbolDataArray hline_data_array;
|
91
|
-
symbolData hline_data;
|
92
|
-
|
93
|
-
symbolDataArray row_spacing_data_array;
|
94
|
-
symbolData row_spacing_data;
|
95
|
-
|
96
|
-
// set up the array stack
|
97
|
-
StackInit(&array_stack, strlen(new_environment));
|
98
|
-
initSymbolDataArray(&hline_data_array, 5);
|
99
|
-
initSymbolDataArray(&row_spacing_data_array, 1);
|
23
|
+
int rowlines_stack_len = 0, em_offset = 0;
|
100
24
|
|
101
25
|
// if not an environment, don't bother going on
|
102
|
-
if ((strstr(
|
103
|
-
|
104
|
-
deleteSymbolDataArray(&hline_data_array);
|
105
|
-
deleteSymbolDataArray(&row_spacing_data_array);
|
106
|
-
free(dupe_str);
|
107
|
-
|
108
|
-
return string;
|
26
|
+
if ( ((strstr(environment, from) == NULL && strstr(environment, until) == NULL)) || strstr(environment, "begin{svg}")) {
|
27
|
+
return;
|
109
28
|
}
|
110
29
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
stack_item.line = line;
|
115
|
-
stack_item.line_pos = start + 1;
|
30
|
+
char *dupe_environment = dupe_string(environment);
|
31
|
+
char *line = strtok(dupe_environment, "\n");
|
116
32
|
|
117
|
-
|
33
|
+
// set up the array stack
|
34
|
+
utarray_new(array_stack, &ut_str_icd);
|
35
|
+
utarray_new(row_spacing_stack, &ut_str_icd);
|
36
|
+
utarray_new(rowlines_stack, &ut_str_icd);
|
118
37
|
|
119
|
-
|
38
|
+
while (line != NULL) {
|
39
|
+
utarray_push_back(array_stack, &line);
|
120
40
|
|
121
41
|
if (strstr(line, until) != NULL) {
|
122
|
-
while (
|
123
|
-
last_stack_item =
|
124
|
-
|
125
|
-
|
42
|
+
while (utarray_len(array_stack) > 0) {
|
43
|
+
last_stack_item = (char **)utarray_back(array_stack);
|
44
|
+
|
45
|
+
rowlines_stack_len = utarray_len(rowlines_stack);
|
46
|
+
at_top = strstr(*last_stack_item, from);
|
126
47
|
|
127
48
|
// we've reached the top, but there looks like there might be some data
|
128
|
-
if (at_top != NULL && strstr(last_stack_item
|
49
|
+
if (at_top != NULL && strstr(*last_stack_item, line_separator) == NULL) {
|
129
50
|
break;
|
130
51
|
}
|
131
52
|
|
132
|
-
// looking for a
|
133
|
-
if (strstr(last_stack_item
|
134
|
-
if (
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
53
|
+
// looking for a hline/hdashline match
|
54
|
+
if (strstr(*last_stack_item, hline) != NULL) {
|
55
|
+
if (rowlines_stack_len > 0) {
|
56
|
+
utarray_pop_back(rowlines_stack);
|
57
|
+
}
|
58
|
+
a = "solid";
|
59
|
+
utarray_push_back(rowlines_stack, &a);
|
60
|
+
} else if (strstr(*last_stack_item, hdashline) != NULL) {
|
61
|
+
if (rowlines_stack_len > 0) {
|
62
|
+
utarray_pop_back(rowlines_stack);
|
63
|
+
}
|
64
|
+
a = "dashed";
|
65
|
+
utarray_push_back(rowlines_stack, &a);
|
66
|
+
} else {
|
67
|
+
a = "none";
|
68
|
+
utarray_push_back(rowlines_stack, &a);
|
145
69
|
}
|
146
70
|
|
147
|
-
if
|
148
|
-
|
149
|
-
|
71
|
+
// if there's a line break...
|
72
|
+
if (strstr(*last_stack_item, line_separator) != NULL) {
|
73
|
+
// when an emphasis match, add it...
|
74
|
+
if ( (tok = strstr(*last_stack_item, em_pattern_begin)) != NULL) {
|
75
|
+
temp = tok + 2; // skip the first part ("\[")
|
150
76
|
if ( (tok = strstr(temp, em_pattern_end)) != NULL) {
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
// MathML always expectes "em" points
|
156
|
-
convertToEm(em_str);
|
157
|
-
row_spacing_data.attribute = em_str;
|
158
|
-
row_spacing_data.offset_pos = -1; // this value is not really important
|
159
|
-
insertSymbolDataArray(&row_spacing_data_array, row_spacing_data);
|
160
|
-
free(em_str);
|
161
|
-
}
|
77
|
+
em_offset = (int)(tok - temp);
|
78
|
+
char *s = dupe_string_n(temp, em_offset);
|
79
|
+
utarray_push_back(row_spacing_stack, &s);
|
80
|
+
free(s);
|
162
81
|
}
|
163
82
|
}
|
83
|
+
// otherwise, use the default
|
164
84
|
else {
|
165
|
-
|
166
|
-
|
167
|
-
|
85
|
+
if (strstr(*last_stack_item, "\\begin{smallmatrix}") != NULL) {
|
86
|
+
em_str = "0.2em";
|
87
|
+
} else if (strstr(*last_stack_item, "\\begin{gathered}") != NULL) {
|
88
|
+
em_str = "1.0ex";
|
89
|
+
} else {
|
90
|
+
em_str = "0.5ex";
|
91
|
+
}
|
92
|
+
utarray_push_back(row_spacing_stack, &em_str);
|
168
93
|
}
|
169
94
|
}
|
170
95
|
|
96
|
+
// make sure to pop at the end here; it messes with some references in Travis/Ubuntu for some reason
|
97
|
+
utarray_pop_back(array_stack);
|
98
|
+
|
171
99
|
// we've reached the top, so stop.
|
172
|
-
if (at_top != NULL)
|
100
|
+
if (at_top != NULL) {
|
173
101
|
break;
|
102
|
+
}
|
103
|
+
}
|
104
|
+
if (at_top != NULL) {
|
105
|
+
is_smallmatrix = strstr(at_top, "\\begin{smallmatrix}");
|
106
|
+
is_gathered = strstr(at_top, "\\begin{gathered}");
|
174
107
|
}
|
175
108
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
// array is form of \begin{array}{cc..c}
|
181
|
-
tok = strstr(last_stack_item.line, "}{");
|
182
|
-
}
|
183
|
-
if (tok == NULL) {
|
184
|
-
// not an array, but rather, some env, like \begin{cases}
|
185
|
-
tok = strstr(last_stack_item.line, "}");
|
186
|
-
}
|
109
|
+
// TODO: we are skipping equation environments
|
110
|
+
if ((rowlines_stack_len != 0 || utarray_len(row_spacing_stack)) && strstr(*last_stack_item, "\\begin{equation}") == NULL) {
|
111
|
+
perform_replacement(environment_data_stack, rowlines_stack, is_smallmatrix, is_gathered, row_spacing_stack);
|
112
|
+
}
|
187
113
|
|
188
|
-
|
189
|
-
|
190
|
-
|
114
|
+
utarray_clear(row_spacing_stack);
|
115
|
+
utarray_clear(rowlines_stack);
|
116
|
+
rowlines_stack_len = 0;
|
117
|
+
}
|
191
118
|
|
192
|
-
|
193
|
-
|
119
|
+
line = strtok(NULL, "\n");
|
120
|
+
}
|
194
121
|
|
195
|
-
|
196
|
-
|
197
|
-
|
122
|
+
utarray_free(array_stack);
|
123
|
+
utarray_free(row_spacing_stack);
|
124
|
+
utarray_free(rowlines_stack);
|
125
|
+
free(dupe_environment);
|
126
|
+
}
|
198
127
|
|
199
|
-
|
128
|
+
void perform_replacement(UT_array **environment_data_stack, UT_array *rowlines_stack, const char *is_smallmatrix, const char *is_gathered, UT_array *row_spacing_stack)
|
129
|
+
{
|
130
|
+
char *a, *attr_rowlines, *attr_rowspacing;
|
131
|
+
envdata_t row_data;
|
200
132
|
|
201
|
-
|
202
|
-
|
203
|
-
}
|
133
|
+
// we cut the last char because we can always skip the first row
|
134
|
+
utarray_pop_back(rowlines_stack);
|
204
135
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
// last char is a pipe (|)
|
211
|
-
remove_last_char(attr_rowspacing);
|
212
|
-
}
|
136
|
+
// empty rowlines should be reset
|
137
|
+
if (utarray_len(rowlines_stack) == 0) {
|
138
|
+
a = "none";
|
139
|
+
utarray_push_back(rowlines_stack, &a);
|
140
|
+
}
|
213
141
|
|
214
|
-
|
215
|
-
|
142
|
+
// given the row_attribute values, construct an attribute list (separated by spaces)
|
143
|
+
UT_string *l;
|
144
|
+
utstring_new(l);
|
145
|
+
char **o=NULL;
|
146
|
+
a = "rowlines=\"";
|
147
|
+
utstring_printf(l, "%s", a);
|
148
|
+
while ( (o=(char**)utarray_prev(rowlines_stack,o))) {
|
149
|
+
utstring_printf(l, "%s ", *o);
|
150
|
+
}
|
216
151
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
152
|
+
attr_rowlines = utstring_body(l);
|
153
|
+
if (strlen(attr_rowlines) > 0) {
|
154
|
+
remove_last_char(attr_rowlines); // remove the final space
|
155
|
+
}
|
221
156
|
|
222
|
-
|
223
|
-
|
224
|
-
|
157
|
+
// given the row_spacing values, construct an attribute list (separated by spaces)
|
158
|
+
UT_string *s;
|
159
|
+
utstring_new(s);
|
160
|
+
char **p=NULL;
|
161
|
+
while ( (p=(char**)utarray_prev(row_spacing_stack,p))) {
|
162
|
+
if (is_smallmatrix && strcmp(*p, "0.5ex") == 0) {
|
163
|
+
utstring_printf(s, "%s ", "0.2em");
|
164
|
+
} else if (is_gathered && strcmp(*p, "0.5ex") == 0) {
|
165
|
+
utstring_printf(s, "%s ", "1.0ex");
|
166
|
+
} else {
|
167
|
+
utstring_printf(s, "%s ", *p);
|
225
168
|
}
|
226
|
-
|
227
|
-
line = strtok(NULL, "\n");
|
228
169
|
}
|
229
170
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
171
|
+
attr_rowspacing = utstring_body(s);
|
172
|
+
if (strlen(attr_rowspacing) > 0) {
|
173
|
+
remove_last_char(attr_rowspacing); // remove the final space
|
174
|
+
} else {
|
175
|
+
if (is_smallmatrix != NULL) {
|
176
|
+
attr_rowspacing = "0.2em";
|
177
|
+
} else if (is_gathered != NULL) {
|
178
|
+
attr_rowspacing = "1.0ex";
|
179
|
+
} else {
|
180
|
+
attr_rowspacing = "0.5ex";
|
181
|
+
}
|
236
182
|
}
|
237
183
|
|
238
|
-
|
239
|
-
|
240
|
-
deleteSymbolDataArray(&row_spacing_data_array);
|
241
|
-
free(dupe_str);
|
184
|
+
row_data.rowspacing = attr_rowspacing;
|
185
|
+
row_data.rowlines = attr_rowlines;
|
242
186
|
|
243
|
-
|
187
|
+
utarray_push_back(*environment_data_stack, &row_data);
|
188
|
+
utstring_free(l);
|
189
|
+
utstring_free(s);
|
244
190
|
}
|
245
191
|
|
246
|
-
const char *vertical_pipe_extract(const char *string)
|
247
|
-
|
248
|
-
char *
|
192
|
+
const char *vertical_pipe_extract(const char *string)
|
193
|
+
{
|
194
|
+
char *dupe = dupe_string(string);
|
195
|
+
UT_string *columnlines, *border;
|
196
|
+
char *previous_column = "", *attr_columnlines, *attr_border;
|
249
197
|
int i = 0;
|
250
198
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
columnlines
|
257
|
-
remove_first_char(
|
258
|
-
}
|
259
|
-
|
260
|
-
|
199
|
+
utstring_new(columnlines);
|
200
|
+
utstring_new(border);
|
201
|
+
|
202
|
+
// the first character (if it exists) determines the frame border
|
203
|
+
if (strncmp(dupe, "s", 1) == 0) {
|
204
|
+
utstring_printf(columnlines, "%s", "frame=\"solid\" columnlines=\"");
|
205
|
+
remove_first_char(dupe);
|
206
|
+
} else if (strncmp(dupe, "d", 1) == 0) {
|
207
|
+
utstring_printf(columnlines, "%s", "frame=\"dashed\" columnlines=\"");
|
208
|
+
remove_first_char(dupe);
|
209
|
+
} else {
|
210
|
+
utstring_printf(columnlines, "%s", "columnlines=\"");
|
261
211
|
}
|
262
212
|
|
263
|
-
char *token = strtok(
|
213
|
+
char *token = strtok(dupe, " ");
|
264
214
|
|
265
215
|
while (token != NULL) {
|
266
216
|
if (strncmp(token, "s", 1) == 0) {
|
267
217
|
previous_column = "s";
|
268
|
-
|
269
|
-
}
|
270
|
-
else if (strncmp(token, "d", 1) == 0) {
|
218
|
+
utstring_printf(border, "%s ", "solid");
|
219
|
+
} else if (strncmp(token, "d", 1) == 0) {
|
271
220
|
previous_column = "d";
|
272
|
-
|
273
|
-
}
|
274
|
-
else {
|
221
|
+
utstring_printf(border, "%s ", "dashed");
|
222
|
+
} else {
|
275
223
|
if (i >= 1) { // we must skip the first blank col
|
276
224
|
// only if there is no previous border should a border be considered, eg. "cc", not "c|c"
|
277
225
|
if (strncmp(previous_column, "s", 1) != 0 && strncmp(previous_column, "d", 1) != 0) {
|
278
|
-
|
226
|
+
utstring_printf(border, "%s ", "none");
|
279
227
|
}
|
280
228
|
previous_column = "0";
|
281
229
|
}
|
@@ -285,22 +233,73 @@ const char *vertical_pipe_extract(const char *string) {
|
|
285
233
|
token = strtok(NULL, " ");
|
286
234
|
}
|
287
235
|
|
288
|
-
|
289
|
-
if (
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
236
|
+
attr_border = utstring_body(border);
|
237
|
+
if (strlen(attr_border) > 0) {
|
238
|
+
remove_last_char(attr_border); // remove the final space
|
239
|
+
}
|
240
|
+
utstring_printf(columnlines, "%s", attr_border);
|
241
|
+
|
242
|
+
// an empty string here angers Lasem, so let's remember to add 'none'
|
243
|
+
if (utstring_len(border) == 0) {
|
244
|
+
utstring_printf(columnlines, "%s", "none");
|
245
|
+
}
|
246
|
+
|
247
|
+
attr_columnlines = utstring_body(columnlines);
|
248
|
+
free(dupe);
|
249
|
+
utstring_free(border);
|
250
|
+
|
251
|
+
return attr_columnlines;
|
252
|
+
}
|
253
|
+
|
254
|
+
const char *remove_excess_pipe_chars(const char *string)
|
255
|
+
{
|
256
|
+
UT_string *columnalign;
|
257
|
+
utstring_new(columnalign);
|
258
|
+
|
259
|
+
char *dupe = dupe_string(string);
|
260
|
+
char *token = strtok(dupe, " ");
|
261
|
+
char *attr_columnalign;
|
262
|
+
|
263
|
+
while (token != NULL) {
|
264
|
+
if (strncmp(token, "s", 1) != 0 && strncmp(token, "d", 1) != 0) {
|
265
|
+
utstring_printf(columnalign, "%s ", token);
|
266
|
+
}
|
267
|
+
token = strtok(NULL, " ");
|
268
|
+
}
|
269
|
+
|
270
|
+
attr_columnalign = utstring_body(columnalign);
|
271
|
+
free(dupe);
|
272
|
+
|
273
|
+
if (strlen(attr_columnalign) > 0) {
|
274
|
+
remove_last_char(attr_columnalign); // remove the final space
|
275
|
+
}
|
294
276
|
|
295
|
-
|
296
|
-
return columnlines;
|
277
|
+
return attr_columnalign;
|
297
278
|
}
|
298
279
|
|
299
|
-
const char *
|
300
|
-
|
280
|
+
const char *combine_row_data(UT_array **environment_data_stack)
|
281
|
+
{
|
282
|
+
// if no information was provided, give a standard sizing
|
283
|
+
if (utarray_len(*environment_data_stack) == 0) {
|
284
|
+
const char* s = "rowspacing=\"0.5ex\" rowlines=\"none\"";
|
285
|
+
char* c = (char*)malloc(strlen(s) + 1);
|
286
|
+
strcpy(c, s);
|
287
|
+
return c;
|
288
|
+
}
|
289
|
+
envdata_t *row_data_elem = (envdata_t*) utarray_front(*environment_data_stack);
|
290
|
+
|
291
|
+
char *row_spacing_data = row_data_elem->rowspacing,
|
292
|
+
*row_lines_data = row_data_elem->rowlines,
|
293
|
+
*row_attr;
|
294
|
+
|
295
|
+
UT_string *row_attr_data;
|
296
|
+
utstring_new(row_attr_data);
|
297
|
+
|
298
|
+
// combine the row spacing and row lines data
|
299
|
+
utstring_printf(row_attr_data, "%s%s\" %s\"", "rowspacing=\"", row_spacing_data, row_lines_data);
|
301
300
|
|
302
|
-
|
303
|
-
|
301
|
+
row_attr = dupe_string(utstring_body(row_attr_data));
|
302
|
+
utarray_erase(*environment_data_stack, 0, 1);
|
304
303
|
|
305
|
-
return
|
304
|
+
return row_attr;
|
306
305
|
}
|