File Coverage

lib/Eshu.xs
Criterion Covered Total %
statement 378 495 76.3
branch 345 648 53.2
condition n/a
subroutine n/a
pod n/a
total 723 1143 63.2


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             /*
7             * On Windows threaded Perl (MULTIPLICITY), perl.h redefines standard C
8             * functions (malloc, free, stat, opendir, etc.) as macros that require
9             * the interpreter context (my_perl). Our engine headers are pure C and
10             * must call the real libc functions, so undo those overrides here.
11             * This is safe: the XS code below uses Perl API directly and never
12             * relies on these macros.
13             */
14             #ifdef WIN32
15             # undef malloc
16             # undef realloc
17             # undef calloc
18             # undef free
19             # undef stat
20             # undef lstat
21             # undef fstat
22             # undef opendir
23             # undef readdir
24             # undef closedir
25             # undef rename
26             # undef open
27             # undef close
28             # undef read
29             # undef write
30             #endif
31              
32             #include "eshu.h"
33             #include "eshu_c.h"
34             #include "eshu_pod.h"
35             #include "eshu_pl.h"
36             #include "eshu_xs.h"
37             #include "eshu_js.h"
38             #include "eshu_xml.h"
39             #include "eshu_css.h"
40             #include "eshu_diff.h"
41             #include "eshu_file.h"
42              
43             MODULE = Eshu PACKAGE = Eshu
44              
45             PROTOTYPES: DISABLE
46              
47             SV *
48             detect_lang(class, filename_sv)
49             SV * class
50             SV * filename_sv
51             CODE:
52             {
53             const char *filename;
54             const char *dot;
55             STRLEN len;
56              
57             PERL_UNUSED_VAR(class);
58 37           RETVAL = &PL_sv_undef;
59              
60 37 50         if (SvOK(filename_sv)) {
61 37           filename = SvPV(filename_sv, len);
62 37           dot = NULL;
63             /* find last dot */
64             {
65 37           const char *p = filename + len;
66 133 100         while (p > filename) {
67 132           p--;
68 132 100         if (*p == '.') { dot = p + 1; break; }
69             }
70             }
71 37 100         if (dot) {
72 36           STRLEN ext_len = (filename + len) - dot;
73 36 100         if ((ext_len == 1 && (dot[0] == 'c' || dot[0] == 'C'))
    100          
    50          
74 28 100         || (ext_len == 1 && (dot[0] == 'h' || dot[0] == 'H'))) {
    100          
    50          
75 9           RETVAL = newSVpvs("c");
76 27 100         } else if (ext_len == 2
77 7 100         && (dot[0] == 'x' || dot[0] == 'X')
    50          
78 1 50         && (dot[1] == 's' || dot[1] == 'S')) {
    0          
79 1           RETVAL = newSVpvs("xs");
80 26 100         } else if ((ext_len == 2
81 6 100         && (dot[0] == 'p' || dot[0] == 'P')
    50          
82 2 100         && (dot[1] == 'l' || dot[1] == 'L'))
    50          
83 25 100         || (ext_len == 2
84 5 100         && (dot[0] == 'p' || dot[0] == 'P')
    50          
85 1 50         && (dot[1] == 'm' || dot[1] == 'M'))
    0          
86 24 100         || (ext_len == 1
87 1 50         && (dot[0] == 't' || dot[0] == 'T'))) {
    0          
88 3           RETVAL = newSVpvs("perl");
89 23 100         } else if ((ext_len == 3
90 13 100         && (dot[0] == 'x' || dot[0] == 'X')
    50          
91 2 100         && (dot[1] == 'm' || dot[1] == 'M')
    50          
92 1 50         && (dot[2] == 'l' || dot[2] == 'L'))
    0          
93 22 100         || (ext_len == 3
94 12 100         && (dot[0] == 'x' || dot[0] == 'X')
    50          
95 1 50         && (dot[1] == 's' || dot[1] == 'S')
    0          
96 1 50         && (dot[2] == 'l' || dot[2] == 'L'))
    0          
97 21 100         || (ext_len == 4
98 5 100         && (dot[0] == 'x' || dot[0] == 'X')
    50          
99 1 50         && (dot[1] == 's' || dot[1] == 'S')
    0          
100 1 50         && (dot[2] == 'l' || dot[2] == 'L')
    0          
101 1 50         && (dot[3] == 't' || dot[3] == 'T'))
    0          
102 20 100         || (ext_len == 3
103 11 100         && (dot[0] == 's' || dot[0] == 'S')
    50          
104 1 50         && (dot[1] == 'v' || dot[1] == 'V')
    0          
105 1 50         && (dot[2] == 'g' || dot[2] == 'G'))
    0          
106 19 100         || (ext_len == 5
107 1 50         && (dot[0] == 'x' || dot[0] == 'X')
    0          
108 1 50         && (dot[1] == 'h' || dot[1] == 'H')
    0          
109 1 50         && (dot[2] == 't' || dot[2] == 'T')
    0          
110 1 50         && (dot[3] == 'm' || dot[3] == 'M')
    0          
111 1 50         && (dot[4] == 'l' || dot[4] == 'L'))) {
    0          
112 5           RETVAL = newSVpvs("xml");
113 18 100         } else if ((ext_len == 4
114 4 100         && (dot[0] == 'h' || dot[0] == 'H')
    50          
115 1 50         && (dot[1] == 't' || dot[1] == 'T')
    0          
116 1 50         && (dot[2] == 'm' || dot[2] == 'M')
    0          
117 1 50         && (dot[3] == 'l' || dot[3] == 'L'))
    0          
118 17 100         || (ext_len == 3
119 10 100         && (dot[0] == 'h' || dot[0] == 'H')
    50          
120 1 50         && (dot[1] == 't' || dot[1] == 'T')
    0          
121 1 50         && (dot[2] == 'm' || dot[2] == 'M'))
    0          
122 16 100         || (ext_len == 4
123 3 100         && (dot[0] == 't' || dot[0] == 'T')
    50          
124 1 50         && (dot[1] == 'm' || dot[1] == 'M')
    0          
125 1 50         && (dot[2] == 'p' || dot[2] == 'P')
    0          
126 1 50         && (dot[3] == 'l' || dot[3] == 'L'))
    0          
127 15 100         || (ext_len == 2
128 4 100         && (dot[0] == 't' || dot[0] == 'T')
    50          
129 2 100         && (dot[1] == 't' || dot[1] == 'T'))
    50          
130 14 100         || (ext_len == 2
131 3 100         && (dot[0] == 'e' || dot[0] == 'E')
    50          
132 1 50         && (dot[1] == 'p' || dot[1] == 'P'))) {
    0          
133 5           RETVAL = newSVpvs("html");
134 13 100         } else if ((ext_len == 3
135 9 100         && (dot[0] == 'c' || dot[0] == 'C')
    100          
136 3 100         && (dot[1] == 's' || dot[1] == 'S')
    100          
137 2 100         && (dot[2] == 's' || dot[2] == 'S'))
    50          
138 11 100         || (ext_len == 4
139 2 100         && (dot[0] == 's' || dot[0] == 'S')
    50          
140 1 50         && (dot[1] == 'c' || dot[1] == 'C')
    0          
141 1 50         && (dot[2] == 's' || dot[2] == 'S')
    0          
142 1 50         && (dot[3] == 's' || dot[3] == 'S'))
    0          
143 10 100         || (ext_len == 4
144 1 50         && (dot[0] == 'l' || dot[0] == 'L')
    0          
145 1 50         && (dot[1] == 'e' || dot[1] == 'E')
    0          
146 1 50         && (dot[2] == 's' || dot[2] == 'S')
    0          
147 1 50         && (dot[3] == 's' || dot[3] == 'S'))) {
    0          
148 4           RETVAL = newSVpvs("css");
149 9 100         } else if ((ext_len == 2
150 2 100         && (dot[0] == 'j' || dot[0] == 'J')
    50          
151 1 50         && (dot[1] == 's' || dot[1] == 'S'))
    0          
152 8 100         || (ext_len == 3
153 7 100         && (dot[0] == 'j' || dot[0] == 'J')
    50          
154 1 50         && (dot[1] == 's' || dot[1] == 'S')
    0          
155 1 50         && (dot[2] == 'x' || dot[2] == 'X'))
    0          
156 7 100         || (ext_len == 3
157 6 100         && (dot[0] == 'm' || dot[0] == 'M')
    50          
158 2 100         && (dot[1] == 'j' || dot[1] == 'J')
    50          
159 1 50         && (dot[2] == 's' || dot[2] == 'S'))
    0          
160 6 100         || (ext_len == 3
161 5 100         && (dot[0] == 'c' || dot[0] == 'C')
    50          
162 1 50         && (dot[1] == 'j' || dot[1] == 'J')
    0          
163 1 50         && (dot[2] == 's' || dot[2] == 'S'))
    0          
164 5 100         || (ext_len == 2
165 1 50         && (dot[0] == 't' || dot[0] == 'T')
    0          
166 1 50         && (dot[1] == 's' || dot[1] == 'S'))
    0          
167 4 50         || (ext_len == 3
168 4 100         && (dot[0] == 't' || dot[0] == 'T')
    50          
169 2 100         && (dot[1] == 's' || dot[1] == 'S')
    50          
170 1 50         && (dot[2] == 'x' || dot[2] == 'X'))
    0          
171 3 50         || (ext_len == 3
172 3 100         && (dot[0] == 'm' || dot[0] == 'M')
    50          
173 1 50         && (dot[1] == 't' || dot[1] == 'T')
    0          
174 1 50         && (dot[2] == 's' || dot[2] == 'S'))) {
    0          
175 7           RETVAL = newSVpvs("js");
176 2 50         } else if (ext_len == 3
177 2 100         && (dot[0] == 'p' || dot[0] == 'P')
    50          
178 1 50         && (dot[1] == 'o' || dot[1] == 'O')
    0          
179 1 50         && (dot[2] == 'd' || dot[2] == 'D')) {
    0          
180 1           RETVAL = newSVpvs("pod");
181             }
182             }
183             }
184             }
185             OUTPUT:
186             RETVAL
187              
188             SV *
189             indent_c(class, src_sv, ...)
190             SV * class
191             SV * src_sv
192             CODE:
193             {
194             const char *src;
195             STRLEN src_len;
196             eshu_config_t cfg;
197             char *result;
198             size_t out_len;
199             int i;
200              
201             PERL_UNUSED_VAR(class);
202 59           src = SvPV(src_sv, src_len);
203 59           cfg = eshu_default_config();
204              
205 66 100         for (i = 2; i + 1 < items; i += 2) {
206 7           const char *key = SvPV_nolen(ST(i));
207 7           SV *val = ST(i + 1);
208 7 100         if (strEQ(key, "indent_char")) {
209 1           const char *ic = SvPV_nolen(val);
210 1 50         cfg.indent_char = (*ic == ' ') ? ' ' : '\t';
211 6 100         } else if (strEQ(key, "indent_width")) {
212 1           cfg.indent_width = SvIV(val);
213 5 100         } else if (strEQ(key, "indent_pp")) {
214 1           cfg.indent_pp = SvTRUE(val) ? 1 : 0;
215 4 100         } else if (strEQ(key, "range_start")) {
216 2           cfg.range_start = SvIV(val);
217 2 50         } else if (strEQ(key, "range_end")) {
218 2           cfg.range_end = SvIV(val);
219             }
220             }
221              
222 59           result = eshu_indent_c(src, (size_t)src_len, &cfg, &out_len);
223 59           RETVAL = newSVpvn(result, out_len);
224 59           free(result);
225             }
226             OUTPUT:
227             RETVAL
228              
229             SV *
230             indent_pl(class, src_sv, ...)
231             SV * class
232             SV * src_sv
233             CODE:
234             {
235             const char *src;
236             STRLEN src_len;
237             eshu_config_t cfg;
238             char *result;
239             size_t out_len;
240             int i;
241              
242             PERL_UNUSED_VAR(class);
243 82           src = SvPV(src_sv, src_len);
244 82           cfg = eshu_default_config();
245 82           cfg.lang = ESHU_LANG_PERL;
246              
247 86 100         for (i = 2; i + 1 < items; i += 2) {
248 4           const char *key = SvPV_nolen(ST(i));
249 4           SV *val = ST(i + 1);
250 4 100         if (strEQ(key, "indent_char")) {
251 1           const char *ic = SvPV_nolen(val);
252 1 50         cfg.indent_char = (*ic == ' ') ? ' ' : '\t';
253 3 100         } else if (strEQ(key, "indent_width")) {
254 1           cfg.indent_width = SvIV(val);
255 2 100         } else if (strEQ(key, "range_start")) {
256 1           cfg.range_start = SvIV(val);
257 1 50         } else if (strEQ(key, "range_end")) {
258 1           cfg.range_end = SvIV(val);
259             }
260             }
261              
262 82           result = eshu_indent_pl(src, (size_t)src_len, &cfg, &out_len);
263 82           RETVAL = newSVpvn(result, out_len);
264 82           free(result);
265             }
266             OUTPUT:
267             RETVAL
268              
269             SV *
270             indent_xs(class, src_sv, ...)
271             SV * class
272             SV * src_sv
273             CODE:
274             {
275             const char *src;
276             STRLEN src_len;
277             eshu_config_t cfg;
278             char *result;
279             size_t out_len;
280             int i;
281              
282             PERL_UNUSED_VAR(class);
283 32           src = SvPV(src_sv, src_len);
284 32           cfg = eshu_default_config();
285 32           cfg.lang = ESHU_LANG_XS;
286              
287 32 50         for (i = 2; i + 1 < items; i += 2) {
288 0           const char *key = SvPV_nolen(ST(i));
289 0           SV *val = ST(i + 1);
290 0 0         if (strEQ(key, "indent_char")) {
291 0           const char *ic = SvPV_nolen(val);
292 0 0         cfg.indent_char = (*ic == ' ') ? ' ' : '\t';
293 0 0         } else if (strEQ(key, "indent_width")) {
294 0           cfg.indent_width = SvIV(val);
295 0 0         } else if (strEQ(key, "indent_pp")) {
296 0           cfg.indent_pp = SvTRUE(val) ? 1 : 0;
297 0 0         } else if (strEQ(key, "range_start")) {
298 0           cfg.range_start = SvIV(val);
299 0 0         } else if (strEQ(key, "range_end")) {
300 0           cfg.range_end = SvIV(val);
301             }
302             }
303              
304 32           result = eshu_indent_xs(src, (size_t)src_len, &cfg, &out_len);
305 32           RETVAL = newSVpvn(result, out_len);
306 32           free(result);
307             }
308             OUTPUT:
309             RETVAL
310              
311             SV *
312             indent_xml(class, src_sv, ...)
313             SV * class
314             SV * src_sv
315             CODE:
316             {
317             const char *src;
318             STRLEN src_len;
319             eshu_config_t cfg;
320             char *result;
321             size_t out_len;
322             int i;
323              
324             PERL_UNUSED_VAR(class);
325 33           src = SvPV(src_sv, src_len);
326 33           cfg = eshu_default_config();
327 33           cfg.lang = ESHU_LANG_XML;
328              
329 43 100         for (i = 2; i + 1 < items; i += 2) {
330 10           const char *key = SvPV_nolen(ST(i));
331 10           SV *val = ST(i + 1);
332 10 100         if (strEQ(key, "indent_char")) {
333 1           const char *ic = SvPV_nolen(val);
334 1 50         cfg.indent_char = (*ic == ' ') ? ' ' : '\t';
335 9 100         } else if (strEQ(key, "indent_width")) {
336 1           cfg.indent_width = SvIV(val);
337 8 50         } else if (strEQ(key, "lang")) {
338 8           const char *l = SvPV_nolen(val);
339 8 50         if (strEQ(l, "html") || strEQ(l, "htm")) {
    0          
340 8           cfg.lang = ESHU_LANG_HTML;
341             }
342 0 0         } else if (strEQ(key, "range_start")) {
343 0           cfg.range_start = SvIV(val);
344 0 0         } else if (strEQ(key, "range_end")) {
345 0           cfg.range_end = SvIV(val);
346             }
347             }
348              
349 33           result = eshu_indent_xml(src, (size_t)src_len, &cfg, &out_len);
350 33           RETVAL = newSVpvn(result, out_len);
351 33           free(result);
352             }
353             OUTPUT:
354             RETVAL
355              
356             SV *
357             indent_html(class, src_sv, ...)
358             SV * class
359             SV * src_sv
360             CODE:
361             {
362             const char *src;
363             STRLEN src_len;
364             eshu_config_t cfg;
365             char *result;
366             size_t out_len;
367             int i;
368              
369             PERL_UNUSED_VAR(class);
370 7           src = SvPV(src_sv, src_len);
371 7           cfg = eshu_default_config();
372 7           cfg.lang = ESHU_LANG_HTML;
373              
374 7 50         for (i = 2; i + 1 < items; i += 2) {
375 0           const char *key = SvPV_nolen(ST(i));
376 0           SV *val = ST(i + 1);
377 0 0         if (strEQ(key, "indent_char")) {
378 0           const char *ic = SvPV_nolen(val);
379 0 0         cfg.indent_char = (*ic == ' ') ? ' ' : '\t';
380 0 0         } else if (strEQ(key, "indent_width")) {
381 0           cfg.indent_width = SvIV(val);
382 0 0         } else if (strEQ(key, "range_start")) {
383 0           cfg.range_start = SvIV(val);
384 0 0         } else if (strEQ(key, "range_end")) {
385 0           cfg.range_end = SvIV(val);
386             }
387             }
388              
389 7           result = eshu_indent_xml(src, (size_t)src_len, &cfg, &out_len);
390 7           RETVAL = newSVpvn(result, out_len);
391 7           free(result);
392             }
393             OUTPUT:
394             RETVAL
395              
396             SV *
397             indent_css(class, src_sv, ...)
398             SV * class
399             SV * src_sv
400             CODE:
401             {
402             const char *src;
403             STRLEN src_len;
404             eshu_config_t cfg;
405             char *result;
406             size_t out_len;
407             int i;
408              
409             PERL_UNUSED_VAR(class);
410 29           src = SvPV(src_sv, src_len);
411 29           cfg = eshu_default_config();
412 29           cfg.lang = ESHU_LANG_CSS;
413              
414 31 100         for (i = 2; i + 1 < items; i += 2) {
415 2           const char *key = SvPV_nolen(ST(i));
416 2           SV *val = ST(i + 1);
417 2 100         if (strEQ(key, "indent_char")) {
418 1           const char *ic = SvPV_nolen(val);
419 1 50         cfg.indent_char = (*ic == ' ') ? ' ' : '\t';
420 1 50         } else if (strEQ(key, "indent_width")) {
421 1           cfg.indent_width = SvIV(val);
422 0 0         } else if (strEQ(key, "range_start")) {
423 0           cfg.range_start = SvIV(val);
424 0 0         } else if (strEQ(key, "range_end")) {
425 0           cfg.range_end = SvIV(val);
426             }
427             }
428              
429 29           result = eshu_indent_css(src, (size_t)src_len, &cfg, &out_len);
430 29           RETVAL = newSVpvn(result, out_len);
431 29           free(result);
432             }
433             OUTPUT:
434             RETVAL
435              
436             SV *
437             indent_js(class, src_sv, ...)
438             SV * class
439             SV * src_sv
440             CODE:
441             {
442             const char *src;
443             STRLEN src_len;
444             eshu_config_t cfg;
445             char *result;
446             size_t out_len;
447             int i;
448              
449             PERL_UNUSED_VAR(class);
450 38           src = SvPV(src_sv, src_len);
451 38           cfg = eshu_default_config();
452 38           cfg.lang = ESHU_LANG_JS;
453              
454 42 100         for (i = 2; i + 1 < items; i += 2) {
455 4           const char *key = SvPV_nolen(ST(i));
456 4           SV *val = ST(i + 1);
457 4 100         if (strEQ(key, "indent_char")) {
458 1           const char *ic = SvPV_nolen(val);
459 1 50         cfg.indent_char = (*ic == ' ') ? ' ' : '\t';
460 3 100         } else if (strEQ(key, "indent_width")) {
461 1           cfg.indent_width = SvIV(val);
462 2 100         } else if (strEQ(key, "range_start")) {
463 1           cfg.range_start = SvIV(val);
464 1 50         } else if (strEQ(key, "range_end")) {
465 1           cfg.range_end = SvIV(val);
466             }
467             }
468              
469 38           result = eshu_indent_js(src, (size_t)src_len, &cfg, &out_len);
470 38           RETVAL = newSVpvn(result, out_len);
471 38           free(result);
472             }
473             OUTPUT:
474             RETVAL
475              
476             SV *
477             indent_pod(class, src_sv, ...)
478             SV * class
479             SV * src_sv
480             CODE:
481             {
482             const char *src;
483             STRLEN src_len;
484             eshu_config_t cfg;
485             char *result;
486             size_t out_len;
487             int i;
488              
489             PERL_UNUSED_VAR(class);
490 14           src = SvPV(src_sv, src_len);
491 14           cfg = eshu_default_config();
492 14           cfg.lang = ESHU_LANG_POD;
493              
494 14 50         for (i = 2; i + 1 < items; i += 2) {
495 0           const char *key = SvPV_nolen(ST(i));
496 0           SV *val = ST(i + 1);
497 0 0         if (strEQ(key, "indent_char")) {
498 0           const char *ic = SvPV_nolen(val);
499 0 0         cfg.indent_char = (*ic == ' ') ? ' ' : '\t';
500 0 0         } else if (strEQ(key, "indent_width")) {
501 0           cfg.indent_width = SvIV(val);
502 0 0         } else if (strEQ(key, "range_start")) {
503 0           cfg.range_start = SvIV(val);
504 0 0         } else if (strEQ(key, "range_end")) {
505 0           cfg.range_end = SvIV(val);
506             }
507             }
508              
509 14           result = eshu_indent_pod(src, (size_t)src_len, &cfg, &out_len);
510 14           RETVAL = newSVpvn(result, out_len);
511 14           free(result);
512             }
513             OUTPUT:
514             RETVAL
515              
516             SV *
517             indent_string(class, src_sv, ...)
518             SV * class
519             SV * src_sv
520             CODE:
521             {
522             const char *src;
523             STRLEN src_len;
524             eshu_config_t cfg;
525             char *result;
526             size_t out_len;
527 19           const char *lang = "c";
528             int i;
529              
530             PERL_UNUSED_VAR(class);
531 19           src = SvPV(src_sv, src_len);
532 19           cfg = eshu_default_config();
533              
534 42 100         for (i = 2; i + 1 < items; i += 2) {
535 23           const char *key = SvPV_nolen(ST(i));
536 23           SV *val = ST(i + 1);
537 23 100         if (strEQ(key, "lang")) {
538 19           lang = SvPV_nolen(val);
539 4 100         } else if (strEQ(key, "indent_char")) {
540 2           const char *ic = SvPV_nolen(val);
541 2 100         cfg.indent_char = (*ic == ' ') ? ' ' : '\t';
542 2 50         } else if (strEQ(key, "indent_width")) {
543 2           cfg.indent_width = SvIV(val);
544 0 0         } else if (strEQ(key, "indent_pp")) {
545 0           cfg.indent_pp = SvTRUE(val) ? 1 : 0;
546 0 0         } else if (strEQ(key, "range_start")) {
547 0           cfg.range_start = SvIV(val);
548 0 0         } else if (strEQ(key, "range_end")) {
549 0           cfg.range_end = SvIV(val);
550             }
551             }
552              
553 19 100         if (strEQ(lang, "c")) {
554 10           result = eshu_indent_c(src, (size_t)src_len, &cfg, &out_len);
555 9 50         } else if (strEQ(lang, "perl") || strEQ(lang, "pl")) {
    50          
556 0           cfg.lang = ESHU_LANG_PERL;
557 0           result = eshu_indent_pl(src, (size_t)src_len, &cfg, &out_len);
558 9 100         } else if (strEQ(lang, "xs")) {
559 1           cfg.lang = ESHU_LANG_XS;
560 1           result = eshu_indent_xs(src, (size_t)src_len, &cfg, &out_len);
561 8 100         } else if (strEQ(lang, "xml") || strEQ(lang, "svg")) {
    50          
562 1           cfg.lang = ESHU_LANG_XML;
563 1           result = eshu_indent_xml(src, (size_t)src_len, &cfg, &out_len);
564 7 100         } else if (strEQ(lang, "html") || strEQ(lang, "htm")) {
    50          
565 1           cfg.lang = ESHU_LANG_HTML;
566 1           result = eshu_indent_xml(src, (size_t)src_len, &cfg, &out_len);
567 6 100         } else if (strEQ(lang, "css") || strEQ(lang, "scss") || strEQ(lang, "less")) {
    50          
    50          
568 1           cfg.lang = ESHU_LANG_CSS;
569 1           result = eshu_indent_css(src, (size_t)src_len, &cfg, &out_len);
570 5 100         } else if (strEQ(lang, "js") || strEQ(lang, "javascript") ||
    100          
571 3 50         strEQ(lang, "jsx") || strEQ(lang, "ts") ||
    100          
572 2 100         strEQ(lang, "typescript") || strEQ(lang, "tsx") ||
    50          
573 1 50         strEQ(lang, "mjs") || strEQ(lang, "cjs") ||
    50          
574 1 50         strEQ(lang, "mts")) {
575 4           cfg.lang = ESHU_LANG_JS;
576 4           result = eshu_indent_js(src, (size_t)src_len, &cfg, &out_len);
577 1 50         } else if (strEQ(lang, "pod")) {
578 1           cfg.lang = ESHU_LANG_POD;
579 1           result = eshu_indent_pod(src, (size_t)src_len, &cfg, &out_len);
580             } else {
581 0           croak("Eshu: unsupported language '%s'", lang);
582             result = NULL; /* not reached */
583             out_len = 0;
584             }
585              
586 19           RETVAL = newSVpvn(result, out_len);
587 19           free(result);
588             }
589             OUTPUT:
590             RETVAL
591              
592             SV *
593             indent_file(class, path_sv, ...)
594             SV * class
595             SV * path_sv
596             CODE:
597             {
598             const char *path;
599             STRLEN path_len;
600             eshu_config_t cfg;
601 0           const char *force_lang = NULL;
602 0           int opts = 0;
603             int i;
604             eshu_file_result_t res;
605             HV *hv;
606              
607             PERL_UNUSED_VAR(class);
608 0           path = SvPV(path_sv, path_len);
609 0           cfg = eshu_default_config();
610              
611 0 0         for (i = 2; i + 1 < items; i += 2) {
612 0           const char *key = SvPV_nolen(ST(i));
613 0           SV *val = ST(i + 1);
614 0 0         if (strEQ(key, "fix")) {
615 0 0         if (SvTRUE(val)) opts |= ESHU_OPT_FIX;
616 0 0         } else if (strEQ(key, "diff")) {
617 0 0         if (SvTRUE(val)) opts |= ESHU_OPT_DIFF;
618 0 0         } else if (strEQ(key, "lang")) {
619 0           force_lang = SvPV_nolen(val);
620 0 0         } else if (strEQ(key, "indent_char")) {
621 0           const char *ic = SvPV_nolen(val);
622 0 0         cfg.indent_char = (*ic == ' ') ? ' ' : '\t';
623 0 0         } else if (strEQ(key, "indent_width")) {
624 0           cfg.indent_width = SvIV(val);
625 0 0         } else if (strEQ(key, "indent_pp")) {
626 0           cfg.indent_pp = SvTRUE(val) ? 1 : 0;
627 0 0         } else if (strEQ(key, "range_start")) {
628 0           cfg.range_start = SvIV(val);
629 0 0         } else if (strEQ(key, "range_end")) {
630 0           cfg.range_end = SvIV(val);
631             }
632             }
633              
634 0           eshu_indent_file(path, &cfg, force_lang, opts, &res);
635              
636 0           hv = newHV();
637 0           hv_store(hv, "file", 4, newSVpv(res.file, 0), 0);
638 0           switch (res.status) {
639 0           case ESHU_STATUS_UNCHANGED:
640 0           hv_store(hv, "status", 6, newSVpvs("unchanged"), 0); break;
641 0           case ESHU_STATUS_CHANGED:
642 0           hv_store(hv, "status", 6, newSVpvs("changed"), 0); break;
643 0           case ESHU_STATUS_NEEDS_FIXING:
644 0           hv_store(hv, "status", 6, newSVpvs("needs_fixing"), 0); break;
645 0           case ESHU_STATUS_SKIPPED:
646 0           hv_store(hv, "status", 6, newSVpvs("skipped"), 0); break;
647 0           case ESHU_STATUS_ERROR:
648 0           hv_store(hv, "status", 6, newSVpvs("error"), 0); break;
649             }
650 0 0         if (res.lang)
651 0           hv_store(hv, "lang", 4, newSVpv(res.lang, 0), 0);
652 0 0         if (res.reason)
653 0           hv_store(hv, "reason", 6, newSVpv(res.reason, 0), 0);
654 0 0         if (res.error)
655 0           hv_store(hv, "error", 5, newSVpv(res.error, 0), 0);
656 0 0         if (res.diff)
657 0           hv_store(hv, "diff", 4, newSVpvn(res.diff, res.diff_len), 0);
658              
659 0           eshu_file_result_free(&res);
660 0           RETVAL = newRV_noinc((SV *)hv);
661             }
662             OUTPUT:
663             RETVAL
664              
665             SV *
666             indent_dir(class, path_sv, ...)
667             SV * class
668             SV * path_sv
669             CODE:
670             {
671             const char *path;
672             STRLEN path_len;
673             eshu_config_t cfg;
674 16           const char *force_lang = NULL;
675 16           int file_opts = 0;
676 16           int recursive = 1;
677             int i;
678             eshu_strlist_t files;
679             eshu_dir_report_t report;
680             HV *report_hv;
681             AV *changes_av;
682             size_t fi;
683 16           AV *exclude_av = NULL;
684 16           AV *include_av = NULL;
685              
686             PERL_UNUSED_VAR(class);
687 16           path = SvPV(path_sv, path_len);
688 16           cfg = eshu_default_config();
689              
690 28 100         for (i = 2; i + 1 < items; i += 2) {
691 12           const char *key = SvPV_nolen(ST(i));
692 12           SV *val = ST(i + 1);
693 12 100         if (strEQ(key, "fix")) {
694 5 50         if (SvTRUE(val)) file_opts |= ESHU_OPT_FIX;
695 7 100         } else if (strEQ(key, "diff")) {
696 1 50         if (SvTRUE(val)) file_opts |= ESHU_OPT_DIFF;
697 6 50         } else if (strEQ(key, "lang")) {
698 0           force_lang = SvPV_nolen(val);
699 6 100         } else if (strEQ(key, "recursive")) {
700 1           recursive = SvTRUE(val) ? 1 : 0;
701 5 100         } else if (strEQ(key, "exclude")) {
702 3 50         if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
    100          
703 1           exclude_av = (AV *)SvRV(val);
704             } else {
705 2           exclude_av = newAV();
706 2           av_push(exclude_av, SvREFCNT_inc(val));
707 2           sv_2mortal((SV *)exclude_av);
708             }
709 2 50         } else if (strEQ(key, "include")) {
710 2 50         if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
    50          
711 0           include_av = (AV *)SvRV(val);
712             } else {
713 2           include_av = newAV();
714 2           av_push(include_av, SvREFCNT_inc(val));
715 2           sv_2mortal((SV *)include_av);
716             }
717 0 0         } else if (strEQ(key, "indent_char")) {
718 0           const char *ic = SvPV_nolen(val);
719 0 0         cfg.indent_char = (*ic == ' ') ? ' ' : '\t';
720 0 0         } else if (strEQ(key, "indent_width")) {
721 0           cfg.indent_width = SvIV(val);
722 0 0         } else if (strEQ(key, "indent_pp")) {
723 0           cfg.indent_pp = SvTRUE(val) ? 1 : 0;
724             }
725             }
726              
727             /* Collect files */
728 16           eshu_strlist_init(&files);
729             {
730             struct stat st;
731 16 50         if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) {
    50          
732 0           eshu_strlist_push(&files, path);
733 16 50         } else if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
    50          
