File Coverage

Iterator.xs
Criterion Covered Total %
statement 93 97 95.8
branch 75 106 70.7
condition n/a
subroutine n/a
pod n/a
total 168 203 82.7


line stmt bran cond sub pod time code
1             /*
2             Copyright 2009, 2010, 2011 Kevin Ryde
3              
4             This file is part of File-Locate-Iterator.
5              
6             File-Locate-Iterator is free software; you can redistribute it and/or
7             modify it under the terms of the GNU General Public License as published
8             by the Free Software Foundation; either version 3, or (at your option)
9             any later version.
10              
11             File-Locate-Iterator is distributed in the hope that it will be useful,
12             but WITHOUT ANY WARRANTY; without even the implied warranty of
13             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14             Public License for more details.
15              
16             You should have received a copy of the GNU General Public License along
17             with File-Locate-Iterator. If not, see . */
18              
19             #include
20             #include
21              
22             #include "EXTERN.h"
23             #include "perl.h"
24             #include "XSUB.h"
25              
26             #define NEED_sv_2pv_flags
27             #include "ppport.h"
28              
29             #define DEBUG 0
30              
31             #if DEBUG >= 1
32             #define DEBUG1(code) do { code; } while (0)
33             #else
34             #define DEBUG1(code)
35             #endif
36             #if DEBUG >= 2
37             #define DEBUG2(code) do { code; } while (0)
38             #else
39             #define DEBUG2(code)
40             #endif
41              
42             #define GET_FIELD(var,name) \
43             do { \
44             SV **svptr; \
45             field = (name); \
46             svptr = hv_fetch (h, field, strlen(field), 0); \
47             if (! svptr) goto FIELD_MISSING; \
48             (var) = *svptr; \
49             } while (0)
50              
51             #define MATCH(target) \
52             do { \
53             if (regexp) { \
54             if (CALLREGEXEC (regexp, \
55             entry_p, entry_p + entry_len, \
56             entry_p, 0, entry, NULL, \
57             REXEC_IGNOREPOS)) { \
58             goto target; \
59             } \
60             DEBUG1 (fprintf (stderr, " no match regexp\n")); \
61             } else { \
62             if (! globs_ptr) { \
63             DEBUG1 (fprintf (stderr, " no regexp or globs, so match\n")); \
64             goto target; \
65             } \
66             } \
67             { \
68             SSize_t i; \
69             for (i = 0; i <= globs_lastidx; i++) { \
70             DEBUG2 (fprintf (stderr, " fnmatch \"%s\" entry \"%s\"\n", \
71             SvPV_nolen(globs_ptr[i]), entry_p)); \
72             if (fnmatch (SvPV_nolen(globs_ptr[i]), entry_p, 0) == 0) \
73             goto target; \
74             } \
75             DEBUG1 (fprintf (stderr, " no match globs\n")); \
76             } \
77             DEBUG1 (fprintf (stderr, " no match\n")); \
78             } while (0)
79              
80             MODULE = File::Locate::Iterator PACKAGE = File::Locate::Iterator
81              
82             void
83             next (SV *self)
84             CODE:
85             {
86             HV *h;
87             SV **mref_svptr, *entry, *sharelen_sv;
88 571           SV **globs_ptr = NULL;
89 571           SSize_t globs_lastidx = -1;
90 571           REGEXP *regexp = NULL;
91             const char *field;
92             char *entry_p;
93             STRLEN entry_len;
94             IV sharelen, adj;
95 571           int at_eof = 0;
96              
97             DEBUG2 (fprintf (stderr, "FLI XS next()\n"));
98              
99 571           goto START;
100             {
101             FIELD_MISSING:
102 0           croak ("oops, missing '%s'", field);
103             }
104             START:
105 571           h = (HV*) SvRV(self);
106              
107 571 50         GET_FIELD (entry, "entry");
108              
109 571 50         GET_FIELD (sharelen_sv, "sharelen");
110 571 50         sharelen = SvIV (sharelen_sv);
111              
112             {
113 571           SV **regexp_svptr = hv_fetch (h, "regexp", 6, 0);
114 571 100         if (regexp_svptr) {
115 30           SV *regexp_sv = *regexp_svptr;
116             DEBUG2(fprintf (stderr, "regexp sv="); sv_dump (regexp_sv));
117 30           regexp = SvRX(regexp_sv);
118             /* regexp=>undef is no regexp to match. Normally the regexp field
119             is omitted if undef (ie regexp_svptr==NULL), but the Moose stuff
120             insists on filling-in named attributes. :-( */
121 30 50         if (SvOK(regexp_sv)) {
    0          
    0          
122 30 50         if (! regexp) croak ("'regexp' not a regexp");
123             }
124             }
125             DEBUG1 (fprintf (stderr, "REGEXP obj %"UVxf"\n", PTR2UV(regexp)));
126             }
127              
128             {
129 571           SV **globs_svptr = hv_fetch (h, "globs", 5, 0);
130 571 100         if (globs_svptr) {
131 24           SV *globs_sv = *globs_svptr;
132             /* globs=>undef is no globs to match. Normally the globs field is
133             omitted if undef (ie globs_svptr==NULL), but the Moose stuff
134             insists on filling-in named attributes. :-(
135             globs has been crunched by new(), so it's a plain array, no need
136             to worry about SvGetMagic() or whatnot. */
137 24 50         if (SvOK (globs_sv)) {
    0          
    0          
138 24 50         if (! SvROK (globs_sv))
139 0           croak ("oops, 'globs' not a reference");
140 24           AV *globs_av = (AV*) SvRV(globs_sv);
141              
142 24 50         if (SvTYPE(globs_av) != SVt_PVAV)
143 0           croak ("oops, 'globs' not an arrayref");
144 24           globs_ptr = AvARRAY (globs_av);
145 24           globs_lastidx = av_len (globs_av);
146             }
147             }
148             DEBUG1 (fprintf
149             (stderr, "globs_svptr %"UVxf" globs_ptr %"UVxf" globs_lastidx %d\n",
150             PTR2UV(globs_svptr), PTR2UV(globs_ptr), globs_lastidx));
151             }
152              
153 571           mref_svptr = hv_fetch (h, "mref", 4, 0);
154 571 100         if (mref_svptr) {
155             SV *mref, *mmap, *pos_sv;
156 284           mref = *mref_svptr;
157             char *mp, *gets_beg, *gets_end;
158             STRLEN mlen;
159             UV pos;
160              
161 284           mmap = (SV*) SvRV(mref);
162 284 100         mp = SvPV (mmap, mlen);
163              
164 284 50         GET_FIELD (pos_sv, "pos");
165 284 50         pos = SvUV(pos_sv);
166             DEBUG2 (fprintf (stderr, "mmap %"UVxf" mlen %u, pos %"UVuf"\n",
167             PTR2UV(mp), mlen, pos));
168              
169             for (;;) {
170             DEBUG2 (fprintf (stderr, "MREF_LOOP\n"));
171 339 100         if (pos >= mlen) {
172             /* EOF */
173 35           at_eof = 1;
174 35           break;
175             }
176 304           adj = ((I8*)mp)[pos++];
177              
178 304 100         if (adj == -128) {
179             DEBUG1 (fprintf (stderr, "two-byte adj at pos=%"UVuf"\n", pos));
180 72 100         if (pos >= mlen-1) goto UNEXPECTED_EOF;
181 132           adj = (I16) ((((U16) ((U8*)mp)[pos]) << 8)
182 66           + ((U8*)mp)[pos+1]);
183 66           pos += 2;
184             }
185             DEBUG1 (fprintf (stderr, "adj %"IVdf" at pos=%"UVuf"\n", adj, pos));
186            
187 302           sharelen += adj;
188 302 100         if (sharelen < 0 || sharelen > SvCUR(entry)) {
    100          
189 8           sv_setpv (entry, NULL);
190 8           croak ("Invalid database contents (bad share length %"IVdf")",
191             sharelen);
192             }
193             DEBUG1 (fprintf (stderr, "sharelen %"IVdf"\n", sharelen));
194            
195 294 100         if (pos >= mlen) goto UNEXPECTED_EOF;
196 292           gets_beg = mp + pos;
197 292           gets_end = memchr (gets_beg, '\0', mlen-pos);
198 292 100         if (! gets_end) {
199             DEBUG1 (fprintf (stderr, "NUL not found gets_beg=%"UVxf" len=%lu\n",
200             PTR2UV(gets_beg), mlen-pos));
201 2           goto UNEXPECTED_EOF;
202             }
203            
204 290           SvCUR_set (entry, sharelen);
205 290           sv_catpvn (entry, gets_beg, gets_end - gets_beg);
206 290           pos = gets_end + 1 - mp;
207            
208 290 50         entry_p = SvPV(entry, entry_len);
209              
210 327 100         MATCH(MREF_LOOP_END);
    100          
    100          
    50          
    100          
    100          
211 55           }
212             MREF_LOOP_END:
213 270           SvUV_set (pos_sv, pos);
214              
215             } else {
216             SV *fh;
217             PerlIO *fp;
218             int got;
219             union {
220             char buf[2];
221             U16 u16;
222             } adj_u;
223             char *gets_ret;
224              
225 287 50         GET_FIELD (fh, "fh");
226 287           fp = IoIFP(sv_2io(fh));
227             DEBUG2 (fprintf (stderr, "fp=%"UVxf" fh=\n", PTR2UV(fp));
228             sv_dump (fh));
229              
230             /* local $/ = "\0" */
231 287           save_item (PL_rs);
232 287           sv_setpvn (PL_rs, "\0", 1);
233              
234             for (;;) {
235             DEBUG2 (fprintf (stderr, "IO_LOOP\n"));
236 302           got = PerlIO_read (fp, adj_u.buf, 1);
237 302 100         if (got == 0) {
238             /* EOF */
239 16           at_eof = 1;
240 16           break;
241             }
242 286 50         if (got != 1) {
243             READ_ERROR:
244             DEBUG1 (fprintf (stderr, "read fp=%"UVxf" got=%d\n",
245             PTR2UV(fp), got));
246 2 50         if (got < 0) {
247 0           croak ("Error reading database");
248             } else {
249             UNEXPECTED_EOF:
250 12           croak ("Invalid database contents (unexpected EOF)");
251             }
252             }
253              
254 286           adj = (I8) adj_u.buf[0];
255 286 100         if (adj == -128) {
256             DEBUG1 (fprintf (stderr, "two-byte adj\n"));
257 72           got = PerlIO_read (fp, adj_u.buf, 2);
258 72 100         if (got != 2) goto READ_ERROR;
259             DEBUG1 (fprintf (stderr, "raw %X,%X %X ntohs %X\n",
260             (int) (U8) adj_u.buf[0], (int) (U8) adj_u.buf[1],
261             adj_u.u16, ntohs(adj_u.u16)));
262 70           adj = (int) (I16) ntohs(adj_u.u16);
263             }
264             DEBUG1 (fprintf (stderr, "adj %"IVdf" %#"UVxf"\n", adj, adj));
265              
266 284           sharelen += adj;
267             DEBUG1 (fprintf (stderr, "sharelen %"IVdf" %#"UVxf" SvCUR %d utf8 %d\n",
268             sharelen, sharelen,
269             SvCUR(entry), SvUTF8(entry)));
270              
271 284 100         if (sharelen < 0 || sharelen > SvCUR(entry)) {
    100          
272 8           sv_setpv (entry, NULL);
273 8           croak ("Invalid database contents (bad share length %"IVdf")",
274             sharelen);
275             }
276              
277             /* sv_gets() in perl 5.10.1 and earlier must have "append" equal to
278             SvCUR(sv). The "fast" direct buffer access takes it as a byte
279             position to store to, but the plain read code takes it as a flag
280             to do sv_catpvn() instead of sv_setpvn(). This appears to be so
281             right back to 5.002 ("fast" access directly into a FILE*). So
282             SvCUR_set() here to work in either case. */
283 276           SvCUR_set (entry, sharelen);
284              
285 276           gets_ret = sv_gets (entry, fp, sharelen);
286 276 100         if (gets_ret == NULL) goto UNEXPECTED_EOF;
287             DEBUG2 (fprintf (stderr,
288             "entry gets to %u, chomp to %u, fpos now %lu(%#lx)\n",
289             SvCUR(entry), SvCUR(entry) - 1,
290             (unsigned long) PerlIO_tell(fp),
291             (unsigned long) PerlIO_tell(fp));
292             fprintf (stderr, "entry gets to %u, chomp to %u\n",
293             SvCUR(entry), SvCUR(entry) - 1));
294              
295 274 50         entry_p = SvPV(entry, entry_len);
296 274 50         if (entry_len < 1 || entry_p[entry_len-1] != '\0') {
    100          
297             DEBUG1 (fprintf (stderr, "no NUL from sv_gets\n"));
298             goto UNEXPECTED_EOF;
299             }
300 272           entry_len--;
301 272           SvCUR_set (entry, entry_len); /* chomp \0 terminator */
302              
303 287 50         MATCH(IO_LOOP_END);
    0          
    100          
    50          
    100          
    100          
304 15           }
305             IO_LOOP_END:
306             /* taint the same as other reads from a file, and in particular the
307             same as from the pure-perl reads */
308 273 50         SvTAINTED_on(entry);
309             }
310 543 100         if (at_eof) {
311 51           sv_setpv (entry, NULL);
312             DEBUG2 (fprintf (stderr, "eof\n entry=\n");
313             sv_dump (entry);
314             fprintf (stderr, "\n"));
315 51           XSRETURN(0);
316              
317             } else {
318 492           SvUV_set (sharelen_sv, sharelen);
319             DEBUG2 (fprintf (stderr, "return entry=\n");
320             sv_dump (entry);
321             fprintf (stderr, "\n"));
322              
323 492 50         SvREFCNT_inc_simple_void (entry);
324 492           ST(0) = sv_2mortal(entry);
325 543           XSRETURN(1);
326             }
327             }