File Coverage

blib/lib/Config/GitLike.pm
Criterion Covered Total %
statement 467 494 94.5
branch 254 308 82.4
condition 80 114 70.1
subroutine 46 53 86.7
pod 27 27 100.0
total 874 996 87.7


line stmt bran cond sub pod time code
1             package Config::GitLike;
2 7     7   675416 use Moo;
  7         23006  
  7         36  
3 7     7   5712 use MooX::Types::MooseLike::Base qw(Bool HashRef ArrayRef Maybe Str Int);
  7         13406  
  7         493  
4              
5 7     7   64 use File::Spec;
  7         21  
  7         163  
6 7     7   41 use Cwd;
  7         30  
  7         415  
7 7     7   37 use Scalar::Util qw(openhandle);
  7         13  
  7         370  
8 7     7   48 use Fcntl qw(O_CREAT O_EXCL O_WRONLY);
  7         21  
  7         294  
9 7     7   147 use 5.008;
  7         27  
10              
11             our $VERSION = '1.18';
12              
13              
14             has 'confname' => (
15             is => 'rw',
16             required => 1,
17             isa => Str,
18             );
19              
20             # not defaulting to {} allows the predicate is_loaded
21             # to determine whether data has been loaded yet or not
22             has 'data' => (
23             is => 'rw',
24             predicate => 'is_loaded',
25             isa => HashRef,
26             );
27              
28             # key => bool
29             has 'multiple' => (
30             is => 'rw',
31             isa => HashRef,
32             default => sub { +{} },
33             );
34              
35             has 'casing' => (
36             is => 'rw',
37             isa => HashRef,
38             default => sub { +{} },
39             );
40              
41             # filename where the definition of each key was loaded from
42             has 'origins' => (
43             is => 'rw',
44             isa => HashRef,
45             default => sub { +{} },
46             );
47              
48             has 'config_files' => (
49             is => 'rw',
50             isa => ArrayRef,
51             default => sub { [] },
52             );
53              
54             # default to being more relaxed than git, but allow enforcement
55             # of only-write-things-that-git-config-can-read if you want to
56             has 'compatible' => (
57             is => 'rw',
58             isa => Bool,
59             default => sub { 0 },
60             );
61              
62             has 'cascade' => (
63             is => 'rw',
64             isa => Bool,
65             default => sub { 0 },
66             );
67              
68             has 'encoding' => (
69             is => 'rw',
70             isa => Maybe[Str],
71             );
72              
73             has 'newlines' => (
74             is => 'rw',
75             isa => HashRef,
76             default => sub { +{} },
77             );
78              
79             has 'include' => (
80             is => 'rw',
81             isa => Str,
82             default => sub { "include.path" },
83             );
84              
85             has 'max_depth' => (
86             is => 'rw',
87             isa => Int,
88             default => sub { 10 },
89             );
90              
91             sub set_multiple {
92 77     77 1 3864 my $self = shift;
93 77         237 my ($name, $mult) = (@_, 1);
94 77         2366 $self->multiple->{ $self->canonical_case( $name ) } = $mult;
95             }
96              
97             sub is_multiple {
98 316     316 1 1057 my $self = shift;
99 316         410 my $name = shift;
100 316 50       614 return if !defined $name;
101 316         4864 return $self->multiple->{ $self->canonical_case( $name ) };
102             }
103              
104             sub load {
105 61     61 1 10425 my $self = shift;
106 61   33     166514 my $path = shift || Cwd::cwd;
107 61         8097 $self->data({});
108 61         9536 $self->multiple({});
109 61         6556 $self->config_files([]);
110 61         5448 $self->load_global;
111 61         1022 $self->load_user;
112 61         912 $self->load_dirs( $path );
113 52 50       1359 return wantarray ? %{$self->data} : \%{$self->data};
  0         0  
  52         1181  
114             }
115              
116             sub dir_file {
117 0     0 1 0 my $self = shift;
118 0         0 return "." . $self->confname;
119             }
120              
121             sub load_dirs {
122 61     61 1 160 my $self = shift;
123 61         398 my $path = shift;
124 61         2125 my($vol, $dirs, undef) = File::Spec->splitpath( $path, 1 );
125 61         1000 my @dirs = File::Spec->splitdir( $dirs );
126 61         169 my @found;
127 61         204 while (@dirs) {
128 305         3134 my $path = File::Spec->catpath(
129             $vol, File::Spec->catdir(@dirs), $self->dir_file
130             );
131 305 100       24202 if (-f $path) {
132 57         534 push @found, $path;
133 57 50       1968 last unless $self->cascade;
134             }
135 248         947 pop @dirs;
136             }
137 61         1830 $self->load_file( $_ ) for reverse @found;
138             }
139              
140             sub global_file {
141 0     0 1 0 my $self = shift;
142 0         0 return "/etc/" . $self->confname;
143             }
144              
145             sub load_global {
146 61     61 1 444 my $self = shift;
147 61         921 return $self->load_file( $self->global_file );
148             }
149              
150             sub user_file {
151 0     0 1 0 my $self = shift;
152             return
153 0         0 File::Spec->catfile( "~", "." . $self->confname );
154             }
155              
156             sub load_user {
157 61     61 1 153 my $self = shift;
158 61         973 return $self->load_file( $self->user_file );
159             }
160              
161             # returns undef if the file was unable to be opened
162             sub _read_config {
163 295     295   1253 my $self = shift;
164 295         1230 my $filename = shift;
165              
166 295 100 66     7437 return unless -f $filename and -r $filename;
167 166 50       8458 open(my $fh, '<', $filename) or return;
168 166 100       5193 if (my $encoding = $self->encoding) {
169 1         60 binmode $fh, ":encoding($encoding)";
170             }
171              
172 166         1448 my $c = do {local $/; <$fh>};
  166         518  
  166         4124  
173              
174 166         705 my $newlines = "\n";
175 166 100 66     1660 if ($c =~ m/\r\n/) {
    100          
176             # Convert from DOS; `git` applies this on read always, and
177             # simply mangles files on write.
178 1         13 $newlines = "\r\n";
179 1         13 $c =~ s/\r\n/\n/g;
180             } elsif ($c !~ /\n/ and $c =~ /\r/) {
181             # Best-guess convert from Mac.
182 1         13 $newlines = "\r";
183 1         12 $c =~ s/\r/\n/g;
184             }
185 166         4156 $self->newlines->{$filename} = $newlines;
186              
187 166         3722 $c =~ s/\n*$/\n/; # Ensure it ends with a newline
188              
189 166         2439 return $c;
190             }
191              
192             sub load_file {
193 184     184 1 20069 my $ref = shift;
194              
195 184         416 my $self;
196 184 100       1609 if (ref $ref) {
197 181         983 $self = $ref;
198             } else {
199             # Set up a temporary object
200 3         67 $self = $ref->new( confname => "" );
201             }
202              
203 184 50       1109 unshift @_, "filename" if @_ % 2;
204 184         464993 my %args = (
205             filename => undef,
206             silent => 0,
207             relative => Cwd::cwd(),
208             depth => 0,
209             force => 0,
210             includes => 1,
211             @_,
212             );
213              
214 184         2710 my $filename = $args{filename};
215              
216             # Do some canonicalization
217 184         1545 $filename =~ s/^~/$ENV{'HOME'}/g;
218 184   66     809 $filename = eval { Cwd::abs_path( File::Spec->rel2abs($filename, $args{relative}) ) }
219             || $filename;
220 184         1034 $filename = File::Spec->canonpath( $filename );
221              
222 9         138 return $self->data if grep {$_ eq $filename} @{$self->config_files}
  184         19867  
223 184 50 33     367 and not $args{force};
224              
225 184         4716 my $c = $self->_read_config($filename);
226 184 50 66     2204 return $self->data if not $c and $args{silent};
227 184 100       759 unless (defined $c) {
228 117 50       807 die "Failed to load $filename: $!\n" if not ref $ref;
229 117         1232 return;
230             }
231              
232             # Note this filename as having been loaded
233 67         208 push @{$self->config_files}, $filename;
  67         1682  
234              
235             $self->set_multiple( $self->include ) if $self->include
236 67 50 33     2299 and $args{includes};
237              
238 67 100       512 $self->data({}) unless $self->is_loaded;
239             $self->parse_content(
240             content => $c,
241             callback => sub {
242 355     355   1996 my %def = @_;
243 355         1586 $self->define(@_, origin => $filename);
244              
245 355 50 33     6963 return unless $self->include and $args{includes};
246 355         7253 my ($sec, $subsec, $name) = _split_key($self->include);
247 355 50 50     1909 return unless lc( $def{section} || '') eq lc( $sec || '');
      50        
248 0 0 0     0 return unless ($def{subsection} || '') eq ($subsec || '');
      0        
249 0 0 0     0 return unless lc( $def{name} || '') eq lc( $name || '');
      0        
250              
251             die "Exceeded maximum include depth (".$self->max_depth.") ".
252             "while including $def{value} from $filename"
253 0 0       0 if $args{depth} > $self->max_depth;
254              
255 0         0 my (undef, $dir, undef) = File::Spec->splitpath($filename);
256              
257             $self->load_file(
258             filename => $def{value},
259             silent => 1,
260             relative => $dir,
261 0         0 depth => $args{depth}+1,
262             force => 1,
263             );
264             },
265             error => sub {
266 9     9   79 error_callback( @_, filename => $filename );
267             },
268 67         2984 );
269              
270 58         1595 return $self->data;
271             }
272              
273             sub error_callback {
274 9     9 1 63 my %args = @_;
275              
276 9         31 my $offset_of_prev_newline = rindex( $args{content}, "\n", $args{offset} );
277 9         23 my $offset_of_next_newline = index( $args{content}, "\n", $args{offset} );
278             my $line = substr(
279             $args{content},
280 9         37 $offset_of_prev_newline + 1,
281             $offset_of_next_newline - ($offset_of_prev_newline + 1),
282             );
283              
284 9         15 my $line_number = 1;
285 9         16 my $current_offset = 0;
286              
287 9         31 while ($current_offset <= $args{offset}) {
288             # nibble off a line of content
289 14         68 $args{content} =~ s/(.*\n)//;
290 14         29 $line_number++;
291 14         44 $current_offset += length $1;
292             }
293 9         19 my $position = (length $line) - ($current_offset - ($args{offset} + 1));
294 9         623 die "Error parsing $args{filename} at line $line_number, position $position."
295             ."\nBad line was: '$line'\n";
296             }
297              
298             sub parse_content {
299 161     161 1 312 my $self = shift;
300             my %args = (
301             content => '',
302       0     callback => sub {},
303       0     error => sub {},
304 161         2202 @_,
305             );
306 161         1004 my $c = $args{content};
307 161 100       418 return if !$c; # nothing to do if content is empty
308 150         237 my $length = length $c;
309              
310 150 100       3310 my $section_regex
311             = $self->compatible ? qr/\A\[([0-9a-z.-]+)(?:[\t ]*"([^\n]*?)")?\]/im
312             : qr/\A\[([^\s\[\]"]+)(?:[\t ]*"([^\n]*?)")?\]/im;
313              
314 150 100       4573 my $key_regex
315             = $self->compatible ? qr/\A([a-z][0-9a-z-]*)[\t ]*(?:[#;].*)?$/im
316             : qr/\A([^\[=\n][^=\n]*?)[\t ]*(?:[#;].*)?$/im;
317              
318 150 100       3041 my $key_value_regex
319             = $self->compatible ? qr/\A([a-z][0-9a-z-]*)[\t ]*=[\t ]*/im
320             : qr/\A([^\[=\n][^=\n]*?)[\t ]*=[\t ]*/im;
321              
322 150         1489 my($section, $prev) = (undef, '');
323 150         264 while (1) {
324             # drop leading white space and blank lines
325 1109         3557 $c =~ s/\A\s*//im;
326              
327 1109         1821 my $offset = $length - length($c);
328             # drop to end of line on comments
329 1109 100       10290 if ($c =~ s/\A[#;].*?$//im) {
    100          
    100          
    100          
    100          
330 91         145 next;
331             }
332             # [sub]section headers of the format [section "subsection"] (with
333             # unlimited whitespace between) or [section.subsection] variable
334             # definitions may directly follow the section header, on the same line!
335             # - rules for sections: not case sensitive, only alphanumeric
336             # characters, -, and . allowed
337             # - rules for subsections enclosed in ""s: case sensitive, can
338             # contain any character except newline, " and \ must be escaped
339             # - rules for subsections with section.subsection alternate syntax:
340             # same rules as for sections
341             elsif ($c =~ s/$section_regex//) {
342 330         840 $section = lc $1;
343 330 100       899 if ($2) {
344 72         287 my $subsection = $2;
345 72         231 my $check = $2;
346 72         252 $check =~ s{\\\\}{}g;
347 72         221 $check =~ s{\\"}{}g;
348             return $args{error}->(
349             content => $args{content},
350 72 50       287 offset => $offset,
351              
352             # don't allow quoted subsections to contain unescaped
353             # double-quotes or backslashes
354             ) if $check =~ /\\|"/;
355              
356 72         224 $subsection =~ s{\\\\}{\\}g;
357 72         109 $subsection =~ s{\\"}{"}g;
358 72         162 $section .= ".$subsection";
359             }
360              
361 330         815 $args{callback}->(
362             section => $section,
363             offset => $offset,
364             length => ($length - length($c)) - $offset,
365             );
366             }
367             # keys followed by a unlimited whitespace and (optionally) a comment
368             # (no value)
369             #
370             # for keys, we allow any characters that won't screw up the parsing
371             # (= and newline) in non-compatible mode, and match non-greedily to
372             # allow any trailing whitespace to be dropped
373             #
374             # in compatible mode, keys can contain only 0-9a-z-
375             elsif ($c =~ s/$key_regex//) {
376             return $args{error}->(
377             content => $args{content},
378 5 50       28 offset => $offset,
379             ) unless defined $section;
380 5         17 $args{callback}->(
381             section => $section,
382             name => $1,
383             offset => $offset,
384             length => ($length - length($c)) - $offset,
385             );
386             }
387             # key/value pairs (this particular regex matches only the key part and
388             # the =, with unlimited whitespace around the =)
389             elsif ($c =~ s/$key_value_regex//) {
390             return $args{error}->(
391             content => $args{content},
392 533 50       1064 offset => $offset,
393             ) unless defined $section;
394 533         1208 my $name = $1;
395 533         885 my $value = "";
396             # parse the value
397 533         678 while (1) {
398             # comment or no content left on line
399 1320 100       8870 if ($c =~ s/\A([ \t]*[#;].*?)?$//im) {
    100          
    100          
    100          
    100          
    50          
    50          
    50          
    100          
    50          
400 533         837 last;
401             }
402             # any amount of whitespace between words becomes a single space
403             elsif ($c =~ s/\A[\t ]+//im) {
404 57         95 $value .= ' ';
405             }
406             # line continuation (\ character followed by new line)
407             elsif ($c =~ s/\A\\\r?\n//im) {
408 2         4 next;
409             }
410             # escaped backslash characters is translated to actual \
411             elsif ($c =~ s/\A\\\\//im) {
412 24         62 $value .= '\\';
413             }
414             # escaped quote characters are part of the value
415             elsif ($c =~ s/\A\\(['"])//im) {
416 98         209 $value .= $1;
417             }
418             # escaped newline in config is translated to actual newline
419             elsif ($c =~ s/\A\\n//im) {
420 0         0 $value .= "\n";
421             }
422             # escaped tab in config is translated to actual tab
423             elsif ($c =~ s/\A\\t//im) {
424 0         0 $value .= "\t";
425             }
426             # escaped backspace in config is translated to actual backspace
427             elsif ($c =~ s/\A\\b//im) {
428 0         0 $value .= "\b";
429             }
430             # quote-delimited value (possibly containing escape codes)
431             elsif ($c =~ s/\A"([^"\\]*(?:(?:\\\n|\\[tbn"\\])[^"\\]*)*)"//im) {
432 12         32 my $v = $1;
433             # remove all continuations (\ followed by a newline)
434 12         22 $v =~ s/\\\n//g;
435             # swap escaped newlines with actual newlines
436 12         19 $v =~ s/\\n/\n/g;
437             # swap escaped tabs with actual tabs
438 12         16 $v =~ s/\\t/\t/g;
439             # swap escaped backspaces with actual backspaces
440 12         19 $v =~ s/\\b/\b/g;
441             # swap escaped \ with actual \
442 12         19 $v =~ s/\\\\/\\/g;
443 12         21 $value .= $v;
444             }
445             # valid value (no escape codes)
446             elsif ($c =~ s/\A([^\t \\\n"]+)//im) {
447 594         1310 $value .= $1;
448             # unparseable
449             }
450             else {
451             # Note that $args{content} is the _original_
452             # content, not the nibbled $c, which is the
453             # remaining unparsed content
454             return $args{error}->(
455             content => $args{content},
456 0         0 offset => $offset,
457             );
458             }
459             }
460 533         1263 $args{callback}->(
461             section => $section,
462             name => $name,
463             value => $value,
464             offset => $offset,
465             length => ($length - length($c)) - $offset,
466             );
467             }
468             # end of content string; all done now
469             elsif (not length $c) {
470 141         498 last;
471             }
472             # unparseable
473             else {
474             # Note that $args{content} is the _original_ content, not
475             # the nibbled $c, which is the remaining unparsed content
476             return $args{error}->(
477             content => $args{content},
478 9         65 offset => $offset,
479             );
480             }
481             }
482             }
483              
484             sub define {
485 355     355 1 571 my $self = shift;
486 355         2120 my %args = (
487             section => undef,
488             name => undef,
489             value => undef,
490             origin => undef,
491             @_,
492             );
493 355 100 66     1847 return unless defined $args{section} and defined $args{name};
494 220         641 my $original_key = join(".", @args{qw/section name/});
495 220         378 $args{name} = lc $args{name};
496 220         495 my $key = join(".", @args{qw/section name/});
497              
498             # we're either adding a whole new key or adding a multiple key from
499             # the same file
500 220 100 100     4095 if ( !defined $self->origins->{$key}
501             || $self->origins->{$key} eq $args{origin} ) {
502 207 100       4635 if ($self->is_multiple($key)) {
    100          
503 1   50     8 push @{$self->data->{$key} ||= []}, $args{value};
  1         27  
504 1   50     14 push @{$self->casing->{$key} ||= []}, $original_key;
  1         24  
505             }
506             elsif (exists $self->data->{$key}) {
507 9         106 $self->set_multiple($key);
508 9         165 $self->data->{$key} = [$self->data->{$key}, $args{value}];
509 9         236 $self->casing->{$key} = [$self->casing->{$key}, $original_key];
510             }
511             else {
512 197         4087 $self->data->{$key} = $args{value};
513 197         4460 $self->casing->{$key} = $original_key;
514             }
515             }
516             # we're overriding a key set previously from a different file
517             else {
518             # un-mark as multiple if it was previously marked as such
519 13 50       593 $self->set_multiple( $key, 0 ) if $self->is_multiple( $key );
520              
521             # set the new value
522 13         362 $self->data->{$key} = $args{value};
523 13         315 $self->casing->{$key} = $original_key;
524             }
525 220         4951 $self->origins->{$key} = $args{origin};
526             }
527              
528             sub cast {
529 127     127 1 235 my $self = shift;
530 127         589 my %args = (
531             value => undef,
532             as => undef, # bool, int, or num
533             human => undef, # true value / false value
534             @_,
535             );
536              
537             use constant {
538 7         4739 BOOL_TRUE_REGEX => qr/^(?:true|yes|on|-?0*1)$/i,
539             BOOL_FALSE_REGEX => qr/^(?:false|no|off|0*)$/i,
540             NUM_REGEX => qr/^-?[0-9]*\.?[0-9]*[kmg]?$/,
541 7     7   65 };
  7         14  
542              
543 127 100 100     446 if (defined $args{as} && $args{as} eq 'bool-or-int') {
544 14 100 66     123 if ( $args{value} =~ NUM_REGEX ) {
    50          
    0          
545 6         16 $args{as} = 'int';
546             }
547             elsif ( $args{value} =~ BOOL_TRUE_REGEX ||
548             $args{value} =~ BOOL_FALSE_REGEX ) {
549 8         18 $args{as} = 'bool';
550             }
551             elsif ( !defined $args{value} ) {
552 0         0 $args{as} = 'bool';
553             }
554             else {
555 0         0 die "Invalid bool-or-int '$args{value}'\n";
556             }
557             }
558              
559 127         272 my $v = $args{value};
560 127 100       972 return $v unless defined $args{as};
561 48 100       344 if ($args{as} =~ /bool/i) {
    50          
562 34 100       77 return 1 unless defined $v;
563 33 100       170 if ( $v =~ BOOL_TRUE_REGEX ) {
    100          
564 16 100       61 if ( $args{human} ) {
565 9         54 return 'true';
566             }
567             else {
568 7         42 return 1;
569             }
570             }
571             elsif ($v =~ BOOL_FALSE_REGEX ) {
572 15 100       30 if ( $args{human} ) {
573 8         36 return 'false';
574             }
575             else {
576 7         32 return 0;
577             }
578             }
579             else {
580 2         37 die "Invalid bool '$args{value}'\n";
581             }
582             }
583             elsif ($args{as} =~ /int|num/) {
584 14 100       118 die "Invalid unit while casting to $args{as}\n"
585             unless $v =~ NUM_REGEX;
586              
587 13 100       59 if ($v =~ s/([kmg])$//) {
588 3 100       21 $v *= 1024 if $1 eq "k";
589 3 100       19 $v *= 1024*1024 if $1 eq "m";
590 3 50       13 $v *= 1024*1024*1024 if $1 eq "g";
591             }
592              
593 13 100       128 return $args{as} eq 'int' ? int $v : $v + 0;
594             }
595             }
596              
597             sub _get {
598 83     83   135 my $self = shift;
599 83         442 my %args = (
600             key => undef,
601             filter => '',
602             @_,
603             );
604 83 50       322 $self->load unless $self->is_loaded;
605              
606 83         216 $args{key} = $self->canonical_case( $args{key} );
607              
608 82 50       1458 return () unless exists $self->data->{$args{key}};
609 82         1592 my $v = $self->data->{$args{key}};
610 82 100       767 my @values = ref $v ? @{$v} : ($v);
  12         49  
611 82 100 66     597 if (defined $args{filter} and length $args{filter}) {
612 10 50       55 if ($args{filter} eq "!") {
    100          
613 0         0 @values = ();
614             }
615             elsif ($args{filter} =~ s/^!//) {
616 2   66     5 @values = grep { not defined or not m/$args{filter}/i } @values;
  4         44  
617             }
618             else {
619 8 100       20 @values = grep { defined and m/$args{filter}/i } @values;
  12         131  
620             }
621             }
622 82         329 return @values;
623             }
624              
625             # I'm pretty sure that someone can come up with an edge case where stripping
626             # all balanced quotes like this is not the right thing to do, but I don't
627             # see it actually being a problem in practice.
628             sub _remove_balanced_quotes {
629 2058     2058   4250 my $key = shift;
630              
631 7     7   61 no warnings 'uninitialized';
  7         15  
  7         26195  
632 2058         6709 $key = join '', map { s/"(.*)"/$1/; $_ } split /("[^"]+"|[^.]+)/, $key;
  4298         6002  
  4298         8483  
633 2058         6223 $key = join '', map { s/'(.*)'/$1/; $_ } split /('[^']+'|[^.]+)/, $key;
  4298         5425  
  4298         6736  
634              
635 2058         5114 return $key;
636             }
637              
638             sub get {
639 71     71 1 3792 my $self = shift;
640 71         1038 my %args = (
641             key => undef,
642             as => undef,
643             human => undef,
644             filter => '',
645             @_,
646             );
647              
648 71         362 my @v = $self->_get( %args );
649 70 100       211 return undef unless @v;
650 69 100       151 die "Multiple values" if @v > 1;
651              
652             return $self->cast( value => $v[0], as => $args{as},
653 68         519 human => $args{human} );
654             }
655              
656             sub get_all {
657 12     12 1 396 my $self = shift;
658 12         123 my %args = (
659             key => undef,
660             as => undef,
661             human => undef,
662             filter => '',
663             @_,
664             );
665              
666 12         109 my @v = $self->_get( %args );
667 12         31 @v = map {$self->cast( value => $_, as => $args{as}, human => $args{human} )} @v;
  17         62  
668 12 50       190 return wantarray ? @v : \@v;
669             }
670              
671             sub get_regexp {
672 28     28 1 853 my $self = shift;
673              
674 28         170 my %args = (
675             key => undef,
676             as => undef,
677             human => undef,
678             filter => '',
679             @_,
680             );
681              
682 28 50       107 $self->load unless $self->is_loaded;
683              
684 28 50 33     139 $args{key} = '.' unless defined $args{key} and length $args{key};
685              
686 28         44 my %results;
687 28         36 for my $key (keys %{$self->data}) {
  28         654  
688 108 100       1140 $results{$key} = $self->data->{$key} if $key =~ m/$args{key}/i;
689             }
690              
691 28 100 66     182 if (defined $args{filter} and length $args{filter}) {
692 21 100       103 if ($args{filter} eq "!") {
    100          
693 1         3 %results = ();
694             }
695             elsif ($args{filter} =~ s/^!//) {
696 10         34 for (keys %results) {
697 12 100       39 my @values = ref $results{$_} ? @{$results{$_}} : $results{$_};
  7         21  
698 12   66     28 @values = grep { not defined or not m/$args{filter}/i } @values;
  19         254  
699 12 100       39 if (!@values) {
700 7         18 delete $results{$_};
701             }
702             else {
703 5 100       23 $results{$_} = @values > 1 ? \@values : $values[0];
704             }
705             }
706             }
707             else {
708 10         29 for (keys %results) {
709 10 100       25 my @values = ref $results{$_} ? @{$results{$_}} : $results{$_};
  8         25  
710 10 50       21 @values = grep { defined and m/$args{filter}/i } @values;
  18         187  
711 10 100       25 if (!@values) {
712 3         9 delete $results{$_};
713             }
714             else {
715 7 100       24 $results{$_} = @values > 1 ? \@values : $values[0];
716             }
717             }
718             }
719             }
720              
721             @results{keys %results} =
722 28         75 map { $self->cast(
723             value => $results{$_},
724             as => $args{as},
725             human => $args{human},
726 22         66 ); } keys %results;
727 28 50       285 return wantarray ? %results : \%results;
728             }
729              
730             sub original_key {
731 6     6 1 15 my $self = shift;
732 6         16 my ($key) = @_;
733 6         119 return $self->casing->{ $self->canonical_case( $key ) };
734             }
735              
736             sub canonical_case {
737 571     571 1 4129 my $self = shift;
738 571         946 my ($key) = @_;
739 571         1585 my ($section, $subsection, $name) = _split_key($key);
740 571 100       1225 die "No section given in key: $key\n" unless $section;
741              
742             return join( '.',
743 570         1462 grep { defined } (lc $section, $subsection, lc $name),
  1710         8211  
744             );
745             }
746              
747             sub dump {
748 5     5 1 112 my $self = shift;
749              
750 5 50       38 $self->load unless $self->is_loaded;
751              
752 5 100       30 return %{$self->data} if wantarray;
  2         41  
753              
754 3         23 my $data = '';
755 3         17 for my $key (sort keys %{$self->data}) {
  3         51  
756 9         44 my $str;
757 9 50       119 if (defined $self->data->{$key}) {
758             # For git compat, we intentionally always write out in
759             # canonical (i.e. lower) case.
760 9         67 $str = "$key=";
761 9 50       20 if ( $self->is_multiple($key) ) {
762 0         0 $str .= '[';
763 0         0 $str .= join(', ', @{$self->data->{$key}});
  0         0  
764 0         0 $str .= "]\n";
765             }
766             else {
767 9         128 $str .= $self->data->{$key}."\n";
768             }
769             }
770             else {
771 0         0 $str = "$key\n";
772             }
773 9 50       66 if (!defined wantarray) {
774 0         0 print $str;
775             }
776             else {
777 9         21 $data .= $str;
778             }
779             }
780              
781 3 50       81 return $data if defined wantarray;
782             }
783              
784             sub format_section {
785 32     32 1 67 my $self = shift;
786              
787 32         344 my %args = (
788             section => undef,
789             bare => undef,
790             @_,
791             );
792              
793 32 100       180 if ($args{section} =~ /^(.*?)\.(.*)$/) {
794 7         47 my ($section, $subsection) = ($1, $2);
795 7         27 my $ret = qq|[$section "$subsection"]|;
796 7 100       38 $ret .= "\n" unless $args{bare};
797 7         36 return $ret;
798             }
799             else {
800 25         139 my $ret = qq|[$args{section}]|;
801 25 50       72 $ret .= "\n" unless $args{bare};
802 25         81 return $ret;
803             }
804             }
805              
806             sub format_definition {
807 83     83 1 151 my $self = shift;
808 83         392 my %args = (
809             key => undef,
810             value => undef,
811             bare => undef,
812             @_,
813             );
814 83 100       617 my $quote = $args{value} =~ /(^\s|;|#|\s$)/ ? '"' : '';
815 83         201 $args{value} =~ s/\\/\\\\/g;
816 83         201 $args{value} =~ s/"/\\"/g;
817 83         113 $args{value} =~ s/\t/\\t/g;
818 83         126 $args{value} =~ s/\n/\\n/g;
819 83         187 my $ret = "$args{key} = $quote$args{value}$quote";
820 83 100       247 $ret = "\t$ret\n" unless $args{bare};
821 83         401 return $ret;
822             }
823              
824             # Given a key, return its variable name, section, and subsection
825             # parts. Doesn't do any lowercase transformation.
826             sub _split_key {
827 1029     1029   2992 my $key = shift;
828              
829 1029         1421 my ($name, $section, $subsection);
830             # allow quoting of the key to, for example, preserve
831             # . characters in the key
832 1029 100       3669 if ( $key =~ s/\.["'](.*)["']$// ) {
833 4         26 $name = $1;
834 4         14 $section = $key;
835             }
836             else {
837 1025         2795 $key =~ /^(.*)\.(.*)$/;
838             # If we wanted, we could interpret quoting of the section name to
839             # allow for setting keys with section names including . characters.
840             # But git-config doesn't do that, so we won't bother for now. (Right
841             # now it will read these section names correctly but won't set them.)
842 1025         1895 ($section, $name) = map { _remove_balanced_quotes($_) } ($1, $2);
  2050         3882  
843             }
844              
845             # Make sure the section name we're comparing against has
846             # case-insensitive section names and case-sensitive subsection names.
847 1029         3483 $section =~ m/^([^.]+)(?:\.(.*))?$/;
848 1029         2554 ($section, $subsection) = ($1, $2);
849              
850 1029         3094 return ($section, $subsection, $name);
851             }
852              
853             sub group_set {
854 102     102 1 1508 my $self = shift;
855 102         193 my ($filename, $args_ref) = @_;
856              
857 102         240 my $c = $self->_read_config($filename); # undef if file doesn't exist
858              
859             # loop through each value to set, modifying the content to be written
860             # or erroring out as we go
861 102         244 for my $args_hash (@{$args_ref}) {
  102         423  
862 103         161 my %args = %{$args_hash};
  103         660  
863              
864 103         341 my ($section, $subsection, $name) = _split_key($args{key});
865              
866 103 100       319 die "No section given in key or invalid key $args{key}\n"
867             unless defined $section;
868              
869 99 100       460 die "Invalid variable name $name\n"
870             if $self->_invalid_variable_name($name);
871              
872 92 100       416 die "Invalid section name $section\n"
873             if $self->_invalid_section_name($section);
874              
875             # if the subsection to write contains unescaped \ or ", escape them
876             # automatically
877 90         172 my $unescaped_subsection;
878 90 100       209 if ( defined $subsection ) {
879 5         18 $unescaped_subsection = $subsection;
880 5         41 $subsection =~ s{\\}{\\\\}g;
881 5         15 $subsection =~ s{"}{\\"}g;
882             }
883              
884             $args{value} = $self->cast(
885             value => $args{value},
886             as => $args{as},
887             human => 1,
888 90 100 100     642 ) if defined $args{value} && defined $args{as};
889              
890 89         166 my $new;
891             my @replace;
892              
893 89         266 my $key = $self->canonical_case( $args{key} );
894              
895             $args{multiple} = $self->is_multiple($key)
896 89 100       337 unless defined $args{multiple};
897              
898             # use this for comparison
899 89 100       259 my $cmp_section =
900             defined $unescaped_subsection
901             ? join( '.', lc $section, $unescaped_subsection )
902             : lc $section;
903             # ...but this for writing (don't lowercase)
904 89 100       244 my $combined_section
905             = defined $subsection ? join('.', $section, $subsection)
906             : $section;
907              
908             # There's not really a good, simple way to get around parsing the
909             # content for each of the values we're setting. If we wanted to
910             # extract the offsets for every single one using only a single parse
911             # run, we'd end up having to munge all the offsets afterwards as we
912             # did the actual replacement since every time we did a replacement it
913             # would change the offsets for anything that was formerly to be added
914             # at a later offset. Which I'm not sure is any better than just
915             # parsing it again.
916             $self->parse_content(
917             content => $c,
918             callback => sub {
919 481     481   1308 my %got = @_;
920 481 100       1114 return unless $got{section} eq $cmp_section;
921 204         276 $new = $got{offset} + $got{length};
922 204 100       435 return unless defined $got{name};
923              
924 146         181 my $matched = 0;
925             # variable names are case-insensitive
926 146 100       290 if (lc $name eq lc $got{name}) {
927 28 100 66     91 if (defined $args{filter} and length $args{filter}) {
928             # copy the filter arg here since this callback may
929             # be called multiple times and we don't want to
930             # modify the original value
931 8         12 my $filter = $args{filter};
932 8 50       71 if ($filter eq "!") {
    100          
    100          
933             # Never matches
934             }
935             elsif ($filter =~ s/^!//) {
936 3 100       28 $matched = 1 if ($got{value} !~ m/$filter/i);
937             }
938             elsif ($got{value} =~ m/$filter/i) {
939 3         8 $matched = 1;
940             }
941             }
942             else {
943 20         38 $matched = 1;
944             }
945             }
946              
947             push @replace, {offset => $got{offset}, length => $got{length}}
948 146 100       472 if $matched;
949             },
950             error => sub {
951             error_callback(@_, filename => $args{filename})
952 0     0   0 },
953 89         1377 );
954              
955             die "Multiple occurrences of non-multiple key?"
956 89 100 100     876 if @replace > 1 && !$args{multiple};
957              
958             # We're only replacing the first occurrance unless they said
959             # to replace them all.
960 88 100 100     358 @replace = ($replace[0]) if @replace and $args{value} and not $args{replace_all};
      100        
961              
962 88 100       209 if (defined $args{value}) {
963 83 100 100     344 if (@replace
    100 100        
964             && (!$args{multiple} || $args{filter} || $args{replace_all})) {
965             # Replacing existing value(s)
966              
967             # if the string we're replacing with is not the same length as
968             # what's being replaced, any offsets following will be wrong.
969             # save the difference between the lengths here and add it to
970             # any offsets that follow.
971 11         24 my $difference = 0;
972              
973             # when replacing multiple values, we combine them all into one,
974             # which is kept at the position of the last one
975 11         19 my $last = pop @replace;
976              
977             # kill all values that are not last
978 11         31 ($c, $difference) = _unset_variables(\@replace, $c,
979             $difference);
980              
981             # substitute the last occurrence with the new value
982             substr(
983             $c,
984             $last->{offset}-$difference,
985             $last->{length},
986             $self->format_definition(
987             key => $name,
988             value => $args{value},
989 11         78 bare => 1,
990             ),
991             );
992             }
993             elsif (defined $new) {
994             # Adding a new value to the end of an existing block
995             substr(
996             $c,
997             index($c, "\n", $new)+1,
998             0,
999             $self->format_definition(
1000             key => $name,
1001             value => $args{value}
1002             )
1003 42         221 );
1004             }
1005             else {
1006             # Adding a new section
1007 30         240 $c .= $self->format_section( section => $combined_section );
1008             $c .= $self->format_definition(
1009             key => $name,
1010             value => $args{value},
1011 30         144 );
1012             }
1013             }
1014             else {
1015             # Removing an existing value (unset / unset-all)
1016 5 100       21 die "No occurrence of $args{key} found to unset in $filename\n"
1017             unless @replace;
1018              
1019 4         11 ($c, undef) = _unset_variables(\@replace, $c, 0);
1020             }
1021             }
1022 86         420 return $self->_write_config( $filename, $c );
1023             }
1024              
1025             sub set {
1026 101     101 1 46577 my $self = shift;
1027 101         1056 my (%args) = (
1028             key => undef,
1029             value => undef,
1030             filename => undef,
1031             filter => undef,
1032             as => undef,
1033             multiple => undef,
1034             @_
1035             );
1036              
1037 101         251 my $filename = $args{filename};
1038 101         181 delete $args{filename};
1039              
1040 101         394 return $self->group_set( $filename, [ \%args ] );
1041             }
1042              
1043             sub _unset_variables {
1044 15     15   37 my ($variables, $c, $difference) = @_;
1045              
1046 15         21 for my $var (@{$variables}) {
  15         81  
1047             # start from either the last newline or the last section
1048             # close bracket, since variable definitions can occur
1049             # immediately following a section header without a \n
1050 6         15 my $newline = rindex($c, "\n", $var->{offset}-$difference);
1051             # need to add 1 here to not kill the ] too
1052 6         13 my $bracket = rindex($c, ']', $var->{offset}-$difference) + 1;
1053 6 100       12 my $start = $newline > $bracket ? $newline : $bracket;
1054              
1055             my $length =
1056 6         14 index($c, "\n", $var->{offset}-$difference+$var->{length})-$start;
1057              
1058 6         14 substr(
1059             $c,
1060             $start,
1061             $length,
1062             '',
1063             );
1064 6         11 $difference += $length;
1065             }
1066              
1067 15         54 return ($c, $difference);
1068             }
1069              
1070             # In non-git-compatible mode, variables names can contain any characters that
1071             # aren't newlines or = characters, but cannot start or end with whitespace.
1072             #
1073             # Allowing . characters in variable names actually makes it so you
1074             # can get collisions between identifiers for things that are not
1075             # actually the same.
1076             #
1077             # For example, you could have a collision like this:
1078             # [section "foo"] bar.com = 1
1079             # [section] foo.bar.com = 1
1080             #
1081             # Both of these would be turned into 'section.foo.bar.com'. But it's
1082             # unlikely to ever actually come up, since you'd have to have
1083             # a *need* to have two things like this that are very similar
1084             # and yet different.
1085             sub _invalid_variable_name {
1086 99     99   216 my ($self, $name) = @_;
1087              
1088 99 100       1918 if ($self->compatible) {
1089 7         140 return $name !~ /^[a-z][0-9a-z-]*$/i;
1090             }
1091             else {
1092 92   100     1721 return $name !~ /^[^=\n\[][^=\n]*$/ || $name =~ /(?:^[ \t]+|[ \t+]$)/;
1093             }
1094             }
1095              
1096             # section, NOT subsection!
1097             sub _invalid_section_name {
1098 92     92   236 my ($self, $section) = @_;
1099              
1100 92 100       1386 if ($self->compatible) {
1101 3         59 return $section !~ /^[0-9a-z-.]+$/i;
1102             }
1103             else {
1104 89         966 return $section =~ /\s|\[|\]|"/;
1105             }
1106             }
1107              
1108             # write config with locking
1109             sub _write_config {
1110 94     94   160 my $self = shift;
1111 94         215 my($filename, $content) = @_;
1112              
1113 94   100     1850 my $newlines = $self->newlines->{$filename} || "\n";
1114 94 50       811 $content =~ s/\n/$newlines/g if $newlines ne "\n";
1115             # allow nested symlinks but only within reason
1116 94         124 my $max_depth = 5;
1117              
1118             # resolve symlinks
1119 94         242 while ($max_depth--) {
1120 470         3244 my $readlink = readlink $filename;
1121 470 100       1254 $filename = $readlink if defined $readlink;
1122             }
1123              
1124             # write new config file to temp file
1125             # (the only reason we call it .lock is because that's the
1126             # way git does it)
1127 94 50       5665 sysopen(my $fh, "${filename}.lock", O_CREAT|O_EXCL|O_WRONLY)
1128             or die "Can't open ${filename}.lock for writing: $!\n";
1129 94 100       2422 if (my $encoding = $self->encoding) {
1130 1     1   177 binmode $fh, ":encoding($encoding)";
  1         31  
  1         4  
  1         24  
1131             }
1132 94         14945 print $fh $content;
1133 94         2826 close $fh;
1134              
1135             # atomic rename
1136 94 50       8406 rename("${filename}.lock", ${filename})
1137             or die "Can't rename ${filename}.lock to ${filename}: $!\n";
1138             }
1139              
1140             sub rename_section {
1141 5     5 1 3630 my $self = shift;
1142              
1143 5         37 my (%args) = (
1144             from => undef,
1145             to => undef,
1146             filename => undef,
1147             @_
1148             );
1149              
1150 5 50       21 die "No section to rename from given\n" unless defined $args{from};
1151              
1152 5         18 my $c = $self->_read_config($args{filename});
1153             # file couldn't be opened = nothing to rename
1154 5 50       20 return if !defined($c);
1155              
1156 8         14 ($args{from}, $args{to}) = map { _remove_balanced_quotes($_) }
1157 5         12 grep { defined $_ } ($args{from}, $args{to});
  10         22  
1158              
1159 5         21 my @replace;
1160 5         10 my $prev_matched = 0;
1161             $self->parse_content(
1162             content => $c,
1163             callback => sub {
1164 32     32   89 my %got = @_;
1165              
1166             $replace[-1]->{section_is_last} = 0
1167 32 100 100     117 if (@replace && !defined($got{name}));
1168              
1169 32 100       82 if (lc($got{section}) eq lc($args{from})) {
1170 14 100       33 if (defined $got{name}) {
1171             # if we're removing rather than replacing and
1172             # there was a previous section match, increase
1173             # its length so it will kill this variable
1174             # assignment too
1175 7 100 66     32 if ($prev_matched && !defined $args{to} ) {
1176             $replace[-1]->{length} += ($got{offset} + $got{length})
1177 4         15 - ($replace[-1]{offset} + $replace[-1]->{length});
1178             }
1179             }
1180             else {
1181             # if we're removing rather than replacing, increase
1182             # the length of the previous match so when it's
1183             # replaced it will kill all the way up to the
1184             # beginning of this next section (this will kill
1185             # any leading whitespace on the line of the
1186             # next section, but that's OK)
1187             $replace[-1]->{length} += $got{offset} -
1188             ($replace[-1]->{offset} + $replace[-1]->{length})
1189 7 100 100     37 if @replace && $prev_matched && !defined($args{to});
      100        
1190              
1191             push @replace, {offset => $got{offset}, length =>
1192 7         23 $got{length}, section_is_last => 1};
1193 7         16 $prev_matched = 1;
1194             }
1195             }
1196             else {
1197             # if we're removing rather than replacing and there was
1198             # a previous section match, increase its length to kill all
1199             # the way up to this non-matching section (takes care
1200             # of newlines between here and there, etc.)
1201             $replace[-1]->{length} += $got{offset} -
1202             ($replace[-1]->{offset} + $replace[-1]->{length})
1203 18 100 100     51 if @replace && $prev_matched && !defined($args{to});
      100        
1204 18         40 $prev_matched = 0;
1205             }
1206             },
1207             error => sub {
1208 0     0   0 error_callback( @_, filename => $args{filename} );
1209             },
1210 5         59 );
1211 5 100       56 die "No such section '$args{from}'\n"
1212             unless @replace;
1213              
1214             # if the string we're replacing with is not the same length as what's
1215             # being replaced, any offsets following will be wrong. save the difference
1216             # between the lengths here and add it to any offsets that follow.
1217 4         8 my $difference = 0;
1218              
1219             # rename ALL section headers that matched to
1220             # (there may be more than one)
1221             my $replace_with = defined $args{to} ?
1222 4 100       41 $self->format_section( section => $args{to}, bare => 1 ) : '';
1223              
1224 4         18 for my $header (@replace) {
1225             substr(
1226             $c,
1227             $header->{offset} + $difference,
1228             # if we're removing the last section, just kill all the way to the
1229             # end of the file
1230             !defined($args{to}) && $header->{section_is_last} ? length($c) -
1231             ($header->{offset} + $difference) : $header->{length},
1232 7 100 100     41 $replace_with,
1233             );
1234 7         22 $difference += (length($replace_with) - $header->{length});
1235             }
1236              
1237 4         26 return $self->_write_config($args{filename}, $c);
1238             }
1239              
1240             sub remove_section {
1241 2     2 1 2494 my $self = shift;
1242              
1243 2         17 my (%args) = (
1244             section => undef,
1245             filename => undef,
1246             @_
1247             );
1248              
1249 2 50       16 die "No section given to remove\n" unless $args{section};
1250              
1251             # remove section is just a rename to nothing
1252             return $self->rename_section( from => $args{section}, filename =>
1253 2         23 $args{filename} );
1254             }
1255              
1256             sub add_comment {
1257 4     4 1 2513 my $self = shift;
1258 4         44 my (%args) = (
1259             comment => undef,
1260             filename => undef,
1261             indented => undef,
1262             semicolon => undef,
1263             @_
1264             );
1265              
1266 4 50       16 my $filename = $args{filename} or die "No filename passed to add_comment()";
1267 4 50       11 die "No comment to add\n" unless defined $args{comment};
1268              
1269             # Comment, preserving leading whitespace.
1270 4 100       10 my $chars = $args{indented} ? '[[:blank:]]*' : '';
1271 4 100       14 my $char = $args{semicolon} ? ';' : '#';
1272 4         100 (my $comment = $args{comment}) =~ s/^($chars)/$1$char /mg;
1273 4 50       29 $comment .= "\n" if $comment !~ /\n\z/;
1274              
1275 4         13 my $c = $self->_read_config($filename);
1276 4 100       19 $c = '' unless defined $c;
1277              
1278 4         41 return $self->_write_config( $filename, $c . $comment );
1279             }
1280              
1281             1;
1282              
1283             __END__