| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
#define PERL_NO_GET_CONTEXT |
|
3
|
|
|
|
|
|
|
#include "EXTERN.h" |
|
4
|
|
|
|
|
|
|
#include "perl.h" |
|
5
|
|
|
|
|
|
|
#include "XSUB.h" |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
#ifndef av_count |
|
8
|
|
|
|
|
|
|
#define av_count(av) (av_top_index(av) + 1) |
|
9
|
|
|
|
|
|
|
#endif |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
#include |
|
12
|
|
|
|
|
|
|
#include |
|
13
|
|
|
|
|
|
|
#include |
|
14
|
|
|
|
|
|
|
#include |
|
15
|
|
|
|
|
|
|
#include |
|
16
|
|
|
|
|
|
|
#include |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
/* ---- escape/format helpers ---- */ |
|
19
|
|
|
|
|
|
|
|
|
20
|
11074
|
|
|
|
|
|
static inline int itoa_fast(char *buf, long val) { |
|
21
|
|
|
|
|
|
|
char tmp[20]; |
|
22
|
11074
|
|
|
|
|
|
int len = 0, neg = 0; |
|
23
|
|
|
|
|
|
|
unsigned long uval; |
|
24
|
11074
|
100
|
|
|
|
|
if (val < 0) { neg = 1; uval = -(unsigned long)val; } else { uval = (unsigned long)val; } |
|
25
|
41870
|
100
|
|
|
|
|
do { tmp[len++] = '0' + (uval % 10); uval /= 10; } while (uval); |
|
26
|
11074
|
100
|
|
|
|
|
if (neg) tmp[len++] = '-'; |
|
27
|
52945
|
100
|
|
|
|
|
for (int i = 0; i < len; i++) buf[i] = tmp[len - 1 - i]; |
|
28
|
11074
|
|
|
|
|
|
return len; |
|
29
|
|
|
|
|
|
|
} |
|
30
|
|
|
|
|
|
|
|
|
31
|
1006
|
|
|
|
|
|
static int itoa_comma(char *buf, long val) { |
|
32
|
|
|
|
|
|
|
char digits[20]; |
|
33
|
1006
|
|
|
|
|
|
int dlen = 0, neg = 0; |
|
34
|
|
|
|
|
|
|
unsigned long uval; |
|
35
|
1006
|
100
|
|
|
|
|
if (val < 0) { neg = 1; uval = -(unsigned long)val; } else { uval = (unsigned long)val; } |
|
36
|
2945
|
100
|
|
|
|
|
do { digits[dlen++] = '0' + (uval % 10); uval /= 10; } while (uval); |
|
37
|
1006
|
|
|
|
|
|
int pos = 0; |
|
38
|
1006
|
100
|
|
|
|
|
if (neg) buf[pos++] = '-'; |
|
39
|
3951
|
100
|
|
|
|
|
for (int i = dlen - 1; i >= 0; i--) { |
|
40
|
2945
|
|
|
|
|
|
buf[pos++] = digits[i]; |
|
41
|
2945
|
100
|
|
|
|
|
if (i > 0 && i % 3 == 0) buf[pos++] = ','; |
|
|
|
100
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
} |
|
43
|
1006
|
|
|
|
|
|
return pos; |
|
44
|
|
|
|
|
|
|
} |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
/* lookup table for html_escape: 1 = special char */ |
|
47
|
|
|
|
|
|
|
static char html_special[256]; |
|
48
|
|
|
|
|
|
|
static char html_br_special[256]; |
|
49
|
|
|
|
|
|
|
static char json_special[256]; |
|
50
|
|
|
|
|
|
|
static int html_tables_inited = 0; |
|
51
|
|
|
|
|
|
|
|
|
52
|
4
|
|
|
|
|
|
static void init_html_tables(void) { |
|
53
|
4
|
50
|
|
|
|
|
if (html_tables_inited) return; |
|
54
|
4
|
|
|
|
|
|
memset(html_special, 0, 256); |
|
55
|
4
|
|
|
|
|
|
html_special[(unsigned char)'&'] = 1; |
|
56
|
4
|
|
|
|
|
|
html_special[(unsigned char)'<'] = 1; |
|
57
|
4
|
|
|
|
|
|
html_special[(unsigned char)'>'] = 1; |
|
58
|
4
|
|
|
|
|
|
html_special[(unsigned char)'"'] = 1; |
|
59
|
4
|
|
|
|
|
|
html_special[(unsigned char)'\''] = 1; |
|
60
|
4
|
|
|
|
|
|
memcpy(html_br_special, html_special, 256); |
|
61
|
4
|
|
|
|
|
|
html_br_special[(unsigned char)'\n'] = 1; |
|
62
|
4
|
|
|
|
|
|
memset(json_special, 0, 256); |
|
63
|
4
|
|
|
|
|
|
json_special[(unsigned char)'"'] = 1; |
|
64
|
4
|
|
|
|
|
|
json_special[(unsigned char)'\\'] = 1; |
|
65
|
4
|
|
|
|
|
|
json_special[(unsigned char)'\b'] = 1; |
|
66
|
4
|
|
|
|
|
|
json_special[(unsigned char)'\f'] = 1; |
|
67
|
4
|
|
|
|
|
|
json_special[(unsigned char)'\n'] = 1; |
|
68
|
4
|
|
|
|
|
|
json_special[(unsigned char)'\r'] = 1; |
|
69
|
4
|
|
|
|
|
|
json_special[(unsigned char)'\t'] = 1; |
|
70
|
132
|
100
|
|
|
|
|
for (int i = 0; i < 0x20; i++) json_special[i] = 1; |
|
71
|
4
|
|
|
|
|
|
html_tables_inited = 1; |
|
72
|
|
|
|
|
|
|
} |
|
73
|
|
|
|
|
|
|
|
|
74
|
15
|
|
|
|
|
|
static int html_escape(char *dst, const char *src, STRLEN slen) { |
|
75
|
15
|
|
|
|
|
|
char *out = dst; |
|
76
|
15
|
|
|
|
|
|
STRLEN i = 0; |
|
77
|
52
|
100
|
|
|
|
|
while (i < slen) { |
|
78
|
|
|
|
|
|
|
/* scan for run of non-special bytes */ |
|
79
|
41
|
|
|
|
|
|
STRLEN run = i; |
|
80
|
242
|
100
|
|
|
|
|
while (run < slen && !html_special[(unsigned char)src[run]]) run++; |
|
|
|
100
|
|
|
|
|
|
|
81
|
41
|
100
|
|
|
|
|
if (run > i) { |
|
82
|
31
|
|
|
|
|
|
memcpy(out, src + i, run - i); |
|
83
|
31
|
|
|
|
|
|
out += run - i; |
|
84
|
31
|
|
|
|
|
|
i = run; |
|
85
|
|
|
|
|
|
|
} |
|
86
|
41
|
100
|
|
|
|
|
if (i >= slen) break; |
|
87
|
37
|
|
|
|
|
|
switch (src[i]) { |
|
88
|
2
|
|
|
|
|
|
case '&': memcpy(out, "&", 5); out += 5; break; |
|
89
|
16
|
|
|
|
|
|
case '<': memcpy(out, "<", 4); out += 4; break; |
|
90
|
16
|
|
|
|
|
|
case '>': memcpy(out, ">", 4); out += 4; break; |
|
91
|
2
|
|
|
|
|
|
case '"': memcpy(out, """, 6); out += 6; break; |
|
92
|
1
|
|
|
|
|
|
case '\'': memcpy(out, "'", 5); out += 5; break; |
|
93
|
|
|
|
|
|
|
} |
|
94
|
37
|
|
|
|
|
|
i++; |
|
95
|
|
|
|
|
|
|
} |
|
96
|
15
|
|
|
|
|
|
return (int)(out - dst); |
|
97
|
|
|
|
|
|
|
} |
|
98
|
|
|
|
|
|
|
|
|
99
|
1
|
|
|
|
|
|
static int html_br_escape(char *dst, const char *src, STRLEN slen) { |
|
100
|
1
|
|
|
|
|
|
char *out = dst; |
|
101
|
1
|
|
|
|
|
|
STRLEN i = 0; |
|
102
|
5
|
100
|
|
|
|
|
while (i < slen) { |
|
103
|
4
|
|
|
|
|
|
STRLEN run = i; |
|
104
|
17
|
50
|
|
|
|
|
while (run < slen && !html_br_special[(unsigned char)src[run]]) run++; |
|
|
|
100
|
|
|
|
|
|
|
105
|
4
|
100
|
|
|
|
|
if (run > i) { |
|
106
|
3
|
|
|
|
|
|
memcpy(out, src + i, run - i); |
|
107
|
3
|
|
|
|
|
|
out += run - i; |
|
108
|
3
|
|
|
|
|
|
i = run; |
|
109
|
|
|
|
|
|
|
} |
|
110
|
4
|
50
|
|
|
|
|
if (i >= slen) break; |
|
111
|
4
|
|
|
|
|
|
switch (src[i]) { |
|
112
|
0
|
|
|
|
|
|
case '&': memcpy(out, "&", 5); out += 5; break; |
|
113
|
1
|
|
|
|
|
|
case '<': memcpy(out, "<", 4); out += 4; break; |
|
114
|
1
|
|
|
|
|
|
case '>': memcpy(out, ">", 4); out += 4; break; |
|
115
|
0
|
|
|
|
|
|
case '"': memcpy(out, """, 6); out += 6; break; |
|
116
|
0
|
|
|
|
|
|
case '\'': memcpy(out, "'", 5); out += 5; break; |
|
117
|
2
|
|
|
|
|
|
case '\n': memcpy(out, " ", 4); out += 4; break; |
|
118
|
|
|
|
|
|
|
} |
|
119
|
4
|
|
|
|
|
|
i++; |
|
120
|
|
|
|
|
|
|
} |
|
121
|
1
|
|
|
|
|
|
return (int)(out - dst); |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
|
|
124
|
1
|
|
|
|
|
|
static int url_escape(char *dst, const char *src, STRLEN slen) { |
|
125
|
|
|
|
|
|
|
static const char hex[] = "0123456789ABCDEF"; |
|
126
|
1
|
|
|
|
|
|
char *out = dst; |
|
127
|
20
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) { |
|
128
|
19
|
|
|
|
|
|
unsigned char c = (unsigned char)src[i]; |
|
129
|
19
|
100
|
|
|
|
|
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
130
|
3
|
50
|
|
|
|
|
(c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~') |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
131
|
16
|
|
|
|
|
|
*out++ = c; |
|
132
|
3
|
|
|
|
|
|
else { *out++ = '%'; *out++ = hex[c >> 4]; *out++ = hex[c & 0xf]; } |
|
133
|
|
|
|
|
|
|
} |
|
134
|
1
|
|
|
|
|
|
return (int)(out - dst); |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
|
|
137
|
3
|
|
|
|
|
|
static int json_escape(char *dst, const char *src, STRLEN slen) { |
|
138
|
|
|
|
|
|
|
static const char hex[] = "0123456789abcdef"; |
|
139
|
3
|
|
|
|
|
|
char *out = dst; |
|
140
|
30
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) { |
|
141
|
27
|
|
|
|
|
|
unsigned char c = (unsigned char)src[i]; |
|
142
|
27
|
|
|
|
|
|
switch (c) { |
|
143
|
4
|
|
|
|
|
|
case '"': *out++ = '\\'; *out++ = '"'; break; |
|
144
|
0
|
|
|
|
|
|
case '\\': *out++ = '\\'; *out++ = '\\'; break; |
|
145
|
0
|
|
|
|
|
|
case '\b': *out++ = '\\'; *out++ = 'b'; break; |
|
146
|
0
|
|
|
|
|
|
case '\f': *out++ = '\\'; *out++ = 'f'; break; |
|
147
|
1
|
|
|
|
|
|
case '\n': *out++ = '\\'; *out++ = 'n'; break; |
|
148
|
0
|
|
|
|
|
|
case '\r': *out++ = '\\'; *out++ = 'r'; break; |
|
149
|
0
|
|
|
|
|
|
case '\t': *out++ = '\\'; *out++ = 't'; break; |
|
150
|
22
|
|
|
|
|
|
default: |
|
151
|
22
|
100
|
|
|
|
|
if (c < 0x20) { |
|
152
|
2
|
|
|
|
|
|
*out++ = '\\'; *out++ = 'u'; *out++ = '0'; *out++ = '0'; |
|
153
|
2
|
|
|
|
|
|
*out++ = hex[c >> 4]; *out++ = hex[c & 0xf]; |
|
154
|
20
|
|
|
|
|
|
} else *out++ = c; |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
} |
|
157
|
3
|
|
|
|
|
|
return (int)(out - dst); |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
|
|
160
|
1
|
|
|
|
|
|
static int hex_encode(char *dst, const char *src, STRLEN slen) { |
|
161
|
|
|
|
|
|
|
static const char hex[] = "0123456789abcdef"; |
|
162
|
4
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) { |
|
163
|
3
|
|
|
|
|
|
unsigned char c = (unsigned char)src[i]; |
|
164
|
3
|
|
|
|
|
|
dst[i*2] = hex[c >> 4]; dst[i*2+1] = hex[c & 0xf]; |
|
165
|
|
|
|
|
|
|
} |
|
166
|
1
|
|
|
|
|
|
return (int)(slen * 2); |
|
167
|
|
|
|
|
|
|
} |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
|
170
|
|
|
|
|
|
|
static const char b64url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; |
|
171
|
|
|
|
|
|
|
|
|
172
|
10
|
|
|
|
|
|
static int base64_encode_with(char *dst, const unsigned char *src, STRLEN slen, const char *alpha, int pad) { |
|
173
|
10
|
|
|
|
|
|
char *out = dst; |
|
174
|
|
|
|
|
|
|
STRLEN i; |
|
175
|
14
|
100
|
|
|
|
|
for (i = 0; i + 2 < slen; i += 3) { |
|
176
|
4
|
|
|
|
|
|
*out++ = alpha[src[i] >> 2]; |
|
177
|
4
|
|
|
|
|
|
*out++ = alpha[((src[i] & 3) << 4) | (src[i+1] >> 4)]; |
|
178
|
4
|
|
|
|
|
|
*out++ = alpha[((src[i+1] & 0xf) << 2) | (src[i+2] >> 6)]; |
|
179
|
4
|
|
|
|
|
|
*out++ = alpha[src[i+2] & 0x3f]; |
|
180
|
|
|
|
|
|
|
} |
|
181
|
10
|
100
|
|
|
|
|
if (i < slen) { |
|
182
|
9
|
|
|
|
|
|
*out++ = alpha[src[i] >> 2]; |
|
183
|
9
|
100
|
|
|
|
|
if (i + 1 < slen) { |
|
184
|
6
|
|
|
|
|
|
*out++ = alpha[((src[i] & 3) << 4) | (src[i+1] >> 4)]; |
|
185
|
6
|
|
|
|
|
|
*out++ = alpha[((src[i+1] & 0xf) << 2)]; |
|
186
|
|
|
|
|
|
|
} else { |
|
187
|
3
|
|
|
|
|
|
*out++ = alpha[((src[i] & 3) << 4)]; |
|
188
|
3
|
100
|
|
|
|
|
if (pad) *out++ = '='; |
|
189
|
|
|
|
|
|
|
} |
|
190
|
9
|
100
|
|
|
|
|
if (pad) *out++ = '='; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
10
|
|
|
|
|
|
return (int)(out - dst); |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
#define base64_encode(d,s,l) base64_encode_with(d,s,l,b64,1) |
|
195
|
|
|
|
|
|
|
#define base64url_encode(d,s,l) base64_encode_with(d,s,l,b64url,0) |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
/* ---- compiled template structures ---- */ |
|
198
|
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
enum xform_type { |
|
200
|
|
|
|
|
|
|
XF_INT, XF_INT_COMMA, XF_FLOAT, XF_HTML, XF_HTML_BR, |
|
201
|
|
|
|
|
|
|
XF_RAW, XF_URL, XF_JSON, XF_TRIM, XF_UC, XF_LC, |
|
202
|
|
|
|
|
|
|
XF_PAD, XF_RPAD, XF_TRUNC, XF_DEFAULT, |
|
203
|
|
|
|
|
|
|
XF_HEX, XF_BASE64, XF_BASE64URL, XF_COUNT, XF_BOOL, XF_DATE, |
|
204
|
|
|
|
|
|
|
XF_SPRINTF, XF_REPLACE, XF_SUBSTR, XF_PLURAL, |
|
205
|
|
|
|
|
|
|
XF_IF, XF_UNLESS, XF_MAP, XF_WRAP, |
|
206
|
|
|
|
|
|
|
XF_NUMBER_SI, XF_BYTES_SI, XF_ELAPSED, XF_AGO, |
|
207
|
|
|
|
|
|
|
XF_MASK, XF_ROWNUM, XF_COALESCE, XF_LENGTH |
|
208
|
|
|
|
|
|
|
}; |
|
209
|
|
|
|
|
|
|
enum row_mode { ROW_ARRAY, ROW_HASH }; |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
typedef struct { |
|
212
|
|
|
|
|
|
|
enum xform_type type; |
|
213
|
|
|
|
|
|
|
int param_int; |
|
214
|
|
|
|
|
|
|
char *param_str; |
|
215
|
|
|
|
|
|
|
STRLEN param_str_len; |
|
216
|
|
|
|
|
|
|
char *param_str2; /* bool falsy, replace new, plural plural-form, wrap suffix */ |
|
217
|
|
|
|
|
|
|
STRLEN param_str2_len; |
|
218
|
|
|
|
|
|
|
int param_int2; /* substr length, map entry count */ |
|
219
|
|
|
|
|
|
|
/* map entries: stored as parallel arrays of keys and values */ |
|
220
|
|
|
|
|
|
|
char **map_keys; STRLEN *map_key_lens; |
|
221
|
|
|
|
|
|
|
char **map_vals; STRLEN *map_val_lens; |
|
222
|
|
|
|
|
|
|
int map_count; |
|
223
|
|
|
|
|
|
|
} tpl_xform; |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
typedef struct { |
|
226
|
|
|
|
|
|
|
/* static text (if chain is NULL) */ |
|
227
|
|
|
|
|
|
|
char *static_data; STRLEN static_len; |
|
228
|
|
|
|
|
|
|
/* field ref */ |
|
229
|
|
|
|
|
|
|
int col; |
|
230
|
|
|
|
|
|
|
char *key; STRLEN key_len; |
|
231
|
|
|
|
|
|
|
/* transform chain */ |
|
232
|
|
|
|
|
|
|
tpl_xform *chain; |
|
233
|
|
|
|
|
|
|
int chain_len; |
|
234
|
|
|
|
|
|
|
int is_rownum; |
|
235
|
|
|
|
|
|
|
} tpl_op; |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
typedef struct { |
|
238
|
|
|
|
|
|
|
char *header; STRLEN header_len; |
|
239
|
|
|
|
|
|
|
char *footer; STRLEN footer_len; |
|
240
|
|
|
|
|
|
|
char *sep; STRLEN sep_len; |
|
241
|
|
|
|
|
|
|
tpl_op *ops; int nops; |
|
242
|
|
|
|
|
|
|
enum row_mode mode; |
|
243
|
|
|
|
|
|
|
SSize_t last_row_count; |
|
244
|
|
|
|
|
|
|
char escape_char; /* delimiter char, default '{' */ |
|
245
|
|
|
|
|
|
|
char *render_buf; STRLEN render_buf_alloc; |
|
246
|
|
|
|
|
|
|
/* skip_if / skip_unless */ |
|
247
|
|
|
|
|
|
|
int skip_if_col; char *skip_if_key; STRLEN skip_if_key_len; int has_skip_if; |
|
248
|
|
|
|
|
|
|
int skip_unless_col; char *skip_unless_key; STRLEN skip_unless_key_len; int has_skip_unless; |
|
249
|
|
|
|
|
|
|
} tpl_compiled; |
|
250
|
|
|
|
|
|
|
|
|
251
|
206
|
|
|
|
|
|
static void tpl_free(tpl_compiled *t) { |
|
252
|
206
|
100
|
|
|
|
|
if (t->header) free(t->header); |
|
253
|
206
|
100
|
|
|
|
|
if (t->footer) free(t->footer); |
|
254
|
206
|
100
|
|
|
|
|
if (t->sep) free(t->sep); |
|
255
|
206
|
100
|
|
|
|
|
if (t->render_buf) free(t->render_buf); |
|
256
|
206
|
100
|
|
|
|
|
if (t->skip_if_key) free(t->skip_if_key); |
|
257
|
206
|
100
|
|
|
|
|
if (t->skip_unless_key) free(t->skip_unless_key); |
|
258
|
474
|
100
|
|
|
|
|
for (int i = 0; i < t->nops; i++) { |
|
259
|
268
|
100
|
|
|
|
|
if (t->ops[i].static_data) free(t->ops[i].static_data); |
|
260
|
268
|
100
|
|
|
|
|
if (t->ops[i].key) free(t->ops[i].key); |
|
261
|
268
|
100
|
|
|
|
|
if (t->ops[i].chain) { |
|
262
|
457
|
100
|
|
|
|
|
for (int j = 0; j < t->ops[i].chain_len; j++) { |
|
263
|
237
|
|
|
|
|
|
tpl_xform *x = &t->ops[i].chain[j]; |
|
264
|
237
|
100
|
|
|
|
|
if (x->param_str) free(x->param_str); |
|
265
|
237
|
100
|
|
|
|
|
if (x->param_str2) free(x->param_str2); |
|
266
|
237
|
100
|
|
|
|
|
if (x->map_keys) { |
|
267
|
18
|
100
|
|
|
|
|
for (int k = 0; k < x->map_count; k++) { free(x->map_keys[k]); free(x->map_vals[k]); } |
|
268
|
5
|
|
|
|
|
|
free(x->map_keys); free(x->map_vals); free(x->map_key_lens); free(x->map_val_lens); |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
} |
|
271
|
220
|
|
|
|
|
|
free(t->ops[i].chain); |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
} |
|
274
|
206
|
50
|
|
|
|
|
if (t->ops) free(t->ops); |
|
275
|
206
|
|
|
|
|
|
free(t); |
|
276
|
206
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
/* parse "type" or "type:param" or "type:param1:param2" */ |
|
279
|
235
|
|
|
|
|
|
static tpl_xform parse_xform(const char *s, int len) { |
|
280
|
235
|
|
|
|
|
|
tpl_xform x = {XF_RAW, 0, NULL, 0, NULL, 0}; |
|
281
|
235
|
|
|
|
|
|
const char *colon = memchr(s, ':', len); |
|
282
|
235
|
100
|
|
|
|
|
int tlen = colon ? (int)(colon - s) : len; |
|
283
|
235
|
100
|
|
|
|
|
const char *param = colon ? colon + 1 : NULL; |
|
284
|
235
|
100
|
|
|
|
|
int plen = colon ? len - tlen - 1 : 0; |
|
285
|
|
|
|
|
|
|
|
|
286
|
235
|
100
|
|
|
|
|
if (tlen == 3 && memcmp(s, "int", 3) == 0) x.type = XF_INT; |
|
|
|
100
|
|
|
|
|
|
|
287
|
206
|
100
|
|
|
|
|
else if (tlen == 9 && memcmp(s, "int_comma", 9) == 0) x.type = XF_INT_COMMA; |
|
|
|
100
|
|
|
|
|
|
|
288
|
199
|
100
|
|
|
|
|
else if (tlen == 5 && memcmp(s, "float", 5) == 0) { x.type = XF_FLOAT; x.param_int = 2; } |
|
|
|
100
|
|
|
|
|
|
|
289
|
195
|
100
|
|
|
|
|
else if (tlen == 4 && memcmp(s, "html", 4) == 0) x.type = XF_HTML; |
|
|
|
100
|
|
|
|
|
|
|
290
|
175
|
100
|
|
|
|
|
else if (tlen == 7 && memcmp(s, "html_br", 7) == 0) x.type = XF_HTML_BR; |
|
|
|
100
|
|
|
|
|
|
|
291
|
173
|
100
|
|
|
|
|
else if (tlen == 3 && memcmp(s, "raw", 3) == 0) x.type = XF_RAW; |
|
|
|
100
|
|
|
|
|
|
|
292
|
141
|
100
|
|
|
|
|
else if (tlen == 3 && memcmp(s, "url", 3) == 0) x.type = XF_URL; |
|
|
|
100
|
|
|
|
|
|
|
293
|
139
|
100
|
|
|
|
|
else if (tlen == 4 && memcmp(s, "json", 4) == 0) x.type = XF_JSON; |
|
|
|
100
|
|
|
|
|
|
|
294
|
134
|
100
|
|
|
|
|
else if (tlen == 4 && memcmp(s, "trim", 4) == 0) x.type = XF_TRIM; |
|
|
|
100
|
|
|
|
|
|
|
295
|
123
|
100
|
|
|
|
|
else if (tlen == 2 && memcmp(s, "uc", 2) == 0) x.type = XF_UC; |
|
|
|
100
|
|
|
|
|
|
|
296
|
115
|
100
|
|
|
|
|
else if (tlen == 2 && memcmp(s, "lc", 2) == 0) x.type = XF_LC; |
|
|
|
100
|
|
|
|
|
|
|
297
|
109
|
100
|
|
|
|
|
else if (tlen == 3 && memcmp(s, "pad", 3) == 0) x.type = XF_PAD; |
|
|
|
100
|
|
|
|
|
|
|
298
|
105
|
100
|
|
|
|
|
else if (tlen == 4 && memcmp(s, "rpad", 4) == 0) x.type = XF_RPAD; |
|
|
|
100
|
|
|
|
|
|
|
299
|
104
|
100
|
|
|
|
|
else if (tlen == 5 && memcmp(s, "trunc", 5) == 0) x.type = XF_TRUNC; |
|
|
|
100
|
|
|
|
|
|
|
300
|
101
|
100
|
|
|
|
|
else if (tlen == 7 && memcmp(s, "default", 7) == 0) x.type = XF_DEFAULT; |
|
|
|
100
|
|
|
|
|
|
|
301
|
96
|
100
|
|
|
|
|
else if (tlen == 3 && memcmp(s, "hex", 3) == 0) x.type = XF_HEX; |
|
|
|
100
|
|
|
|
|
|
|
302
|
94
|
100
|
|
|
|
|
else if (tlen == 9 && memcmp(s, "base64url", 9) == 0) x.type = XF_BASE64URL; |
|
|
|
100
|
|
|
|
|
|
|
303
|
90
|
100
|
|
|
|
|
else if (tlen == 6 && memcmp(s, "base64", 6) == 0) x.type = XF_BASE64; |
|
|
|
100
|
|
|
|
|
|
|
304
|
86
|
100
|
|
|
|
|
else if (tlen == 5 && memcmp(s, "count", 5) == 0) x.type = XF_COUNT; |
|
|
|
100
|
|
|
|
|
|
|
305
|
80
|
100
|
|
|
|
|
else if (tlen == 4 && memcmp(s, "bool", 4) == 0) x.type = XF_BOOL; |
|
|
|
100
|
|
|
|
|
|
|
306
|
73
|
100
|
|
|
|
|
else if (tlen == 4 && memcmp(s, "date", 4) == 0) x.type = XF_DATE; |
|
|
|
100
|
|
|
|
|
|
|
307
|
71
|
100
|
|
|
|
|
else if (tlen == 7 && memcmp(s, "sprintf", 7) == 0) x.type = XF_SPRINTF; |
|
|
|
100
|
|
|
|
|
|
|
308
|
65
|
100
|
|
|
|
|
else if (tlen == 7 && memcmp(s, "replace", 7) == 0) x.type = XF_REPLACE; |
|
|
|
100
|
|
|
|
|
|
|
309
|
60
|
100
|
|
|
|
|
else if (tlen == 6 && memcmp(s, "substr", 6) == 0) x.type = XF_SUBSTR; |
|
|
|
100
|
|
|
|
|
|
|
310
|
56
|
100
|
|
|
|
|
else if (tlen == 6 && memcmp(s, "plural", 6) == 0) x.type = XF_PLURAL; |
|
|
|
100
|
|
|
|
|
|
|
311
|
51
|
100
|
|
|
|
|
else if (tlen == 2 && memcmp(s, "if", 2) == 0) x.type = XF_IF; |
|
|
|
50
|
|
|
|
|
|
|
312
|
47
|
100
|
|
|
|
|
else if (tlen == 6 && memcmp(s, "unless", 6) == 0) x.type = XF_UNLESS; |
|
|
|
100
|
|
|
|
|
|
|
313
|
45
|
100
|
|
|
|
|
else if (tlen == 3 && memcmp(s, "map", 3) == 0) x.type = XF_MAP; |
|
|
|
100
|
|
|
|
|
|
|
314
|
40
|
100
|
|
|
|
|
else if (tlen == 4 && memcmp(s, "wrap", 4) == 0) x.type = XF_WRAP; |
|
|
|
100
|
|
|
|
|
|
|
315
|
36
|
100
|
|
|
|
|
else if (tlen == 9 && memcmp(s, "number_si", 9) == 0) x.type = XF_NUMBER_SI; |
|
|
|
50
|
|
|
|
|
|
|
316
|
31
|
100
|
|
|
|
|
else if (tlen == 8 && memcmp(s, "bytes_si", 8) == 0) x.type = XF_BYTES_SI; |
|
|
|
100
|
|
|
|
|
|
|
317
|
26
|
100
|
|
|
|
|
else if (tlen == 7 && memcmp(s, "elapsed", 7) == 0) x.type = XF_ELAPSED; |
|
|
|
50
|
|
|
|
|
|
|
318
|
20
|
100
|
|
|
|
|
else if (tlen == 3 && memcmp(s, "ago", 3) == 0) x.type = XF_AGO; |
|
|
|
50
|
|
|
|
|
|
|
319
|
16
|
100
|
|
|
|
|
else if (tlen == 4 && memcmp(s, "mask", 4) == 0) { x.type = XF_MASK; x.param_int = 4; } |
|
|
|
50
|
|
|
|
|
|
|
320
|
9
|
100
|
|
|
|
|
else if (tlen == 6 && memcmp(s, "length", 6) == 0) x.type = XF_LENGTH; |
|
|
|
50
|
|
|
|
|
|
|
321
|
6
|
100
|
|
|
|
|
else if (tlen == 8 && memcmp(s, "coalesce", 8) == 0) x.type = XF_COALESCE; |
|
|
|
50
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
|
|
323
|
235
|
100
|
|
|
|
|
if (param && plen == 0 && x.type == XF_DEFAULT) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
324
|
1
|
|
|
|
|
|
x.param_str = (char *)malloc(1); |
|
325
|
1
|
|
|
|
|
|
x.param_str[0] = '\0'; |
|
326
|
1
|
|
|
|
|
|
x.param_str_len = 0; |
|
327
|
234
|
100
|
|
|
|
|
} else if (param && plen > 0) { |
|
|
|
50
|
|
|
|
|
|
|
328
|
65
|
100
|
|
|
|
|
if (x.type == XF_FLOAT || x.type == XF_PAD || x.type == XF_RPAD || x.type == XF_TRUNC || x.type == XF_MASK) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
329
|
15
|
|
|
|
|
|
x.param_int = 0; |
|
330
|
33
|
100
|
|
|
|
|
for (int i = 0; i < plen; i++) x.param_int = x.param_int * 10 + (param[i] - '0'); |
|
331
|
50
|
100
|
|
|
|
|
} else if (x.type == XF_SPRINTF) { |
|
332
|
6
|
|
|
|
|
|
x.param_str = (char *)malloc(plen + 1); |
|
333
|
6
|
|
|
|
|
|
memcpy(x.param_str, param, plen); |
|
334
|
6
|
|
|
|
|
|
x.param_str[plen] = '\0'; |
|
335
|
6
|
|
|
|
|
|
x.param_str_len = plen; |
|
336
|
44
|
100
|
|
|
|
|
} else if (x.type == XF_REPLACE) { |
|
337
|
5
|
|
|
|
|
|
const char *c2 = memchr(param, ':', plen); |
|
338
|
5
|
50
|
|
|
|
|
if (c2) { |
|
339
|
5
|
|
|
|
|
|
int p1len = (int)(c2 - param); |
|
340
|
5
|
|
|
|
|
|
int p2len = plen - p1len - 1; |
|
341
|
5
|
100
|
|
|
|
|
if (p1len > 0) { |
|
342
|
4
|
|
|
|
|
|
x.param_str = (char *)malloc(p1len + 1); |
|
343
|
4
|
|
|
|
|
|
memcpy(x.param_str, param, p1len); |
|
344
|
4
|
|
|
|
|
|
x.param_str_len = p1len; |
|
345
|
4
|
|
|
|
|
|
x.param_str2 = (char *)malloc(p2len + 1); |
|
346
|
4
|
|
|
|
|
|
memcpy(x.param_str2, c2 + 1, p2len); |
|
347
|
4
|
|
|
|
|
|
x.param_str2_len = p2len; |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
} |
|
350
|
39
|
100
|
|
|
|
|
} else if (x.type == XF_SUBSTR) { |
|
351
|
4
|
|
|
|
|
|
const char *c2 = memchr(param, ':', plen); |
|
352
|
4
|
|
|
|
|
|
x.param_int = 0; |
|
353
|
4
|
100
|
|
|
|
|
int p1len = c2 ? (int)(c2 - param) : plen; |
|
354
|
10
|
100
|
|
|
|
|
for (int i = 0; i < p1len; i++) x.param_int = x.param_int * 10 + (param[i] - '0'); |
|
355
|
4
|
|
|
|
|
|
x.param_int2 = -1; |
|
356
|
4
|
100
|
|
|
|
|
if (c2) { |
|
357
|
1
|
|
|
|
|
|
x.param_int2 = 0; |
|
358
|
1
|
|
|
|
|
|
int p2len = plen - p1len - 1; |
|
359
|
2
|
100
|
|
|
|
|
for (int i = 0; i < p2len; i++) x.param_int2 = x.param_int2 * 10 + (c2[1+i] - '0'); |
|
360
|
|
|
|
|
|
|
} |
|
361
|
35
|
100
|
|
|
|
|
} else if (x.type == XF_PLURAL) { |
|
362
|
5
|
|
|
|
|
|
const char *c2 = memchr(param, ':', plen); |
|
363
|
5
|
50
|
|
|
|
|
if (c2) { |
|
364
|
5
|
|
|
|
|
|
int p1len = (int)(c2 - param); |
|
365
|
5
|
|
|
|
|
|
int p2len = plen - p1len - 1; |
|
366
|
5
|
|
|
|
|
|
x.param_str = (char *)malloc(p1len + 1); |
|
367
|
5
|
|
|
|
|
|
memcpy(x.param_str, param, p1len); |
|
368
|
5
|
|
|
|
|
|
x.param_str_len = p1len; |
|
369
|
5
|
|
|
|
|
|
x.param_str2 = (char *)malloc(p2len + 1); |
|
370
|
5
|
|
|
|
|
|
memcpy(x.param_str2, c2 + 1, p2len); |
|
371
|
5
|
|
|
|
|
|
x.param_str2_len = p2len; |
|
372
|
|
|
|
|
|
|
} |
|
373
|
30
|
100
|
|
|
|
|
} else if (x.type == XF_IF || x.type == XF_UNLESS) { |
|
|
|
100
|
|
|
|
|
|
|
374
|
6
|
|
|
|
|
|
x.param_str = (char *)malloc(plen + 1); |
|
375
|
6
|
|
|
|
|
|
memcpy(x.param_str, param, plen); |
|
376
|
6
|
|
|
|
|
|
x.param_str_len = plen; |
|
377
|
24
|
100
|
|
|
|
|
} else if (x.type == XF_WRAP) { |
|
378
|
4
|
|
|
|
|
|
const char *c2 = memchr(param, ':', plen); |
|
379
|
4
|
100
|
|
|
|
|
if (c2) { |
|
380
|
3
|
|
|
|
|
|
int p1len = (int)(c2 - param); |
|
381
|
3
|
|
|
|
|
|
int p2len = plen - p1len - 1; |
|
382
|
3
|
|
|
|
|
|
x.param_str = (char *)malloc(p1len + 1); |
|
383
|
3
|
|
|
|
|
|
memcpy(x.param_str, param, p1len); |
|
384
|
3
|
|
|
|
|
|
x.param_str_len = p1len; |
|
385
|
3
|
|
|
|
|
|
x.param_str2 = (char *)malloc(p2len + 1); |
|
386
|
3
|
|
|
|
|
|
memcpy(x.param_str2, c2 + 1, p2len); |
|
387
|
3
|
|
|
|
|
|
x.param_str2_len = p2len; |
|
388
|
|
|
|
|
|
|
} else { |
|
389
|
1
|
|
|
|
|
|
x.param_str = (char *)malloc(plen + 1); |
|
390
|
1
|
|
|
|
|
|
memcpy(x.param_str, param, plen); |
|
391
|
1
|
|
|
|
|
|
x.param_str_len = plen; |
|
392
|
|
|
|
|
|
|
} |
|
393
|
20
|
100
|
|
|
|
|
} else if (x.type == XF_MAP) { |
|
394
|
5
|
|
|
|
|
|
int cnt = 1; |
|
395
|
96
|
100
|
|
|
|
|
for (int i = 0; i < plen; i++) if (param[i] == ':') cnt++; |
|
|
|
100
|
|
|
|
|
|
|
396
|
5
|
|
|
|
|
|
x.map_keys = (char **)calloc(cnt, sizeof(char *)); |
|
397
|
5
|
|
|
|
|
|
x.map_vals = (char **)calloc(cnt, sizeof(char *)); |
|
398
|
5
|
|
|
|
|
|
x.map_key_lens = (STRLEN *)calloc(cnt, sizeof(STRLEN)); |
|
399
|
5
|
|
|
|
|
|
x.map_val_lens = (STRLEN *)calloc(cnt, sizeof(STRLEN)); |
|
400
|
5
|
|
|
|
|
|
x.map_count = 0; |
|
401
|
5
|
|
|
|
|
|
const char *p2 = param, *pe = param + plen; |
|
402
|
18
|
100
|
|
|
|
|
while (p2 < pe) { |
|
403
|
13
|
|
|
|
|
|
const char *next = memchr(p2, ':', pe - p2); |
|
404
|
13
|
100
|
|
|
|
|
if (!next) next = pe; |
|
405
|
13
|
|
|
|
|
|
const char *eq = memchr(p2, '=', next - p2); |
|
406
|
13
|
50
|
|
|
|
|
if (eq) { |
|
407
|
13
|
|
|
|
|
|
int kl = (int)(eq - p2), vl = (int)(next - eq - 1); |
|
408
|
13
|
|
|
|
|
|
int idx = x.map_count++; |
|
409
|
13
|
|
|
|
|
|
x.map_keys[idx] = (char *)malloc(kl + 1); memcpy(x.map_keys[idx], p2, kl); x.map_key_lens[idx] = kl; |
|
410
|
13
|
|
|
|
|
|
x.map_vals[idx] = (char *)malloc(vl + 1); memcpy(x.map_vals[idx], eq + 1, vl); x.map_val_lens[idx] = vl; |
|
411
|
|
|
|
|
|
|
} |
|
412
|
13
|
|
|
|
|
|
p2 = next + 1; |
|
413
|
|
|
|
|
|
|
} |
|
414
|
15
|
100
|
|
|
|
|
} else if (x.type == XF_COALESCE) { |
|
415
|
5
|
|
|
|
|
|
x.param_str = (char *)malloc(plen + 1); |
|
416
|
5
|
|
|
|
|
|
memcpy(x.param_str, param, plen); |
|
417
|
5
|
|
|
|
|
|
x.param_str[plen] = '\0'; |
|
418
|
5
|
|
|
|
|
|
x.param_str_len = plen; |
|
419
|
10
|
100
|
|
|
|
|
} else if (x.type == XF_DEFAULT || x.type == XF_DATE) { |
|
|
|
100
|
|
|
|
|
|
|
420
|
5
|
|
|
|
|
|
x.param_str = (char *)malloc(plen + 1); |
|
421
|
5
|
|
|
|
|
|
memcpy(x.param_str, param, plen); |
|
422
|
5
|
|
|
|
|
|
x.param_str[plen] = '\0'; |
|
423
|
5
|
|
|
|
|
|
x.param_str_len = plen; |
|
424
|
5
|
50
|
|
|
|
|
} else if (x.type == XF_BOOL) { |
|
425
|
5
|
|
|
|
|
|
const char *c2 = memchr(param, ':', plen); |
|
426
|
5
|
50
|
|
|
|
|
if (c2) { |
|
427
|
5
|
|
|
|
|
|
int p1len = (int)(c2 - param); |
|
428
|
5
|
|
|
|
|
|
int p2len = plen - p1len - 1; |
|
429
|
5
|
|
|
|
|
|
x.param_str = (char *)malloc(p1len + 1); |
|
430
|
5
|
|
|
|
|
|
memcpy(x.param_str, param, p1len); |
|
431
|
5
|
|
|
|
|
|
x.param_str_len = p1len; |
|
432
|
5
|
|
|
|
|
|
x.param_str2 = (char *)malloc(p2len + 1); |
|
433
|
5
|
|
|
|
|
|
memcpy(x.param_str2, c2 + 1, p2len); |
|
434
|
5
|
|
|
|
|
|
x.param_str2_len = p2len; |
|
435
|
|
|
|
|
|
|
} else { |
|
436
|
0
|
|
|
|
|
|
x.param_str = (char *)malloc(plen + 1); |
|
437
|
0
|
|
|
|
|
|
memcpy(x.param_str, param, plen); |
|
438
|
0
|
|
|
|
|
|
x.param_str_len = plen; |
|
439
|
0
|
|
|
|
|
|
x.param_str2 = NULL; x.param_str2_len = 0; |
|
440
|
|
|
|
|
|
|
} |
|
441
|
|
|
|
|
|
|
} |
|
442
|
|
|
|
|
|
|
} |
|
443
|
235
|
|
|
|
|
|
return x; |
|
444
|
|
|
|
|
|
|
} |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
/* Parse "{field:type1:p1|type2:p2}" into chain */ |
|
447
|
222
|
|
|
|
|
|
static void parse_field_spec(const char *spec, int spec_len, |
|
448
|
|
|
|
|
|
|
tpl_op *op, enum row_mode *mode) { |
|
449
|
|
|
|
|
|
|
/* split field name from transform chain at first : or | */ |
|
450
|
222
|
|
|
|
|
|
const char *sep = NULL; |
|
451
|
493
|
100
|
|
|
|
|
for (int i = 0; i < spec_len; i++) { |
|
452
|
487
|
100
|
|
|
|
|
if (spec[i] == ':' || spec[i] == '|') { sep = spec + i; break; } |
|
|
|
50
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
} |
|
454
|
|
|
|
|
|
|
|
|
455
|
222
|
|
|
|
|
|
const char *field = spec; |
|
456
|
222
|
100
|
|
|
|
|
int field_len = sep ? (int)(sep - spec) : spec_len; |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
/* check for row number placeholder {#} */ |
|
459
|
222
|
100
|
|
|
|
|
if (field_len == 1 && field[0] == '#') { |
|
|
|
100
|
|
|
|
|
|
|
460
|
5
|
|
|
|
|
|
op->is_rownum = 1; |
|
461
|
|
|
|
|
|
|
/* parse transform chain if any */ |
|
462
|
5
|
100
|
|
|
|
|
if (!sep) { |
|
463
|
3
|
|
|
|
|
|
op->chain = (tpl_xform *)malloc(sizeof(tpl_xform)); |
|
464
|
3
|
|
|
|
|
|
op->chain[0] = (tpl_xform){XF_RAW, 0, NULL, 0}; |
|
465
|
3
|
|
|
|
|
|
op->chain_len = 1; |
|
466
|
3
|
|
|
|
|
|
return; |
|
467
|
|
|
|
|
|
|
} |
|
468
|
2
|
|
|
|
|
|
goto parse_chain; |
|
469
|
|
|
|
|
|
|
} |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
/* detect array vs hash mode */ |
|
472
|
217
|
|
|
|
|
|
int is_num = 1, is_neg = 0, start_idx = 0; |
|
473
|
217
|
50
|
|
|
|
|
if (field_len > 0 && field[0] == '-') { is_neg = 1; start_idx = 1; } |
|
|
|
100
|
|
|
|
|
|
|
474
|
415
|
100
|
|
|
|
|
for (int i = start_idx; i < field_len; i++) |
|
475
|
217
|
50
|
|
|
|
|
if (field[i] < '0' || field[i] > '9') { is_num = 0; break; } |
|
|
|
100
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
|
|
477
|
217
|
100
|
|
|
|
|
if (is_num && field_len > start_idx) { |
|
|
|
50
|
|
|
|
|
|
|
478
|
198
|
|
|
|
|
|
op->col = 0; |
|
479
|
396
|
100
|
|
|
|
|
for (int i = start_idx; i < field_len; i++) op->col = op->col * 10 + (field[i] - '0'); |
|
480
|
198
|
100
|
|
|
|
|
if (is_neg) op->col = -op->col; |
|
481
|
|
|
|
|
|
|
} else { |
|
482
|
19
|
|
|
|
|
|
*mode = ROW_HASH; |
|
483
|
19
|
|
|
|
|
|
op->key = (char *)malloc(field_len + 1); |
|
484
|
19
|
|
|
|
|
|
memcpy(op->key, field, field_len); |
|
485
|
19
|
|
|
|
|
|
op->key[field_len] = '\0'; |
|
486
|
19
|
|
|
|
|
|
op->key_len = field_len; |
|
487
|
|
|
|
|
|
|
} |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
/* parse transform chain */ |
|
490
|
217
|
100
|
|
|
|
|
if (!sep) { |
|
491
|
3
|
|
|
|
|
|
op->chain = (tpl_xform *)malloc(sizeof(tpl_xform)); |
|
492
|
3
|
|
|
|
|
|
op->chain[0] = (tpl_xform){XF_RAW, 0, NULL, 0}; |
|
493
|
3
|
|
|
|
|
|
op->chain_len = 1; |
|
494
|
3
|
|
|
|
|
|
return; |
|
495
|
|
|
|
|
|
|
} |
|
496
|
|
|
|
|
|
|
|
|
497
|
214
|
|
|
|
|
|
parse_chain:; |
|
498
|
216
|
|
|
|
|
|
const char *xforms_start = spec + field_len; |
|
499
|
216
|
50
|
|
|
|
|
if (*xforms_start == ':' || *xforms_start == '|') xforms_start++; |
|
|
|
0
|
|
|
|
|
|
|
500
|
216
|
|
|
|
|
|
int xforms_len = spec_len - (int)(xforms_start - spec); |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
/* count pipes to size the chain */ |
|
503
|
216
|
|
|
|
|
|
int nxforms = 1; |
|
504
|
1724
|
100
|
|
|
|
|
for (int i = 0; i < xforms_len; i++) if (xforms_start[i] == '|') nxforms++; |
|
|
|
100
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
|
|
506
|
216
|
|
|
|
|
|
op->chain = (tpl_xform *)malloc(nxforms * sizeof(tpl_xform)); |
|
507
|
216
|
|
|
|
|
|
op->chain_len = 0; |
|
508
|
|
|
|
|
|
|
|
|
509
|
216
|
|
|
|
|
|
const char *p = xforms_start; |
|
510
|
216
|
|
|
|
|
|
const char *xend = xforms_start + xforms_len; |
|
511
|
451
|
100
|
|
|
|
|
while (p < xend) { |
|
512
|
235
|
|
|
|
|
|
const char *pipe = memchr(p, '|', xend - p); |
|
513
|
235
|
100
|
|
|
|
|
if (!pipe) pipe = xend; |
|
514
|
235
|
|
|
|
|
|
op->chain[op->chain_len++] = parse_xform(p, (int)(pipe - p)); |
|
515
|
235
|
|
|
|
|
|
p = pipe + 1; |
|
516
|
|
|
|
|
|
|
} |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
/* count/coalesce operate on the raw field value (array/hash size, or the |
|
519
|
|
|
|
|
|
|
first truthy field), so they are only meaningful as the first transform. |
|
520
|
|
|
|
|
|
|
Mid-chain they would silently emit "0"/nothing; reject at compile time. */ |
|
521
|
233
|
100
|
|
|
|
|
for (int i = 1; i < op->chain_len; i++) { |
|
522
|
19
|
100
|
|
|
|
|
if (op->chain[i].type == XF_COUNT) |
|
523
|
1
|
|
|
|
|
|
croak("Text::Stencil: 'count' must be the first transform in a chain"); |
|
524
|
18
|
100
|
|
|
|
|
if (op->chain[i].type == XF_COALESCE) |
|
525
|
1
|
|
|
|
|
|
croak("Text::Stencil: 'coalesce' must be the first transform in a chain"); |
|
526
|
|
|
|
|
|
|
} |
|
527
|
|
|
|
|
|
|
} |
|
528
|
|
|
|
|
|
|
|
|
529
|
208
|
|
|
|
|
|
static tpl_compiled *tpl_compile(pTHX_ const char *header, STRLEN hlen, |
|
530
|
|
|
|
|
|
|
const char *row, STRLEN rlen, |
|
531
|
|
|
|
|
|
|
const char *footer, STRLEN flen, |
|
532
|
|
|
|
|
|
|
const char *sep, STRLEN slen, |
|
533
|
|
|
|
|
|
|
char esc_char) { |
|
534
|
208
|
|
|
|
|
|
tpl_compiled *t = (tpl_compiled *)calloc(1, sizeof(tpl_compiled)); |
|
535
|
208
|
100
|
|
|
|
|
if (hlen) { t->header = (char *)malloc(hlen); memcpy(t->header, header, hlen); } t->header_len = hlen; |
|
536
|
208
|
100
|
|
|
|
|
if (flen) { t->footer = (char *)malloc(flen); memcpy(t->footer, footer, flen); } t->footer_len = flen; |
|
537
|
208
|
100
|
|
|
|
|
if (slen) { t->sep = (char *)malloc(slen); memcpy(t->sep, sep, slen); t->sep_len = slen; } |
|
538
|
208
|
|
|
|
|
|
t->mode = ROW_ARRAY; |
|
539
|
208
|
100
|
|
|
|
|
t->escape_char = esc_char ? esc_char : '{'; |
|
540
|
|
|
|
|
|
|
|
|
541
|
208
|
|
|
|
|
|
int cap = 16; |
|
542
|
208
|
|
|
|
|
|
t->ops = (tpl_op *)calloc(cap, sizeof(tpl_op)); |
|
543
|
208
|
|
|
|
|
|
t->nops = 0; |
|
544
|
|
|
|
|
|
|
|
|
545
|
208
|
100
|
|
|
|
|
char close_char = (t->escape_char == '{') ? '}' : t->escape_char; |
|
546
|
208
|
100
|
|
|
|
|
if (t->escape_char == '[') close_char = ']'; |
|
547
|
208
|
50
|
|
|
|
|
if (t->escape_char == '(') close_char = ')'; |
|
548
|
208
|
100
|
|
|
|
|
if (t->escape_char == '<') close_char = '>'; |
|
549
|
|
|
|
|
|
|
|
|
550
|
208
|
|
|
|
|
|
const char *p = row, *end = row + rlen; |
|
551
|
431
|
100
|
|
|
|
|
while (p < end) { |
|
552
|
242
|
|
|
|
|
|
const char *brace = memchr(p, t->escape_char, end - p); |
|
553
|
242
|
100
|
|
|
|
|
if (!brace) brace = end; |
|
554
|
242
|
100
|
|
|
|
|
if (brace > p) { |
|
555
|
45
|
50
|
|
|
|
|
if (t->nops >= cap) { cap *= 2; t->ops = (tpl_op *)realloc(t->ops, cap * sizeof(tpl_op)); if (!t->ops) croak("realloc"); memset(&t->ops[cap/2], 0, (cap/2)*sizeof(tpl_op)); } |
|
|
|
0
|
|
|
|
|
|
|
556
|
45
|
|
|
|
|
|
tpl_op *op = &t->ops[t->nops++]; |
|
557
|
45
|
|
|
|
|
|
memset(op, 0, sizeof(*op)); |
|
558
|
45
|
|
|
|
|
|
op->static_data = (char *)malloc(brace - p); |
|
559
|
45
|
|
|
|
|
|
memcpy(op->static_data, p, brace - p); |
|
560
|
45
|
|
|
|
|
|
op->static_len = brace - p; |
|
561
|
|
|
|
|
|
|
} |
|
562
|
242
|
100
|
|
|
|
|
if (brace >= end) break; |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
/* doubled escape char (e.g. {{ ) = literal */ |
|
565
|
225
|
50
|
|
|
|
|
if (brace + 1 < end && brace[1] == t->escape_char) { |
|
|
|
100
|
|
|
|
|
|
|
566
|
3
|
50
|
|
|
|
|
if (t->nops >= cap) { cap *= 2; t->ops = (tpl_op *)realloc(t->ops, cap * sizeof(tpl_op)); if (!t->ops) croak("realloc"); memset(&t->ops[cap/2], 0, (cap/2)*sizeof(tpl_op)); } |
|
|
|
0
|
|
|
|
|
|
|
567
|
3
|
|
|
|
|
|
tpl_op *op = &t->ops[t->nops++]; |
|
568
|
3
|
|
|
|
|
|
memset(op, 0, sizeof(*op)); |
|
569
|
3
|
|
|
|
|
|
op->static_data = (char *)malloc(1); |
|
570
|
3
|
|
|
|
|
|
op->static_data[0] = t->escape_char; |
|
571
|
3
|
|
|
|
|
|
op->static_len = 1; |
|
572
|
3
|
|
|
|
|
|
p = brace + 2; |
|
573
|
3
|
|
|
|
|
|
continue; |
|
574
|
|
|
|
|
|
|
} |
|
575
|
|
|
|
|
|
|
|
|
576
|
222
|
|
|
|
|
|
const char *close = memchr(brace + 1, close_char, end - brace - 1); |
|
577
|
222
|
50
|
|
|
|
|
if (!close) break; |
|
578
|
|
|
|
|
|
|
|
|
579
|
222
|
50
|
|
|
|
|
if (t->nops >= cap) { cap *= 2; t->ops = (tpl_op *)realloc(t->ops, cap * sizeof(tpl_op)); if (!t->ops) croak("realloc"); memset(&t->ops[cap/2], 0, (cap/2)*sizeof(tpl_op)); } |
|
|
|
0
|
|
|
|
|
|
|
580
|
222
|
|
|
|
|
|
tpl_op *op = &t->ops[t->nops++]; |
|
581
|
222
|
|
|
|
|
|
memset(op, 0, sizeof(*op)); |
|
582
|
222
|
|
|
|
|
|
parse_field_spec(brace + 1, (int)(close - brace - 1), op, &t->mode); |
|
583
|
220
|
|
|
|
|
|
p = close + 1; |
|
584
|
|
|
|
|
|
|
} |
|
585
|
206
|
|
|
|
|
|
return t; |
|
586
|
|
|
|
|
|
|
} |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
/* ---- render ---- */ |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
#define BUF_ENSURE(need) do { if (pos + (need) > alloc) { alloc = (pos + (need)) * 2; buf = (char *)realloc(buf, alloc); if (!buf) croak("realloc"); } } while(0) |
|
591
|
|
|
|
|
|
|
#define BUF_WRITE(s, l) do { BUF_ENSURE(l); memcpy(buf + pos, s, l); pos += l; } while(0) |
|
592
|
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
/* reusable buffer macros for render_buf */ |
|
594
|
|
|
|
|
|
|
#define RBUF_INIT(t, est) do { \ |
|
595
|
|
|
|
|
|
|
if (t->render_buf && t->render_buf_alloc >= (est)) { \ |
|
596
|
|
|
|
|
|
|
buf = t->render_buf; alloc = t->render_buf_alloc; \ |
|
597
|
|
|
|
|
|
|
} else { \ |
|
598
|
|
|
|
|
|
|
alloc = (est); buf = (char *)realloc(t->render_buf, alloc); \ |
|
599
|
|
|
|
|
|
|
if (!buf) croak("realloc"); \ |
|
600
|
|
|
|
|
|
|
t->render_buf = buf; t->render_buf_alloc = alloc; \ |
|
601
|
|
|
|
|
|
|
} \ |
|
602
|
|
|
|
|
|
|
/* Detach the reusable buffer for the render's duration. The live buffer \ |
|
603
|
|
|
|
|
|
|
lives only in the local `buf`; a BUF_ENSURE/render_field realloc can \ |
|
604
|
|
|
|
|
|
|
move it, and a render path can croak (e.g. a row callback dies). \ |
|
605
|
|
|
|
|
|
|
Leaving t->render_buf NULL until RBUF_FINISH guarantees DESTROY and the \ |
|
606
|
|
|
|
|
|
|
next render never free/realloc a stale pointer. A croak mid-render \ |
|
607
|
|
|
|
|
|
|
loses the in-flight buffer -- a bounded, error-path-only leak traded \ |
|
608
|
|
|
|
|
|
|
for memory safety. */ \ |
|
609
|
|
|
|
|
|
|
t->render_buf = NULL; t->render_buf_alloc = 0; \ |
|
610
|
|
|
|
|
|
|
pos = 0; \ |
|
611
|
|
|
|
|
|
|
} while(0) |
|
612
|
|
|
|
|
|
|
#define RBUF_FINISH(t) do { t->render_buf = buf; t->render_buf_alloc = alloc; } while(0) |
|
613
|
|
|
|
|
|
|
|
|
614
|
10566
|
|
|
|
|
|
static inline SV *fetch_field(pTHX_ SV *row_sv, tpl_op *op, enum row_mode mode) { |
|
615
|
10566
|
100
|
|
|
|
|
if (mode == ROW_HASH) { |
|
616
|
110
|
50
|
|
|
|
|
if (!SvROK(row_sv) || SvTYPE(SvRV(row_sv)) != SVt_PVHV) return NULL; |
|
|
|
50
|
|
|
|
|
|
|
617
|
110
|
|
|
|
|
|
SV **sv = hv_fetch((HV *)SvRV(row_sv), op->key, op->key_len, 0); |
|
618
|
110
|
50
|
|
|
|
|
return sv ? *sv : NULL; |
|
619
|
|
|
|
|
|
|
} else { |
|
620
|
10456
|
50
|
|
|
|
|
if (!SvROK(row_sv) || SvTYPE(SvRV(row_sv)) != SVt_PVAV) return NULL; |
|
|
|
50
|
|
|
|
|
|
|
621
|
10456
|
|
|
|
|
|
AV *av = (AV *)SvRV(row_sv); |
|
622
|
10456
|
|
|
|
|
|
SV **ary = AvARRAY(av); |
|
623
|
10456
|
50
|
|
|
|
|
SSize_t top = av_top_index(av); |
|
624
|
10456
|
|
|
|
|
|
int col = op->col; |
|
625
|
10456
|
100
|
|
|
|
|
if (col < 0) col = (int)(top + 1) + col; |
|
626
|
10456
|
50
|
|
|
|
|
if (col >= 0 && col <= (int)top) return ary[col]; |
|
|
|
50
|
|
|
|
|
|
|
627
|
0
|
|
|
|
|
|
return NULL; |
|
628
|
|
|
|
|
|
|
} |
|
629
|
|
|
|
|
|
|
} |
|
630
|
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
/* Apply a single transform, writing result to buf+pos or tmp */ |
|
632
|
1495
|
|
|
|
|
|
static void apply_xform(tpl_xform *xf, const char *src, STRLEN slen, |
|
633
|
|
|
|
|
|
|
char **bufp, STRLEN *posp, STRLEN *allocp, |
|
634
|
|
|
|
|
|
|
char **tmpp, STRLEN *tmp_lenp, STRLEN *tmp_allocp, |
|
635
|
|
|
|
|
|
|
int to_output) { |
|
636
|
1495
|
|
|
|
|
|
char *buf = *bufp; STRLEN pos = *posp; STRLEN alloc = *allocp; |
|
637
|
1495
|
|
|
|
|
|
char *tmp = *tmpp; STRLEN tmp_len = *tmp_lenp; STRLEN tmp_alloc = *tmp_allocp; |
|
638
|
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
/* macro to write to either output or temp */ |
|
640
|
|
|
|
|
|
|
#define OUT_ENSURE(n) do { \ |
|
641
|
|
|
|
|
|
|
if (to_output) { BUF_ENSURE(n); } \ |
|
642
|
|
|
|
|
|
|
else { if ((n) > tmp_alloc || !tmp) { tmp_alloc = (n) < 1 ? 1 : (STRLEN)(n) * 2; tmp = (char *)realloc(tmp, tmp_alloc); if (!tmp) croak("realloc"); } } \ |
|
643
|
|
|
|
|
|
|
} while(0) |
|
644
|
|
|
|
|
|
|
#define OUT_PTR (to_output ? buf + pos : tmp) |
|
645
|
|
|
|
|
|
|
|
|
646
|
1495
|
|
|
|
|
|
switch (xf->type) { |
|
647
|
0
|
|
|
|
|
|
case XF_INT: { |
|
648
|
0
|
0
|
|
|
|
|
OUT_ENSURE(20); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
649
|
0
|
|
|
|
|
|
long v = 0; int neg = 0; |
|
650
|
0
|
0
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) { |
|
651
|
0
|
0
|
|
|
|
|
if (src[i] == '-') neg = 1; |
|
652
|
0
|
0
|
|
|
|
|
else if (src[i] >= '0' && src[i] <= '9') v = v * 10 + (src[i] - '0'); |
|
|
|
0
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
} |
|
654
|
0
|
0
|
|
|
|
|
if (neg) v = -v; |
|
655
|
0
|
0
|
|
|
|
|
int w = itoa_fast(OUT_PTR, v); |
|
656
|
0
|
0
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
657
|
0
|
|
|
|
|
|
break; |
|
658
|
|
|
|
|
|
|
} |
|
659
|
1001
|
|
|
|
|
|
case XF_INT_COMMA: { |
|
660
|
1001
|
50
|
|
|
|
|
OUT_ENSURE(28); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
661
|
1001
|
|
|
|
|
|
long v = 0; int neg = 0; |
|
662
|
3895
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) { |
|
663
|
2894
|
50
|
|
|
|
|
if (src[i] == '-') neg = 1; |
|
664
|
2894
|
50
|
|
|
|
|
else if (src[i] >= '0' && src[i] <= '9') v = v * 10 + (src[i] - '0'); |
|
|
|
50
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
} |
|
666
|
1001
|
50
|
|
|
|
|
if (neg) v = -v; |
|
667
|
1001
|
50
|
|
|
|
|
int w = itoa_comma(OUT_PTR, v); |
|
668
|
1001
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
669
|
1001
|
|
|
|
|
|
break; |
|
670
|
|
|
|
|
|
|
} |
|
671
|
0
|
|
|
|
|
|
case XF_FLOAT: { |
|
672
|
0
|
0
|
|
|
|
|
OUT_ENSURE(64); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
673
|
0
|
|
|
|
|
|
double fv = 0; |
|
674
|
0
|
0
|
|
|
|
|
{ char tb[64]; int tl = slen < 63 ? (int)slen : 63; memcpy(tb, src, tl); tb[tl] = 0; fv = atof(tb); } |
|
675
|
0
|
0
|
|
|
|
|
int w = snprintf(OUT_PTR, 64, "%.*f", xf->param_int, fv); |
|
676
|
0
|
0
|
|
|
|
|
if (w > 63) w = 63; |
|
677
|
0
|
0
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
678
|
0
|
|
|
|
|
|
break; |
|
679
|
|
|
|
|
|
|
} |
|
680
|
32
|
|
|
|
|
|
case XF_HTML: { |
|
681
|
32
|
50
|
|
|
|
|
OUT_ENSURE(slen * 6); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
682
|
32
|
|
|
|
|
|
int needs_escape = 0; |
|
683
|
100689
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) |
|
684
|
100672
|
100
|
|
|
|
|
if (html_special[(unsigned char)src[i]]) { needs_escape = 1; break; } |
|
685
|
32
|
100
|
|
|
|
|
if (!needs_escape) { |
|
686
|
17
|
50
|
|
|
|
|
memcpy(OUT_PTR, src, slen); |
|
687
|
17
|
50
|
|
|
|
|
if (to_output) pos += slen; else tmp_len = slen; |
|
688
|
|
|
|
|
|
|
} else { |
|
689
|
15
|
50
|
|
|
|
|
int w = html_escape(OUT_PTR, src, slen); |
|
690
|
15
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
691
|
|
|
|
|
|
|
} |
|
692
|
32
|
|
|
|
|
|
break; |
|
693
|
|
|
|
|
|
|
} |
|
694
|
1
|
|
|
|
|
|
case XF_HTML_BR: { |
|
695
|
1
|
50
|
|
|
|
|
OUT_ENSURE(slen * 6); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
696
|
1
|
|
|
|
|
|
int needs_escape = 0; |
|
697
|
6
|
50
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) |
|
698
|
6
|
100
|
|
|
|
|
if (html_br_special[(unsigned char)src[i]]) { needs_escape = 1; break; } |
|
699
|
1
|
50
|
|
|
|
|
if (!needs_escape) { |
|
700
|
0
|
0
|
|
|
|
|
memcpy(OUT_PTR, src, slen); |
|
701
|
0
|
0
|
|
|
|
|
if (to_output) pos += slen; else tmp_len = slen; |
|
702
|
|
|
|
|
|
|
} else { |
|
703
|
1
|
50
|
|
|
|
|
int w = html_br_escape(OUT_PTR, src, slen); |
|
704
|
1
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
705
|
|
|
|
|
|
|
} |
|
706
|
1
|
|
|
|
|
|
break; |
|
707
|
|
|
|
|
|
|
} |
|
708
|
1
|
|
|
|
|
|
case XF_URL: { |
|
709
|
1
|
50
|
|
|
|
|
OUT_ENSURE(slen * 3); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
710
|
1
|
50
|
|
|
|
|
int w = url_escape(OUT_PTR, src, slen); |
|
711
|
1
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
712
|
1
|
|
|
|
|
|
break; |
|
713
|
|
|
|
|
|
|
} |
|
714
|
4
|
|
|
|
|
|
case XF_JSON: { |
|
715
|
4
|
50
|
|
|
|
|
OUT_ENSURE(slen * 6); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
716
|
4
|
|
|
|
|
|
int needs_escape = 0; |
|
717
|
25
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) |
|
718
|
24
|
100
|
|
|
|
|
if (json_special[(unsigned char)src[i]]) { needs_escape = 1; break; } |
|
719
|
4
|
100
|
|
|
|
|
if (!needs_escape) { |
|
720
|
1
|
50
|
|
|
|
|
memcpy(OUT_PTR, src, slen); |
|
721
|
1
|
50
|
|
|
|
|
if (to_output) pos += slen; else tmp_len = slen; |
|
722
|
|
|
|
|
|
|
} else { |
|
723
|
3
|
50
|
|
|
|
|
int w = json_escape(OUT_PTR, src, slen); |
|
724
|
3
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
725
|
|
|
|
|
|
|
} |
|
726
|
4
|
|
|
|
|
|
break; |
|
727
|
|
|
|
|
|
|
} |
|
728
|
9
|
|
|
|
|
|
case XF_TRIM: { |
|
729
|
9
|
|
|
|
|
|
const char *s = src; STRLEN l = slen; |
|
730
|
21
|
50
|
|
|
|
|
while (l > 0 && (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')) { s++; l--; } |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
731
|
22
|
50
|
|
|
|
|
while (l > 0 && (s[l-1] == ' ' || s[l-1] == '\t' || s[l-1] == '\n' || s[l-1] == '\r')) l--; |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
732
|
9
|
100
|
|
|
|
|
OUT_ENSURE(l); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
733
|
9
|
100
|
|
|
|
|
memcpy(OUT_PTR, s, l); |
|
734
|
9
|
100
|
|
|
|
|
if (to_output) pos += l; else tmp_len = l; |
|
735
|
9
|
|
|
|
|
|
break; |
|
736
|
|
|
|
|
|
|
} |
|
737
|
6
|
|
|
|
|
|
case XF_UC: { |
|
738
|
6
|
100
|
|
|
|
|
OUT_ENSURE(slen); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
739
|
34
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) OUT_PTR[i] = toupper((unsigned char)src[i]); |
|
|
|
100
|
|
|
|
|
|
|
740
|
6
|
100
|
|
|
|
|
if (to_output) pos += slen; else tmp_len = slen; |
|
741
|
6
|
|
|
|
|
|
break; |
|
742
|
|
|
|
|
|
|
} |
|
743
|
5
|
|
|
|
|
|
case XF_LC: { |
|
744
|
5
|
100
|
|
|
|
|
OUT_ENSURE(slen); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
745
|
38
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) OUT_PTR[i] = tolower((unsigned char)src[i]); |
|
|
|
100
|
|
|
|
|
|
|
746
|
5
|
100
|
|
|
|
|
if (to_output) pos += slen; else tmp_len = slen; |
|
747
|
5
|
|
|
|
|
|
break; |
|
748
|
|
|
|
|
|
|
} |
|
749
|
4
|
|
|
|
|
|
case XF_PAD: { |
|
750
|
4
|
|
|
|
|
|
int w = xf->param_int; |
|
751
|
4
|
50
|
|
|
|
|
OUT_ENSURE(w > (int)slen ? w : slen); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
752
|
4
|
|
|
|
|
|
int pad = w - (int)slen; |
|
753
|
4
|
100
|
|
|
|
|
if (pad > 0) { memset(OUT_PTR, ' ', pad); memcpy(OUT_PTR + pad, src, slen); } |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
754
|
1
|
50
|
|
|
|
|
else memcpy(OUT_PTR, src, slen); |
|
755
|
4
|
100
|
|
|
|
|
int total = pad > 0 ? w : (int)slen; |
|
756
|
4
|
50
|
|
|
|
|
if (to_output) pos += total; else tmp_len = total; |
|
757
|
4
|
|
|
|
|
|
break; |
|
758
|
|
|
|
|
|
|
} |
|
759
|
1
|
|
|
|
|
|
case XF_RPAD: { |
|
760
|
1
|
|
|
|
|
|
int w = xf->param_int; |
|
761
|
1
|
50
|
|
|
|
|
OUT_ENSURE(w > (int)slen ? w : slen); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
762
|
1
|
50
|
|
|
|
|
memcpy(OUT_PTR, src, slen); |
|
763
|
1
|
|
|
|
|
|
int pad = w - (int)slen; |
|
764
|
1
|
50
|
|
|
|
|
if (pad > 0) memset(OUT_PTR + slen, ' ', pad); |
|
|
|
50
|
|
|
|
|
|
|
765
|
1
|
50
|
|
|
|
|
int total = pad > 0 ? w : (int)slen; |
|
766
|
1
|
50
|
|
|
|
|
if (to_output) pos += total; else tmp_len = total; |
|
767
|
1
|
|
|
|
|
|
break; |
|
768
|
|
|
|
|
|
|
} |
|
769
|
3
|
|
|
|
|
|
case XF_TRUNC: { |
|
770
|
3
|
|
|
|
|
|
int mx = xf->param_int; |
|
771
|
3
|
100
|
|
|
|
|
if ((int)slen <= mx) { |
|
772
|
1
|
50
|
|
|
|
|
OUT_ENSURE(slen); memcpy(OUT_PTR, src, slen); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
773
|
1
|
50
|
|
|
|
|
if (to_output) pos += slen; else tmp_len = slen; |
|
774
|
|
|
|
|
|
|
} else { |
|
775
|
2
|
|
|
|
|
|
int tl = mx > 3 ? mx - 3 : 0; |
|
776
|
2
|
100
|
|
|
|
|
OUT_ENSURE(tl + 3); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
777
|
2
|
100
|
|
|
|
|
memcpy(OUT_PTR, src, tl); |
|
778
|
2
|
100
|
|
|
|
|
memcpy(OUT_PTR + tl, "...", 3); |
|
779
|
2
|
100
|
|
|
|
|
if (to_output) pos += tl + 3; else tmp_len = tl + 3; |
|
780
|
|
|
|
|
|
|
} |
|
781
|
3
|
|
|
|
|
|
break; |
|
782
|
|
|
|
|
|
|
} |
|
783
|
1
|
|
|
|
|
|
case XF_HEX: { |
|
784
|
1
|
50
|
|
|
|
|
OUT_ENSURE(slen * 2); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
785
|
1
|
50
|
|
|
|
|
int w = hex_encode(OUT_PTR, src, slen); |
|
786
|
1
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
787
|
1
|
|
|
|
|
|
break; |
|
788
|
|
|
|
|
|
|
} |
|
789
|
7
|
|
|
|
|
|
case XF_BASE64: { |
|
790
|
7
|
50
|
|
|
|
|
OUT_ENSURE(((slen + 2) / 3) * 4); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
791
|
7
|
50
|
|
|
|
|
int w = base64_encode(OUT_PTR, (const unsigned char *)src, slen); |
|
792
|
7
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
793
|
7
|
|
|
|
|
|
break; |
|
794
|
|
|
|
|
|
|
} |
|
795
|
3
|
|
|
|
|
|
case XF_BASE64URL: { |
|
796
|
3
|
50
|
|
|
|
|
OUT_ENSURE(((slen + 2) / 3) * 4); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
797
|
3
|
50
|
|
|
|
|
int w = base64url_encode(OUT_PTR, (const unsigned char *)src, slen); |
|
798
|
3
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
799
|
3
|
|
|
|
|
|
break; |
|
800
|
|
|
|
|
|
|
} |
|
801
|
0
|
|
|
|
|
|
case XF_COUNT: { |
|
802
|
|
|
|
|
|
|
/* unreachable: count must be the first transform (compile-time croak |
|
803
|
|
|
|
|
|
|
otherwise) and is handled in render_field before apply_xform; kept |
|
804
|
|
|
|
|
|
|
for -Wswitch completeness */ |
|
805
|
0
|
0
|
|
|
|
|
OUT_ENSURE(12); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
806
|
0
|
0
|
|
|
|
|
int w = itoa_fast(OUT_PTR, xf->param_int); |
|
807
|
0
|
0
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
808
|
0
|
|
|
|
|
|
break; |
|
809
|
|
|
|
|
|
|
} |
|
810
|
7
|
|
|
|
|
|
case XF_BOOL: { |
|
811
|
7
|
|
|
|
|
|
int truthy = (slen > 0); |
|
812
|
7
|
100
|
|
|
|
|
if (slen == 1 && src[0] == '0') truthy = 0; |
|
|
|
100
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
const char *val; STRLEN vlen; |
|
814
|
7
|
100
|
|
|
|
|
if (truthy) { |
|
815
|
3
|
100
|
|
|
|
|
val = xf->param_str ? xf->param_str : "true"; |
|
816
|
3
|
100
|
|
|
|
|
vlen = xf->param_str ? xf->param_str_len : 4; |
|
817
|
|
|
|
|
|
|
} else { |
|
818
|
4
|
100
|
|
|
|
|
val = xf->param_str2 ? xf->param_str2 : "false"; |
|
819
|
4
|
100
|
|
|
|
|
vlen = xf->param_str2 ? xf->param_str2_len : 5; |
|
820
|
|
|
|
|
|
|
} |
|
821
|
7
|
50
|
|
|
|
|
OUT_ENSURE(vlen); memcpy(OUT_PTR, val, vlen); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
822
|
7
|
50
|
|
|
|
|
if (to_output) pos += vlen; else tmp_len = vlen; |
|
823
|
7
|
|
|
|
|
|
break; |
|
824
|
|
|
|
|
|
|
} |
|
825
|
2
|
|
|
|
|
|
case XF_DATE: { |
|
826
|
2
|
|
|
|
|
|
time_t epoch = 0; |
|
827
|
13
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) { |
|
828
|
11
|
50
|
|
|
|
|
if (src[i] >= '0' && src[i] <= '9') epoch = epoch * 10 + (src[i] - '0'); |
|
|
|
50
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
} |
|
830
|
|
|
|
|
|
|
struct tm tm; |
|
831
|
2
|
|
|
|
|
|
gmtime_r(&epoch, &tm); |
|
832
|
2
|
100
|
|
|
|
|
const char *fmt = xf->param_str ? xf->param_str : "%Y-%m-%d %H:%M:%S"; |
|
833
|
2
|
50
|
|
|
|
|
OUT_ENSURE(256); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
834
|
2
|
50
|
|
|
|
|
int w = (int)strftime(OUT_PTR, 256, fmt, &tm); |
|
835
|
2
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
836
|
2
|
|
|
|
|
|
break; |
|
837
|
|
|
|
|
|
|
} |
|
838
|
6
|
|
|
|
|
|
case XF_SPRINTF: { |
|
839
|
6
|
50
|
|
|
|
|
OUT_ENSURE(256); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
840
|
6
|
50
|
|
|
|
|
if (xf->param_str && xf->param_str_len > 0) { |
|
|
|
50
|
|
|
|
|
|
|
841
|
6
|
|
|
|
|
|
char last = xf->param_str[xf->param_str_len - 1]; |
|
842
|
6
|
100
|
|
|
|
|
if (last != 'd' && last != 'i' && last != 'x' && last != 'X' && |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
843
|
4
|
50
|
|
|
|
|
last != 'o' && last != 'u' && last != 'f' && last != 'e' && |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
844
|
3
|
100
|
|
|
|
|
last != 'g' && last != 's') { |
|
845
|
1
|
50
|
|
|
|
|
OUT_ENSURE(slen); memcpy(OUT_PTR, src, slen); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
846
|
1
|
50
|
|
|
|
|
if (to_output) pos += slen; else tmp_len = slen; |
|
847
|
2
|
|
|
|
|
|
break; |
|
848
|
|
|
|
|
|
|
} |
|
849
|
|
|
|
|
|
|
char fmtbuf[64]; |
|
850
|
5
|
|
|
|
|
|
int fmtlen = snprintf(fmtbuf, sizeof(fmtbuf), "%s%s", |
|
851
|
5
|
50
|
|
|
|
|
xf->param_str[0] == '%' ? "" : "%", xf->param_str); |
|
852
|
5
|
50
|
|
|
|
|
if (fmtlen >= (int)sizeof(fmtbuf)) { |
|
853
|
0
|
0
|
|
|
|
|
OUT_ENSURE(slen); memcpy(OUT_PTR, src, slen); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
854
|
0
|
0
|
|
|
|
|
if (to_output) pos += slen; else tmp_len = slen; |
|
855
|
0
|
|
|
|
|
|
break; |
|
856
|
|
|
|
|
|
|
} |
|
857
|
5
|
|
|
|
|
|
int pct_count = 0; int has_star = 0; |
|
858
|
26
|
100
|
|
|
|
|
for (int fi = 0; fi < fmtlen && fmtbuf[fi]; fi++) { |
|
|
|
50
|
|
|
|
|
|
|
859
|
21
|
100
|
|
|
|
|
if (fmtbuf[fi] == '%') { if (fi + 1 < fmtlen && fmtbuf[fi+1] == '%') fi++; else pct_count++; } |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
860
|
15
|
50
|
|
|
|
|
else if (fmtbuf[fi] == '*') has_star = 1; |
|
861
|
|
|
|
|
|
|
} |
|
862
|
5
|
100
|
|
|
|
|
if (pct_count != 1 || has_star) { |
|
|
|
50
|
|
|
|
|
|
|
863
|
1
|
50
|
|
|
|
|
OUT_ENSURE(slen); memcpy(OUT_PTR, src, slen); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
864
|
1
|
50
|
|
|
|
|
if (to_output) pos += slen; else tmp_len = slen; |
|
865
|
1
|
|
|
|
|
|
break; |
|
866
|
|
|
|
|
|
|
} |
|
867
|
|
|
|
|
|
|
int w; |
|
868
|
4
|
100
|
|
|
|
|
if (last == 'd' || last == 'i' || last == 'x' || last == 'X' || last == 'o' || last == 'u') { |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
869
|
2
|
|
|
|
|
|
long lv = 0; int neg = 0; |
|
870
|
7
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) { |
|
871
|
5
|
50
|
|
|
|
|
if (src[i] == '-') neg = 1; |
|
872
|
5
|
50
|
|
|
|
|
else if (src[i] >= '0' && src[i] <= '9') lv = lv * 10 + (src[i] - '0'); |
|
|
|
50
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
} |
|
874
|
2
|
50
|
|
|
|
|
if (neg) lv = -lv; |
|
875
|
2
|
50
|
|
|
|
|
w = snprintf(OUT_PTR, 256, fmtbuf, lv); |
|
876
|
2
|
100
|
|
|
|
|
} else if (last == 'f' || last == 'e' || last == 'g') { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
877
|
1
|
50
|
|
|
|
|
char tb[64]; int tl = slen < 63 ? (int)slen : 63; |
|
878
|
1
|
|
|
|
|
|
memcpy(tb, src, tl); tb[tl] = 0; |
|
879
|
1
|
50
|
|
|
|
|
w = snprintf(OUT_PTR, 256, fmtbuf, atof(tb)); |
|
880
|
|
|
|
|
|
|
} else { |
|
881
|
1
|
50
|
|
|
|
|
char tb[256]; int tl = slen < 255 ? (int)slen : 255; |
|
882
|
1
|
|
|
|
|
|
memcpy(tb, src, tl); tb[tl] = 0; |
|
883
|
1
|
50
|
|
|
|
|
w = snprintf(OUT_PTR, 256, fmtbuf, tb); |
|
884
|
|
|
|
|
|
|
} |
|
885
|
4
|
50
|
|
|
|
|
if (w > 255) w = 255; |
|
886
|
4
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
887
|
|
|
|
|
|
|
} |
|
888
|
4
|
|
|
|
|
|
break; |
|
889
|
|
|
|
|
|
|
} |
|
890
|
5
|
|
|
|
|
|
case XF_REPLACE: { |
|
891
|
5
|
100
|
|
|
|
|
if (!xf->param_str) { |
|
892
|
1
|
50
|
|
|
|
|
OUT_ENSURE(slen); memcpy(OUT_PTR, src, slen); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
893
|
1
|
50
|
|
|
|
|
if (to_output) pos += slen; else tmp_len = slen; |
|
894
|
1
|
|
|
|
|
|
break; |
|
895
|
|
|
|
|
|
|
} |
|
896
|
4
|
|
|
|
|
|
const char *needle = xf->param_str; |
|
897
|
4
|
|
|
|
|
|
STRLEN nlen = xf->param_str_len; |
|
898
|
4
|
50
|
|
|
|
|
const char *repl = xf->param_str2 ? xf->param_str2 : ""; |
|
899
|
4
|
50
|
|
|
|
|
STRLEN rlen = xf->param_str2 ? xf->param_str2_len : 0; |
|
900
|
4
|
50
|
|
|
|
|
OUT_ENSURE(slen * (rlen + 1)); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
901
|
4
|
50
|
|
|
|
|
char *out = OUT_PTR; |
|
902
|
4
|
|
|
|
|
|
STRLEN opos = 0; |
|
903
|
4
|
|
|
|
|
|
STRLEN i = 0; |
|
904
|
23
|
100
|
|
|
|
|
while (i < slen) { |
|
905
|
19
|
100
|
|
|
|
|
if (i + nlen <= slen && memcmp(src + i, needle, nlen) == 0) { |
|
|
|
100
|
|
|
|
|
|
|
906
|
7
|
|
|
|
|
|
memcpy(out + opos, repl, rlen); opos += rlen; |
|
907
|
7
|
|
|
|
|
|
i += nlen; |
|
908
|
|
|
|
|
|
|
} else { |
|
909
|
12
|
|
|
|
|
|
out[opos++] = src[i++]; |
|
910
|
|
|
|
|
|
|
} |
|
911
|
|
|
|
|
|
|
} |
|
912
|
4
|
50
|
|
|
|
|
if (to_output) pos += opos; else tmp_len = opos; |
|
913
|
4
|
|
|
|
|
|
break; |
|
914
|
|
|
|
|
|
|
} |
|
915
|
4
|
|
|
|
|
|
case XF_SUBSTR: { |
|
916
|
4
|
|
|
|
|
|
int start = xf->param_int; |
|
917
|
4
|
|
|
|
|
|
int maxlen = xf->param_int2; |
|
918
|
4
|
100
|
|
|
|
|
if (start < 0 || start >= (int)slen) { |
|
|
|
100
|
|
|
|
|
|
|
919
|
2
|
50
|
|
|
|
|
if (!to_output) tmp_len = 0; |
|
920
|
2
|
|
|
|
|
|
break; |
|
921
|
|
|
|
|
|
|
} |
|
922
|
2
|
|
|
|
|
|
const char *s = src + start; |
|
923
|
2
|
|
|
|
|
|
STRLEN l = slen - start; |
|
924
|
2
|
100
|
|
|
|
|
if (maxlen >= 0 && (int)l > maxlen) l = maxlen; |
|
|
|
50
|
|
|
|
|
|
|
925
|
2
|
50
|
|
|
|
|
OUT_ENSURE(l); memcpy(OUT_PTR, s, l); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
926
|
2
|
50
|
|
|
|
|
if (to_output) pos += l; else tmp_len = l; |
|
927
|
2
|
|
|
|
|
|
break; |
|
928
|
|
|
|
|
|
|
} |
|
929
|
5
|
|
|
|
|
|
case XF_PLURAL: { |
|
930
|
5
|
|
|
|
|
|
long v = 0; |
|
931
|
5
|
50
|
|
|
|
|
STRLEN pi = (slen > 0 && src[0] == '-') ? 1 : 0; |
|
|
|
100
|
|
|
|
|
|
|
932
|
10
|
100
|
|
|
|
|
for (STRLEN i = pi; i < slen; i++) |
|
933
|
5
|
50
|
|
|
|
|
if (src[i] >= '0' && src[i] <= '9') v = v * 10 + (src[i] - '0'); |
|
|
|
50
|
|
|
|
|
|
|
934
|
5
|
100
|
|
|
|
|
if (pi) v = -v; /* preserve sign: -1 is plural, and itoa keeps the '-' */ |
|
935
|
|
|
|
|
|
|
const char *form; STRLEN flen; |
|
936
|
5
|
100
|
|
|
|
|
if (v == 1) { |
|
937
|
2
|
50
|
|
|
|
|
form = xf->param_str ? xf->param_str : ""; flen = xf->param_str_len; |
|
938
|
|
|
|
|
|
|
} else { |
|
939
|
3
|
50
|
|
|
|
|
form = xf->param_str2 ? xf->param_str2 : "s"; flen = xf->param_str2 ? xf->param_str2_len : 1; |
|
|
|
50
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
} |
|
941
|
5
|
50
|
|
|
|
|
OUT_ENSURE(20 + 1 + flen); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
942
|
5
|
50
|
|
|
|
|
int nw = itoa_fast(OUT_PTR, v); |
|
943
|
5
|
50
|
|
|
|
|
OUT_PTR[nw] = ' '; |
|
944
|
5
|
50
|
|
|
|
|
memcpy(OUT_PTR + nw + 1, form, flen); |
|
945
|
5
|
|
|
|
|
|
int total = nw + 1 + (int)flen; |
|
946
|
5
|
50
|
|
|
|
|
if (to_output) pos += total; else tmp_len = total; |
|
947
|
5
|
|
|
|
|
|
break; |
|
948
|
|
|
|
|
|
|
} |
|
949
|
4
|
|
|
|
|
|
case XF_IF: { |
|
950
|
4
|
100
|
|
|
|
|
int truthy = (slen > 0 && !(slen == 1 && src[0] == '0')); |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
951
|
4
|
100
|
|
|
|
|
if (truthy && xf->param_str) { |
|
|
|
50
|
|
|
|
|
|
|
952
|
1
|
50
|
|
|
|
|
OUT_ENSURE(xf->param_str_len); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
953
|
1
|
50
|
|
|
|
|
memcpy(OUT_PTR, xf->param_str, xf->param_str_len); |
|
954
|
1
|
50
|
|
|
|
|
if (to_output) pos += xf->param_str_len; else tmp_len = xf->param_str_len; |
|
955
|
|
|
|
|
|
|
} else { |
|
956
|
3
|
50
|
|
|
|
|
if (!to_output) tmp_len = 0; |
|
957
|
|
|
|
|
|
|
} |
|
958
|
4
|
|
|
|
|
|
break; |
|
959
|
|
|
|
|
|
|
} |
|
960
|
2
|
|
|
|
|
|
case XF_UNLESS: { |
|
961
|
2
|
100
|
|
|
|
|
int truthy = (slen > 0 && !(slen == 1 && src[0] == '0')); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
962
|
2
|
100
|
|
|
|
|
if (!truthy && xf->param_str) { |
|
|
|
50
|
|
|
|
|
|
|
963
|
1
|
50
|
|
|
|
|
OUT_ENSURE(xf->param_str_len); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
964
|
1
|
50
|
|
|
|
|
memcpy(OUT_PTR, xf->param_str, xf->param_str_len); |
|
965
|
1
|
50
|
|
|
|
|
if (to_output) pos += xf->param_str_len; else tmp_len = xf->param_str_len; |
|
966
|
|
|
|
|
|
|
} else { |
|
967
|
1
|
50
|
|
|
|
|
if (!to_output) tmp_len = 0; |
|
968
|
|
|
|
|
|
|
} |
|
969
|
2
|
|
|
|
|
|
break; |
|
970
|
|
|
|
|
|
|
} |
|
971
|
5
|
|
|
|
|
|
case XF_MAP: { |
|
972
|
5
|
|
|
|
|
|
const char *val = src; STRLEN vlen = slen; |
|
973
|
12
|
100
|
|
|
|
|
for (int mi = 0; mi < xf->map_count; mi++) { |
|
974
|
10
|
50
|
|
|
|
|
if ((xf->map_key_lens[mi] == slen && memcmp(xf->map_keys[mi], src, slen) == 0) || |
|
|
|
100
|
|
|
|
|
|
|
975
|
7
|
50
|
|
|
|
|
(xf->map_key_lens[mi] == 1 && xf->map_keys[mi][0] == '*')) { |
|
|
|
100
|
|
|
|
|
|
|
976
|
5
|
|
|
|
|
|
val = xf->map_vals[mi]; vlen = xf->map_val_lens[mi]; |
|
977
|
5
|
50
|
|
|
|
|
if (xf->map_key_lens[mi] != 1 || xf->map_keys[mi][0] != '*') break; |
|
|
|
100
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
} |
|
979
|
|
|
|
|
|
|
} |
|
980
|
5
|
50
|
|
|
|
|
OUT_ENSURE(vlen); memcpy(OUT_PTR, val, vlen); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
981
|
5
|
50
|
|
|
|
|
if (to_output) pos += vlen; else tmp_len = vlen; |
|
982
|
5
|
|
|
|
|
|
break; |
|
983
|
|
|
|
|
|
|
} |
|
984
|
4
|
|
|
|
|
|
case XF_WRAP: { |
|
985
|
6
|
100
|
|
|
|
|
if (slen > 0 && xf->param_str) { |
|
|
|
50
|
|
|
|
|
|
|
986
|
2
|
100
|
|
|
|
|
STRLEN plen2 = xf->param_str_len + slen + (xf->param_str2 ? xf->param_str2_len : 0); |
|
987
|
2
|
50
|
|
|
|
|
OUT_ENSURE(plen2); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
988
|
2
|
50
|
|
|
|
|
memcpy(OUT_PTR, xf->param_str, xf->param_str_len); |
|
989
|
2
|
|
|
|
|
|
int wpos = (int)xf->param_str_len; |
|
990
|
2
|
50
|
|
|
|
|
memcpy(OUT_PTR + wpos, src, slen); wpos += slen; |
|
991
|
2
|
100
|
|
|
|
|
if (xf->param_str2) { memcpy(OUT_PTR + wpos, xf->param_str2, xf->param_str2_len); wpos += xf->param_str2_len; } |
|
|
|
50
|
|
|
|
|
|
|
992
|
2
|
50
|
|
|
|
|
if (to_output) pos += wpos; else tmp_len = wpos; |
|
993
|
|
|
|
|
|
|
} else { |
|
994
|
2
|
50
|
|
|
|
|
if (!to_output) tmp_len = 0; |
|
995
|
|
|
|
|
|
|
} |
|
996
|
4
|
|
|
|
|
|
break; |
|
997
|
|
|
|
|
|
|
} |
|
998
|
4
|
|
|
|
|
|
case XF_NUMBER_SI: { |
|
999
|
4
|
|
|
|
|
|
double v = 0; |
|
1000
|
4
|
50
|
|
|
|
|
{ char tb[64]; int tl = slen < 63 ? (int)slen : 63; memcpy(tb, src, tl); tb[tl] = 0; v = atof(tb); } |
|
1001
|
4
|
50
|
|
|
|
|
OUT_ENSURE(32); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
int w; |
|
1003
|
4
|
50
|
|
|
|
|
if (v >= 1e15 || v <= -1e15) w = snprintf(OUT_PTR, 32, "%.1fP", v / 1e15); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1004
|
4
|
50
|
|
|
|
|
else if (v >= 1e12 || v <= -1e12) w = snprintf(OUT_PTR, 32, "%.1fT", v / 1e12); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1005
|
4
|
50
|
|
|
|
|
else if (v >= 1e9 || v <= -1e9) w = snprintf(OUT_PTR, 32, "%.1fG", v / 1e9); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1006
|
4
|
100
|
|
|
|
|
else if (v >= 1e6 || v <= -1e6) w = snprintf(OUT_PTR, 32, "%.1fM", v / 1e6); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1007
|
3
|
100
|
|
|
|
|
else if (v >= 1e3 || v <= -1e3) w = snprintf(OUT_PTR, 32, "%.1fK", v / 1e3); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1008
|
1
|
50
|
|
|
|
|
else w = snprintf(OUT_PTR, 32, "%.0f", v); |
|
1009
|
4
|
50
|
|
|
|
|
if (w > 31) w = 31; |
|
1010
|
4
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
1011
|
4
|
|
|
|
|
|
break; |
|
1012
|
|
|
|
|
|
|
} |
|
1013
|
4
|
|
|
|
|
|
case XF_BYTES_SI: { |
|
1014
|
4
|
|
|
|
|
|
double v = 0; |
|
1015
|
4
|
50
|
|
|
|
|
{ char tb[64]; int tl = slen < 63 ? (int)slen : 63; memcpy(tb, src, tl); tb[tl] = 0; v = atof(tb); } |
|
1016
|
4
|
50
|
|
|
|
|
OUT_ENSURE(32); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
int w; |
|
1018
|
4
|
100
|
|
|
|
|
if (v >= 1099511627776.0) w = snprintf(OUT_PTR, 32, "%.1f TB", v / 1099511627776.0); |
|
|
|
50
|
|
|
|
|
|
|
1019
|
3
|
100
|
|
|
|
|
else if (v >= 1073741824.0) w = snprintf(OUT_PTR, 32, "%.1f GB", v / 1073741824.0); |
|
|
|
50
|
|
|
|
|
|
|
1020
|
2
|
50
|
|
|
|
|
else if (v >= 1048576.0) w = snprintf(OUT_PTR, 32, "%.1f MB", v / 1048576.0); |
|
|
|
0
|
|
|
|
|
|
|
1021
|
2
|
100
|
|
|
|
|
else if (v >= 1024.0) w = snprintf(OUT_PTR, 32, "%.1f KB", v / 1024.0); |
|
|
|
50
|
|
|
|
|
|
|
1022
|
1
|
50
|
|
|
|
|
else w = snprintf(OUT_PTR, 32, "%.0f B", v); |
|
1023
|
4
|
50
|
|
|
|
|
if (w > 31) w = 31; |
|
1024
|
4
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
1025
|
4
|
|
|
|
|
|
break; |
|
1026
|
|
|
|
|
|
|
} |
|
1027
|
5
|
|
|
|
|
|
case XF_ELAPSED: { |
|
1028
|
5
|
|
|
|
|
|
long v = 0; |
|
1029
|
22
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) |
|
1030
|
17
|
50
|
|
|
|
|
if (src[i] >= '0' && src[i] <= '9') v = v * 10 + (src[i] - '0'); |
|
|
|
50
|
|
|
|
|
|
|
1031
|
5
|
50
|
|
|
|
|
OUT_ENSURE(64); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1032
|
5
|
|
|
|
|
|
int w = 0; |
|
1033
|
5
|
100
|
|
|
|
|
if (v >= 86400 && w < 63) { int n = snprintf(OUT_PTR + w, 64 - w, "%ldd ", v / 86400); if (n > 0) w += n; v %= 86400; } |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1034
|
5
|
100
|
|
|
|
|
if (v >= 3600 && w < 63) { int n = snprintf(OUT_PTR + w, 64 - w, "%ldh ", v / 3600); if (n > 0) w += n; v %= 3600; } |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1035
|
5
|
100
|
|
|
|
|
if (v >= 60 && w < 63) { int n = snprintf(OUT_PTR + w, 64 - w, "%ldm ", v / 60); if (n > 0) w += n; v %= 60; } |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1036
|
5
|
50
|
|
|
|
|
if (w < 63) { int n = snprintf(OUT_PTR + w, 64 - w, "%lds", v); if (n > 0) w += n; } |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1037
|
5
|
50
|
|
|
|
|
if (w > 63) w = 63; |
|
1038
|
5
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
1039
|
5
|
|
|
|
|
|
break; |
|
1040
|
|
|
|
|
|
|
} |
|
1041
|
3
|
|
|
|
|
|
case XF_AGO: { |
|
1042
|
3
|
|
|
|
|
|
time_t now = time(NULL); |
|
1043
|
3
|
|
|
|
|
|
time_t epoch = 0; |
|
1044
|
33
|
100
|
|
|
|
|
for (STRLEN i = 0; i < slen; i++) |
|
1045
|
30
|
50
|
|
|
|
|
if (src[i] >= '0' && src[i] <= '9') epoch = epoch * 10 + (src[i] - '0'); |
|
|
|
50
|
|
|
|
|
|
|
1046
|
3
|
|
|
|
|
|
long diff = (long)(now - epoch); |
|
1047
|
3
|
50
|
|
|
|
|
OUT_ENSURE(32); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
int w; |
|
1049
|
3
|
100
|
|
|
|
|
if (diff < 0) w = snprintf(OUT_PTR, 32, "in the future"); |
|
|
|
50
|
|
|
|
|
|
|
1050
|
2
|
50
|
|
|
|
|
else if (diff < 60) w = snprintf(OUT_PTR, 32, "%lds ago", diff); |
|
|
|
0
|
|
|
|
|
|
|
1051
|
2
|
100
|
|
|
|
|
else if (diff < 3600) w = snprintf(OUT_PTR, 32, "%ldm ago", diff / 60); |
|
|
|
50
|
|
|
|
|
|
|
1052
|
1
|
50
|
|
|
|
|
else if (diff < 86400) w = snprintf(OUT_PTR, 32, "%ldh ago", diff / 3600); |
|
|
|
50
|
|
|
|
|
|
|
1053
|
0
|
0
|
|
|
|
|
else if (diff < 2592000) w = snprintf(OUT_PTR, 32, "%ldd ago", diff / 86400); |
|
|
|
0
|
|
|
|
|
|
|
1054
|
0
|
0
|
|
|
|
|
else if (diff < 31536000) w = snprintf(OUT_PTR, 32, "%ldmo ago", diff / 2592000); |
|
|
|
0
|
|
|
|
|
|
|
1055
|
0
|
0
|
|
|
|
|
else w = snprintf(OUT_PTR, 32, "%ldy ago", diff / 31536000); |
|
1056
|
3
|
50
|
|
|
|
|
if (w > 31) w = 31; |
|
1057
|
3
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
1058
|
3
|
|
|
|
|
|
break; |
|
1059
|
|
|
|
|
|
|
} |
|
1060
|
6
|
|
|
|
|
|
case XF_MASK: { |
|
1061
|
6
|
|
|
|
|
|
int keep = xf->param_int; |
|
1062
|
6
|
100
|
|
|
|
|
OUT_ENSURE(slen); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1063
|
6
|
|
|
|
|
|
int mask_len = (int)slen - keep; |
|
1064
|
6
|
100
|
|
|
|
|
if (mask_len > 0) memset(OUT_PTR, '*', mask_len); |
|
|
|
100
|
|
|
|
|
|
|
1065
|
6
|
100
|
|
|
|
|
if (keep > 0) { |
|
1066
|
5
|
|
|
|
|
|
int start = mask_len > 0 ? mask_len : 0; |
|
1067
|
5
|
|
|
|
|
|
int copy = keep > (int)slen ? (int)slen : keep; |
|
1068
|
5
|
100
|
|
|
|
|
memcpy(OUT_PTR + start, src + slen - copy, copy); |
|
1069
|
|
|
|
|
|
|
} |
|
1070
|
6
|
|
|
|
|
|
int total = (int)slen; |
|
1071
|
6
|
100
|
|
|
|
|
if (to_output) pos += total; else tmp_len = total; |
|
1072
|
6
|
|
|
|
|
|
break; |
|
1073
|
|
|
|
|
|
|
} |
|
1074
|
3
|
|
|
|
|
|
case XF_LENGTH: { |
|
1075
|
3
|
50
|
|
|
|
|
OUT_ENSURE(20); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1076
|
3
|
50
|
|
|
|
|
int w = itoa_fast(OUT_PTR, (long)slen); |
|
1077
|
3
|
50
|
|
|
|
|
if (to_output) pos += w; else tmp_len = w; |
|
1078
|
3
|
|
|
|
|
|
break; |
|
1079
|
|
|
|
|
|
|
} |
|
1080
|
348
|
|
|
|
|
|
case XF_COALESCE: /* handled in render_field, fallthrough to raw */ |
|
1081
|
|
|
|
|
|
|
case XF_ROWNUM: /* should not appear in chain; rownum is handled by render_field */ |
|
1082
|
|
|
|
|
|
|
case XF_DEFAULT: |
|
1083
|
|
|
|
|
|
|
case XF_RAW: { |
|
1084
|
348
|
50
|
|
|
|
|
OUT_ENSURE(slen); memcpy(OUT_PTR, src, slen); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1085
|
348
|
50
|
|
|
|
|
if (to_output) pos += slen; else tmp_len = slen; |
|
1086
|
348
|
|
|
|
|
|
break; |
|
1087
|
|
|
|
|
|
|
} |
|
1088
|
|
|
|
|
|
|
} |
|
1089
|
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
#undef OUT_ENSURE |
|
1091
|
|
|
|
|
|
|
#undef OUT_PTR |
|
1092
|
1495
|
|
|
|
|
|
*bufp = buf; *posp = pos; *allocp = alloc; |
|
1093
|
1495
|
|
|
|
|
|
*tmpp = tmp; *tmp_lenp = tmp_len; *tmp_allocp = tmp_alloc; |
|
1094
|
1495
|
|
|
|
|
|
} |
|
1095
|
|
|
|
|
|
|
|
|
1096
|
11571
|
|
|
|
|
|
static void render_field(pTHX_ tpl_op *op, SV *row_sv, enum row_mode mode, |
|
1097
|
|
|
|
|
|
|
char **bufp, STRLEN *posp, STRLEN *allocp, |
|
1098
|
|
|
|
|
|
|
SSize_t row_idx) { |
|
1099
|
11571
|
|
|
|
|
|
const char *src = NULL; STRLEN slen = 0; |
|
1100
|
11571
|
|
|
|
|
|
int use_default = 0; |
|
1101
|
11571
|
|
|
|
|
|
char rownum_buf[20]; int rownum_len = 0; |
|
1102
|
|
|
|
|
|
|
|
|
1103
|
11571
|
100
|
|
|
|
|
if (op->is_rownum) { |
|
1104
|
1010
|
|
|
|
|
|
rownum_len = itoa_fast(rownum_buf, (long)row_idx); |
|
1105
|
1010
|
|
|
|
|
|
src = rownum_buf; slen = rownum_len; |
|
1106
|
1010
|
|
|
|
|
|
use_default = 1; /* skip fetch_field path, go straight to chain */ |
|
1107
|
|
|
|
|
|
|
} |
|
1108
|
|
|
|
|
|
|
|
|
1109
|
11571
|
|
|
|
|
|
SV *sv = NULL; |
|
1110
|
11571
|
100
|
|
|
|
|
if (!op->is_rownum) { |
|
1111
|
10561
|
|
|
|
|
|
sv = fetch_field(aTHX_ row_sv, op, mode); |
|
1112
|
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
/* handle default transform */ |
|
1114
|
10561
|
50
|
|
|
|
|
if (!sv || !SvOK(sv)) { |
|
|
|
100
|
|
|
|
|
|
|
1115
|
46
|
100
|
|
|
|
|
for (int i = 0; i < op->chain_len; i++) { |
|
1116
|
27
|
100
|
|
|
|
|
if (op->chain[i].type == XF_DEFAULT && op->chain[i].param_str) { |
|
|
|
50
|
|
|
|
|
|
|
1117
|
4
|
|
|
|
|
|
src = op->chain[i].param_str; |
|
1118
|
4
|
|
|
|
|
|
slen = op->chain[i].param_str_len; |
|
1119
|
4
|
|
|
|
|
|
use_default = 1; |
|
1120
|
4
|
|
|
|
|
|
break; |
|
1121
|
|
|
|
|
|
|
} |
|
1122
|
23
|
100
|
|
|
|
|
if (op->chain[i].type == XF_COALESCE) { |
|
1123
|
1
|
|
|
|
|
|
use_default = 1; /* coalesce will handle it */ |
|
1124
|
1
|
|
|
|
|
|
break; |
|
1125
|
|
|
|
|
|
|
} |
|
1126
|
22
|
100
|
|
|
|
|
if (op->chain[i].type == XF_BOOL || op->chain[i].type == XF_IF || |
|
|
|
100
|
|
|
|
|
|
|
1127
|
20
|
50
|
|
|
|
|
op->chain[i].type == XF_UNLESS || op->chain[i].type == XF_MAP || |
|
|
|
50
|
|
|
|
|
|
|
1128
|
20
|
100
|
|
|
|
|
op->chain[i].type == XF_WRAP) { |
|
1129
|
3
|
|
|
|
|
|
src = ""; slen = 0; |
|
1130
|
3
|
|
|
|
|
|
use_default = 1; |
|
1131
|
3
|
|
|
|
|
|
break; |
|
1132
|
|
|
|
|
|
|
} |
|
1133
|
|
|
|
|
|
|
} |
|
1134
|
11570
|
100
|
|
|
|
|
if (!use_default) return; |
|
1135
|
|
|
|
|
|
|
} |
|
1136
|
|
|
|
|
|
|
} |
|
1137
|
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
/* handle coalesce: try fallback fields, then literal default */ |
|
1139
|
11552
|
100
|
|
|
|
|
if (!op->is_rownum && op->chain_len > 0 && op->chain[0].type == XF_COALESCE) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1140
|
6
|
|
|
|
|
|
int primary_ok = 0; |
|
1141
|
6
|
50
|
|
|
|
|
if (sv && SvOK(sv)) { |
|
|
|
100
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
STRLEN plen; |
|
1143
|
5
|
|
|
|
|
|
const char *pstr = SvPV_nomg(sv, plen); |
|
1144
|
5
|
100
|
|
|
|
|
if (plen > 0) { primary_ok = 1; src = pstr; slen = plen; use_default = 0; } |
|
1145
|
|
|
|
|
|
|
} |
|
1146
|
6
|
100
|
|
|
|
|
if (!primary_ok && !op->chain[0].param_str) return; |
|
|
|
50
|
|
|
|
|
|
|
1147
|
6
|
100
|
|
|
|
|
if (!primary_ok && op->chain[0].param_str) { |
|
|
|
50
|
|
|
|
|
|
|
1148
|
5
|
|
|
|
|
|
const char *params = op->chain[0].param_str; |
|
1149
|
5
|
|
|
|
|
|
STRLEN params_len = op->chain[0].param_str_len; |
|
1150
|
5
|
|
|
|
|
|
const char *p = params, *pe = params + params_len; |
|
1151
|
5
|
|
|
|
|
|
const char *last_param = NULL; STRLEN last_param_len = 0; |
|
1152
|
|
|
|
|
|
|
/* find last param (the literal default) */ |
|
1153
|
5
|
|
|
|
|
|
const char *tp = params; |
|
1154
|
10
|
50
|
|
|
|
|
while (tp < pe) { |
|
1155
|
10
|
|
|
|
|
|
const char *next = memchr(tp, ':', pe - tp); |
|
1156
|
10
|
100
|
|
|
|
|
if (!next) { last_param = tp; last_param_len = pe - tp; break; } |
|
1157
|
5
|
|
|
|
|
|
last_param = tp; last_param_len = next - tp; |
|
1158
|
5
|
|
|
|
|
|
tp = next + 1; |
|
1159
|
|
|
|
|
|
|
} |
|
1160
|
|
|
|
|
|
|
/* try each fallback field (all params except the last) */ |
|
1161
|
5
|
|
|
|
|
|
int found = 0; |
|
1162
|
5
|
|
|
|
|
|
p = params; |
|
1163
|
7
|
50
|
|
|
|
|
while (p < pe) { |
|
1164
|
7
|
|
|
|
|
|
const char *next = memchr(p, ':', pe - p); |
|
1165
|
7
|
100
|
|
|
|
|
STRLEN seg_len = next ? (STRLEN)(next - p) : (STRLEN)(pe - p); |
|
1166
|
7
|
100
|
|
|
|
|
if (!next && p == last_param) break; /* this is the literal default */ |
|
|
|
50
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
/* check if this is not the last param */ |
|
1168
|
5
|
50
|
|
|
|
|
if (p != last_param || next) { |
|
|
|
0
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
/* try to fetch this field from the row */ |
|
1170
|
5
|
|
|
|
|
|
tpl_op tmp_op = {0}; |
|
1171
|
5
|
100
|
|
|
|
|
if (mode == ROW_HASH) { |
|
1172
|
4
|
|
|
|
|
|
tmp_op.key = (char *)p; tmp_op.key_len = seg_len; |
|
1173
|
|
|
|
|
|
|
} else { |
|
1174
|
1
|
|
|
|
|
|
int is_neg = 0, si = 0; |
|
1175
|
1
|
50
|
|
|
|
|
if (seg_len > 0 && p[0] == '-') { is_neg = 1; si = 1; } |
|
|
|
50
|
|
|
|
|
|
|
1176
|
1
|
|
|
|
|
|
int is_num = 1; |
|
1177
|
2
|
100
|
|
|
|
|
for (STRLEN fi = si; fi < seg_len; fi++) |
|
1178
|
1
|
50
|
|
|
|
|
if (p[fi] < '0' || p[fi] > '9') { is_num = 0; break; } |
|
|
|
50
|
|
|
|
|
|
|
1179
|
1
|
50
|
|
|
|
|
if (is_num && seg_len > (STRLEN)si) { |
|
|
|
50
|
|
|
|
|
|
|
1180
|
1
|
|
|
|
|
|
tmp_op.col = 0; |
|
1181
|
2
|
100
|
|
|
|
|
for (STRLEN fi = si; fi < seg_len; fi++) tmp_op.col = tmp_op.col * 10 + (p[fi] - '0'); |
|
1182
|
1
|
50
|
|
|
|
|
if (is_neg) tmp_op.col = -tmp_op.col; |
|
1183
|
|
|
|
|
|
|
} else { |
|
1184
|
3
|
0
|
|
|
|
|
if (!next) break; /* non-numeric in array mode = treat as literal default */ |
|
1185
|
0
|
|
|
|
|
|
p = next + 1; continue; |
|
1186
|
|
|
|
|
|
|
} |
|
1187
|
|
|
|
|
|
|
} |
|
1188
|
5
|
|
|
|
|
|
SV *fallback = fetch_field(aTHX_ row_sv, &tmp_op, mode); |
|
1189
|
5
|
50
|
|
|
|
|
if (fallback && SvOK(fallback)) { |
|
|
|
100
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
STRLEN flen; |
|
1191
|
4
|
|
|
|
|
|
const char *fstr = SvPV_nomg(fallback, flen); |
|
1192
|
4
|
100
|
|
|
|
|
if (flen > 0) { sv = fallback; src = fstr; slen = flen; use_default = 0; found = 1; break; } |
|
1193
|
|
|
|
|
|
|
} |
|
1194
|
|
|
|
|
|
|
} |
|
1195
|
2
|
50
|
|
|
|
|
if (!next) break; |
|
1196
|
2
|
|
|
|
|
|
p = next + 1; |
|
1197
|
|
|
|
|
|
|
} |
|
1198
|
5
|
100
|
|
|
|
|
if (!found) { |
|
1199
|
2
|
|
|
|
|
|
src = last_param; slen = last_param_len; |
|
1200
|
2
|
|
|
|
|
|
use_default = 1; |
|
1201
|
|
|
|
|
|
|
} |
|
1202
|
|
|
|
|
|
|
} |
|
1203
|
|
|
|
|
|
|
} |
|
1204
|
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
/* handle count type: count elements of array/hash ref */ |
|
1206
|
11552
|
100
|
|
|
|
|
if (!use_default && !op->is_rownum && op->chain_len > 0 && op->chain[0].type == XF_COUNT) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1207
|
3
|
|
|
|
|
|
int cnt = 0; |
|
1208
|
3
|
50
|
|
|
|
|
if (sv && SvROK(sv)) { |
|
|
|
100
|
|
|
|
|
|
|
1209
|
2
|
|
|
|
|
|
SV *inner = SvRV(sv); |
|
1210
|
2
|
100
|
|
|
|
|
if (SvTYPE(inner) == SVt_PVAV) cnt = (int)av_count((AV *)inner); |
|
1211
|
1
|
50
|
|
|
|
|
else if (SvTYPE(inner) == SVt_PVHV) cnt = (int)HvUSEDKEYS((HV *)inner); |
|
|
|
50
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
} |
|
1213
|
|
|
|
|
|
|
char cbuf[12]; |
|
1214
|
3
|
|
|
|
|
|
int clen = itoa_fast(cbuf, cnt); |
|
1215
|
3
|
50
|
|
|
|
|
if (op->chain_len == 1) { |
|
1216
|
3
|
|
|
|
|
|
char *buf = *bufp; STRLEN pos = *posp; STRLEN alloc = *allocp; |
|
1217
|
3
|
50
|
|
|
|
|
BUF_ENSURE(clen); memcpy(buf + pos, cbuf, clen); pos += clen; |
|
|
|
0
|
|
|
|
|
|
|
1218
|
3
|
|
|
|
|
|
*bufp = buf; *posp = pos; *allocp = alloc; |
|
1219
|
3
|
|
|
|
|
|
return; |
|
1220
|
|
|
|
|
|
|
} |
|
1221
|
0
|
|
|
|
|
|
src = cbuf; slen = clen; |
|
1222
|
|
|
|
|
|
|
} |
|
1223
|
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
/* get initial string value */ |
|
1225
|
11549
|
100
|
|
|
|
|
if (!use_default && !op->is_rownum && !(op->chain_len > 0 && |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1226
|
10530
|
50
|
|
|
|
|
(op->chain[0].type == XF_COUNT || op->chain[0].type == XF_COALESCE))) { |
|
|
|
100
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
/* for int/float types as first transform, use numeric conversion */ |
|
1228
|
10526
|
50
|
|
|
|
|
if (op->chain_len > 0 && (op->chain[0].type == XF_INT || op->chain[0].type == XF_INT_COMMA)) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
char ibuf[28]; /* itoa_comma needs up to 26 ("-9,223,372,036,854,775,808"); itoa_fast 20 */ |
|
1230
|
10058
|
100
|
|
|
|
|
int ilen = (op->chain[0].type == XF_INT) ? itoa_fast(ibuf, SvIV_nomg(sv)) : itoa_comma(ibuf, SvIV_nomg(sv)); |
|
1231
|
10058
|
50
|
|
|
|
|
if (op->chain_len == 1) { |
|
1232
|
10058
|
|
|
|
|
|
char *buf = *bufp; STRLEN pos = *posp; STRLEN alloc = *allocp; |
|
1233
|
10058
|
50
|
|
|
|
|
BUF_ENSURE(ilen); memcpy(buf + pos, ibuf, ilen); pos += ilen; |
|
|
|
0
|
|
|
|
|
|
|
1234
|
10058
|
|
|
|
|
|
*bufp = buf; *posp = pos; *allocp = alloc; |
|
1235
|
10058
|
|
|
|
|
|
return; |
|
1236
|
|
|
|
|
|
|
} |
|
1237
|
0
|
|
|
|
|
|
src = ibuf; slen = ilen; |
|
1238
|
468
|
50
|
|
|
|
|
} else if (op->chain_len > 0 && op->chain[0].type == XF_FLOAT) { |
|
|
|
100
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
char fbuf[64]; |
|
1240
|
3
|
|
|
|
|
|
int flen = snprintf(fbuf, 64, "%.*f", op->chain[0].param_int, SvNV_nomg(sv)); |
|
1241
|
3
|
50
|
|
|
|
|
if (flen > 63) flen = 63; |
|
1242
|
3
|
50
|
|
|
|
|
if (op->chain_len == 1) { |
|
1243
|
3
|
|
|
|
|
|
char *buf = *bufp; STRLEN pos = *posp; STRLEN alloc = *allocp; |
|
1244
|
3
|
50
|
|
|
|
|
BUF_ENSURE(flen); memcpy(buf + pos, fbuf, flen); pos += flen; |
|
|
|
0
|
|
|
|
|
|
|
1245
|
3
|
|
|
|
|
|
*bufp = buf; *posp = pos; *allocp = alloc; |
|
1246
|
3
|
|
|
|
|
|
return; |
|
1247
|
|
|
|
|
|
|
} |
|
1248
|
0
|
|
|
|
|
|
src = fbuf; slen = flen; |
|
1249
|
|
|
|
|
|
|
} else { |
|
1250
|
465
|
|
|
|
|
|
src = SvPV_nomg(sv, slen); |
|
1251
|
|
|
|
|
|
|
} |
|
1252
|
|
|
|
|
|
|
} |
|
1253
|
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
/* single transform fast path (most common) */ |
|
1255
|
1488
|
|
|
|
|
|
int start = 0; |
|
1256
|
1488
|
100
|
|
|
|
|
if (!use_default && !op->is_rownum && op->chain_len > 0 && |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1257
|
469
|
50
|
|
|
|
|
(op->chain[0].type == XF_INT || op->chain[0].type == XF_INT_COMMA || |
|
|
|
50
|
|
|
|
|
|
|
1258
|
469
|
50
|
|
|
|
|
op->chain[0].type == XF_FLOAT || op->chain[0].type == XF_COUNT || |
|
|
|
50
|
|
|
|
|
|
|
1259
|
469
|
100
|
|
|
|
|
op->chain[0].type == XF_COALESCE)) |
|
1260
|
4
|
|
|
|
|
|
start = 1; |
|
1261
|
|
|
|
|
|
|
|
|
1262
|
1488
|
100
|
|
|
|
|
if (op->chain_len - start == 0) { |
|
1263
|
4
|
|
|
|
|
|
char *buf = *bufp; STRLEN pos = *posp; STRLEN alloc = *allocp; |
|
1264
|
4
|
50
|
|
|
|
|
BUF_ENSURE(slen); memcpy(buf + pos, src, slen); pos += slen; |
|
|
|
0
|
|
|
|
|
|
|
1265
|
4
|
|
|
|
|
|
*bufp = buf; *posp = pos; *allocp = alloc; |
|
1266
|
4
|
|
|
|
|
|
return; |
|
1267
|
|
|
|
|
|
|
} |
|
1268
|
|
|
|
|
|
|
|
|
1269
|
1484
|
100
|
|
|
|
|
if (op->chain_len - start == 1) { |
|
1270
|
1475
|
|
|
|
|
|
tpl_xform *xf = &op->chain[start]; |
|
1271
|
1475
|
100
|
|
|
|
|
if (xf->type == XF_DEFAULT) { |
|
1272
|
3
|
|
|
|
|
|
char *buf = *bufp; STRLEN pos = *posp; STRLEN alloc = *allocp; |
|
1273
|
3
|
50
|
|
|
|
|
BUF_ENSURE(slen); memcpy(buf + pos, src, slen); pos += slen; |
|
|
|
0
|
|
|
|
|
|
|
1274
|
3
|
|
|
|
|
|
*bufp = buf; *posp = pos; *allocp = alloc; |
|
1275
|
|
|
|
|
|
|
} else { |
|
1276
|
1472
|
|
|
|
|
|
char *tmp = NULL; STRLEN tmp_len = 0, tmp_alloc = 0; |
|
1277
|
1472
|
|
|
|
|
|
apply_xform(xf, src, slen, bufp, posp, allocp, &tmp, &tmp_len, &tmp_alloc, 1); |
|
1278
|
1472
|
50
|
|
|
|
|
if (tmp) free(tmp); |
|
1279
|
|
|
|
|
|
|
} |
|
1280
|
1475
|
|
|
|
|
|
return; |
|
1281
|
|
|
|
|
|
|
} |
|
1282
|
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
/* chain: apply transforms with ping-pong buffers */ |
|
1284
|
9
|
|
|
|
|
|
char *tmp_a = NULL, *tmp_b = NULL; |
|
1285
|
9
|
|
|
|
|
|
STRLEN tmp_a_len = 0, tmp_a_alloc = 0, tmp_b_len = 0, tmp_b_alloc = 0; |
|
1286
|
9
|
|
|
|
|
|
const char *cur = src; STRLEN cur_len = slen; |
|
1287
|
9
|
|
|
|
|
|
int use_a = 1; |
|
1288
|
|
|
|
|
|
|
|
|
1289
|
34
|
100
|
|
|
|
|
for (int i = start; i < op->chain_len; i++) { |
|
1290
|
25
|
100
|
|
|
|
|
if (op->chain[i].type == XF_DEFAULT) continue; |
|
1291
|
23
|
|
|
|
|
|
int is_last = 1; |
|
1292
|
23
|
100
|
|
|
|
|
for (int k = i + 1; k < op->chain_len; k++) |
|
1293
|
14
|
50
|
|
|
|
|
if (op->chain[k].type != XF_DEFAULT) { is_last = 0; break; } |
|
1294
|
|
|
|
|
|
|
|
|
1295
|
23
|
100
|
|
|
|
|
if (is_last) { |
|
1296
|
9
|
|
|
|
|
|
char *dummy = NULL; STRLEN dummy_len = 0, dummy_alloc = 0; |
|
1297
|
9
|
|
|
|
|
|
apply_xform(&op->chain[i], cur, cur_len, bufp, posp, allocp, &dummy, &dummy_len, &dummy_alloc, 1); |
|
1298
|
9
|
50
|
|
|
|
|
if (dummy) free(dummy); |
|
1299
|
|
|
|
|
|
|
} else { |
|
1300
|
14
|
100
|
|
|
|
|
if (use_a) { |
|
1301
|
10
|
|
|
|
|
|
tmp_a_len = 0; |
|
1302
|
10
|
|
|
|
|
|
apply_xform(&op->chain[i], cur, cur_len, bufp, posp, allocp, &tmp_a, &tmp_a_len, &tmp_a_alloc, 0); |
|
1303
|
10
|
|
|
|
|
|
cur = tmp_a; cur_len = tmp_a_len; |
|
1304
|
10
|
|
|
|
|
|
use_a = 0; |
|
1305
|
|
|
|
|
|
|
} else { |
|
1306
|
4
|
|
|
|
|
|
tmp_b_len = 0; |
|
1307
|
4
|
|
|
|
|
|
apply_xform(&op->chain[i], cur, cur_len, bufp, posp, allocp, &tmp_b, &tmp_b_len, &tmp_b_alloc, 0); |
|
1308
|
4
|
|
|
|
|
|
cur = tmp_b; cur_len = tmp_b_len; |
|
1309
|
4
|
|
|
|
|
|
use_a = 1; |
|
1310
|
|
|
|
|
|
|
} |
|
1311
|
|
|
|
|
|
|
} |
|
1312
|
|
|
|
|
|
|
} |
|
1313
|
9
|
100
|
|
|
|
|
if (tmp_a) free(tmp_a); |
|
1314
|
9
|
100
|
|
|
|
|
if (tmp_b) free(tmp_b); |
|
1315
|
|
|
|
|
|
|
} |
|
1316
|
|
|
|
|
|
|
|
|
1317
|
|
|
|
|
|
|
/* check if a field value in a row is truthy */ |
|
1318
|
31
|
|
|
|
|
|
static int is_field_truthy(pTHX_ SV *row_sv, tpl_compiled *t, int is_skip_if) { |
|
1319
|
|
|
|
|
|
|
int col; char *key; STRLEN key_len; |
|
1320
|
31
|
100
|
|
|
|
|
if (is_skip_if) { col = t->skip_if_col; key = t->skip_if_key; key_len = t->skip_if_key_len; } |
|
1321
|
13
|
|
|
|
|
|
else { col = t->skip_unless_col; key = t->skip_unless_key; key_len = t->skip_unless_key_len; } |
|
1322
|
|
|
|
|
|
|
|
|
1323
|
31
|
|
|
|
|
|
SV *field = NULL; |
|
1324
|
31
|
100
|
|
|
|
|
if (key) { |
|
1325
|
6
|
50
|
|
|
|
|
if (SvROK(row_sv) && SvTYPE(SvRV(row_sv)) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
1326
|
6
|
|
|
|
|
|
SV **sv = hv_fetch((HV *)SvRV(row_sv), key, key_len, 0); |
|
1327
|
6
|
50
|
|
|
|
|
if (sv) field = *sv; |
|
1328
|
|
|
|
|
|
|
} |
|
1329
|
|
|
|
|
|
|
} else { |
|
1330
|
25
|
50
|
|
|
|
|
if (SvROK(row_sv) && SvTYPE(SvRV(row_sv)) == SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
1331
|
25
|
|
|
|
|
|
AV *av = (AV *)SvRV(row_sv); |
|
1332
|
25
|
|
|
|
|
|
SV **ary = AvARRAY(av); |
|
1333
|
25
|
50
|
|
|
|
|
SSize_t top = av_top_index(av); |
|
1334
|
25
|
100
|
|
|
|
|
if (col < 0) col = (int)(top + 1) + col; |
|
1335
|
25
|
50
|
|
|
|
|
if (col >= 0 && col <= (int)top) field = ary[col]; |
|
|
|
50
|
|
|
|
|
|
|
1336
|
|
|
|
|
|
|
} |
|
1337
|
|
|
|
|
|
|
} |
|
1338
|
31
|
50
|
|
|
|
|
if (!field || !SvOK(field)) return 0; |
|
|
|
100
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
STRLEN flen; |
|
1340
|
29
|
|
|
|
|
|
const char *fstr = SvPV(field, flen); |
|
1341
|
29
|
100
|
|
|
|
|
if (flen == 0) return 0; |
|
1342
|
18
|
100
|
|
|
|
|
if (flen == 1 && fstr[0] == '0') return 0; |
|
|
|
100
|
|
|
|
|
|
|
1343
|
13
|
|
|
|
|
|
return 1; |
|
1344
|
|
|
|
|
|
|
} |
|
1345
|
|
|
|
|
|
|
|
|
1346
|
11543
|
|
|
|
|
|
static int should_skip_row(pTHX_ SV *row_sv, tpl_compiled *t) { |
|
1347
|
11543
|
100
|
|
|
|
|
if (t->has_skip_if && is_field_truthy(aTHX_ row_sv, t, 1)) return 1; |
|
|
|
100
|
|
|
|
|
|
|
1348
|
11536
|
100
|
|
|
|
|
if (t->has_skip_unless && !is_field_truthy(aTHX_ row_sv, t, 0)) return 1; |
|
|
|
100
|
|
|
|
|
|
|
1349
|
11529
|
|
|
|
|
|
return 0; |
|
1350
|
|
|
|
|
|
|
} |
|
1351
|
|
|
|
|
|
|
|
|
1352
|
185
|
|
|
|
|
|
static SV *tpl_render(pTHX_ tpl_compiled *t, AV *rows) { |
|
1353
|
185
|
|
|
|
|
|
SSize_t nrows = av_count(rows); |
|
1354
|
185
|
|
|
|
|
|
t->last_row_count = nrows; |
|
1355
|
|
|
|
|
|
|
STRLEN alloc, pos; |
|
1356
|
|
|
|
|
|
|
char *buf; |
|
1357
|
185
|
100
|
|
|
|
|
RBUF_INIT(t, t->header_len + t->footer_len + nrows * 300 + 1); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
|
|
1359
|
185
|
50
|
|
|
|
|
BUF_WRITE(t->header, t->header_len); |
|
|
|
0
|
|
|
|
|
|
|
1360
|
185
|
|
|
|
|
|
int first = 1; |
|
1361
|
11406
|
100
|
|
|
|
|
for (SSize_t i = 0; i < nrows; i++) { |
|
1362
|
11221
|
|
|
|
|
|
SV **rowref = av_fetch(rows, i, 0); |
|
1363
|
11221
|
50
|
|
|
|
|
if (!rowref) continue; |
|
1364
|
11221
|
100
|
|
|
|
|
if (should_skip_row(aTHX_ *rowref, t)) continue; |
|
1365
|
11211
|
100
|
|
|
|
|
if (!first && t->sep_len) BUF_WRITE(t->sep, t->sep_len); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1366
|
11211
|
|
|
|
|
|
first = 0; |
|
1367
|
22532
|
100
|
|
|
|
|
for (int j = 0; j < t->nops; j++) { |
|
1368
|
11321
|
|
|
|
|
|
tpl_op *op = &t->ops[j]; |
|
1369
|
11321
|
100
|
|
|
|
|
if (op->static_data) |
|
1370
|
87
|
50
|
|
|
|
|
BUF_WRITE(op->static_data, op->static_len); |
|
|
|
0
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
else |
|
1372
|
11234
|
|
|
|
|
|
render_field(aTHX_ op, *rowref, t->mode, &buf, &pos, &alloc, i); |
|
1373
|
|
|
|
|
|
|
} |
|
1374
|
|
|
|
|
|
|
} |
|
1375
|
185
|
50
|
|
|
|
|
BUF_WRITE(t->footer, t->footer_len); |
|
|
|
0
|
|
|
|
|
|
|
1376
|
185
|
|
|
|
|
|
RBUF_FINISH(t); |
|
1377
|
185
|
|
|
|
|
|
return newSVpvn_utf8(buf, pos, 1); |
|
1378
|
|
|
|
|
|
|
} |
|
1379
|
|
|
|
|
|
|
|
|
1380
|
|
|
|
|
|
|
/* ---- sorted render ---- */ |
|
1381
|
|
|
|
|
|
|
|
|
1382
|
|
|
|
|
|
|
typedef struct { SV *sv; const char **keys; STRLEN *key_lens; } sort_entry; |
|
1383
|
|
|
|
|
|
|
|
|
1384
|
|
|
|
|
|
|
static int sort_nsort; |
|
1385
|
|
|
|
|
|
|
static int sort_numeric; |
|
1386
|
|
|
|
|
|
|
|
|
1387
|
37
|
|
|
|
|
|
static int sort_cmp_multi(const sort_entry *ea, const sort_entry *eb) { |
|
1388
|
39
|
50
|
|
|
|
|
for (int k = 0; k < sort_nsort; k++) { |
|
1389
|
39
|
100
|
|
|
|
|
if (sort_numeric) { |
|
1390
|
|
|
|
|
|
|
char ba[64], bb[64]; |
|
1391
|
5
|
50
|
|
|
|
|
int la = ea->key_lens[k] < 63 ? (int)ea->key_lens[k] : 63; |
|
1392
|
5
|
50
|
|
|
|
|
int lb = eb->key_lens[k] < 63 ? (int)eb->key_lens[k] : 63; |
|
1393
|
5
|
|
|
|
|
|
memcpy(ba, ea->keys[k], la); ba[la] = 0; |
|
1394
|
5
|
|
|
|
|
|
memcpy(bb, eb->keys[k], lb); bb[lb] = 0; |
|
1395
|
5
|
|
|
|
|
|
double da = atof(ba), db = atof(bb); |
|
1396
|
8
|
100
|
|
|
|
|
if (da < db) return -1; |
|
1397
|
3
|
50
|
|
|
|
|
if (da > db) return 1; |
|
1398
|
|
|
|
|
|
|
} else { |
|
1399
|
34
|
|
|
|
|
|
STRLEN minlen = ea->key_lens[k] < eb->key_lens[k] ? ea->key_lens[k] : eb->key_lens[k]; |
|
1400
|
34
|
|
|
|
|
|
int r = memcmp(ea->keys[k], eb->keys[k], minlen); |
|
1401
|
34
|
100
|
|
|
|
|
if (r) return r; |
|
1402
|
2
|
50
|
|
|
|
|
if (ea->key_lens[k] != eb->key_lens[k]) |
|
1403
|
0
|
0
|
|
|
|
|
return ea->key_lens[k] < eb->key_lens[k] ? -1 : 1; |
|
1404
|
|
|
|
|
|
|
} |
|
1405
|
|
|
|
|
|
|
} |
|
1406
|
0
|
|
|
|
|
|
return 0; |
|
1407
|
|
|
|
|
|
|
} |
|
1408
|
|
|
|
|
|
|
|
|
1409
|
24
|
|
|
|
|
|
static int sort_cmp_asc(const void *a, const void *b) { |
|
1410
|
24
|
|
|
|
|
|
return sort_cmp_multi((const sort_entry *)a, (const sort_entry *)b); |
|
1411
|
|
|
|
|
|
|
} |
|
1412
|
|
|
|
|
|
|
|
|
1413
|
13
|
|
|
|
|
|
static int sort_cmp_desc(const void *a, const void *b) { |
|
1414
|
13
|
|
|
|
|
|
return sort_cmp_multi((const sort_entry *)b, (const sort_entry *)a); |
|
1415
|
|
|
|
|
|
|
} |
|
1416
|
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
/* libc free() as a SAVEDESTRUCTOR_X callback. The sort scratch is malloc'd, |
|
1418
|
|
|
|
|
|
|
not Newx'd, so SAVEFREEPV/Safefree would mismatch on DEBUGGING perls. */ |
|
1419
|
39
|
|
|
|
|
|
static void ts_free(pTHX_ void *p) { free(p); } |
|
1420
|
|
|
|
|
|
|
|
|
1421
|
14
|
|
|
|
|
|
static SV *tpl_render_sorted(pTHX_ tpl_compiled *t, AV *rows, |
|
1422
|
|
|
|
|
|
|
int *sort_cols, const char **sort_keys, STRLEN *sort_key_lens, |
|
1423
|
|
|
|
|
|
|
int nsort, int descending, int numeric) { |
|
1424
|
14
|
|
|
|
|
|
SSize_t nrows = av_count(rows); |
|
1425
|
14
|
|
|
|
|
|
t->last_row_count = nrows; |
|
1426
|
|
|
|
|
|
|
|
|
1427
|
14
|
100
|
|
|
|
|
sort_entry *entries = nrows > 0 ? (sort_entry *)malloc(nrows * sizeof(sort_entry)) : NULL; |
|
1428
|
14
|
100
|
|
|
|
|
if (nrows > 0 && !entries) croak("malloc"); |
|
|
|
50
|
|
|
|
|
|
|
1429
|
14
|
100
|
|
|
|
|
const char **all_keys = nrows > 0 ? (const char **)calloc(nrows * nsort, sizeof(char *)) : NULL; |
|
1430
|
14
|
100
|
|
|
|
|
STRLEN *all_lens = nrows > 0 ? (STRLEN *)calloc(nrows * nsort, sizeof(STRLEN)) : NULL; |
|
1431
|
|
|
|
|
|
|
/* Free the sort scratch on scope exit, so a croak mid-render (e.g. an OOM |
|
1432
|
|
|
|
|
|
|
realloc in BUF_ENSURE) frees it on the unwind instead of leaking it; |
|
1433
|
|
|
|
|
|
|
LEAVE frees it on the normal path. */ |
|
1434
|
14
|
|
|
|
|
|
ENTER; |
|
1435
|
14
|
100
|
|
|
|
|
if (entries) SAVEDESTRUCTOR_X(ts_free, entries); |
|
1436
|
14
|
100
|
|
|
|
|
if (all_keys) SAVEDESTRUCTOR_X(ts_free, all_keys); |
|
1437
|
14
|
100
|
|
|
|
|
if (all_lens) SAVEDESTRUCTOR_X(ts_free, all_lens); |
|
1438
|
|
|
|
|
|
|
/* checked after the SAVEDESTRUCTORs so the unwind frees entries; an |
|
1439
|
|
|
|
|
|
|
unchecked NULL here would segfault at entries[i].keys below */ |
|
1440
|
14
|
100
|
|
|
|
|
if (nrows > 0 && (!all_keys || !all_lens)) croak("calloc"); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1441
|
53
|
100
|
|
|
|
|
for (SSize_t i = 0; i < nrows; i++) { |
|
1442
|
39
|
|
|
|
|
|
SV **rowref = av_fetch(rows, i, 0); |
|
1443
|
39
|
50
|
|
|
|
|
entries[i].sv = rowref ? *rowref : &PL_sv_undef; |
|
1444
|
39
|
|
|
|
|
|
entries[i].keys = all_keys + i * nsort; |
|
1445
|
39
|
|
|
|
|
|
entries[i].key_lens = all_lens + i * nsort; |
|
1446
|
84
|
100
|
|
|
|
|
for (int k = 0; k < nsort; k++) { |
|
1447
|
45
|
|
|
|
|
|
entries[i].keys[k] = ""; entries[i].key_lens[k] = 0; |
|
1448
|
45
|
50
|
|
|
|
|
if (rowref && SvROK(*rowref)) { |
|
|
|
50
|
|
|
|
|
|
|
1449
|
45
|
|
|
|
|
|
SV *field = NULL; |
|
1450
|
45
|
100
|
|
|
|
|
if (sort_keys) { |
|
1451
|
21
|
50
|
|
|
|
|
if (SvTYPE(SvRV(*rowref)) == SVt_PVHV) { |
|
1452
|
21
|
|
|
|
|
|
SV **sv = hv_fetch((HV *)SvRV(*rowref), sort_keys[k], sort_key_lens[k], 0); |
|
1453
|
21
|
50
|
|
|
|
|
if (sv) field = *sv; |
|
1454
|
|
|
|
|
|
|
} |
|
1455
|
|
|
|
|
|
|
} else { |
|
1456
|
24
|
50
|
|
|
|
|
if (SvTYPE(SvRV(*rowref)) == SVt_PVAV) { |
|
1457
|
24
|
|
|
|
|
|
SV **sv = av_fetch((AV *)SvRV(*rowref), sort_cols[k], 0); |
|
1458
|
24
|
50
|
|
|
|
|
if (sv) field = *sv; |
|
1459
|
|
|
|
|
|
|
} |
|
1460
|
|
|
|
|
|
|
} |
|
1461
|
45
|
50
|
|
|
|
|
if (field) entries[i].keys[k] = SvPV(field, entries[i].key_lens[k]); |
|
1462
|
|
|
|
|
|
|
} |
|
1463
|
|
|
|
|
|
|
} |
|
1464
|
|
|
|
|
|
|
} |
|
1465
|
|
|
|
|
|
|
|
|
1466
|
14
|
|
|
|
|
|
sort_nsort = nsort; |
|
1467
|
14
|
|
|
|
|
|
sort_numeric = numeric; |
|
1468
|
14
|
100
|
|
|
|
|
int (*cmp)(const void *, const void *) = descending ? sort_cmp_desc : sort_cmp_asc; |
|
1469
|
14
|
|
|
|
|
|
qsort(entries, nrows, sizeof(sort_entry), cmp); |
|
1470
|
|
|
|
|
|
|
|
|
1471
|
|
|
|
|
|
|
STRLEN alloc, pos; |
|
1472
|
|
|
|
|
|
|
char *buf; |
|
1473
|
14
|
100
|
|
|
|
|
RBUF_INIT(t, t->header_len + t->footer_len + nrows * 300 + 1); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1474
|
|
|
|
|
|
|
|
|
1475
|
14
|
50
|
|
|
|
|
BUF_WRITE(t->header, t->header_len); |
|
|
|
0
|
|
|
|
|
|
|
1476
|
14
|
|
|
|
|
|
int first = 1; |
|
1477
|
53
|
100
|
|
|
|
|
for (SSize_t i = 0; i < nrows; i++) { |
|
1478
|
39
|
|
|
|
|
|
SV *row_sv = entries[i].sv; |
|
1479
|
39
|
100
|
|
|
|
|
if (should_skip_row(aTHX_ row_sv, t)) continue; |
|
1480
|
38
|
100
|
|
|
|
|
if (!first && t->sep_len) BUF_WRITE(t->sep, t->sep_len); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1481
|
38
|
|
|
|
|
|
first = 0; |
|
1482
|
106
|
100
|
|
|
|
|
for (int j = 0; j < t->nops; j++) { |
|
1483
|
68
|
|
|
|
|
|
tpl_op *op = &t->ops[j]; |
|
1484
|
68
|
100
|
|
|
|
|
if (op->static_data) BUF_WRITE(op->static_data, op->static_len); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1485
|
53
|
|
|
|
|
|
else render_field(aTHX_ op, row_sv, t->mode, &buf, &pos, &alloc, i); |
|
1486
|
|
|
|
|
|
|
} |
|
1487
|
|
|
|
|
|
|
} |
|
1488
|
14
|
50
|
|
|
|
|
BUF_WRITE(t->footer, t->footer_len); |
|
|
|
0
|
|
|
|
|
|
|
1489
|
14
|
|
|
|
|
|
RBUF_FINISH(t); |
|
1490
|
14
|
|
|
|
|
|
SV *result = newSVpvn_utf8(buf, pos, 1); |
|
1491
|
14
|
|
|
|
|
|
LEAVE; /* runs the SAVEDESTRUCTOR_X frees for entries/all_keys/all_lens */ |
|
1492
|
14
|
|
|
|
|
|
return result; |
|
1493
|
|
|
|
|
|
|
} |
|
1494
|
|
|
|
|
|
|
|
|
1495
|
5
|
|
|
|
|
|
static SV *tpl_render_one(pTHX_ tpl_compiled *t, SV *row_sv) { |
|
1496
|
5
|
100
|
|
|
|
|
if (should_skip_row(aTHX_ row_sv, t)) |
|
1497
|
1
|
|
|
|
|
|
return newSVpvn_utf8("", 0, 1); |
|
1498
|
|
|
|
|
|
|
STRLEN alloc, pos; |
|
1499
|
|
|
|
|
|
|
char *buf; |
|
1500
|
4
|
50
|
|
|
|
|
RBUF_INIT(t, t->header_len + t->footer_len + 512); |
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1501
|
4
|
50
|
|
|
|
|
BUF_WRITE(t->header, t->header_len); |
|
|
|
0
|
|
|
|
|
|
|
1502
|
13
|
100
|
|
|
|
|
for (int j = 0; j < t->nops; j++) { |
|
1503
|
9
|
|
|
|
|
|
tpl_op *op = &t->ops[j]; |
|
1504
|
9
|
100
|
|
|
|
|
if (op->static_data) BUF_WRITE(op->static_data, op->static_len); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1505
|
5
|
|
|
|
|
|
else render_field(aTHX_ op, row_sv, t->mode, &buf, &pos, &alloc, 0); |
|
1506
|
|
|
|
|
|
|
} |
|
1507
|
4
|
50
|
|
|
|
|
BUF_WRITE(t->footer, t->footer_len); |
|
|
|
0
|
|
|
|
|
|
|
1508
|
4
|
|
|
|
|
|
RBUF_FINISH(t); |
|
1509
|
4
|
|
|
|
|
|
return newSVpvn_utf8(buf, pos, 1); |
|
1510
|
|
|
|
|
|
|
} |
|
1511
|
|
|
|
|
|
|
|
|
1512
|
2
|
|
|
|
|
|
static void tpl_render_to_fh(pTHX_ tpl_compiled *t, AV *rows, PerlIO *fh) { |
|
1513
|
2
|
|
|
|
|
|
SSize_t nrows = av_count(rows); |
|
1514
|
2
|
|
|
|
|
|
t->last_row_count = nrows; |
|
1515
|
2
|
|
|
|
|
|
STRLEN alloc = t->header_len + t->footer_len + nrows * 300 + 1; |
|
1516
|
2
|
|
|
|
|
|
char *buf = (char *)malloc(alloc); |
|
1517
|
2
|
50
|
|
|
|
|
if (!buf) croak("malloc"); |
|
1518
|
2
|
|
|
|
|
|
STRLEN pos = 0; |
|
1519
|
2
|
50
|
|
|
|
|
BUF_WRITE(t->header, t->header_len); |
|
|
|
0
|
|
|
|
|
|
|
1520
|
2
|
|
|
|
|
|
int first = 1; |
|
1521
|
205
|
100
|
|
|
|
|
for (SSize_t i = 0; i < nrows; i++) { |
|
1522
|
203
|
|
|
|
|
|
SV **rowref = av_fetch(rows, i, 0); |
|
1523
|
203
|
50
|
|
|
|
|
if (!rowref) continue; |
|
1524
|
203
|
50
|
|
|
|
|
if (should_skip_row(aTHX_ *rowref, t)) continue; |
|
1525
|
203
|
100
|
|
|
|
|
if (!first && t->sep_len) BUF_WRITE(t->sep, t->sep_len); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1526
|
203
|
|
|
|
|
|
first = 0; |
|
1527
|
406
|
100
|
|
|
|
|
for (int j = 0; j < t->nops; j++) { |
|
1528
|
203
|
|
|
|
|
|
tpl_op *op = &t->ops[j]; |
|
1529
|
203
|
50
|
|
|
|
|
if (op->static_data) BUF_WRITE(op->static_data, op->static_len); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1530
|
203
|
|
|
|
|
|
else render_field(aTHX_ op, *rowref, t->mode, &buf, &pos, &alloc, i); |
|
1531
|
|
|
|
|
|
|
} |
|
1532
|
203
|
100
|
|
|
|
|
if (pos > 65536) { PerlIO_write(fh, buf, pos); pos = 0; } |
|
1533
|
|
|
|
|
|
|
} |
|
1534
|
2
|
50
|
|
|
|
|
BUF_WRITE(t->footer, t->footer_len); |
|
|
|
0
|
|
|
|
|
|
|
1535
|
2
|
50
|
|
|
|
|
if (pos) PerlIO_write(fh, buf, pos); |
|
1536
|
2
|
|
|
|
|
|
free(buf); |
|
1537
|
2
|
|
|
|
|
|
} |
|
1538
|
|
|
|
|
|
|
|
|
1539
|
8
|
|
|
|
|
|
static SV *tpl_render_cb(pTHX_ tpl_compiled *t, SV *cb, PerlIO *fh) { |
|
1540
|
|
|
|
|
|
|
STRLEN alloc, pos; |
|
1541
|
|
|
|
|
|
|
char *buf; |
|
1542
|
8
|
|
|
|
|
|
int use_fh = (fh != NULL); |
|
1543
|
|
|
|
|
|
|
|
|
1544
|
8
|
100
|
|
|
|
|
if (use_fh) { |
|
1545
|
3
|
|
|
|
|
|
alloc = 65536; |
|
1546
|
3
|
|
|
|
|
|
buf = (char *)malloc(alloc); |
|
1547
|
3
|
50
|
|
|
|
|
if (!buf) croak("malloc"); |
|
1548
|
|
|
|
|
|
|
} else { |
|
1549
|
5
|
50
|
|
|
|
|
RBUF_INIT(t, 4096); |
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1550
|
|
|
|
|
|
|
} |
|
1551
|
8
|
|
|
|
|
|
pos = 0; |
|
1552
|
|
|
|
|
|
|
|
|
1553
|
8
|
50
|
|
|
|
|
BUF_WRITE(t->header, t->header_len); |
|
|
|
0
|
|
|
|
|
|
|
1554
|
8
|
|
|
|
|
|
SSize_t row_idx = 0; |
|
1555
|
8
|
|
|
|
|
|
int first = 1; |
|
1556
|
8
|
|
|
|
|
|
t->last_row_count = 0; |
|
1557
|
|
|
|
|
|
|
|
|
1558
|
75
|
|
|
|
|
|
while (1) { |
|
1559
|
83
|
|
|
|
|
|
dSP; |
|
1560
|
83
|
|
|
|
|
|
ENTER; SAVETMPS; |
|
1561
|
83
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
1562
|
83
|
|
|
|
|
|
PUTBACK; |
|
1563
|
83
|
|
|
|
|
|
int count = call_sv(cb, G_SCALAR | G_EVAL); |
|
1564
|
83
|
|
|
|
|
|
SPAGAIN; |
|
1565
|
83
|
50
|
|
|
|
|
if (SvTRUE(ERRSV)) { |
|
|
|
100
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
/* The row callback died: free our render buffer (the fh path's |
|
1567
|
|
|
|
|
|
|
own malloc, or the detached render_buf) before propagating, so |
|
1568
|
|
|
|
|
|
|
a dying callback can't leak it. */ |
|
1569
|
2
|
50
|
|
|
|
|
SV *err = newSVsv(ERRSV); |
|
1570
|
2
|
50
|
|
|
|
|
PUTBACK; FREETMPS; LEAVE; |
|
1571
|
2
|
|
|
|
|
|
free(buf); |
|
1572
|
2
|
|
|
|
|
|
croak_sv(sv_2mortal(err)); |
|
1573
|
|
|
|
|
|
|
} |
|
1574
|
81
|
|
|
|
|
|
SV *row_sv = NULL; |
|
1575
|
81
|
50
|
|
|
|
|
if (count > 0) row_sv = POPs; |
|
1576
|
81
|
50
|
|
|
|
|
if (!row_sv || !SvOK(row_sv) || !SvROK(row_sv)) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1577
|
6
|
50
|
|
|
|
|
PUTBACK; FREETMPS; LEAVE; |
|
1578
|
6
|
|
|
|
|
|
break; |
|
1579
|
|
|
|
|
|
|
} |
|
1580
|
75
|
|
|
|
|
|
SvREFCNT_inc_simple_void_NN(row_sv); |
|
1581
|
75
|
50
|
|
|
|
|
PUTBACK; FREETMPS; LEAVE; |
|
1582
|
|
|
|
|
|
|
|
|
1583
|
75
|
100
|
|
|
|
|
if (!should_skip_row(aTHX_ row_sv, t)) { |
|
1584
|
73
|
100
|
|
|
|
|
if (!first && t->sep_len) BUF_WRITE(t->sep, t->sep_len); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1585
|
73
|
|
|
|
|
|
first = 0; |
|
1586
|
282
|
100
|
|
|
|
|
for (int j = 0; j < t->nops; j++) { |
|
1587
|
209
|
|
|
|
|
|
tpl_op *op = &t->ops[j]; |
|
1588
|
209
|
100
|
|
|
|
|
if (op->static_data) BUF_WRITE(op->static_data, op->static_len); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1589
|
76
|
|
|
|
|
|
else render_field(aTHX_ op, row_sv, t->mode, &buf, &pos, &alloc, row_idx); |
|
1590
|
|
|
|
|
|
|
} |
|
1591
|
73
|
100
|
|
|
|
|
if (use_fh && pos > 65536) { PerlIO_write(fh, buf, pos); pos = 0; } |
|
|
|
50
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
} |
|
1593
|
75
|
|
|
|
|
|
SvREFCNT_dec(row_sv); |
|
1594
|
75
|
|
|
|
|
|
row_idx++; |
|
1595
|
75
|
|
|
|
|
|
t->last_row_count = row_idx; |
|
1596
|
|
|
|
|
|
|
} |
|
1597
|
|
|
|
|
|
|
|
|
1598
|
6
|
50
|
|
|
|
|
BUF_WRITE(t->footer, t->footer_len); |
|
|
|
0
|
|
|
|
|
|
|
1599
|
|
|
|
|
|
|
|
|
1600
|
6
|
100
|
|
|
|
|
if (use_fh) { |
|
1601
|
2
|
50
|
|
|
|
|
if (pos) PerlIO_write(fh, buf, pos); |
|
1602
|
2
|
|
|
|
|
|
free(buf); |
|
1603
|
2
|
|
|
|
|
|
return &PL_sv_undef; |
|
1604
|
|
|
|
|
|
|
} else { |
|
1605
|
4
|
|
|
|
|
|
RBUF_FINISH(t); |
|
1606
|
4
|
|
|
|
|
|
return newSVpvn_utf8(buf, pos, 1); |
|
1607
|
|
|
|
|
|
|
} |
|
1608
|
|
|
|
|
|
|
} |
|
1609
|
|
|
|
|
|
|
|
|
1610
|
|
|
|
|
|
|
/* columns introspection */ |
|
1611
|
2
|
|
|
|
|
|
static AV *tpl_columns(pTHX_ tpl_compiled *t) { |
|
1612
|
2
|
|
|
|
|
|
AV *cols = newAV(); |
|
1613
|
8
|
100
|
|
|
|
|
for (int i = 0; i < t->nops; i++) { |
|
1614
|
6
|
|
|
|
|
|
tpl_op *op = &t->ops[i]; |
|
1615
|
6
|
100
|
|
|
|
|
if (op->chain && !op->is_rownum) { |
|
|
|
50
|
|
|
|
|
|
|
1616
|
4
|
100
|
|
|
|
|
if (op->key) |
|
1617
|
2
|
|
|
|
|
|
av_push(cols, newSVpvn(op->key, op->key_len)); |
|
1618
|
|
|
|
|
|
|
else |
|
1619
|
2
|
|
|
|
|
|
av_push(cols, newSViv(op->col)); |
|
1620
|
|
|
|
|
|
|
} |
|
1621
|
|
|
|
|
|
|
} |
|
1622
|
2
|
|
|
|
|
|
return cols; |
|
1623
|
|
|
|
|
|
|
} |
|
1624
|
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
|
|
1626
|
|
|
|
|
|
|
MODULE = Text::Stencil PACKAGE = Text::Stencil |
|
1627
|
|
|
|
|
|
|
|
|
1628
|
|
|
|
|
|
|
BOOT: |
|
1629
|
4
|
|
|
|
|
|
init_html_tables(); |
|
1630
|
|
|
|
|
|
|
|
|
1631
|
|
|
|
|
|
|
SV * |
|
1632
|
|
|
|
|
|
|
new(class, ...) |
|
1633
|
|
|
|
|
|
|
const char *class |
|
1634
|
|
|
|
|
|
|
CODE: |
|
1635
|
|
|
|
|
|
|
{ |
|
1636
|
206
|
|
|
|
|
|
const char *header = "", *row = "", *footer = "", *sep = ""; |
|
1637
|
206
|
|
|
|
|
|
STRLEN hlen = 0, rlen = 0, flen = 0, slen = 0; |
|
1638
|
206
|
|
|
|
|
|
char esc = 0; |
|
1639
|
206
|
|
|
|
|
|
SV *skip_if_sv = NULL, *skip_unless_sv = NULL; |
|
1640
|
|
|
|
|
|
|
/* shorthand: Text::Stencil->new($row_template) */ |
|
1641
|
206
|
100
|
|
|
|
|
if (items == 2 && SvPOK(ST(1))) { |
|
|
|
50
|
|
|
|
|
|
|
1642
|
2
|
|
|
|
|
|
row = SvPV(ST(1), rlen); |
|
1643
|
|
|
|
|
|
|
} else { |
|
1644
|
204
|
50
|
|
|
|
|
if (items % 2 == 0) croak("Odd number of arguments"); |
|
1645
|
474
|
100
|
|
|
|
|
for (int i = 1; i < items; i += 2) { |
|
1646
|
270
|
|
|
|
|
|
const char *key = SvPV_nolen(ST(i)); |
|
1647
|
270
|
|
|
|
|
|
SV *val = ST(i + 1); |
|
1648
|
270
|
100
|
|
|
|
|
if (strcmp(key, "header") == 0) header = SvPV(val, hlen); |
|
1649
|
260
|
100
|
|
|
|
|
else if (strcmp(key, "row") == 0) row = SvPV(val, rlen); |
|
1650
|
56
|
100
|
|
|
|
|
else if (strcmp(key, "footer") == 0) footer = SvPV(val, flen); |
|
1651
|
46
|
100
|
|
|
|
|
else if (strcmp(key, "separator") == 0) sep = SvPV(val, slen); |
|
1652
|
16
|
100
|
|
|
|
|
else if (strcmp(key, "escape_char") == 0) { STRLEN el; const char *ev = SvPV(val, el); if (el) esc = ev[0]; } |
|
|
|
50
|
|
|
|
|
|
|
1653
|
11
|
100
|
|
|
|
|
else if (strcmp(key, "skip_if") == 0) skip_if_sv = val; |
|
1654
|
4
|
50
|
|
|
|
|
else if (strcmp(key, "skip_unless") == 0) skip_unless_sv = val; |
|
1655
|
|
|
|
|
|
|
} |
|
1656
|
|
|
|
|
|
|
} |
|
1657
|
206
|
|
|
|
|
|
tpl_compiled *t = tpl_compile(aTHX_ header, hlen, row, rlen, footer, flen, sep, slen, esc); |
|
1658
|
204
|
100
|
|
|
|
|
if (skip_if_sv) { |
|
1659
|
7
|
|
|
|
|
|
t->has_skip_if = 1; |
|
1660
|
7
|
100
|
|
|
|
|
if (SvIOK(skip_if_sv) || looks_like_number(skip_if_sv)) { |
|
|
|
50
|
|
|
|
|
|
|
1661
|
6
|
|
|
|
|
|
t->skip_if_col = SvIV(skip_if_sv); |
|
1662
|
|
|
|
|
|
|
} else { |
|
1663
|
|
|
|
|
|
|
STRLEN kl; |
|
1664
|
1
|
|
|
|
|
|
const char *ks = SvPV(skip_if_sv, kl); |
|
1665
|
1
|
|
|
|
|
|
t->skip_if_key = (char *)malloc(kl + 1); |
|
1666
|
1
|
|
|
|
|
|
memcpy(t->skip_if_key, ks, kl); |
|
1667
|
1
|
|
|
|
|
|
t->skip_if_key[kl] = '\0'; |
|
1668
|
1
|
|
|
|
|
|
t->skip_if_key_len = kl; |
|
1669
|
|
|
|
|
|
|
} |
|
1670
|
|
|
|
|
|
|
} |
|
1671
|
204
|
100
|
|
|
|
|
if (skip_unless_sv) { |
|
1672
|
4
|
|
|
|
|
|
t->has_skip_unless = 1; |
|
1673
|
4
|
100
|
|
|
|
|
if (SvIOK(skip_unless_sv) || looks_like_number(skip_unless_sv)) { |
|
|
|
50
|
|
|
|
|
|
|
1674
|
3
|
|
|
|
|
|
t->skip_unless_col = SvIV(skip_unless_sv); |
|
1675
|
|
|
|
|
|
|
} else { |
|
1676
|
|
|
|
|
|
|
STRLEN kl; |
|
1677
|
1
|
|
|
|
|
|
const char *ks = SvPV(skip_unless_sv, kl); |
|
1678
|
1
|
|
|
|
|
|
t->skip_unless_key = (char *)malloc(kl + 1); |
|
1679
|
1
|
|
|
|
|
|
memcpy(t->skip_unless_key, ks, kl); |
|
1680
|
1
|
|
|
|
|
|
t->skip_unless_key[kl] = '\0'; |
|
1681
|
1
|
|
|
|
|
|
t->skip_unless_key_len = kl; |
|
1682
|
|
|
|
|
|
|
} |
|
1683
|
|
|
|
|
|
|
} |
|
1684
|
204
|
|
|
|
|
|
SV *obj = newSViv(PTR2IV(t)); |
|
1685
|
204
|
|
|
|
|
|
SV *ref = newRV_noinc(obj); |
|
1686
|
204
|
|
|
|
|
|
sv_bless(ref, gv_stashpv(class, GV_ADD)); |
|
1687
|
204
|
|
|
|
|
|
RETVAL = ref; |
|
1688
|
|
|
|
|
|
|
} |
|
1689
|
|
|
|
|
|
|
OUTPUT: |
|
1690
|
|
|
|
|
|
|
RETVAL |
|
1691
|
|
|
|
|
|
|
|
|
1692
|
|
|
|
|
|
|
SV * |
|
1693
|
|
|
|
|
|
|
render(self, rows) |
|
1694
|
|
|
|
|
|
|
SV *self |
|
1695
|
|
|
|
|
|
|
AV *rows |
|
1696
|
|
|
|
|
|
|
CODE: |
|
1697
|
|
|
|
|
|
|
{ |
|
1698
|
185
|
|
|
|
|
|
tpl_compiled *t = INT2PTR(tpl_compiled *, SvIV(SvRV(self))); |
|
1699
|
185
|
|
|
|
|
|
RETVAL = tpl_render(aTHX_ t, rows); |
|
1700
|
|
|
|
|
|
|
} |
|
1701
|
|
|
|
|
|
|
OUTPUT: |
|
1702
|
|
|
|
|
|
|
RETVAL |
|
1703
|
|
|
|
|
|
|
|
|
1704
|
|
|
|
|
|
|
SV * |
|
1705
|
|
|
|
|
|
|
render_sorted(self, rows, sort_by, ...) |
|
1706
|
|
|
|
|
|
|
SV *self |
|
1707
|
|
|
|
|
|
|
AV *rows |
|
1708
|
|
|
|
|
|
|
SV *sort_by |
|
1709
|
|
|
|
|
|
|
CODE: |
|
1710
|
|
|
|
|
|
|
{ |
|
1711
|
14
|
|
|
|
|
|
tpl_compiled *t = INT2PTR(tpl_compiled *, SvIV(SvRV(self))); |
|
1712
|
14
|
|
|
|
|
|
int descending = 0, numeric = 0; |
|
1713
|
14
|
100
|
|
|
|
|
if (items > 3 && SvROK(ST(3)) && SvTYPE(SvRV(ST(3))) == SVt_PVHV) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1714
|
5
|
|
|
|
|
|
HV *opts = (HV *)SvRV(ST(3)); |
|
1715
|
|
|
|
|
|
|
SV **sv; |
|
1716
|
5
|
|
|
|
|
|
sv = hv_fetchs(opts, "descending", 0); |
|
1717
|
5
|
100
|
|
|
|
|
if (sv && SvTRUE(*sv)) descending = 1; |
|
|
|
50
|
|
|
|
|
|
|
1718
|
5
|
|
|
|
|
|
sv = hv_fetchs(opts, "numeric", 0); |
|
1719
|
5
|
100
|
|
|
|
|
if (sv && SvTRUE(*sv)) numeric = 1; |
|
|
|
50
|
|
|
|
|
|
|
1720
|
|
|
|
|
|
|
} |
|
1721
|
17
|
100
|
|
|
|
|
if (SvROK(sort_by) && SvTYPE(SvRV(sort_by)) == SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
1722
|
3
|
|
|
|
|
|
AV *sort_av = (AV *)SvRV(sort_by); |
|
1723
|
3
|
|
|
|
|
|
int nsort = (int)av_count(sort_av); |
|
1724
|
3
|
50
|
|
|
|
|
if (nsort == 0) { |
|
1725
|
0
|
|
|
|
|
|
RETVAL = tpl_render(aTHX_ t, rows); |
|
1726
|
|
|
|
|
|
|
} else { |
|
1727
|
3
|
|
|
|
|
|
SV **first = av_fetch(sort_av, 0, 0); |
|
1728
|
3
|
50
|
|
|
|
|
int use_keys = first && !SvIOK(*first) && !looks_like_number(*first); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1729
|
3
|
100
|
|
|
|
|
if (use_keys) { |
|
1730
|
2
|
|
|
|
|
|
const char **skeys = (const char **)malloc(nsort * sizeof(char *)); |
|
1731
|
2
|
|
|
|
|
|
STRLEN *sklens = (STRLEN *)malloc(nsort * sizeof(STRLEN)); |
|
1732
|
5
|
100
|
|
|
|
|
for (int i = 0; i < nsort; i++) { |
|
1733
|
3
|
|
|
|
|
|
SV **el = av_fetch(sort_av, i, 0); |
|
1734
|
3
|
50
|
|
|
|
|
skeys[i] = el ? SvPV(*el, sklens[i]) : ""; |
|
1735
|
3
|
50
|
|
|
|
|
if (!el) sklens[i] = 0; |
|
1736
|
|
|
|
|
|
|
} |
|
1737
|
2
|
|
|
|
|
|
RETVAL = tpl_render_sorted(aTHX_ t, rows, NULL, skeys, sklens, nsort, descending, numeric); |
|
1738
|
2
|
|
|
|
|
|
free(skeys); free(sklens); |
|
1739
|
|
|
|
|
|
|
} else { |
|
1740
|
1
|
|
|
|
|
|
int *scols = (int *)malloc(nsort * sizeof(int)); |
|
1741
|
3
|
100
|
|
|
|
|
for (int i = 0; i < nsort; i++) { |
|
1742
|
2
|
|
|
|
|
|
SV **el = av_fetch(sort_av, i, 0); |
|
1743
|
2
|
50
|
|
|
|
|
scols[i] = el ? (int)SvIV(*el) : 0; |
|
1744
|
|
|
|
|
|
|
} |
|
1745
|
1
|
|
|
|
|
|
RETVAL = tpl_render_sorted(aTHX_ t, rows, scols, NULL, NULL, nsort, descending, numeric); |
|
1746
|
1
|
|
|
|
|
|
free(scols); |
|
1747
|
|
|
|
|
|
|
} |
|
1748
|
|
|
|
|
|
|
} |
|
1749
|
18
|
100
|
|
|
|
|
} else if (SvIOK(sort_by) || looks_like_number(sort_by)) { |
|
|
|
50
|
|
|
|
|
|
|
1750
|
7
|
|
|
|
|
|
int col = (int)SvIV(sort_by); |
|
1751
|
7
|
|
|
|
|
|
RETVAL = tpl_render_sorted(aTHX_ t, rows, &col, NULL, NULL, 1, descending, numeric); |
|
1752
|
|
|
|
|
|
|
} else { |
|
1753
|
|
|
|
|
|
|
STRLEN klen; |
|
1754
|
4
|
|
|
|
|
|
const char *key = SvPV(sort_by, klen); |
|
1755
|
4
|
50
|
|
|
|
|
if (klen > 1 && key[0] == '-') { key++; klen--; descending = 1; } |
|
|
|
100
|
|
|
|
|
|
|
1756
|
4
|
|
|
|
|
|
RETVAL = tpl_render_sorted(aTHX_ t, rows, NULL, &key, &klen, 1, descending, numeric); |
|
1757
|
|
|
|
|
|
|
} |
|
1758
|
|
|
|
|
|
|
} |
|
1759
|
|
|
|
|
|
|
OUTPUT: |
|
1760
|
|
|
|
|
|
|
RETVAL |
|
1761
|
|
|
|
|
|
|
|
|
1762
|
|
|
|
|
|
|
SV * |
|
1763
|
|
|
|
|
|
|
render_one(self, row) |
|
1764
|
|
|
|
|
|
|
SV *self |
|
1765
|
|
|
|
|
|
|
SV *row |
|
1766
|
|
|
|
|
|
|
CODE: |
|
1767
|
|
|
|
|
|
|
{ |
|
1768
|
5
|
|
|
|
|
|
tpl_compiled *t = INT2PTR(tpl_compiled *, SvIV(SvRV(self))); |
|
1769
|
5
|
|
|
|
|
|
RETVAL = tpl_render_one(aTHX_ t, row); |
|
1770
|
|
|
|
|
|
|
} |
|
1771
|
|
|
|
|
|
|
OUTPUT: |
|
1772
|
|
|
|
|
|
|
RETVAL |
|
1773
|
|
|
|
|
|
|
|
|
1774
|
|
|
|
|
|
|
void |
|
1775
|
|
|
|
|
|
|
render_to_fh(self, fh, rows) |
|
1776
|
|
|
|
|
|
|
SV *self |
|
1777
|
|
|
|
|
|
|
PerlIO *fh |
|
1778
|
|
|
|
|
|
|
AV *rows |
|
1779
|
|
|
|
|
|
|
CODE: |
|
1780
|
|
|
|
|
|
|
{ |
|
1781
|
2
|
|
|
|
|
|
tpl_compiled *t = INT2PTR(tpl_compiled *, SvIV(SvRV(self))); |
|
1782
|
2
|
|
|
|
|
|
tpl_render_to_fh(aTHX_ t, rows, fh); |
|
1783
|
|
|
|
|
|
|
} |
|
1784
|
|
|
|
|
|
|
|
|
1785
|
|
|
|
|
|
|
SV * |
|
1786
|
|
|
|
|
|
|
render_cb(self, cb, ...) |
|
1787
|
|
|
|
|
|
|
SV *self |
|
1788
|
|
|
|
|
|
|
SV *cb |
|
1789
|
|
|
|
|
|
|
CODE: |
|
1790
|
|
|
|
|
|
|
{ |
|
1791
|
8
|
|
|
|
|
|
tpl_compiled *t = INT2PTR(tpl_compiled *, SvIV(SvRV(self))); |
|
1792
|
8
|
50
|
|
|
|
|
if (!SvROK(cb) || SvTYPE(SvRV(cb)) != SVt_PVCV) |
|
|
|
50
|
|
|
|
|
|
|
1793
|
0
|
|
|
|
|
|
croak("render_cb: second argument must be a coderef"); |
|
1794
|
8
|
|
|
|
|
|
PerlIO *fh = NULL; |
|
1795
|
8
|
100
|
|
|
|
|
if (items > 2) { |
|
1796
|
3
|
|
|
|
|
|
fh = IoIFP(sv_2io(ST(2))); |
|
1797
|
|
|
|
|
|
|
} |
|
1798
|
8
|
|
|
|
|
|
RETVAL = tpl_render_cb(aTHX_ t, cb, fh); |
|
1799
|
|
|
|
|
|
|
} |
|
1800
|
|
|
|
|
|
|
OUTPUT: |
|
1801
|
|
|
|
|
|
|
RETVAL |
|
1802
|
|
|
|
|
|
|
|
|
1803
|
|
|
|
|
|
|
AV * |
|
1804
|
|
|
|
|
|
|
columns(self) |
|
1805
|
|
|
|
|
|
|
SV *self |
|
1806
|
|
|
|
|
|
|
CODE: |
|
1807
|
|
|
|
|
|
|
{ |
|
1808
|
2
|
|
|
|
|
|
tpl_compiled *t = INT2PTR(tpl_compiled *, SvIV(SvRV(self))); |
|
1809
|
2
|
|
|
|
|
|
RETVAL = tpl_columns(aTHX_ t); |
|
1810
|
|
|
|
|
|
|
} |
|
1811
|
|
|
|
|
|
|
OUTPUT: |
|
1812
|
|
|
|
|
|
|
RETVAL |
|
1813
|
|
|
|
|
|
|
|
|
1814
|
|
|
|
|
|
|
IV |
|
1815
|
|
|
|
|
|
|
row_count(self) |
|
1816
|
|
|
|
|
|
|
SV *self |
|
1817
|
|
|
|
|
|
|
CODE: |
|
1818
|
|
|
|
|
|
|
{ |
|
1819
|
2
|
|
|
|
|
|
tpl_compiled *t = INT2PTR(tpl_compiled *, SvIV(SvRV(self))); |
|
1820
|
2
|
50
|
|
|
|
|
RETVAL = (IV)t->last_row_count; |
|
1821
|
|
|
|
|
|
|
} |
|
1822
|
|
|
|
|
|
|
OUTPUT: |
|
1823
|
|
|
|
|
|
|
RETVAL |
|
1824
|
|
|
|
|
|
|
|
|
1825
|
|
|
|
|
|
|
SV * |
|
1826
|
|
|
|
|
|
|
clone(self, ...) |
|
1827
|
|
|
|
|
|
|
SV *self |
|
1828
|
|
|
|
|
|
|
CODE: |
|
1829
|
|
|
|
|
|
|
{ |
|
1830
|
2
|
|
|
|
|
|
tpl_compiled *orig = INT2PTR(tpl_compiled *, SvIV(SvRV(self))); |
|
1831
|
2
|
50
|
|
|
|
|
if (items % 2 == 0) croak("Odd number of arguments"); |
|
1832
|
2
|
|
|
|
|
|
const char *row = NULL; STRLEN rlen = 0; |
|
1833
|
2
|
|
|
|
|
|
const char *sep = NULL; STRLEN slen = 0; |
|
1834
|
4
|
100
|
|
|
|
|
for (int i = 1; i < items; i += 2) { |
|
1835
|
2
|
|
|
|
|
|
const char *key = SvPV_nolen(ST(i)); |
|
1836
|
2
|
|
|
|
|
|
SV *val = ST(i + 1); |
|
1837
|
2
|
50
|
|
|
|
|
if (strcmp(key, "row") == 0) row = SvPV(val, rlen); |
|
1838
|
0
|
0
|
|
|
|
|
else if (strcmp(key, "separator") == 0) sep = SvPV(val, slen); |
|
1839
|
|
|
|
|
|
|
} |
|
1840
|
2
|
50
|
|
|
|
|
if (!row) croak("clone requires 'row' argument"); |
|
1841
|
2
|
50
|
|
|
|
|
tpl_compiled *t = tpl_compile(aTHX_ |
|
1842
|
2
|
|
|
|
|
|
orig->header, orig->header_len, |
|
1843
|
|
|
|
|
|
|
row, rlen, |
|
1844
|
2
|
|
|
|
|
|
orig->footer, orig->footer_len, |
|
1845
|
|
|
|
|
|
|
sep ? sep : orig->sep, sep ? slen : orig->sep_len, |
|
1846
|
2
|
50
|
|
|
|
|
orig->escape_char); |
|
1847
|
|
|
|
|
|
|
/* copy skip conditions from original */ |
|
1848
|
2
|
|
|
|
|
|
t->has_skip_if = orig->has_skip_if; |
|
1849
|
2
|
|
|
|
|
|
t->skip_if_col = orig->skip_if_col; |
|
1850
|
2
|
50
|
|
|
|
|
if (orig->skip_if_key) { |
|
1851
|
0
|
|
|
|
|
|
t->skip_if_key = (char *)malloc(orig->skip_if_key_len + 1); |
|
1852
|
0
|
|
|
|
|
|
memcpy(t->skip_if_key, orig->skip_if_key, orig->skip_if_key_len + 1); |
|
1853
|
0
|
|
|
|
|
|
t->skip_if_key_len = orig->skip_if_key_len; |
|
1854
|
|
|
|
|
|
|
} |
|
1855
|
2
|
|
|
|
|
|
t->has_skip_unless = orig->has_skip_unless; |
|
1856
|
2
|
|
|
|
|
|
t->skip_unless_col = orig->skip_unless_col; |
|
1857
|
2
|
50
|
|
|
|
|
if (orig->skip_unless_key) { |
|
1858
|
0
|
|
|
|
|
|
t->skip_unless_key = (char *)malloc(orig->skip_unless_key_len + 1); |
|
1859
|
0
|
|
|
|
|
|
memcpy(t->skip_unless_key, orig->skip_unless_key, orig->skip_unless_key_len + 1); |
|
1860
|
0
|
|
|
|
|
|
t->skip_unless_key_len = orig->skip_unless_key_len; |
|
1861
|
|
|
|
|
|
|
} |
|
1862
|
2
|
|
|
|
|
|
SV *obj = newSViv(PTR2IV(t)); |
|
1863
|
2
|
|
|
|
|
|
SV *ref = newRV_noinc(obj); |
|
1864
|
2
|
|
|
|
|
|
sv_bless(ref, SvSTASH(SvRV(self))); |
|
1865
|
2
|
|
|
|
|
|
RETVAL = ref; |
|
1866
|
|
|
|
|
|
|
} |
|
1867
|
|
|
|
|
|
|
OUTPUT: |
|
1868
|
|
|
|
|
|
|
RETVAL |
|
1869
|
|
|
|
|
|
|
|
|
1870
|
|
|
|
|
|
|
void |
|
1871
|
|
|
|
|
|
|
DESTROY(self) |
|
1872
|
|
|
|
|
|
|
SV *self |
|
1873
|
|
|
|
|
|
|
CODE: |
|
1874
|
|
|
|
|
|
|
{ |
|
1875
|
206
|
|
|
|
|
|
tpl_compiled *t = INT2PTR(tpl_compiled *, SvIV(SvRV(self))); |
|
1876
|
206
|
|
|
|
|
|
tpl_free(t); |
|
1877
|
|
|
|
|
|
|
} |