734 16           eshu_walk_dir(path, &files, recursive);
735             } else {
736 0           eshu_strlist_free(&files);
737 0           croak("Eshu: '%s' is not a file or directory", path);
738             }
739             }
740 16           eshu_strlist_sort(&files);
741              
742             /* Process files */
743 16           eshu_dir_report_init(&report);
744 16           changes_av = newAV();
745              
746 62 100         for (fi = 0; fi < files.count; fi++) {
747 46           const char *fpath = files.items[fi];
748 46           int skip = 0;
749              
750             /* Exclude filter */
751 46 100         if (exclude_av) {
752 12           SSize_t j, alen = av_len(exclude_av) + 1;
753 23 100         for (j = 0; j < alen; j++) {
754 15           SV **elem = av_fetch(exclude_av, j, 0);
755 15 50         if (elem && SvOK(*elem)) {
    50          
756 15           REGEXP *rx = SvRX(*elem);
757 15 50         if (rx) {
758 15           SV *file_sv = sv_2mortal(newSVpv(fpath, 0));
759 15 100         if (pregexec(rx, SvPV_nolen(file_sv), SvPV_nolen(file_sv) + strlen(fpath), SvPV_nolen(file_sv), 0, file_sv, 0)) {
760 4           skip = 1; break;
761             }
762             }
763             }
764             }
765             }
766 46 100         if (skip) {
767 4           HV *hv = newHV();
768 4           hv_store(hv, "file", 4, newSVpv(fpath, 0), 0);
769 4           hv_store(hv, "status", 6, newSVpvs("skipped"), 0);
770 4           hv_store(hv, "reason", 6, newSVpvs("excluded"), 0);
771 4           av_push(changes_av, newRV_noinc((SV *)hv));
772 4           report.files_skipped++;
773 4           continue;
774             }
775              
776             /* Include filter */
777 42 100         if (include_av) {
778 7           SSize_t j, alen = av_len(include_av) + 1;
779 7           int matched = 0;
780 10 100         for (j = 0; j < alen; j++) {
781 7           SV **elem = av_fetch(include_av, j, 0);
782 7 50         if (elem && SvOK(*elem)) {
    50          
783 7           REGEXP *rx = SvRX(*elem);
784 7 50         if (rx) {
785 7           SV *file_sv = sv_2mortal(newSVpv(fpath, 0));
786 7 100         if (pregexec(rx, SvPV_nolen(file_sv), SvPV_nolen(file_sv) + strlen(fpath), SvPV_nolen(file_sv), 0, file_sv, 0)) {
787 4           matched = 1; break;
788             }
789             }
790             }
791             }
792 7 100         if (!matched) {
793 3           HV *hv = newHV();
794 3           hv_store(hv, "file", 4, newSVpv(fpath, 0), 0);
795 3           hv_store(hv, "status", 6, newSVpvs("skipped"), 0);
796 3           hv_store(hv, "reason", 6, newSVpvs("not included"), 0);
797 3           av_push(changes_av, newRV_noinc((SV *)hv));
798 3           report.files_skipped++;
799 3           continue;
800             }
801             }
802              
803             /* Process file */
804             {
805             eshu_file_result_t res;
806 39           HV *hv = newHV();
807              
808 39           eshu_indent_file(fpath, &cfg, force_lang, file_opts, &res);
809              
810 39           hv_store(hv, "file", 4, newSVpv(res.file, 0), 0);
811 39           switch (res.status) {
812 2           case ESHU_STATUS_UNCHANGED:
813 2           hv_store(hv, "status", 6, newSVpvs("unchanged"), 0); break;
814 12           case ESHU_STATUS_CHANGED:
815 12           hv_store(hv, "status", 6, newSVpvs("changed"), 0); break;
816 22           case ESHU_STATUS_NEEDS_FIXING:
817 22           hv_store(hv, "status", 6, newSVpvs("needs_fixing"), 0); break;
818 3           case ESHU_STATUS_SKIPPED:
819 3           hv_store(hv, "status", 6, newSVpvs("skipped"), 0); break;
820 0           case ESHU_STATUS_ERROR:
821 0           hv_store(hv, "status", 6, newSVpvs("error"), 0); break;
822             }
823 39 100         if (res.lang)
824 36           hv_store(hv, "lang", 4, newSVpv(res.lang, 0), 0);
825 39 100         if (res.reason)
826 3           hv_store(hv, "reason", 6, newSVpv(res.reason, 0), 0);
827 39 50         if (res.error)
828 0           hv_store(hv, "error", 5, newSVpv(res.error, 0), 0);
829 39 100         if (res.diff)
830 1           hv_store(hv, "diff", 4, newSVpvn(res.diff, res.diff_len), 0);
831              
832 39 100         if (res.status == ESHU_STATUS_CHANGED || res.status == ESHU_STATUS_NEEDS_FIXING) {
    100          
833 34           report.files_changed++;
834 34           report.files_checked++;
835 5 100         } else if (res.status == ESHU_STATUS_UNCHANGED) {
836 2           report.files_checked++;
837 3 50         } else if (res.status == ESHU_STATUS_SKIPPED) {
838 3           report.files_skipped++;
839 0 0         } else if (res.status == ESHU_STATUS_ERROR) {
840 0           report.files_errored++;
841             }
842              
843 39           eshu_file_result_free(&res);
844 39           av_push(changes_av, newRV_noinc((SV *)hv));
845             }
846             }
847              
848 16           eshu_strlist_free(&files);
849              
850 16           report_hv = newHV();
851 16           hv_store(report_hv, "files_checked", 13, newSViv(report.files_checked), 0);
852 16           hv_store(report_hv, "files_changed", 13, newSViv(report.files_changed), 0);
853 16           hv_store(report_hv, "files_skipped", 13, newSViv(report.files_skipped), 0);
854 16           hv_store(report_hv, "files_errored", 13, newSViv(report.files_errored), 0);
855 16           hv_store(report_hv, "changes", 7, newRV_noinc((SV *)changes_av), 0);
856              
857 16           free(report.changes);
858              
859 16           RETVAL = newRV_noinc((SV *)report_hv);
860             }
861             OUTPUT:
862             RETVAL