File Coverage

Code.xs
Criterion Covered Total %
statement 273 337 81.0
branch 187 318 58.8
condition n/a
subroutine n/a
pod n/a
total 460 655 70.2


line stmt bran cond sub pod time code
1             /*
2             Copyright 2012, 2013, 2023 Lukas Mai.
3              
4             This program is free software; you can redistribute it and/or modify it
5             under the terms of either: the GNU General Public License as published
6             by the Free Software Foundation; or the Artistic License.
7              
8             See http://dev.perl.org/licenses/ for more information.
9             */
10              
11             #ifdef __GNUC__
12             #if __GNUC__ >= 5
13             #define IF_HAVE_GCC_5(X) X
14             #endif
15              
16             #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ >= 5
17             #define PRAGMA_GCC_(X) _Pragma(#X)
18             #define PRAGMA_GCC(X) PRAGMA_GCC_(GCC X)
19             #endif
20             #endif
21              
22             #ifndef IF_HAVE_GCC_5
23             #define IF_HAVE_GCC_5(X)
24             #endif
25              
26             #ifndef PRAGMA_GCC
27             #define PRAGMA_GCC(X)
28             #endif
29              
30             #ifdef DEVEL
31             #define WARNINGS_RESET PRAGMA_GCC(diagnostic pop)
32             #define WARNINGS_ENABLEW(X) PRAGMA_GCC(diagnostic error #X)
33             #define WARNINGS_ENABLE \
34             WARNINGS_ENABLEW(-Wall) \
35             WARNINGS_ENABLEW(-Wextra) \
36             WARNINGS_ENABLEW(-Wundef) \
37             WARNINGS_ENABLEW(-Wshadow) \
38             WARNINGS_ENABLEW(-Wbad-function-cast) \
39             WARNINGS_ENABLEW(-Wcast-align) \
40             WARNINGS_ENABLEW(-Wwrite-strings) \
41             WARNINGS_ENABLEW(-Wstrict-prototypes) \
42             WARNINGS_ENABLEW(-Wmissing-prototypes) \
43             WARNINGS_ENABLEW(-Winline) \
44             WARNINGS_ENABLEW(-Wdisabled-optimization) \
45             IF_HAVE_GCC_5(WARNINGS_ENABLEW(-Wnested-externs))
46              
47             #else
48             #define WARNINGS_RESET
49             #define WARNINGS_ENABLE
50             #endif
51              
52             #ifdef __has_attribute
53             #if __has_attribute(noreturn)
54             #define ATTR_NORETURN __attribute__((noreturn))
55             #endif
56             #endif
57              
58             #ifndef ATTR_NORETURN
59             #define ATTR_NORETURN
60             #endif
61              
62              
63             #define PERL_NO_GET_CONTEXT
64             #include "EXTERN.h"
65             #include "perl.h"
66             #include "XSUB.h"
67              
68             #include
69             #include
70             #include
71              
72              
73             WARNINGS_ENABLE
74              
75              
76             #define HAVE_PERL_VERSION(R, V, S) \
77             (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
78              
79             #if HAVE_PERL_VERSION(5, 16, 0)
80             #define IF_HAVE_PERL_5_16(YES, NO) YES
81             #else
82             #define IF_HAVE_PERL_5_16(YES, NO) NO
83             #endif
84              
85              
86             #define MY_PKG "Quote::Code"
87              
88             #define HINTK_QC MY_PKG "/qc"
89             #define HINTK_QC_TO MY_PKG "/qc_to"
90             #define HINTK_QCW MY_PKG "/qcw"
91              
92             static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
93              
94 92           static void free_ptr_op(pTHX_ void *vp) {
95 92           OP **pp = vp;
96 92           op_free(*pp);
97 92           Safefree(pp);
98 92           }
99              
100             enum {
101             FLAG_BACKSLASH_ESCAPE = 0x1,
102             FLAG_HASH_INTERPOLATE = 0x2,
103             FLAG_STOP_AT_SPACE = 0x4
104             };
105              
106             typedef struct {
107             I32 delim_start, delim_stop;
108             SV *delim_str, *leftover;
109             int flags;
110             } QCSpec;
111              
112             ATTR_NORETURN
113 1           static void missing_terminator(pTHX_ const QCSpec *spec, line_t line) {
114 1           I32 c = spec->delim_stop;
115 1           SV *sv = spec->delim_str;
116              
117 1 50         if (!sv) {
118 0           sv = sv_2mortal(newSVpvs("'\"'"));
119 0 0         if (c != '"') {
120             U8 utf8_tmp[UTF8_MAXBYTES + 1], *d;
121 0           d = uvchr_to_utf8(utf8_tmp, c);
122 0           pv_uni_display(sv, utf8_tmp, d - utf8_tmp, 100, UNI_DISPLAY_QQ);
123             }
124             }
125              
126 1 50         if (c != '"') {
127 1           sv_insert(sv, 0, 0, "\"", 1);
128 1           sv_catpvs(sv, "\"");
129             }
130              
131 1 50         if (line) {
132 1           CopLINE_set(PL_curcop, line);
133             }
134 1           croak("Can't find string terminator %"SVf" anywhere before EOF", SVfARG(sv));
135             }
136              
137 489           static void my_sv_cat_c(pTHX_ SV *sv, U32 c) {
138             U8 ds[UTF8_MAXBYTES + 1], *d;
139 489           d = uvchr_to_utf8(ds, c);
140 489 100         if (d - ds > 1) {
141 7           sv_utf8_upgrade(sv);
142             }
143 489           sv_catpvn(sv, (char *)ds, d - ds);
144 489           }
145              
146 16           static U32 hex2int(unsigned char c) {
147             static char xdigits[] = "0123456789abcdef";
148 16           char *p = strchr(xdigits, tolower(c));
149 16 50         if (!c || !p) {
    50          
150 0           return 0;
151             }
152 16           return p - xdigits;
153             }
154              
155 86           static void my_op_cat_sv(pTHX_ OP **pop, SV *sv) {
156 86           OP *const str = newSVOP(OP_CONST, 0, SvREFCNT_inc_simple_NN(sv));
157 86 100         *pop = !*pop ? str : newBINOP(OP_CONCAT, 0, *pop, str);
158 86           }
159              
160 77           static OP *parse_qctail(pTHX_ const QCSpec *spec, int *pnesting) {
161             I32 c;
162             OP **gen_sentinel;
163             SV *sv;
164             line_t start;
165 77           SV *const delim_str = spec->delim_str;
166             const int
167 77           have_delim_stop = spec->delim_stop != -1;
168              
169             assert(have_delim_stop == !delim_str);
170             assert(!delim_str || spec->leftover);
171              
172 77           start = CopLINE(PL_curcop);
173              
174 77           Newx(gen_sentinel, 1, OP *);
175 77           *gen_sentinel = NULL;
176 77           SAVEDESTRUCTOR_X(free_ptr_op, gen_sentinel);
177              
178 77           sv = sv_2mortal(newSVpvs(""));
179 77 100         if (lex_bufutf8()) {
180 2           SvUTF8_on(sv);
181             }
182 77           c = '\n';
183              
184             for (;;) {
185             char *elim;
186 456           I32 b = c;
187              
188 456           c = lex_peek_unichar(0);
189 456 100         if (c == -1) {
190 1           missing_terminator(aTHX_ spec, start);
191             }
192              
193             assert(PL_parser->bufend >= PL_parser->bufptr);
194              
195 455 100         if (
196 111 100         b == '\n' &&
197 53 100         delim_str &&
198             /* c == spec->delim_start && */
199 49 100         (STRLEN)(PL_parser->bufend - PL_parser->bufptr) >= SvCUR(delim_str) &&
200 49           (elim = PL_parser->bufptr + SvCUR(delim_str),
201 20 50         memcmp(PL_parser->bufptr, SvPVX(delim_str), SvCUR(delim_str)) == 0) && (
202 0 0         !(
203 20           (STRLEN)(PL_parser->bufend - PL_parser->bufptr) > SvCUR(delim_str) ||
204 0           lex_next_chunk(0)
205 20 50         ) ||
206 0 0         (elim++, PL_parser->bufptr[SvCUR(delim_str)] == '\n') || (
207 0           elim++,
208 0 0         PL_parser->bufptr[SvCUR(delim_str)] == '\r' &&
    0          
209 0           PL_parser->bufptr[SvCUR(delim_str) + 1] == '\n'
210             )
211             )
212             ) {
213 20           lex_read_to(elim);
214 20           lex_stuff_sv(spec->leftover, 0);
215 20           break;
216             }
217              
218 435 100         if (
219 643 100         !(spec->flags & FLAG_HASH_INTERPOLATE)
220             ? c == '{'
221 13 50         : c == '#' &&
222 13 100         spec->delim_stop != '#' &&
223 221 100         PL_parser->bufptr[1] == '{' &&
224 11           (lex_read_unichar(0), 1)
225             ) {
226             OP *op;
227              
228 37           op = parse_block(0);
229 37           op = newUNOP(OP_NULL, OPf_SPECIAL, op_scope(op));
230              
231 37 100         if (SvCUR(sv)) {
232 23           my_op_cat_sv(aTHX_ gen_sentinel, sv);
233              
234 23           sv = sv_2mortal(newSVpvs(""));
235 23 50         if (lex_bufutf8()) {
236 0           SvUTF8_on(sv);
237             }
238             }
239              
240 37 100         if (*gen_sentinel) {
241 26           *gen_sentinel = newBINOP(OP_CONCAT, 0, *gen_sentinel, op);
242             } else {
243 11           *gen_sentinel = op;
244             }
245              
246 37           c = -1;
247 37           continue;
248             }
249              
250 398           lex_read_unichar(0);
251              
252 398 100         if (pnesting && c == spec->delim_start) {
    100          
253 5           (*pnesting)++;
254 393 100         } else if (have_delim_stop && c == spec->delim_stop) {
    100          
255 37 100         if (!pnesting || *pnesting == 0) {
    100          
256 32 100         if (spec->flags & FLAG_STOP_AT_SPACE) {
257             /* terrible hack to let qcw() know to stop parsing */
258 8           char tmp = c;
259 8           lex_stuff_pvn(&tmp, 1, 0);
260             }
261 32           break;
262             }
263 5           (*pnesting)--;
264 356 100         } else if ((spec->flags & FLAG_STOP_AT_SPACE) && isSPACE(c)) {
    50          
    100          
265             break;
266 333 100         } else if (c == '\\' && (spec->flags & FLAG_BACKSLASH_ESCAPE)) {
    100          
267             U32 u;
268              
269 34           c = lex_read_unichar(0);
270 34           switch (c) {
271             case -1:
272 0           missing_terminator(aTHX_ spec, start);
273              
274 0           case 'a': c = '\a'; break;
275 0           case 'b': c = '\b'; break;
276 0           case 'e': c = '\033'; break;
277 0           case 'f': c = '\f'; break;
278 3           case 'n': c = '\n'; break;
279 0           case 'r': c = '\r'; break;
280 4           case 't': c = '\t'; break;
281              
282             case 'c':
283 0           c = lex_read_unichar(0);
284 0 0         if (c == -1) {
285 0           missing_terminator(aTHX_ spec, start);
286             }
287 0 0         c = toUPPER(c) ^ 64;
    0          
288 0           break;
289              
290             case 'o':
291 0           c = lex_read_unichar(0);
292 0 0         if (c != '{') {
293 0           croak("Missing braces on \\o{}");
294             }
295 0           u = 0;
296 0 0         while (c = lex_peek_unichar(0), c >= '0' && c <= '7') {
    0          
    0          
297 0           u = u * 8 + (c - '0');
298 0           lex_read_unichar(0);
299             }
300 0 0         if (c != '}') {
301 0           croak("Missing right brace on \\o{}");
302             }
303 0           lex_read_unichar(0);
304 0           c = u;
305 0           break;
306              
307             case 'x':
308 6           c = lex_read_unichar(0);
309 6 100         if (c == '{') {
310 2           u = 0;
311 10 50         while (c = lex_peek_unichar(0), isXDIGIT(c)) {
    100          
    100          
312 8           u = u * 16 + hex2int(c);
313 8           lex_read_unichar(0);
314             }
315 2 50         if (c != '}') {
316 0           croak("Missing right brace on \\x{}");
317             }
318 2           lex_read_unichar(0);
319 2           c = u;
320 4 50         } else if (isXDIGIT(c)) {
    50          
321 4           u = hex2int(c);
322 4           c = lex_peek_unichar(0);
323 4 50         if (isXDIGIT(c)) {
    50          
324 4           u = u * 16 + hex2int(c);
325 4           lex_read_unichar(0);
326             }
327 4           c = u;
328             } else {
329 0           c = 0;
330             }
331 6           break;
332              
333             case 'N': {
334             SV *name;
335             char *n_ptr;
336             STRLEN n_len;
337              
338 6           c = lex_read_unichar(0);
339 6 50         if (c != '{') {
340 0           croak("Missing braces on \\N{}");
341             }
342              
343 6           name = sv_2mortal(newSVpvs(""));
344 6 50         if (lex_bufutf8()) {
345 0           SvUTF8_on(name);
346             }
347              
348 87 100         while ((c = lex_read_unichar(0)) != '}') {
349 81 50         if (c == -1) {
350 0           croak("Missing right brace on \\N{}");
351             }
352 81           my_sv_cat_c(aTHX_ name, c);
353             }
354              
355 6 50         n_ptr = SvPV(name, n_len);
356              
357 6 50         if (n_len >= 2 && n_ptr[0] == 'U' && n_ptr[1] == '+') {
    100          
    50          
358 2           I32 flags = PERL_SCAN_ALLOW_UNDERSCORES | PERL_SCAN_DISALLOW_PREFIX;
359             STRLEN x_len;
360              
361 2           n_ptr += 2;
362 2           n_len -= 2;
363              
364 2           x_len = n_len;
365 2           c = grok_hex(n_ptr, &x_len, &flags, NULL);
366 2 50         if (x_len == 0 || x_len != n_len) {
    50          
367 0           croak("Invalid hexadecimal number in \\N{U+...}");
368             }
369              
370 2           break;
371             }
372              
373             {
374             HV *table;
375             SV **cvp;
376              
377             #if HAVE_PERL_VERSION(5, 15, 7)
378 4 50         if (
379 4 50         !(table = GvHV(PL_hintgv)) ||
380 4 100         !(PL_hints & HINT_LOCALIZE_HH) ||
381 3 50         !(cvp = hv_fetchs(table, "charnames", FALSE)) ||
382 0 0         !SvOK(*cvp)
    0          
383             ) {
384 1           load_module(
385             0, newSVpvs("charnames"), NULL,
386             newSVpvs(":full"), newSVpvs(":short"), (SV *)NULL
387             );
388             }
389             #endif
390              
391 4 50         if (
392 4 50         !(table = GvHV(PL_hintgv)) ||
393 4           !(PL_hints & HINT_LOCALIZE_HH)
394             ) {
395             /* ??? */
396 0           croak("Constant(\\N{%"SVf"} unknown", SVfARG(name));
397             }
398              
399 4 50         if (
400 4 50         !(cvp = hv_fetchs(table, "charnames", FALSE)) ||
401 0 0         !SvOK(*cvp)
    0          
402             ) {
403 0           croak("Unknown charname '%"SVf"'", SVfARG(name));
404             }
405              
406             {
407 4           SV *r, *cv = *cvp;
408             {
409 4           dSP;
410              
411 4 50         PUSHSTACKi(PERLSI_OVERLOAD);
412 4           ENTER;
413 4           SAVETMPS;
414              
415 4 50         PUSHMARK(SP);
416 4 50         EXTEND(SP, 1);
417 4           PUSHs(name);
418 4           PUTBACK;
419              
420 4           call_sv(cv, G_SCALAR);
421 4           SPAGAIN;
422              
423 4           r = POPs;
424 4           SvREFCNT_inc_simple_void_NN(r);
425              
426 4           PUTBACK;
427 4 50         FREETMPS;
428 4           LEAVE;
429             }
430 4 50         POPSTACK;
431              
432 4 100         if (!SvOK(r)) {
    50          
    50          
433 1           SvREFCNT_dec(r);
434 1           croak("Unknown charname '%"SVf"'", SVfARG(name));
435             }
436              
437 3           sv_catsv(sv, r);
438 3           SvREFCNT_dec(r);
439 3           continue;
440             }
441             }
442              
443             break;
444             }
445              
446             default:
447 15 100         if (c >= '0' && c <= '7') {
    50          
448 0           u = c - '0';
449 0           c = lex_peek_unichar(0);
450 0 0         if (c >= '0' && c <= '7') {
    0          
451 0           u = u * 8 + (c - '0');
452 0           lex_read_unichar(0);
453 0           c = lex_peek_unichar(0);
454 0 0         if (c >= '0' && c <= '7') {
    0          
455 0           u = u * 8 + (c - '0');
456 0           lex_read_unichar(0);
457             }
458             }
459 0           c = u;
460             }
461 15           break;
462             }
463             }
464              
465 339           my_sv_cat_c(aTHX_ sv, c);
466 379           }
467              
468 75 100         if (SvCUR(sv) || !*gen_sentinel) {
    50          
469 63           my_op_cat_sv(aTHX_ gen_sentinel, sv);
470             }
471              
472             {
473 75           OP *gen = *gen_sentinel;
474 75           *gen_sentinel = NULL;
475              
476 75 100         if (gen->op_type == OP_CONST) {
477 47           SvPOK_only_UTF8(((SVOP *)gen)->op_sv);
478 28 100         } else if (gen->op_type != OP_CONCAT) {
479             /* can't do this because B::Deparse dies on it:
480             * gen = newUNOP(OP_STRINGIFY, 0, gen);
481             */
482 5           gen = newBINOP(OP_CONCAT, 0, gen, newSVOP(OP_CONST, 0, newSVpvs("")));
483             }
484              
485 75           return gen;
486             }
487             }
488              
489 25           static void parse_qc(pTHX_ OP **op_ptr) {
490             I32 c;
491              
492 25           c = lex_peek_unichar(0);
493              
494 25 50         if (c != '#') {
495 25           lex_read_space(0);
496 25           c = lex_peek_unichar(0);
497 25 50         if (c == -1) {
498 0           croak("Unexpected EOF after qc");
499             }
500             }
501 25           lex_read_unichar(0);
502              
503             {
504 25           I32 delim_start = c;
505 25           I32 delim_stop =
506 49 100         c == '(' ? ')' :
507 47 100         c == '[' ? ']' :
508 41 100         c == '{' ? '}' :
509 18 100         c == '<' ? '>' :
510             c
511             ;
512 25           int nesting = 0;
513 25           const QCSpec spec = {
514             delim_start, delim_stop,
515             NULL, NULL,
516             FLAG_BACKSLASH_ESCAPE
517             };
518              
519 25 100         *op_ptr = parse_qctail(aTHX_ &spec, delim_start == delim_stop ? NULL : &nesting);
520             }
521 24           }
522              
523 15           static void parse_qcw(pTHX_ OP **op_ptr) {
524             I32 c;
525              
526 15           c = lex_peek_unichar(0);
527              
528 15 50         if (c != '#') {
529 15           lex_read_space(0);
530 15           c = lex_peek_unichar(0);
531 15 50         if (c == -1) {
532 0           croak("Unexpected EOF after qcw");
533             }
534             }
535 15           lex_read_unichar(0);
536              
537             {
538 15           I32 delim_start = c;
539 15           I32 delim_stop =
540 23 100         c == '(' ? ')' :
541 15 100         c == '[' ? ']' :
542 11 100         c == '{' ? '}' :
543 4 50         c == '<' ? '>' :
544             c
545             ;
546 15           int nesting = 0;
547 15           const QCSpec spec = {
548             delim_start, delim_stop,
549             NULL, NULL,
550             FLAG_BACKSLASH_ESCAPE | FLAG_STOP_AT_SPACE
551             };
552             OP **gen_sentinel;
553              
554 15           Newx(gen_sentinel, 1, OP *);
555 15           *gen_sentinel = NULL;
556 15           SAVEDESTRUCTOR_X(free_ptr_op, gen_sentinel);
557              
558             for (;;) {
559             OP *cur;
560 63 50         while (c = lex_peek_unichar(0), c != -1 && isSPACE(c)) {
    50          
    100          
    100          
561 17           lex_read_unichar(0);
562             }
563 46 100         if (c == delim_stop && nesting == 0) {
    50          
564 15           lex_read_unichar(0);
565 15           break;
566             }
567 31 100         cur = parse_qctail(aTHX_ &spec, delim_start == delim_stop ? NULL : &nesting);
568 31           *gen_sentinel = op_append_elem(OP_LIST, *gen_sentinel, cur);
569 31           }
570              
571 15 100         if (*gen_sentinel) {
572 13           *op_ptr = *gen_sentinel;
573 13           *gen_sentinel = NULL;
574             } else {
575 2           *op_ptr = newNULLLIST();
576             }
577 15           (*op_ptr)->op_flags |= OPf_PARENS;
578             }
579 15           }
580              
581 21           static void parse_qc_to(pTHX_ OP **op_ptr) {
582             I32 c, qdelim;
583             SV *delim, *leftover;
584             line_t start;
585              
586 21           lex_read_space(0);
587 21 50         if (!strnEQ(PL_parser->bufptr, "<<", 2)) {
588 0           croak("Missing \"<<\" after qc_to");
589             }
590 21           lex_read_to(PL_parser->bufptr + 2);
591              
592 21           lex_read_space(0);
593 21           start = CopLINE(PL_curcop);
594 21           c = lex_peek_unichar(0);
595 21 100         if (!(c == '\'' || c == '"')) {
    50          
596 0           croak("Missing \"'\" or '\"' after qc_to <<");
597             }
598 21           qdelim = c;
599 21           lex_read_unichar(0);
600              
601 21           delim = sv_2mortal(newSVpvs(""));
602 21 50         if (lex_bufutf8()) {
603 0           SvUTF8_on(delim);
604             }
605              
606             for (;;) {
607 90           c = lex_read_unichar(0);
608 90 50         if (c == -1) {
609 0           CopLINE_set(PL_curcop, start);
610 0 0         croak("Can't find string terminator %s anywhere before EOF", qdelim == '"' ? "'\"'" : "\"'\"");
611             }
612 90 100         if (c == qdelim) {
613 21           break;
614             }
615 69           my_sv_cat_c(aTHX_ delim, c);
616 69           }
617              
618             {
619 21           char *fin = memchr(PL_parser->bufptr, '\n', PL_parser->bufend - PL_parser->bufptr);
620 21 50         if (fin) {
621 21           fin++;
622             } else {
623 0           fin = PL_parser->bufend;
624             }
625              
626 21 50         leftover = sv_2mortal(newSVpvn_utf8(PL_parser->bufptr, fin - PL_parser->bufptr, lex_bufutf8()));
627 21           lex_unstuff(fin);
628             }
629              
630             {
631 42           const QCSpec spec = {
632             -1, -1,
633             delim, leftover,
634 21 100         FLAG_HASH_INTERPOLATE | (qdelim == '"' ? FLAG_BACKSLASH_ESCAPE : 0)
635             };
636 21           *op_ptr = parse_qctail(aTHX_ &spec, NULL);
637             }
638 20           }
639              
640 64           static int qc_enabled(pTHX_ const char *hk_ptr, size_t hk_len) {
641             HV *hints;
642             SV *sv, **psv;
643              
644 64 50         if (!(hints = GvHV(PL_hintgv))) {
645 0           return FALSE;
646             }
647 64 100         if (!(psv = hv_fetch(hints, hk_ptr, hk_len, 0))) {
648 3           return FALSE;
649             }
650 61           sv = *psv;
651 61 50         return SvTRUE(sv);
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
652             }
653             #define qc_enableds(S) qc_enabled(aTHX_ STR_WITH_LEN(S))
654              
655 11750           static int my_keyword_plugin(pTHX_ char *keyword_ptr, STRLEN keyword_len, OP **op_ptr) {
656             int ret;
657              
658 11750 100         if (keyword_len == 2 && keyword_ptr[0] == 'q' && keyword_ptr[1] == 'c' && qc_enableds(HINTK_QC)) {
    100          
    100          
    100          
659 25           ENTER;
660 25           parse_qc(aTHX_ op_ptr);
661 24           LEAVE;
662 24           ret = KEYWORD_PLUGIN_EXPR;
663 11725 100         } else if (keyword_len == 5 && memcmp(keyword_ptr, "qc_to", 5) == 0 && qc_enableds(HINTK_QC_TO)) {
    100          
    50          
664 21           ENTER;
665 21           parse_qc_to(aTHX_ op_ptr);
666 20           LEAVE;
667 20           ret = KEYWORD_PLUGIN_EXPR;
668 11704 100         } else if (keyword_len == 3 && memcmp(keyword_ptr, "qcw", 3) == 0 && qc_enableds(HINTK_QCW)) {
    100          
    50          
669 15           ENTER;
670 15           parse_qcw(aTHX_ op_ptr);
671 15           LEAVE;
672 15           ret = KEYWORD_PLUGIN_EXPR;
673             } else {
674 11689           ret = next_keyword_plugin(aTHX_ keyword_ptr, keyword_len, op_ptr);
675             }
676              
677 11748           return ret;
678             }
679              
680              
681             WARNINGS_RESET
682              
683             MODULE = Quote::Code PACKAGE = Quote::Code
684             PROTOTYPES: ENABLE
685              
686             BOOT:
687             WARNINGS_ENABLE {
688 10           HV *const stash = gv_stashpvs(MY_PKG, GV_ADD);
689             /**/
690 10           newCONSTSUB(stash, "HINTK_QC", newSVpvs(HINTK_QC));
691 10           newCONSTSUB(stash, "HINTK_QC_TO", newSVpvs(HINTK_QC_TO));
692 10           newCONSTSUB(stash, "HINTK_QCW", newSVpvs(HINTK_QCW));
693             /**/
694 10           next_keyword_plugin = PL_keyword_plugin;
695 10           PL_keyword_plugin = my_keyword_plugin;
696             } WARNINGS_RESET