]> git.vpit.fr Git - perl/modules/re-engine-Plugin.git/blob - Plugin.xs
Get rid of smart matches
[perl/modules/re-engine-Plugin.git] / Plugin.xs
1 /* This file is part of the re::engine::Plugin Perl module.
2  * See http://search.cpan.org/dist/re-engine-Plugin/ */
3
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 /* --- Helpers ------------------------------------------------------------- */
10
11 #define XSH_PACKAGE "re::engine::Plugin"
12
13 #include "xsh/caps.h"
14 #include "xsh/util.h"
15
16 /* ... Lexical hints ....................................................... */
17
18 typedef struct {
19  SV *comp;
20  SV *exec;
21  SV *free;
22 } xsh_hints_user_t;
23
24 static SV *rep_validate_callback(SV *code) {
25  if (!SvROK(code))
26   return NULL;
27
28  code = SvRV(code);
29  if (SvTYPE(code) < SVt_PVCV)
30   return NULL;
31
32  return SvREFCNT_inc_simple_NN(code);
33 }
34
35 static void xsh_hints_user_init(pTHX_ xsh_hints_user_t *hv, xsh_hints_user_t *v) {
36  hv->comp = rep_validate_callback(v->comp);
37  hv->exec = rep_validate_callback(v->exec);
38  hv->free = rep_validate_callback(v->free);
39
40  return;
41 }
42
43 #if XSH_THREADSAFE
44
45 static void xsh_hints_user_clone(pTHX_ xsh_hints_user_t *nv, xsh_hints_user_t *ov, CLONE_PARAMS *params) {
46  nv->comp = xsh_dup_inc(ov->comp, params);
47  nv->exec = xsh_dup_inc(ov->exec, params);
48  nv->free = xsh_dup_inc(ov->free, params);
49
50  return;
51 }
52
53 #endif /* XSH_THREADSAFE */
54
55 static void xsh_hints_user_deinit(pTHX_ xsh_hints_user_t *hv) {
56  SvREFCNT_dec(hv->comp);
57  SvREFCNT_dec(hv->exec);
58  SvREFCNT_dec(hv->free);
59
60  return;
61 }
62
63 #define rep_hint() xsh_hints_detag(xsh_hints_fetch())
64
65 #define XSH_HINTS_TYPE_USER         1
66 #define XSH_HINTS_ONLY_COMPILE_TIME 0
67
68 #include "xsh/hints.h"
69
70 /* ... Thread-local storage ................................................ */
71
72 #define XSH_THREADS_USER_CONTEXT            0
73 #define XSH_THREADS_USER_LOCAL_SETUP        0
74 #define XSH_THREADS_USER_LOCAL_TEARDOWN     0
75 #define XSH_THREADS_USER_GLOBAL_TEARDOWN    0
76 #define XSH_THREADS_COMPILE_TIME_PROTECTION 0
77
78 #include "xsh/threads.h"
79
80 /* --- Custom regexp engine ------------------------------------------------ */
81
82 /* re__engine__Plugin self; SELF_FROM_PPRIVATE(self,rx->pprivate) */
83 #define SELF_FROM_PPRIVATE(self, pprivate) \
84  if (sv_isobject(pprivate)) {              \
85   SV *ref = SvRV((SV *) pprivate);         \
86   IV  tmp = SvIV((SV *) ref);              \
87   self = INT2PTR(re__engine__Plugin, tmp); \
88  } else {                                  \
89   Perl_croak(aTHX_ "Not an object");       \
90  }
91
92 #if XSH_HAS_PERL(5, 19, 4)
93 # define REP_ENG_EXEC_MINEND_TYPE SSize_t
94 #else
95 # define REP_ENG_EXEC_MINEND_TYPE I32
96 #endif
97
98 START_EXTERN_C
99 EXTERN_C const regexp_engine engine_plugin;
100 #if XSH_HAS_PERL(5, 11, 0)
101 EXTERN_C REGEXP * Plugin_comp(pTHX_ SV * const, U32);
102 #else
103 EXTERN_C REGEXP * Plugin_comp(pTHX_ const SV * const, const U32);
104 #endif
105 EXTERN_C I32      Plugin_exec(pTHX_ REGEXP * const, char *, char *,
106                               char *, REP_ENG_EXEC_MINEND_TYPE, SV *, void *, U32);
107 #if XSH_HAS_PERL(5, 19, 1)
108 EXTERN_C char *   Plugin_intuit(pTHX_ REGEXP * const, SV *, const char * const,
109                                 char *, char *, U32, re_scream_pos_data *);
110 #else
111 EXTERN_C char *   Plugin_intuit(pTHX_ REGEXP * const, SV *, char *,
112                                 char *, U32, re_scream_pos_data *);
113 #endif
114 EXTERN_C SV *     Plugin_checkstr(pTHX_ REGEXP * const);
115 EXTERN_C void     Plugin_free(pTHX_ REGEXP * const);
116 EXTERN_C void *   Plugin_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *);
117 EXTERN_C void     Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const,
118                                              const I32, SV * const);
119 EXTERN_C void     Plugin_numbered_buff_STORE(pTHX_ REGEXP * const,
120                                              const I32, SV const * const);
121 EXTERN_C I32      Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const,
122                                               const SV * const, const I32);
123 EXTERN_C SV *     Plugin_named_buff(pTHX_ REGEXP * const, SV * const,
124                                     SV * const, const U32);
125 EXTERN_C SV *     Plugin_named_buff_iter(pTHX_ REGEXP * const, const SV * const,
126                                          const U32);
127 EXTERN_C SV *     Plugin_package(pTHX_ REGEXP * const);
128 #ifdef USE_ITHREADS
129 EXTERN_C void *   Plugin_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *);
130 #endif
131
132 EXTERN_C const regexp_engine engine_plugin;
133 END_EXTERN_C
134
135 #define RE_ENGINE_PLUGIN (&engine_plugin)
136 const regexp_engine engine_plugin = {
137  Plugin_comp,
138  Plugin_exec,
139  Plugin_intuit,
140  Plugin_checkstr,
141  Plugin_free,
142  Plugin_numbered_buff_FETCH,
143  Plugin_numbered_buff_STORE,
144  Plugin_numbered_buff_LENGTH,
145  Plugin_named_buff,
146  Plugin_named_buff_iter,
147  Plugin_package
148 #if defined(USE_ITHREADS)
149  , Plugin_dupe
150 #endif
151 #if XSH_HAS_PERL(5, 17, 0)
152  , 0
153 #endif
154 };
155
156 typedef struct replug {
157  /* Pointer back to the containing regexp struct so that accessors
158   * can modify nparens, gofs, etc... */
159  struct regexp *rx;
160
161  /* A copy of the pattern given to comp, for ->pattern */
162  SV *pattern;
163
164  /* A copy of the string being matched against, for ->str */
165  SV *str;
166
167  /* The ->stash */
168  SV *stash;
169
170  /* Callbacks */
171  SV *cb_exec;
172  SV *cb_free;
173
174  /* ->num_captures */
175  SV *cb_num_capture_buff_FETCH;
176  SV *cb_num_capture_buff_STORE;
177  SV *cb_num_capture_buff_LENGTH;
178 } *re__engine__Plugin;
179
180 #if XSH_HAS_PERL(5, 11, 0)
181 # define rxREGEXP(RX)  (SvANY(RX))
182 # define newREGEXP(RX) ((RX) = ((REGEXP *) newSV_type(SVt_REGEXP)))
183 #else
184 # define rxREGEXP(RX)  (RX)
185 # define newREGEXP(RX) (Newxz((RX), 1, struct regexp))
186 #endif
187
188 REGEXP *
189 #if XSH_HAS_PERL(5, 11, 0)
190 Plugin_comp(pTHX_ SV * const pattern, U32 flags)
191 #else
192 Plugin_comp(pTHX_ const SV * const pattern, const U32 flags)
193 #endif
194 {
195  dSP;
196  struct regexp *rx;
197  REGEXP *RX;
198
199  re__engine__Plugin re;
200  const xsh_hints_user_t *h;
201
202  STRLEN plen;
203  char *pbuf;
204
205  SV *obj;
206
207  h = rep_hint();
208  if (!h) /* This looks like a pragma leak. Apply the default behaviour */
209   return re_compile(pattern, flags);
210
211  /* exp/xend version of the pattern & length */
212  pbuf = SvPV((SV *) pattern, plen);
213
214  /* Our blessed object */
215  obj = newSV(0);
216  Newxz(re, 1, struct replug);
217  sv_setref_pv(obj, XSH_PACKAGE, (void *) re);
218
219  newREGEXP(RX);
220  rx = rxREGEXP(RX);
221
222  re->rx       = rx;               /* Make the rx accessible from self->rx */
223  rx->intflags = flags;            /* Flags for internal use */
224  rx->extflags = flags;            /* Flags for perl to use */
225  rx->engine   = RE_ENGINE_PLUGIN; /* Compile to use this engine */
226
227 #if !XSH_HAS_PERL(5, 11, 0)
228  rx->refcnt   = 1;                /* Refcount so we won't be destroyed */
229
230  /* Precompiled pattern for pp_regcomp to use */
231  rx->prelen   = plen;
232  rx->precomp  = savepvn(pbuf, rx->prelen);
233
234  /* Set up qr// stringification to be equivalent to the supplied
235   * pattern, this should be done via overload eventually */
236  rx->wraplen  = rx->prelen;
237  Newx(rx->wrapped, rx->wraplen, char);
238  Copy(rx->precomp, rx->wrapped, rx->wraplen, char);
239 #endif
240
241  /* Store our private object */
242  rx->pprivate = obj;
243
244  /* Store the pattern for ->pattern */
245  re->pattern  = (SV *) pattern;
246  SvREFCNT_inc_simple_void(re->pattern);
247
248  /* If there's an exec callback, store it into the private object so that it
249   * will be the one to be called, even if the engine changes in between */
250  if (h->exec) {
251   re->cb_exec = h->exec;
252   SvREFCNT_inc_simple_void_NN(h->exec);
253  }
254
255  /* Same goes for the free callback, if there's one. */
256  if (h->free) {
257   re->cb_free = h->free;
258   SvREFCNT_inc_simple_void_NN(h->free);
259  }
260
261  re->cb_num_capture_buff_FETCH  = NULL;
262  re->cb_num_capture_buff_STORE  = NULL;
263  re->cb_num_capture_buff_LENGTH = NULL;
264
265  /* Call our callback function if one was defined, if not we've already set up
266   * all the stuff we're going to to need for subsequent exec and other calls */
267  if (h->comp) {
268   ENTER;
269   SAVETMPS;
270
271   PUSHMARK(SP);
272   XPUSHs(obj);
273   PUTBACK;
274
275   call_sv(h->comp, G_DISCARD);
276
277   FREETMPS;
278   LEAVE;
279  }
280
281  /* If any of the comp-time accessors were called we'll have to
282   * update the regexp struct with the new info */
283  Newxz(rx->offs, rx->nparens + 1, regexp_paren_pair);
284
285  return RX;
286 }
287
288 I32
289 Plugin_exec(pTHX_ REGEXP * const RX, char *stringarg, char *strend,
290             char *strbeg, REP_ENG_EXEC_MINEND_TYPE minend,
291             SV *sv, void *data, U32 flags)
292 {
293  struct regexp *rx;
294  re__engine__Plugin self;
295  I32 matched;
296
297  rx = rxREGEXP(RX);
298  SELF_FROM_PPRIVATE(self, rx->pprivate);
299
300  if (self->cb_exec) {
301   SV *ret;
302   dSP;
303
304   /* Store the current str for ->str */
305   SvREFCNT_dec(self->str);
306   self->str = sv;
307   SvREFCNT_inc_simple_void(self->str);
308
309   ENTER;
310   SAVETMPS;
311
312   PUSHMARK(SP);
313   XPUSHs(rx->pprivate);
314   XPUSHs(sv);
315   PUTBACK;
316
317   call_sv(self->cb_exec, G_SCALAR);
318
319   SPAGAIN;
320
321   ret = POPs;
322   if (SvTRUE(ret))
323    matched = 1;
324   else
325    matched = 0;
326
327   PUTBACK;
328   FREETMPS;
329   LEAVE;
330  } else {
331   matched = 0;
332  }
333
334  return matched;
335 }
336
337 char *
338 #if XSH_HAS_PERL(5, 19, 1)
339 Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, const char * const strbeg,
340               char *strpos, char *strend, U32 flags, re_scream_pos_data *data)
341 #else
342 Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, char *strpos,
343               char *strend, U32 flags, re_scream_pos_data *data)
344 #endif
345 {
346  PERL_UNUSED_ARG(RX);
347  PERL_UNUSED_ARG(sv);
348 #if XSH_HAS_PERL(5, 19, 1)
349  PERL_UNUSED_ARG(strbeg);
350 #endif
351  PERL_UNUSED_ARG(strpos);
352  PERL_UNUSED_ARG(strend);
353  PERL_UNUSED_ARG(flags);
354  PERL_UNUSED_ARG(data);
355
356  return NULL;
357 }
358
359 SV *
360 Plugin_checkstr(pTHX_ REGEXP * const RX)
361 {
362  PERL_UNUSED_ARG(RX);
363
364  return NULL;
365 }
366
367 void
368 Plugin_free(pTHX_ REGEXP * const RX)
369 {
370  struct regexp *rx;
371  re__engine__Plugin self;
372  SV *callback;
373  dSP;
374
375  if (PL_dirty)
376   return;
377
378  rx = rxREGEXP(RX);
379  SELF_FROM_PPRIVATE(self, rx->pprivate);
380
381  callback = self->cb_free;
382
383  if (callback) {
384   ENTER;
385   SAVETMPS;
386
387   PUSHMARK(SP);
388   XPUSHs(rx->pprivate);
389   PUTBACK;
390
391   call_sv(callback, G_DISCARD);
392
393   PUTBACK;
394   FREETMPS;
395   LEAVE;
396  }
397
398  SvREFCNT_dec(self->pattern);
399  SvREFCNT_dec(self->str);
400  SvREFCNT_dec(self->stash);
401
402  SvREFCNT_dec(self->cb_exec);
403
404  SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
405  SvREFCNT_dec(self->cb_num_capture_buff_STORE);
406  SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
407
408  self->rx = NULL;
409
410  Safefree(self);
411
412  SvREFCNT_dec(rx->pprivate);
413
414  return;
415 }
416
417 void *
418 Plugin_dupe(pTHX_ REGEXP * const RX, CLONE_PARAMS *param)
419 {
420  struct regexp *rx = rxREGEXP(RX);
421
422  Perl_croak(aTHX_ "dupe not supported yet");
423
424  return rx->pprivate;
425 }
426
427
428 void
429 Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const RX, const I32 paren,
430                            SV * const sv)
431 {
432  struct regexp *rx;
433  re__engine__Plugin self;
434  SV *callback;
435
436  rx = rxREGEXP(RX);
437  SELF_FROM_PPRIVATE(self, rx->pprivate);
438
439  callback = self->cb_num_capture_buff_FETCH;
440
441  if (callback) {
442   I32 items;
443   dSP;
444
445   ENTER;
446   SAVETMPS;
447
448   PUSHMARK(SP);
449   XPUSHs(rx->pprivate);
450   XPUSHs(sv_2mortal(newSViv(paren)));
451   PUTBACK;
452
453   items = call_sv(callback, G_SCALAR);
454
455   if (items == 1) {
456    SV *ret;
457    SPAGAIN;
458    ret = POPs;
459    sv_setsv(sv, ret);
460   } else {
461    sv_setsv(sv, &PL_sv_undef);
462   }
463
464   PUTBACK;
465   FREETMPS;
466   LEAVE;
467  } else {
468   sv_setsv(sv, &PL_sv_undef);
469  }
470 }
471
472 void
473 Plugin_numbered_buff_STORE(pTHX_ REGEXP * const RX, const I32 paren,
474                            SV const * const value)
475 {
476  struct regexp *rx;
477  re__engine__Plugin self;
478  SV *callback;
479
480  rx = rxREGEXP(RX);
481  SELF_FROM_PPRIVATE(self, rx->pprivate);
482
483  callback = self->cb_num_capture_buff_STORE;
484
485  if (callback) {
486   dSP;
487
488   ENTER;
489   SAVETMPS;
490
491   PUSHMARK(SP);
492   XPUSHs(rx->pprivate);
493   XPUSHs(sv_2mortal(newSViv(paren)));
494   XPUSHs((SV *) value);
495   PUTBACK;
496
497   call_sv(callback, G_DISCARD);
498
499   PUTBACK;
500   FREETMPS;
501   LEAVE;
502  }
503 }
504
505 I32
506 Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const RX, const SV * const sv,
507                             const I32 paren)
508 {
509  struct regexp *rx;
510  re__engine__Plugin self;
511  SV *callback;
512
513  rx = rxREGEXP(RX);
514  SELF_FROM_PPRIVATE(self, rx->pprivate);
515
516  callback = self->cb_num_capture_buff_LENGTH;
517
518  if (callback) {
519   IV ret;
520   dSP;
521
522   ENTER;
523   SAVETMPS;
524
525   PUSHMARK(SP);
526   XPUSHs(rx->pprivate);
527   XPUSHs(sv_2mortal(newSViv(paren)));
528   PUTBACK;
529
530   call_sv(callback, G_SCALAR);
531
532   SPAGAIN;
533
534   ret = POPi;
535
536   PUTBACK;
537   FREETMPS;
538   LEAVE;
539
540   return (I32) ret;
541  } else {
542   /* TODO: call FETCH and get the length on that value */
543   return 0;
544  }
545 }
546
547 SV *
548 Plugin_named_buff(pTHX_ REGEXP * const RX, SV * const key, SV * const value,
549                   const U32 flags)
550 {
551  return NULL;
552 }
553
554 SV *
555 Plugin_named_buff_iter(pTHX_ REGEXP * const RX, const SV * const lastkey,
556                        const U32 flags)
557 {
558  return NULL;
559 }
560
561 SV *
562 Plugin_package(pTHX_ REGEXP * const RX)
563 {
564  PERL_UNUSED_ARG(RX);
565
566  return newSVpvs(XSH_PACKAGE);
567 }
568
569 static void xsh_user_global_setup(pTHX) {
570  HV *stash;
571
572  stash = gv_stashpvn(XSH_PACKAGE, XSH_PACKAGE_LEN, 1);
573  newCONSTSUB(stash, "REP_THREADSAFE", newSVuv(XSH_THREADSAFE));
574  newCONSTSUB(stash, "REP_FORKSAFE",   newSVuv(XSH_FORKSAFE));
575
576  return;
577 }
578
579 /* --- XS ------------------------------------------------------------------ */
580
581 MODULE = re::engine::Plugin       PACKAGE = re::engine::Plugin
582
583 PROTOTYPES: DISABLE
584
585 BOOT:
586 {
587  xsh_setup();
588 }
589
590 #if XSH_THREADSAFE
591
592 void
593 CLONE(...)
594 PPCODE:
595  xsh_clone();
596  XSRETURN(0);
597
598 #endif /* XSH_THREADSAFE */
599
600 void
601 pattern(re::engine::Plugin self, ...)
602 PPCODE:
603  XPUSHs(self->pattern);
604  XSRETURN(1);
605
606 void
607 str(re::engine::Plugin self, ...)
608 PPCODE:
609  XPUSHs(self->str);
610  XSRETURN(1);
611
612 void
613 mod(re::engine::Plugin self)
614 PREINIT:
615  U32 flags;
616  char mods[5 + 1];
617  int n = 0, i;
618 PPCODE:
619  flags = self->rx->intflags;
620  if (flags & PMf_FOLD)         /* /i */
621   mods[n++] = 'i';
622  if (flags & PMf_MULTILINE)    /* /m */
623   mods[n++] = 'm';
624  if (flags & PMf_SINGLELINE)   /* /s */
625   mods[n++] = 's';
626  if (flags & PMf_EXTENDED)     /* /x */
627   mods[n++] = 'x';
628  if (flags & RXf_PMf_KEEPCOPY) /* /p */
629   mods[n++] = 'p';
630  mods[n] = '\0';
631  EXTEND(SP, 2 * n);
632  for (i = 0; i < n; ++i) {
633   mPUSHp(mods + i, 1);
634   PUSHs(&PL_sv_yes);
635  }
636  XSRETURN(2 * n);
637
638 void
639 stash(re::engine::Plugin self, ...)
640 PPCODE:
641  if (items > 1) {
642   SvREFCNT_dec(self->stash);
643   self->stash = ST(1);
644   SvREFCNT_inc_simple_void(self->stash);
645   XSRETURN_EMPTY;
646  } else {
647   XPUSHs(self->stash);
648   XSRETURN(1);
649  }
650
651 void
652 minlen(re::engine::Plugin self, ...)
653 PPCODE:
654  if (items > 1) {
655   self->rx->minlen = (I32)SvIV(ST(1));
656   XSRETURN_EMPTY;
657  } else {
658   if (self->rx->minlen) {
659    XPUSHs(sv_2mortal(newSViv(self->rx->minlen)));
660   } else {
661    XPUSHs(sv_2mortal(&PL_sv_undef));
662   }
663   XSRETURN(1);
664  }
665
666 void
667 gofs(re::engine::Plugin self, ...)
668 PPCODE:
669  if (items > 1) {
670   self->rx->gofs = (U32)SvIV(ST(1));
671   XSRETURN_EMPTY;
672  } else {
673   if (self->rx->gofs) {
674    XPUSHs(sv_2mortal(newSVuv(self->rx->gofs)));
675   } else {
676    XPUSHs(sv_2mortal(&PL_sv_undef));
677   }
678   XSRETURN(1);
679  }
680
681 void
682 nparens(re::engine::Plugin self, ...)
683 PPCODE:
684  if (items > 1) {
685   self->rx->nparens = (U32)SvIV(ST(1));
686   XSRETURN_EMPTY;
687  } else {
688   if (self->rx->nparens) {
689    XPUSHs(sv_2mortal(newSVuv(self->rx->nparens)));
690   } else {
691    XPUSHs(sv_2mortal(&PL_sv_undef));
692   }
693   XSRETURN(1);
694  }
695
696 void
697 _exec(re::engine::Plugin self, ...)
698 PPCODE:
699  if (items > 1) {
700   SvREFCNT_dec(self->cb_exec);
701   self->cb_exec = ST(1);
702   SvREFCNT_inc_simple_void(self->cb_exec);
703  }
704  XSRETURN(0);
705
706 void
707 _free(re::engine::Plugin self, ...)
708 PPCODE:
709  if (items > 1) {
710   SvREFCNT_dec(self->cb_free);
711   self->cb_free = ST(1);
712   SvREFCNT_inc_simple_void(self->cb_free);
713  }
714  XSRETURN(0);
715
716 void
717 _num_capture_buff_FETCH(re::engine::Plugin self, ...)
718 PPCODE:
719  if (items > 1) {
720   SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
721   self->cb_num_capture_buff_FETCH = ST(1);
722   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_FETCH);
723  }
724  XSRETURN(0);
725
726 void
727 _num_capture_buff_STORE(re::engine::Plugin self, ...)
728 PPCODE:
729  if (items > 1) {
730   SvREFCNT_dec(self->cb_num_capture_buff_STORE);
731   self->cb_num_capture_buff_STORE = ST(1);
732   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_STORE);
733  }
734  XSRETURN(0);
735
736 void
737 _num_capture_buff_LENGTH(re::engine::Plugin self, ...)
738 PPCODE:
739  if (items > 1) {
740   SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
741   self->cb_num_capture_buff_LENGTH = ST(1);
742   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_LENGTH);
743  }
744  XSRETURN(0);
745
746 SV *
747 _tag(SV *comp, SV *exec, SV *free)
748 PREINIT:
749  xsh_hints_user_t arg;
750 CODE:
751  arg.comp = comp;
752  arg.exec = exec;
753  arg.free = free;
754  RETVAL = xsh_hints_tag(&arg);
755 OUTPUT:
756  RETVAL
757
758 void
759 ENGINE()
760 PPCODE:
761  XPUSHs(sv_2mortal(newSViv(PTR2IV(&engine_plugin))));
762  XSRETURN(1);