File Coverage

Config.xs
Criterion Covered Total %
statement 79 79 100.0
branch 113 134 84.3
condition n/a
subroutine n/a
pod n/a
total 192 213 90.1


line stmt bran cond sub pod time code
1             /*
2             *
3             * Copyright (c) 2018, cPanel, LLC.
4             * All rights reserved.
5             * http://cpanel.net
6             *
7             * This is free software; you can redistribute it and/or modify it under the
8             * same terms as Perl itself.
9             *
10             */
11              
12             #include
13             #include
14             #include
15             #include
16              
17             #define NEED_newSVpvn_flags
18             #include "ppport.h"
19              
20             /* prototypes */
21             SV* _parse_string_field(pTHX_ SV *sv, int need_field, const char sep);
22              
23 276           SV* _parse_string_field(pTHX_ SV *sv, int need_field, const char sep) {
24 276           STRLEN len = SvCUR(sv);
25 276           char *ptr = (char *) SvPVX_const(sv); /* todo: preserve the const state of the pointer */
26             AV *av;
27             char *start_key, *end_key;
28             char *start_val, *end_val;
29             char *max;
30 276           int is_utf8 = SvUTF8(sv);
31 276           const char eol = '\n';
32 276           const char comment = '#';
33 276           const char line_feed = '\r';
34 276           int found_eol = 1;
35 276           int found_comment = 0;
36 276           int found_sep = 0;
37 276           int found_field = 0;
38              
39 276           av = newAV();
40              
41 276           start_key = ptr;
42 276           end_key = 0;
43 276           start_val = 0;
44 276           end_val = 0;
45              
46 13768 100         for ( max = ptr + len ; ptr < max; ++ptr ) {
47 13492 100         if ( ! *ptr ) continue; /* skip \0 so we can parse binaries strings */
48 13490 100         if ( *ptr == line_feed ) continue; /* ignore \r */
49              
50             /* printf( "# %c\n", *ptr ); */
51              
52             /* skip all characters in a comment block */
53 13432 100         if ( found_comment ) {
54 1144 100         if ( *ptr == eol ) found_comment = 0;
55 1144           continue;
56             }
57              
58 12288 100         if ( (need_field == 0 && found_sep) || (need_field && found_sep == need_field) ) {
    100          
    100          
    100          
59 620 100         if ( *ptr == ' ' || *ptr == '\t' ) continue;
    100          
60 565 100         if (need_field == 0) found_sep = 0;
61 182           else ++found_sep; /* moving it away */
62 565           end_val = start_val = ptr;
63 565           found_field = 0;
64             }
65              
66             /* get to the first valuable char of the line */
67 12233 100         if ( found_eol ) { /* starting a line */
68             /* spaces at the beginning of a line */
69 876 100         if ( *ptr == ' ' || *ptr == '\t' || *ptr == line_feed )
    100          
    50          
70 39           continue;
71 837 100         if ( *ptr == comment ) {
72 83           found_comment = 1;
73 83           continue;
74             }
75             /* we have a real character to start the line */
76 754           found_eol = 0;
77 754           start_key = ptr;
78 754           end_key = 0;
79 754           end_val = 0;
80 754           found_sep = 0;
81 754           start_val = 0;
82 754           found_field = 0;
83             }
84              
85 12111 100         if ( *ptr == sep ) {
86             /* printf ("# separator key/value\n" ); */
87 1747 100         if (need_field) ++found_sep;
88 1747 100         if ( !end_key ) {
89 628           end_key = ptr;
90 628 100         if ( !need_field) found_sep = 1;
91             }
92              
93 1747 100         if ( need_field && found_sep == need_field + 2 ) {
    100          
94 142           end_val = ptr;
95 142           found_field = 1;
96             }
97              
98 10364 100         } else if ( *ptr == eol ) { /* only handle the line once we reach a \n */
99              
100             #define __PARSE_STRING_LINE_FIELD /* reuse code for the last line */ \
101             if ( ( need_field == 0 || found_field == 0) && end_val == start_val) end_val = ptr; \
102             if (end_val && *end_val == line_feed) end_val = ptr - 1; \
103             found_eol = 1; \
104             \
105             /* check if we got a key (end_key is NULL when no separator was found) */ \
106             if ( end_key && end_key > start_key ) { \
107             /* we got a key */ \
108             av_push(av, newSVpvn_flags( start_key, (STRLEN) (end_key - start_key), is_utf8 )); \
109             \
110             /* remove the line_feed chars if any */ \
111             while ( start_val && end_val > start_val && \
112             ( ( *(end_val - 1) == line_feed ) || ( *(end_val - 1) == ' ' ) || ( *(end_val - 1) == '\t' ) ) \
113             ) { \
114             --end_val; \
115             } \
116             /* only add the value if we have a key */ \
117             if ( start_val && end_val > start_val ) { \
118             av_push(av, newSVpvn_flags( start_val, (STRLEN) (end_val - start_val), is_utf8 )); \
119             } else { \
120             av_push(av, &PL_sv_undef); \
121             } \
122             } \
123             /* end of __PARSE_STRING_LINE_FIELD */
124              
125 770 100         __PARSE_STRING_LINE_FIELD
    100          
    50          
    50          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
126              
127 724           start_key = 0;
128             }
129              
130             } /* end main for loop for *ptr */
131              
132             /* handle the last entry */
133 276 100         if ( start_key ) {
134 41 50         __PARSE_STRING_LINE_FIELD
    0          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
135             }
136              
137 276           return (SV*) (newRV_noinc((SV*) av));
138             }
139              
140             MODULE = Colon__Config PACKAGE = Colon::Config
141              
142             SV*
143             read(sv, ...)
144             SV *sv;
145             CODE:
146 570 50         if ( sv && SvPOK(sv) ) {
    100          
147 292           int field = 0;
148 292           char sep = ':';
149 292 100         if ( items > 3 )
150 2           croak( "Too many arguments when calling 'Colon::Config::read'." );
151 290 100         if ( items >= 2 ) {
152 194           SV *sv_field = ST(1);
153 194 50         if ( !SvOK(sv_field) || !looks_like_number(sv_field) )
    100          
154 2           croak( "Colon::Config::read - Second argument must be one integer." );
155 192           field = SvIV(sv_field);
156 192 100         if ( field < 0 )
157 3           croak( "Colon::Config::read - field must be >= 0" );
158             }
159 285 100         if ( items == 3 ) {
160 50           SV *sv_sep = ST(2);
161             STRLEN sep_len;
162             char *sep_str;
163 50 50         if ( !SvOK(sv_sep) || !SvPOK(sv_sep) )
    100          
164 2           croak( "Colon::Config::read - Third argument must be a string." );
165 48           sep_str = SvPV(sv_sep, sep_len);
166 48 100         if ( sep_len != 1 )
167 3           croak( "Colon::Config::read - separator must be a single character." );
168 45 100         if ( sep_str[0] == '\n' || sep_str[0] == '\r' || sep_str[0] == '\0' )
    100          
    100          
169 4           croak( "Colon::Config::read - separator cannot be a newline, carriage return, or null character." );
170 41           sep = sep_str[0];
171             }
172 276           RETVAL = _parse_string_field( aTHX_ sv, field, sep );
173             } else {
174 2           RETVAL = &PL_sv_undef;
175             }
176             OUTPUT:
177             RETVAL
178