| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#define PERL_NO_GET_CONTEXT |
|
2
|
|
|
|
|
|
|
#include "EXTERN.h" |
|
3
|
|
|
|
|
|
|
#include "perl.h" |
|
4
|
|
|
|
|
|
|
#include "XSUB.h" |
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
/* okchr = %x21 / %x23-3c / %x3e-5b / %x5d-7e ; VCHAR minus \ and " and = */ |
|
7
|
|
|
|
|
|
|
PERL_STATIC_INLINE int |
|
8
|
1180
|
|
|
|
|
|
is_okchr(unsigned char c) |
|
9
|
|
|
|
|
|
|
{ |
|
10
|
1180
|
100
|
|
|
|
|
return (c == 0x21 || |
|
11
|
1180
|
100
|
|
|
|
|
(c >= 0x23 && c <= 0x3C) || |
|
|
|
100
|
|
|
|
|
|
|
12
|
3354
|
50
|
|
|
|
|
(c >= 0x3E && c <= 0x5B) || |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
13
|
994
|
100
|
|
|
|
|
(c >= 0x5D && c <= 0x7E)); |
|
14
|
|
|
|
|
|
|
} |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
/* Check if string can be used as a bare logfmt value (matches KEY_RE) */ |
|
17
|
|
|
|
|
|
|
static int |
|
18
|
143
|
|
|
|
|
|
is_bare_value(const char *s, STRLEN len) |
|
19
|
|
|
|
|
|
|
{ |
|
20
|
|
|
|
|
|
|
STRLEN i; |
|
21
|
143
|
100
|
|
|
|
|
if (len == 0) |
|
22
|
2
|
|
|
|
|
|
return 0; |
|
23
|
728
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
24
|
630
|
100
|
|
|
|
|
if (!is_okchr((unsigned char)s[i])) |
|
25
|
43
|
|
|
|
|
|
return 0; |
|
26
|
|
|
|
|
|
|
} |
|
27
|
98
|
|
|
|
|
|
return 1; |
|
28
|
|
|
|
|
|
|
} |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
/* |
|
31
|
|
|
|
|
|
|
* Check if a Unicode codepoint needs \x{XX} escaping. |
|
32
|
|
|
|
|
|
|
* This is called AFTER \, ", \t, \n, \r are handled. |
|
33
|
|
|
|
|
|
|
* Matches Perl's [\pC\v] — General Category C (Cc, Cf, Co, Cs) plus |
|
34
|
|
|
|
|
|
|
* vertical whitespace characters. |
|
35
|
|
|
|
|
|
|
*/ |
|
36
|
|
|
|
|
|
|
static int |
|
37
|
7
|
|
|
|
|
|
needs_escape(UV cp) |
|
38
|
|
|
|
|
|
|
{ |
|
39
|
|
|
|
|
|
|
/* Cc: C0 controls (0x00-0x1F) — includes \t, \n, \r but those are |
|
40
|
|
|
|
|
|
|
* already handled before this function is called */ |
|
41
|
7
|
50
|
|
|
|
|
if (cp <= 0x1F) |
|
42
|
0
|
|
|
|
|
|
return 1; |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
/* DEL */ |
|
45
|
7
|
50
|
|
|
|
|
if (cp == 0x7F) |
|
46
|
0
|
|
|
|
|
|
return 1; |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
/* C1 controls (0x80-0x9F), includes NEL (0x85) */ |
|
49
|
7
|
50
|
|
|
|
|
if (cp >= 0x80 && cp <= 0x9F) |
|
|
|
50
|
|
|
|
|
|
|
50
|
0
|
|
|
|
|
|
return 1; |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
/* Vertical whitespace not in Cc: LINE SEPARATOR, PARAGRAPH SEPARATOR */ |
|
53
|
7
|
100
|
|
|
|
|
if (cp == 0x2028 || cp == 0x2029) |
|
|
|
50
|
|
|
|
|
|
|
54
|
2
|
|
|
|
|
|
return 1; |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
/* Cf (format) characters — comprehensive list */ |
|
57
|
5
|
50
|
|
|
|
|
if (cp == 0x00AD) return 1; /* SOFT HYPHEN */ |
|
58
|
5
|
100
|
|
|
|
|
if (cp >= 0x0600 && cp <= 0x0605) return 1; |
|
|
|
50
|
|
|
|
|
|
|
59
|
5
|
50
|
|
|
|
|
if (cp == 0x061C) return 1; |
|
60
|
5
|
50
|
|
|
|
|
if (cp == 0x06DD) return 1; |
|
61
|
5
|
50
|
|
|
|
|
if (cp == 0x070F) return 1; |
|
62
|
5
|
50
|
|
|
|
|
if (cp == 0x08E2) return 1; |
|
63
|
5
|
50
|
|
|
|
|
if (cp == 0x180E) return 1; |
|
64
|
5
|
100
|
|
|
|
|
if (cp >= 0x200B && cp <= 0x200F) return 1; /* includes ZWJ (0x200D) */ |
|
|
|
50
|
|
|
|
|
|
|
65
|
3
|
50
|
|
|
|
|
if (cp >= 0x202A && cp <= 0x202E) return 1; |
|
|
|
0
|
|
|
|
|
|
|
66
|
3
|
50
|
|
|
|
|
if (cp >= 0x2060 && cp <= 0x2064) return 1; |
|
|
|
0
|
|
|
|
|
|
|
67
|
3
|
50
|
|
|
|
|
if (cp >= 0x2066 && cp <= 0x206F) return 1; |
|
|
|
0
|
|
|
|
|
|
|
68
|
3
|
50
|
|
|
|
|
if (cp == 0xFEFF) return 1; /* BOM */ |
|
69
|
3
|
50
|
|
|
|
|
if (cp >= 0xFFF9 && cp <= 0xFFFB) return 1; |
|
|
|
0
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
/* Co (private use) */ |
|
72
|
3
|
50
|
|
|
|
|
if (cp >= 0xE000 && cp <= 0xF8FF) return 1; |
|
|
|
0
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
/* Cs (surrogates) — shouldn't appear in valid strings */ |
|
75
|
3
|
50
|
|
|
|
|
if (cp >= 0xD800 && cp <= 0xDFFF) return 1; |
|
|
|
0
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
/* Higher plane Cf */ |
|
78
|
3
|
50
|
|
|
|
|
if (cp == 0x110BD || cp == 0x110CD) return 1; |
|
|
|
50
|
|
|
|
|
|
|
79
|
3
|
50
|
|
|
|
|
if (cp >= 0x13430 && cp <= 0x1343F) return 1; |
|
|
|
0
|
|
|
|
|
|
|
80
|
3
|
50
|
|
|
|
|
if (cp >= 0x1BCA0 && cp <= 0x1BCA3) return 1; |
|
|
|
0
|
|
|
|
|
|
|
81
|
3
|
50
|
|
|
|
|
if (cp >= 0x1D173 && cp <= 0x1D17A) return 1; |
|
|
|
0
|
|
|
|
|
|
|
82
|
3
|
50
|
|
|
|
|
if (cp == 0xE0001) return 1; |
|
83
|
3
|
50
|
|
|
|
|
if (cp >= 0xE0020 && cp <= 0xE007F) return 1; |
|
|
|
0
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
/* Higher plane Co (private use) */ |
|
86
|
3
|
50
|
|
|
|
|
if (cp >= 0xF0000 && cp <= 0xFFFFD) return 1; |
|
|
|
0
|
|
|
|
|
|
|
87
|
3
|
50
|
|
|
|
|
if (cp >= 0x100000 && cp <= 0x10FFFD) return 1; |
|
|
|
0
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
/* Noncharacters (subset of Cn) */ |
|
90
|
3
|
50
|
|
|
|
|
if ((cp & 0xFFFE) == 0xFFFE) return 1; |
|
91
|
3
|
50
|
|
|
|
|
if (cp >= 0xFDD0 && cp <= 0xFDEF) return 1; |
|
|
|
0
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
|
|
93
|
3
|
|
|
|
|
|
return 0; |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
/* |
|
97
|
|
|
|
|
|
|
* Quote a string value for logfmt output. |
|
98
|
|
|
|
|
|
|
* Input: a Perl SV (character string, may have UTF8 flag). |
|
99
|
|
|
|
|
|
|
* Output: a new SV containing the quoted byte string (no UTF8 flag), |
|
100
|
|
|
|
|
|
|
* wrapped in double quotes, with proper escaping. |
|
101
|
|
|
|
|
|
|
*/ |
|
102
|
|
|
|
|
|
|
static SV * |
|
103
|
24
|
|
|
|
|
|
quote_string_xs(pTHX_ SV *input) |
|
104
|
|
|
|
|
|
|
{ |
|
105
|
|
|
|
|
|
|
STRLEN len; |
|
106
|
24
|
|
|
|
|
|
const char *s = SvPV(input, len); |
|
107
|
24
|
|
|
|
|
|
bool is_utf8 = cBOOL(SvUTF8(input)); |
|
108
|
24
|
|
|
|
|
|
const char *end = s + len; |
|
109
|
|
|
|
|
|
|
SV *out; |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
/* Optimistic pre-allocate: most chars pass through or become 2-char escapes */ |
|
112
|
24
|
|
|
|
|
|
out = newSV(len * 2 + 3); |
|
113
|
24
|
|
|
|
|
|
SvPOK_on(out); |
|
114
|
24
|
|
|
|
|
|
sv_catpvn(out, "\"", 1); |
|
115
|
|
|
|
|
|
|
|
|
116
|
294
|
100
|
|
|
|
|
while (s < end) { |
|
117
|
|
|
|
|
|
|
UV cp; |
|
118
|
|
|
|
|
|
|
STRLEN char_len; |
|
119
|
|
|
|
|
|
|
|
|
120
|
270
|
100
|
|
|
|
|
if (is_utf8) { |
|
121
|
92
|
|
|
|
|
|
cp = utf8_to_uvchr_buf((const U8 *)s, (const U8 *)end, &char_len); |
|
122
|
92
|
50
|
|
|
|
|
if (char_len == 0) { |
|
123
|
|
|
|
|
|
|
/* Malformed UTF-8, skip byte */ |
|
124
|
0
|
|
|
|
|
|
s++; |
|
125
|
0
|
|
|
|
|
|
continue; |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
} else { |
|
128
|
178
|
|
|
|
|
|
cp = (UV)(unsigned char)*s; |
|
129
|
178
|
|
|
|
|
|
char_len = 1; |
|
130
|
|
|
|
|
|
|
} |
|
131
|
|
|
|
|
|
|
|
|
132
|
270
|
100
|
|
|
|
|
if (cp == '\\') { |
|
133
|
5
|
|
|
|
|
|
sv_catpvn(out, "\\\\", 2); |
|
134
|
265
|
100
|
|
|
|
|
} else if (cp == '"') { |
|
135
|
14
|
|
|
|
|
|
sv_catpvn(out, "\\\"", 2); |
|
136
|
251
|
100
|
|
|
|
|
} else if (cp == '\t') { |
|
137
|
3
|
|
|
|
|
|
sv_catpvn(out, "\\t", 2); |
|
138
|
248
|
100
|
|
|
|
|
} else if (cp == '\n') { |
|
139
|
3
|
|
|
|
|
|
sv_catpvn(out, "\\n", 2); |
|
140
|
245
|
100
|
|
|
|
|
} else if (cp == '\r') { |
|
141
|
3
|
|
|
|
|
|
sv_catpvn(out, "\\r", 2); |
|
142
|
242
|
50
|
|
|
|
|
} else if (cp >= 0x20 && cp < 0x7F) { |
|
|
|
100
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
/* Common ASCII printable — pass through directly */ |
|
144
|
235
|
|
|
|
|
|
sv_catpvn(out, s, 1); |
|
145
|
7
|
100
|
|
|
|
|
} else if (needs_escape(cp)) { |
|
146
|
|
|
|
|
|
|
/* Control, format, or vertical whitespace — \x{XX} each UTF-8 byte */ |
|
147
|
|
|
|
|
|
|
U8 utf8buf[UTF8_MAXBYTES + 1]; |
|
148
|
4
|
|
|
|
|
|
U8 *utf8end = uvchr_to_utf8(utf8buf, cp); |
|
149
|
4
|
|
|
|
|
|
STRLEN utf8len = utf8end - utf8buf; |
|
150
|
|
|
|
|
|
|
STRLEN j; |
|
151
|
16
|
100
|
|
|
|
|
for (j = 0; j < utf8len; j++) { |
|
152
|
|
|
|
|
|
|
char hexbuf[8]; |
|
153
|
12
|
|
|
|
|
|
int hexlen = snprintf(hexbuf, sizeof(hexbuf), "\\x{%02x}", utf8buf[j]); |
|
154
|
12
|
|
|
|
|
|
sv_catpvn(out, hexbuf, hexlen); |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
} else { |
|
157
|
|
|
|
|
|
|
/* Safe non-ASCII (e.g. ë, ü) — output as UTF-8 bytes */ |
|
158
|
3
|
50
|
|
|
|
|
if (is_utf8) { |
|
159
|
3
|
|
|
|
|
|
sv_catpvn(out, s, char_len); |
|
160
|
|
|
|
|
|
|
} else { |
|
161
|
|
|
|
|
|
|
/* Latin-1 codepoint → UTF-8 encode */ |
|
162
|
|
|
|
|
|
|
U8 utf8buf[UTF8_MAXBYTES + 1]; |
|
163
|
0
|
|
|
|
|
|
U8 *utf8end = uvchr_to_utf8(utf8buf, cp); |
|
164
|
0
|
|
|
|
|
|
sv_catpvn(out, (const char *)utf8buf, utf8end - utf8buf); |
|
165
|
|
|
|
|
|
|
} |
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
|
|
168
|
270
|
|
|
|
|
|
s += char_len; |
|
169
|
|
|
|
|
|
|
} |
|
170
|
|
|
|
|
|
|
|
|
171
|
24
|
|
|
|
|
|
sv_catpvn(out, "\"", 1); |
|
172
|
|
|
|
|
|
|
/* Result is bytes — do NOT set UTF8 flag */ |
|
173
|
24
|
|
|
|
|
|
return out; |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
/* |
|
177
|
|
|
|
|
|
|
* Sanitize a key: replace non-okchr characters with '?'. |
|
178
|
|
|
|
|
|
|
* Empty keys become '~'. |
|
179
|
|
|
|
|
|
|
* Returns a new mortal SV. |
|
180
|
|
|
|
|
|
|
*/ |
|
181
|
|
|
|
|
|
|
static SV * |
|
182
|
146
|
|
|
|
|
|
sanitize_key(pTHX_ SV *key_sv) |
|
183
|
|
|
|
|
|
|
{ |
|
184
|
|
|
|
|
|
|
STRLEN len; |
|
185
|
|
|
|
|
|
|
const char *key_s; |
|
186
|
|
|
|
|
|
|
SV *result; |
|
187
|
|
|
|
|
|
|
|
|
188
|
146
|
50
|
|
|
|
|
if (!SvOK(key_sv)) { |
|
189
|
0
|
|
|
|
|
|
return sv_2mortal(newSVpvn("~", 1)); |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
|
|
192
|
146
|
|
|
|
|
|
key_s = SvPV(key_sv, len); |
|
193
|
146
|
100
|
|
|
|
|
if (len == 0) { |
|
194
|
2
|
|
|
|
|
|
return sv_2mortal(newSVpvn("~", 1)); |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
|
|
197
|
144
|
50
|
|
|
|
|
if (SvUTF8(key_sv)) { |
|
198
|
|
|
|
|
|
|
/* Walk codepoint by codepoint */ |
|
199
|
0
|
|
|
|
|
|
const char *s = key_s; |
|
200
|
0
|
|
|
|
|
|
const char *end = s + len; |
|
201
|
0
|
|
|
|
|
|
result = newSVpvn("", 0); |
|
202
|
0
|
0
|
|
|
|
|
while (s < end) { |
|
203
|
|
|
|
|
|
|
STRLEN char_len; |
|
204
|
0
|
|
|
|
|
|
UV cp = utf8_to_uvchr_buf((const U8 *)s, (const U8 *)end, &char_len); |
|
205
|
0
|
0
|
|
|
|
|
if (char_len == 0) { s++; continue; } |
|
206
|
0
|
0
|
|
|
|
|
if (cp <= 0x7E && is_okchr((unsigned char)cp)) { |
|
|
|
0
|
|
|
|
|
|
|
207
|
0
|
|
|
|
|
|
sv_catpvn(result, s, char_len); |
|
208
|
|
|
|
|
|
|
} else { |
|
209
|
0
|
|
|
|
|
|
sv_catpvn(result, "?", 1); |
|
210
|
|
|
|
|
|
|
} |
|
211
|
0
|
|
|
|
|
|
s += char_len; |
|
212
|
|
|
|
|
|
|
} |
|
213
|
0
|
|
|
|
|
|
return sv_2mortal(result); |
|
214
|
|
|
|
|
|
|
} else { |
|
215
|
|
|
|
|
|
|
/* Byte mode — modify in place on a copy */ |
|
216
|
|
|
|
|
|
|
char *buf; |
|
217
|
|
|
|
|
|
|
STRLEN i; |
|
218
|
144
|
|
|
|
|
|
result = newSVpvn(key_s, len); |
|
219
|
144
|
|
|
|
|
|
buf = SvPVX(result); |
|
220
|
694
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
221
|
550
|
100
|
|
|
|
|
if (!is_okchr((unsigned char)buf[i])) { |
|
222
|
7
|
|
|
|
|
|
buf[i] = '?'; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
} |
|
225
|
144
|
|
|
|
|
|
return sv_2mortal(result); |
|
226
|
|
|
|
|
|
|
} |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
/* Forward declaration */ |
|
230
|
|
|
|
|
|
|
static AV * |
|
231
|
|
|
|
|
|
|
pairs_to_kvstr_impl(pTHX_ SV *self, AV *aref, HV *seen, SV *prefix); |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
/* |
|
234
|
|
|
|
|
|
|
* Core implementation of _pairs_to_kvstr_aref. |
|
235
|
|
|
|
|
|
|
* Returns a new (non-mortal) AV* of key=value strings. |
|
236
|
|
|
|
|
|
|
*/ |
|
237
|
|
|
|
|
|
|
static AV * |
|
238
|
85
|
|
|
|
|
|
pairs_to_kvstr_impl(pTHX_ SV *self, AV *aref, HV *seen, SV *prefix) |
|
239
|
|
|
|
|
|
|
{ |
|
240
|
85
|
|
|
|
|
|
AV *kvstrs = newAV(); |
|
241
|
85
|
|
|
|
|
|
SSize_t alen = av_len(aref); /* last index, -1 if empty */ |
|
242
|
|
|
|
|
|
|
SSize_t i; |
|
243
|
|
|
|
|
|
|
|
|
244
|
231
|
100
|
|
|
|
|
for (i = 0; i <= alen; i += 2) { |
|
245
|
|
|
|
|
|
|
SV **key_svp, **val_svp; |
|
246
|
|
|
|
|
|
|
SV *key, *value, *str; |
|
247
|
|
|
|
|
|
|
STRLEN val_len; |
|
248
|
|
|
|
|
|
|
const char *val_s; |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
/* Get and sanitize key */ |
|
251
|
146
|
|
|
|
|
|
key_svp = av_fetch(aref, i, 0); |
|
252
|
146
|
50
|
|
|
|
|
key = sanitize_key(aTHX_ key_svp ? *key_svp : &PL_sv_undef); |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
/* Prepend prefix if defined */ |
|
255
|
146
|
100
|
|
|
|
|
if (prefix && SvOK(prefix)) { |
|
|
|
50
|
|
|
|
|
|
|
256
|
49
|
|
|
|
|
|
SV *prefixed = sv_2mortal(newSVsv(prefix)); |
|
257
|
49
|
|
|
|
|
|
sv_catpvn(prefixed, ".", 1); |
|
258
|
49
|
|
|
|
|
|
sv_catsv(prefixed, key); |
|
259
|
49
|
|
|
|
|
|
key = prefixed; |
|
260
|
|
|
|
|
|
|
} |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
/* Build "key=" early so the fast path can just append */ |
|
263
|
146
|
|
|
|
|
|
str = newSVsv(key); |
|
264
|
146
|
|
|
|
|
|
sv_catpvn(str, "=", 1); |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
/* Get value */ |
|
267
|
146
|
50
|
|
|
|
|
val_svp = (i + 1 <= alen) ? av_fetch(aref, i + 1, 0) : NULL; |
|
268
|
146
|
50
|
|
|
|
|
value = val_svp ? *val_svp : &PL_sv_undef; |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
/* Fast path: defined non-ref scalar that's a bare value */ |
|
271
|
146
|
100
|
|
|
|
|
if (SvOK(value) && !SvROK(value)) { |
|
|
|
100
|
|
|
|
|
|
|
272
|
103
|
|
|
|
|
|
val_s = SvPV(value, val_len); |
|
273
|
103
|
100
|
|
|
|
|
if (is_bare_value(val_s, val_len)) { |
|
274
|
82
|
|
|
|
|
|
sv_catpvn(str, val_s, val_len); |
|
275
|
82
|
|
|
|
|
|
av_push(kvstrs, str); |
|
276
|
106
|
|
|
|
|
|
continue; |
|
277
|
|
|
|
|
|
|
} |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
/* Handle coderef: call it to get the actual value */ |
|
281
|
64
|
100
|
|
|
|
|
if (SvROK(value) && SvTYPE(SvRV(value)) == SVt_PVCV) { |
|
|
|
100
|
|
|
|
|
|
|
282
|
5
|
|
|
|
|
|
dSP; |
|
283
|
|
|
|
|
|
|
int count; |
|
284
|
5
|
|
|
|
|
|
ENTER; |
|
285
|
5
|
|
|
|
|
|
SAVETMPS; |
|
286
|
5
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
287
|
5
|
|
|
|
|
|
PUTBACK; |
|
288
|
5
|
|
|
|
|
|
count = call_sv(value, G_SCALAR); |
|
289
|
5
|
|
|
|
|
|
SPAGAIN; |
|
290
|
5
|
50
|
|
|
|
|
if (count > 0) { |
|
291
|
5
|
|
|
|
|
|
value = SvREFCNT_inc(POPs); |
|
292
|
|
|
|
|
|
|
} else { |
|
293
|
0
|
|
|
|
|
|
value = &PL_sv_undef; |
|
294
|
|
|
|
|
|
|
} |
|
295
|
5
|
|
|
|
|
|
PUTBACK; |
|
296
|
5
|
50
|
|
|
|
|
FREETMPS; |
|
297
|
5
|
|
|
|
|
|
LEAVE; |
|
298
|
5
|
|
|
|
|
|
value = sv_2mortal(value); |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
/* Handle ref-to-ref: flog via String::Flogger */ |
|
302
|
64
|
100
|
|
|
|
|
if (SvROK(value) && !sv_isobject(value) && SvROK(SvRV(value))) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
303
|
3
|
|
|
|
|
|
dSP; |
|
304
|
|
|
|
|
|
|
int count; |
|
305
|
|
|
|
|
|
|
SV *flogger_class; |
|
306
|
3
|
|
|
|
|
|
SV *derefed = SvRV(value); |
|
307
|
|
|
|
|
|
|
AV *flog_args; |
|
308
|
|
|
|
|
|
|
SV *flog_args_ref; |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
/* Call $self->string_flogger to get the class */ |
|
311
|
3
|
|
|
|
|
|
ENTER; |
|
312
|
3
|
|
|
|
|
|
SAVETMPS; |
|
313
|
3
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
314
|
3
|
50
|
|
|
|
|
XPUSHs(self); |
|
315
|
3
|
|
|
|
|
|
PUTBACK; |
|
316
|
3
|
|
|
|
|
|
count = call_method("string_flogger", G_SCALAR); |
|
317
|
3
|
|
|
|
|
|
SPAGAIN; |
|
318
|
3
|
50
|
|
|
|
|
flogger_class = (count > 0) ? POPs : &PL_sv_undef; |
|
319
|
3
|
|
|
|
|
|
SvREFCNT_inc(flogger_class); |
|
320
|
3
|
|
|
|
|
|
PUTBACK; |
|
321
|
3
|
50
|
|
|
|
|
FREETMPS; |
|
322
|
3
|
|
|
|
|
|
LEAVE; |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
/* Call $flogger_class->flog([ '%s', $$value ]) */ |
|
325
|
3
|
|
|
|
|
|
flog_args = newAV(); |
|
326
|
3
|
|
|
|
|
|
av_push(flog_args, newSVpvn("%s", 2)); |
|
327
|
3
|
|
|
|
|
|
av_push(flog_args, newSVsv(derefed)); |
|
328
|
3
|
|
|
|
|
|
flog_args_ref = sv_2mortal(newRV_noinc((SV*)flog_args)); |
|
329
|
|
|
|
|
|
|
|
|
330
|
3
|
|
|
|
|
|
ENTER; |
|
331
|
3
|
|
|
|
|
|
SAVETMPS; |
|
332
|
3
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
333
|
3
|
50
|
|
|
|
|
XPUSHs(sv_2mortal(flogger_class)); |
|
334
|
3
|
50
|
|
|
|
|
XPUSHs(flog_args_ref); |
|
335
|
3
|
|
|
|
|
|
PUTBACK; |
|
336
|
3
|
|
|
|
|
|
count = call_method("flog", G_SCALAR); |
|
337
|
3
|
|
|
|
|
|
SPAGAIN; |
|
338
|
3
|
50
|
|
|
|
|
if (count > 0) { |
|
339
|
3
|
|
|
|
|
|
value = SvREFCNT_inc(POPs); |
|
340
|
|
|
|
|
|
|
} else { |
|
341
|
0
|
|
|
|
|
|
value = &PL_sv_undef; |
|
342
|
|
|
|
|
|
|
} |
|
343
|
3
|
|
|
|
|
|
PUTBACK; |
|
344
|
3
|
50
|
|
|
|
|
FREETMPS; |
|
345
|
3
|
|
|
|
|
|
LEAVE; |
|
346
|
3
|
|
|
|
|
|
value = sv_2mortal(value); |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
|
|
349
|
64
|
100
|
|
|
|
|
if (!SvOK(value)) { |
|
350
|
|
|
|
|
|
|
/* undef → ~missing~ */ |
|
351
|
1
|
|
|
|
|
|
value = sv_2mortal(newSVpvn("~missing~", 9)); |
|
352
|
63
|
100
|
|
|
|
|
} else if (SvROK(value)) { |
|
353
|
34
|
|
|
|
|
|
UV refaddr = PTR2UV(SvRV(value)); |
|
354
|
|
|
|
|
|
|
char refaddr_str[32]; |
|
355
|
34
|
|
|
|
|
|
int refaddr_len = snprintf(refaddr_str, sizeof(refaddr_str), |
|
356
|
|
|
|
|
|
|
"%" UVuf, refaddr); |
|
357
|
34
|
|
|
|
|
|
SV **seen_svp = hv_fetch(seen, refaddr_str, refaddr_len, 0); |
|
358
|
|
|
|
|
|
|
|
|
359
|
34
|
100
|
|
|
|
|
if (seen_svp && SvOK(*seen_svp)) { |
|
|
|
50
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
/* Already seen this ref — use the backreference */ |
|
361
|
2
|
|
|
|
|
|
value = *seen_svp; |
|
362
|
32
|
100
|
|
|
|
|
} else if (!sv_isobject(value) && |
|
363
|
24
|
100
|
|
|
|
|
SvTYPE(SvRV(value)) == SVt_PVAV) { |
|
364
|
|
|
|
|
|
|
/* Unblessed arrayref — recurse */ |
|
365
|
13
|
|
|
|
|
|
AV *arr = (AV*)SvRV(value); |
|
366
|
13
|
|
|
|
|
|
SSize_t arr_len = av_len(arr); |
|
367
|
13
|
|
|
|
|
|
AV *new_aref = newAV(); |
|
368
|
|
|
|
|
|
|
AV *sub_kvstrs; |
|
369
|
|
|
|
|
|
|
SSize_t j, sub_len; |
|
370
|
|
|
|
|
|
|
SV *backref; |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
/* Store backreference in seen */ |
|
373
|
13
|
|
|
|
|
|
backref = newSVpvf("&%" SVf, SVfARG(key)); |
|
374
|
13
|
|
|
|
|
|
hv_store(seen, refaddr_str, refaddr_len, backref, 0); |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
/* Build pairs: [ 0 => arr[0], 1 => arr[1], ... ] */ |
|
377
|
44
|
100
|
|
|
|
|
for (j = 0; j <= arr_len; j++) { |
|
378
|
31
|
|
|
|
|
|
SV **elem = av_fetch(arr, j, 0); |
|
379
|
31
|
|
|
|
|
|
av_push(new_aref, newSViv(j)); |
|
380
|
31
|
50
|
|
|
|
|
av_push(new_aref, elem ? newSVsv(*elem) : newSVsv(&PL_sv_undef)); |
|
381
|
|
|
|
|
|
|
} |
|
382
|
|
|
|
|
|
|
|
|
383
|
13
|
|
|
|
|
|
sub_kvstrs = pairs_to_kvstr_impl(aTHX_ self, new_aref, seen, key); |
|
384
|
13
|
|
|
|
|
|
sub_len = av_len(sub_kvstrs); |
|
385
|
54
|
100
|
|
|
|
|
for (j = 0; j <= sub_len; j++) { |
|
386
|
41
|
|
|
|
|
|
SV **svp = av_fetch(sub_kvstrs, j, 0); |
|
387
|
41
|
50
|
|
|
|
|
if (svp) av_push(kvstrs, newSVsv(*svp)); |
|
388
|
|
|
|
|
|
|
} |
|
389
|
13
|
|
|
|
|
|
SvREFCNT_dec(new_aref); |
|
390
|
13
|
|
|
|
|
|
SvREFCNT_dec(sub_kvstrs); |
|
391
|
13
|
|
|
|
|
|
SvREFCNT_dec(str); |
|
392
|
24
|
|
|
|
|
|
continue; /* next KEY */ |
|
393
|
19
|
100
|
|
|
|
|
} else if (!sv_isobject(value) && |
|
394
|
11
|
50
|
|
|
|
|
SvTYPE(SvRV(value)) == SVt_PVHV) { |
|
395
|
|
|
|
|
|
|
/* Unblessed hashref — recurse with sorted keys */ |
|
396
|
11
|
|
|
|
|
|
HV *hv = (HV*)SvRV(value); |
|
397
|
|
|
|
|
|
|
AV *sorted_keys; |
|
398
|
|
|
|
|
|
|
AV *new_aref; |
|
399
|
|
|
|
|
|
|
AV *sub_kvstrs; |
|
400
|
|
|
|
|
|
|
SSize_t j, nkeys, sub_len; |
|
401
|
|
|
|
|
|
|
SV *backref; |
|
402
|
|
|
|
|
|
|
HE *entry; |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
/* Store backreference in seen */ |
|
405
|
11
|
|
|
|
|
|
backref = newSVpvf("&%" SVf, SVfARG(key)); |
|
406
|
11
|
|
|
|
|
|
hv_store(seen, refaddr_str, refaddr_len, backref, 0); |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
/* Collect and sort keys */ |
|
409
|
11
|
|
|
|
|
|
sorted_keys = newAV(); |
|
410
|
11
|
|
|
|
|
|
hv_iterinit(hv); |
|
411
|
29
|
100
|
|
|
|
|
while ((entry = hv_iternext(hv))) { |
|
412
|
18
|
|
|
|
|
|
av_push(sorted_keys, newSVsv(hv_iterkeysv(entry))); |
|
413
|
|
|
|
|
|
|
} |
|
414
|
11
|
|
|
|
|
|
nkeys = av_len(sorted_keys); |
|
415
|
11
|
100
|
|
|
|
|
if (nkeys >= 1) { |
|
416
|
6
|
|
|
|
|
|
sortsv(AvARRAY(sorted_keys), nkeys + 1, Perl_sv_cmp); |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
/* Build pairs from sorted keys */ |
|
420
|
11
|
|
|
|
|
|
new_aref = newAV(); |
|
421
|
29
|
100
|
|
|
|
|
for (j = 0; j <= nkeys; j++) { |
|
422
|
18
|
|
|
|
|
|
SV **kp = av_fetch(sorted_keys, j, 0); |
|
423
|
18
|
50
|
|
|
|
|
if (kp) { |
|
424
|
|
|
|
|
|
|
STRLEN klen; |
|
425
|
18
|
|
|
|
|
|
const char *ks = SvPV(*kp, klen); |
|
426
|
18
|
50
|
|
|
|
|
SV **vp = hv_fetch(hv, ks, |
|
427
|
|
|
|
|
|
|
SvUTF8(*kp) ? -(I32)klen : (I32)klen, 0); |
|
428
|
18
|
|
|
|
|
|
av_push(new_aref, newSVsv(*kp)); |
|
429
|
18
|
50
|
|
|
|
|
av_push(new_aref, vp ? newSVsv(*vp) : newSVsv(&PL_sv_undef)); |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
|
|
433
|
11
|
|
|
|
|
|
sub_kvstrs = pairs_to_kvstr_impl(aTHX_ self, new_aref, seen, key); |
|
434
|
11
|
|
|
|
|
|
sub_len = av_len(sub_kvstrs); |
|
435
|
38
|
100
|
|
|
|
|
for (j = 0; j <= sub_len; j++) { |
|
436
|
27
|
|
|
|
|
|
SV **svp = av_fetch(sub_kvstrs, j, 0); |
|
437
|
27
|
50
|
|
|
|
|
if (svp) av_push(kvstrs, newSVsv(*svp)); |
|
438
|
|
|
|
|
|
|
} |
|
439
|
11
|
|
|
|
|
|
SvREFCNT_dec(sorted_keys); |
|
440
|
11
|
|
|
|
|
|
SvREFCNT_dec(new_aref); |
|
441
|
11
|
|
|
|
|
|
SvREFCNT_dec(sub_kvstrs); |
|
442
|
11
|
|
|
|
|
|
SvREFCNT_dec(str); |
|
443
|
11
|
|
|
|
|
|
continue; /* next KEY */ |
|
444
|
|
|
|
|
|
|
} else { |
|
445
|
|
|
|
|
|
|
/* Other ref types — stringify */ |
|
446
|
|
|
|
|
|
|
STRLEN slen; |
|
447
|
|
|
|
|
|
|
const char *ss; |
|
448
|
8
|
|
|
|
|
|
SV *strval = newSVpvn("", 0); |
|
449
|
8
|
|
|
|
|
|
sv_catsv(strval, value); |
|
450
|
8
|
|
|
|
|
|
value = sv_2mortal(strval); |
|
451
|
|
|
|
|
|
|
} |
|
452
|
|
|
|
|
|
|
} |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
/* Append value (bare or quoted) to the "key=" we built earlier */ |
|
455
|
40
|
|
|
|
|
|
val_s = SvPV(value, val_len); |
|
456
|
40
|
100
|
|
|
|
|
if (is_bare_value(val_s, val_len)) { |
|
457
|
16
|
|
|
|
|
|
sv_catpvn(str, val_s, val_len); |
|
458
|
|
|
|
|
|
|
} else { |
|
459
|
24
|
|
|
|
|
|
SV *quoted = quote_string_xs(aTHX_ value); |
|
460
|
24
|
|
|
|
|
|
sv_catsv(str, quoted); |
|
461
|
24
|
|
|
|
|
|
SvREFCNT_dec(quoted); |
|
462
|
|
|
|
|
|
|
} |
|
463
|
|
|
|
|
|
|
|
|
464
|
40
|
|
|
|
|
|
av_push(kvstrs, str); |
|
465
|
|
|
|
|
|
|
} |
|
466
|
|
|
|
|
|
|
|
|
467
|
85
|
|
|
|
|
|
return kvstrs; |
|
468
|
|
|
|
|
|
|
} |
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
MODULE = Log::Fmt::XS PACKAGE = Log::Fmt::XS |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
PROTOTYPES: DISABLE |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
SV * |
|
476
|
|
|
|
|
|
|
_pairs_to_kvstr_aref(self, aref_ref, ...) |
|
477
|
|
|
|
|
|
|
SV *self |
|
478
|
|
|
|
|
|
|
SV *aref_ref |
|
479
|
|
|
|
|
|
|
PREINIT: |
|
480
|
|
|
|
|
|
|
AV *aref; |
|
481
|
|
|
|
|
|
|
HV *seen; |
|
482
|
|
|
|
|
|
|
SV *prefix; |
|
483
|
|
|
|
|
|
|
AV *result; |
|
484
|
|
|
|
|
|
|
CODE: |
|
485
|
61
|
50
|
|
|
|
|
if (!SvROK(aref_ref) || SvTYPE(SvRV(aref_ref)) != SVt_PVAV) { |
|
|
|
50
|
|
|
|
|
|
|
486
|
0
|
|
|
|
|
|
croak("Second argument must be an array reference"); |
|
487
|
|
|
|
|
|
|
} |
|
488
|
61
|
|
|
|
|
|
aref = (AV *)SvRV(aref_ref); |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
/* Handle optional $seen hashref */ |
|
491
|
61
|
50
|
|
|
|
|
if (items >= 3 && SvROK(ST(2)) && SvTYPE(SvRV(ST(2))) == SVt_PVHV) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
492
|
0
|
|
|
|
|
|
seen = (HV *)SvRV(ST(2)); |
|
493
|
|
|
|
|
|
|
} else { |
|
494
|
61
|
|
|
|
|
|
seen = newHV(); |
|
495
|
61
|
|
|
|
|
|
sv_2mortal((SV*)seen); |
|
496
|
|
|
|
|
|
|
} |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
/* Handle optional $prefix */ |
|
499
|
61
|
50
|
|
|
|
|
if (items >= 4 && SvOK(ST(3))) { |
|
|
|
0
|
|
|
|
|
|
|
500
|
0
|
|
|
|
|
|
prefix = ST(3); |
|
501
|
|
|
|
|
|
|
} else { |
|
502
|
61
|
|
|
|
|
|
prefix = NULL; |
|
503
|
|
|
|
|
|
|
} |
|
504
|
|
|
|
|
|
|
|
|
505
|
61
|
|
|
|
|
|
result = pairs_to_kvstr_impl(aTHX_ self, aref, seen, prefix); |
|
506
|
61
|
|
|
|
|
|
RETVAL = newRV_noinc((SV *)result); |
|
507
|
|
|
|
|
|
|
OUTPUT: |
|
508
|
|
|
|
|
|
|
RETVAL |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
SV * |
|
511
|
|
|
|
|
|
|
_quote_string(input) |
|
512
|
|
|
|
|
|
|
SV *input |
|
513
|
|
|
|
|
|
|
CODE: |
|
514
|
0
|
|
|
|
|
|
RETVAL = quote_string_xs(aTHX_ input); |
|
515
|
|
|
|
|
|
|
OUTPUT: |
|
516
|
|
|
|
|
|
|
RETVAL |