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   468787 use Moo;
  7         48661  
  7         58  
3 7     7   8691 use MooX::Types::MooseLike::Base qw(Bool HashRef ArrayRef Maybe Str Int);
  7         18552  
  7         719  
4              
5 7     7   77 use File::Spec;
  7         24  
  7         219  
6 7     7   96 use Cwd;
  7         29  
  7         630  
7 7     7   61 use Scalar::Util qw(openhandle);
  7         21  
  7         441  
8 7     7   57 use Fcntl qw(O_CREAT O_EXCL O_WRONLY);
  7         19  
  7         384  
9 7     7   194 use 5.008;
  7         34  
10              
11             our $VERSION = '1.17';
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 4560 my $self = shift;
93 77         331 my ($name, $mult) = (@_, 1);
94 77         2802 $self->multiple->{ $self->canonical_case( $name ) } = $mult;
95             }
96              
97             sub is_multiple {
98 316     316 1 1860 my $self = shift;
99 316         765 my $name = shift;
100 316 50       1092 return if !defined $name;
101 316         9016 return $self->multiple->{ $self->canonical_case( $name ) };
102             }
103              
104             sub load {
105 61     61 1 12903 my $self = shift;
106 61   33     419082 my $path = shift || Cwd::cwd;
107 61         6899 $self->data({});
108 61         13613 $self->multiple({});
109 61         9590 $self->config_files([]);
110 61         6550 $self->load_global;
111 61         762 $self->load_user;
112 61         964 $self->load_dirs( $path );
113 52 50       2066 return wantarray ? %{$self->data} : \%{$self->data};
  0         0  
  52         1358  
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 230 my $self = shift;
123 61         410 my $path = shift;
124 61         1237 my($vol, $dirs, undef) = File::Spec->splitpath( $path, 1 );
125 61         1429 my @dirs = File::Spec->splitdir( $dirs );
126 61         246 my @found;
127 61         297 while (@dirs) {
128 305         5078 my $path = File::Spec->catpath(
129             $vol, File::Spec->catdir(@dirs), $self->dir_file
130             );
131 305 100       39201 if (-f $path) {
132 57         344 push @found, $path;
133 57 50       2116 last unless $self->cascade;
134             }
135 248         1213 pop @dirs;
136             }
137 61         1756 $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 295 my $self = shift;
147 61         668 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 300 my $self = shift;
158 61         630 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   1160 my $self = shift;
164 295         925 my $filename = shift;
165              
166 295 100 66     10094 return unless -f $filename and -r $filename;
167 166 50       8183 open(my $fh, '<', $filename) or return;
168 166 100       8680 if (my $encoding = $self->encoding) {
169 1         53 binmode $fh, ":encoding($encoding)";
170             }
171              
172 166         2268 my $c = do {local $/; <$fh>};
  166         965  
  166         3685  
173              
174 166         663 my $newlines = "\n";
175 166 100 66     2584 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         9 $newlines = "\r\n";
179 1         12 $c =~ s/\r\n/\n/g;
180             } elsif ($c !~ /\n/ and $c =~ /\r/) {
181             # Best-guess convert from Mac.
182 1         6 $newlines = "\r";
183 1         9 $c =~ s/\r/\n/g;
184             }
185 166         5173 $self->newlines->{$filename} = $newlines;
186              
187 166         5615 $c =~ s/\n*$/\n/; # Ensure it ends with a newline
188              
189 166         3659 return $c;
190             }
191              
192             sub load_file {
193 184     184 1 29915 my $ref = shift;
194              
195 184         549 my $self;
196 184 100       1575 if (ref $ref) {
197 181         827 $self = $ref;
198             } else {
199             # Set up a temporary object
200 3         76 $self = $ref->new( confname => "" );
201             }
202              
203 184 50       1647 unshift @_, "filename" if @_ % 2;
204 184         1072171 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         2960 my $filename = $args{filename};
215              
216             # Do some canonicalization
217 184         1529 $filename =~ s/^~/$ENV{'HOME'}/g;
218 184   66     1223 $filename = eval { Cwd::abs_path( File::Spec->rel2abs($filename, $args{relative}) ) }
219             || $filename;
220 184         2076 $filename = File::Spec->canonpath( $filename );
221              
222 9         121 return $self->data if grep {$_ eq $filename} @{$self->config_files}
  184         14778  
223 184 50 33     803 and not $args{force};
224              
225 184         5136 my $c = $self->_read_config($filename);
226 184 50 66     2613 return $self->data if not $c and $args{silent};
227 184 100       1066 unless (defined $c) {
228 117 50       1015 die "Failed to load $filename: $!\n" if not ref $ref;
229 117         1766 return;
230             }
231              
232             # Note this filename as having been loaded
233 67         223 push @{$self->config_files}, $filename;
  67         2400  
234              
235             $self->set_multiple( $self->include ) if $self->include
236 67 50 33     3150 and $args{includes};
237              
238 67 100       877 $self->data({}) unless $self->is_loaded;
239             $self->parse_content(
240             content => $c,
241             callback => sub {
242 355     355   2491 my %def = @_;
243 355         1972 $self->define(@_, origin => $filename);
244              
245 355 50 33     13337 return unless $self->include and $args{includes};
246 355         13405 my ($sec, $subsec, $name) = _split_key($self->include);
247 355 50 50     3451 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   61 error_callback( @_, filename => $filename );
267             },
268 67         1966 );
269              
270 58         3279 return $self->data;
271             }
272              
273             sub error_callback {
274 9     9 1 60 my %args = @_;
275              
276 9         51 my $offset_of_prev_newline = rindex( $args{content}, "\n", $args{offset} );
277 9         42 my $offset_of_next_newline = index( $args{content}, "\n", $args{offset} );
278             my $line = substr(
279             $args{content},
280 9         65 $offset_of_prev_newline + 1,
281             $offset_of_next_newline - ($offset_of_prev_newline + 1),
282             );
283              
284 9         38 my $line_number = 1;
285 9         24 my $current_offset = 0;
286              
287 9         76 while ($current_offset <= $args{offset}) {
288             # nibble off a line of content
289 14         99 $args{content} =~ s/(.*\n)//;
290 14         48 $line_number++;
291 14         77 $current_offset += length $1;
292             }
293 9         40 my $position = (length $line) - ($current_offset - ($args{offset} + 1));
294 9         445 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 682 my $self = shift;
300             my %args = (
301             content => '',
302       0     callback => sub {},
303       0     error => sub {},
304 161         2516 @_,
305             );
306 161         1110 my $c = $args{content};
307 161 100       720 return if !$c; # nothing to do if content is empty
308 150         513 my $length = length $c;
309              
310 150 100       4786 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       6160 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       4758 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         2489 my($section, $prev) = (undef, '');
323 150         392 while (1) {
324             # drop leading white space and blank lines
325 1109         5922 $c =~ s/\A\s*//im;
326              
327 1109         3307 my $offset = $length - length($c);
328             # drop to end of line on comments
329 1109 100       25109 if ($c =~ s/\A[#;].*?$//im) {
    100          
    100          
    100          
    100          
330 91         270 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         1340 $section = lc $1;
343 330 100       1292 if ($2) {
344 72         231 my $subsection = $2;
345 72         236 my $check = $2;
346 72         278 $check =~ s{\\\\}{}g;
347 72         218 $check =~ s{\\"}{}g;
348             return $args{error}->(
349             content => $args{content},
350 72 50       594 offset => $offset,
351              
352             # don't allow quoted subsections to contain unescaped
353             # double-quotes or backslashes
354             ) if $check =~ /\\|"/;
355              
356 72         343 $subsection =~ s{\\\\}{\\}g;
357 72         198 $subsection =~ s{\\"}{"}g;
358 72         290 $section .= ".$subsection";
359             }
360              
361 330         1654 $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       26 offset => $offset,
379             ) unless defined $section;
380 5         28 $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       2541 offset => $offset,
393             ) unless defined $section;
394 533         1747 my $name = $1;
395 533         1505 my $value = "";
396             # parse the value
397 533         1017 while (1) {
398             # comment or no content left on line
399 1322 100       23834 if ($c =~ s/\A([ \t]*[#;].*?)?$//im) {
    100          
    100          
    100          
    100          
    50          
    50          
    50          
    100          
    50          
400 533         1266 last;
401             }
402             # any amount of whitespace between words becomes a single space
403             elsif ($c =~ s/\A[\t ]+//im) {
404 57         168 $value .= ' ';
405             }
406             # line continuation (\ character followed by new line)
407             elsif ($c =~ s/\A\\\r?\n//im) {
408 2         8 next;
409             }
410             # escaped backslash characters is translated to actual \
411             elsif ($c =~ s/\A\\\\//im) {
412 24         66 $value .= '\\';
413             }
414             # escaped quote characters are part of the value
415             elsif ($c =~ s/\A\\(['"])//im) {
416 98         308 $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         54 my $v = $1;
433             # remove all continuations (\ followed by a newline)
434 12         41 $v =~ s/\\\n//g;
435             # swap escaped newlines with actual newlines
436 12         32 $v =~ s/\\n/\n/g;
437             # swap escaped tabs with actual tabs
438 12         31 $v =~ s/\\t/\t/g;
439             # swap escaped backspaces with actual backspaces
440 12         35 $v =~ s/\\b/\b/g;
441             # swap escaped \ with actual \
442 12         35 $v =~ s/\\\\/\\/g;
443 12         36 $value .= $v;
444             }
445             # valid value (no escape codes)
446             elsif ($c =~ s/\A([^\t \\\n"]+)//im) {
447 596         2342 $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         2291 $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         937 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         72 offset => $offset,
479             );
480             }
481             }
482             }
483              
484             sub define {
485 355     355 1 850 my $self = shift;
486 355         2794 my %args = (
487             section => undef,
488             name => undef,
489             value => undef,
490             origin => undef,
491             @_,
492             );
493 355 100 66     3147 return unless defined $args{section} and defined $args{name};
494 220         1032 my $original_key = join(".", @args{qw/section name/});
495 220         648 $args{name} = lc $args{name};
496 220         795 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     6892 if ( !defined $self->origins->{$key}
501             || $self->origins->{$key} eq $args{origin} ) {
502 207 100       8159 if ($self->is_multiple($key)) {
    100          
503 1   50     6 push @{$self->data->{$key} ||= []}, $args{value};
  1         79  
504 1   50     17 push @{$self->casing->{$key} ||= []}, $original_key;
  1         26  
505             }
506             elsif (exists $self->data->{$key}) {
507 9         121 $self->set_multiple($key);
508 9         278 $self->data->{$key} = [$self->data->{$key}, $args{value}];
509 9         414 $self->casing->{$key} = [$self->casing->{$key}, $original_key];
510             }
511             else {
512 197         6134 $self->data->{$key} = $args{value};
513 197         6227 $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       514 $self->set_multiple( $key, 0 ) if $self->is_multiple( $key );
520              
521             # set the new value
522 13         296 $self->data->{$key} = $args{value};
523 13         324 $self->casing->{$key} = $original_key;
524             }
525 220         7412 $self->origins->{$key} = $args{origin};
526             }
527              
528             sub cast {
529 127     127 1 355 my $self = shift;
530 127         779 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         6111 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   118 };
  7         23  
542              
543 127 100 100     770 if (defined $args{as} && $args{as} eq 'bool-or-int') {
544 14 100 66     158 if ( $args{value} =~ NUM_REGEX ) {
    50          
    0          
545 6         24 $args{as} = 'int';
546             }
547             elsif ( $args{value} =~ BOOL_TRUE_REGEX ||
548             $args{value} =~ BOOL_FALSE_REGEX ) {
549 8         68 $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         593 my $v = $args{value};
560 127 100       1591 return $v unless defined $args{as};
561 48 100       404 if ($args{as} =~ /bool/i) {
    50          
562 34 100       139 return 1 unless defined $v;
563 33 100       269 if ( $v =~ BOOL_TRUE_REGEX ) {
    100          
564 16 100       179 if ( $args{human} ) {
565 9         156 return 'true';
566             }
567             else {
568 7         69 return 1;
569             }
570             }
571             elsif ($v =~ BOOL_FALSE_REGEX ) {
572 15 100       66 if ( $args{human} ) {
573 8         60 return 'false';
574             }
575             else {
576 7         63 return 0;
577             }
578             }
579             else {
580 2         38 die "Invalid bool '$args{value}'\n";
581             }
582             }
583             elsif ($args{as} =~ /int|num/) {
584 14 100       98 die "Invalid unit while casting to $args{as}\n"
585             unless $v =~ NUM_REGEX;
586              
587 13 100       101 if ($v =~ s/([kmg])$//) {
588 3 100       16 $v *= 1024 if $1 eq "k";
589 3 100       16 $v *= 1024*1024 if $1 eq "m";
590 3 50       11 $v *= 1024*1024*1024 if $1 eq "g";
591             }
592              
593 13 100       146 return $args{as} eq 'int' ? int $v : $v + 0;
594             }
595             }
596              
597             sub _get {
598 83     83   225 my $self = shift;
599 83         565 my %args = (
600             key => undef,
601             filter => '',
602             @_,
603             );
604 83 50       548 $self->load unless $self->is_loaded;
605              
606 83         392 $args{key} = $self->canonical_case( $args{key} );
607              
608 82 50       2645 return () unless exists $self->data->{$args{key}};
609 82         3555 my $v = $self->data->{$args{key}};
610 82 100       1102 my @values = ref $v ? @{$v} : ($v);
  12         69  
611 82 100 66     1227 if (defined $args{filter} and length $args{filter}) {
612 10 50       88 if ($args{filter} eq "!") {
    100          
613 0         0 @values = ();
614             }
615             elsif ($args{filter} =~ s/^!//) {
616 2   66     9 @values = grep { not defined or not m/$args{filter}/i } @values;
  4         62  
617             }
618             else {
619 8 100       34 @values = grep { defined and m/$args{filter}/i } @values;
  12         174  
620             }
621             }
622 82         839 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   7836 my $key = shift;
630              
631 7     7   77 no warnings 'uninitialized';
  7         24  
  7         32078  
632 2058         11488 $key = join '', map { s/"(.*)"/$1/; $_ } split /("[^"]+"|[^.]+)/, $key;
  4298         10915  
  4298         13652  
633 2058         17668 $key = join '', map { s/'(.*)'/$1/; $_ } split /('[^']+'|[^.]+)/, $key;
  4298         11631  
  4298         16485  
634              
635 2058         9590 return $key;
636             }
637              
638             sub get {
639 71     71 1 4358 my $self = shift;
640 71         810 my %args = (
641             key => undef,
642             as => undef,
643             human => undef,
644             filter => '',
645             @_,
646             );
647              
648 71         498 my @v = $self->_get( %args );
649 70 100       395 return undef unless @v;
650 69 100       272 die "Multiple values" if @v > 1;
651              
652             return $self->cast( value => $v[0], as => $args{as},
653 68         504 human => $args{human} );
654             }
655              
656             sub get_all {
657 12     12 1 740 my $self = shift;
658 12         112 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         51 @v = map {$self->cast( value => $_, as => $args{as}, human => $args{human} )} @v;
  17         88  
668 12 50       215 return wantarray ? @v : \@v;
669             }
670              
671             sub get_regexp {
672 28     28 1 787 my $self = shift;
673              
674 28         361 my %args = (
675             key => undef,
676             as => undef,
677             human => undef,
678             filter => '',
679             @_,
680             );
681              
682 28 50       167 $self->load unless $self->is_loaded;
683              
684 28 50 33     292 $args{key} = '.' unless defined $args{key} and length $args{key};
685              
686 28         70 my %results;
687 28         65 for my $key (keys %{$self->data}) {
  28         1110  
688 108 100       1837 $results{$key} = $self->data->{$key} if $key =~ m/$args{key}/i;
689             }
690              
691 28 100 66     305 if (defined $args{filter} and length $args{filter}) {
692 21 100       147 if ($args{filter} eq "!") {
    100          
693 1         5 %results = ();
694             }
695             elsif ($args{filter} =~ s/^!//) {
696 10         53 for (keys %results) {
697 12 100       55 my @values = ref $results{$_} ? @{$results{$_}} : $results{$_};
  7         77  
698 12   66     39 @values = grep { not defined or not m/$args{filter}/i } @values;
  19         435  
699 12 100       66 if (!@values) {
700 7         36 delete $results{$_};
701             }
702             else {
703 5 100       37 $results{$_} = @values > 1 ? \@values : $values[0];
704             }
705             }
706             }
707             else {
708 10         44 for (keys %results) {
709 10 100       40 my @values = ref $results{$_} ? @{$results{$_}} : $results{$_};
  8         40  
710 10 50       29 @values = grep { defined and m/$args{filter}/i } @values;
  18         257  
711 10 100       43 if (!@values) {
712 3         14 delete $results{$_};
713             }
714             else {
715 7 100       39 $results{$_} = @values > 1 ? \@values : $values[0];
716             }
717             }
718             }
719             }
720              
721             @results{keys %results} =
722 28         114 map { $self->cast(
723             value => $results{$_},
724             as => $args{as},
725             human => $args{human},
726 22         111 ); } keys %results;
727 28 50       341 return wantarray ? %results : \%results;
728             }
729              
730             sub original_key {
731 6     6 1 17 my $self = shift;
732 6         21 my ($key) = @_;
733 6         166 return $self->casing->{ $self->canonical_case( $key ) };
734             }
735              
736             sub canonical_case {
737 571     571 1 9080 my $self = shift;
738 571         1495 my ($key) = @_;
739 571         2131 my ($section, $subsection, $name) = _split_key($key);
740 571 100       2245 die "No section given in key: $key\n" unless $section;
741              
742             return join( '.',
743 570         2177 grep { defined } (lc $section, $subsection, lc $name),
  1710         13924  
744             );
745             }
746              
747             sub dump {
748 5     5 1 91 my $self = shift;
749              
750 5 50       53 $self->load unless $self->is_loaded;
751              
752 5 100       22 return %{$self->data} if wantarray;
  2         40  
753              
754 3         16 my $data = '';
755 3         8 for my $key (sort keys %{$self->data}) {
  3         65  
756 9         288 my $str;
757 9 50       189 if (defined $self->data->{$key}) {
758             # For git compat, we intentionally always write out in
759             # canonical (i.e. lower) case.
760 9         76 $str = "$key=";
761 9 50       31 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         222 $str .= $self->data->{$key}."\n";
768             }
769             }
770             else {
771 0         0 $str = "$key\n";
772             }
773 9 50       86 if (!defined wantarray) {
774 0         0 print $str;
775             }
776             else {
777 9         33 $data .= $str;
778             }
779             }
780              
781 3 50       64 return $data if defined wantarray;
782             }
783              
784             sub format_section {
785 32     32 1 110 my $self = shift;
786              
787 32         223 my %args = (
788             section => undef,
789             bare => undef,
790             @_,
791             );
792              
793 32 100       310 if ($args{section} =~ /^(.*?)\.(.*)$/) {
794 7         52 my ($section, $subsection) = ($1, $2);
795 7         38 my $ret = qq|[$section "$subsection"]|;
796 7 100       41 $ret .= "\n" unless $args{bare};
797 7         41 return $ret;
798             }
799             else {
800 25         109 my $ret = qq|[$args{section}]|;
801 25 50       175 $ret .= "\n" unless $args{bare};
802 25         134 return $ret;
803             }
804             }
805              
806             sub format_definition {
807 83     83 1 225 my $self = shift;
808 83         465 my %args = (
809             key => undef,
810             value => undef,
811             bare => undef,
812             @_,
813             );
814 83 100       714 my $quote = $args{value} =~ /(^\s|;|#|\s$)/ ? '"' : '';
815 83         306 $args{value} =~ s/\\/\\\\/g;
816 83         243 $args{value} =~ s/"/\\"/g;
817 83         4844 $args{value} =~ s/\t/\\t/g;
818 83         228 $args{value} =~ s/\n/\\n/g;
819 83         323 my $ret = "$args{key} = $quote$args{value}$quote";
820 83 100       395 $ret = "\t$ret\n" unless $args{bare};
821 83         620 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   5025 my $key = shift;
828              
829 1029         3030 my ($name, $section, $subsection);
830             # allow quoting of the key to, for example, preserve
831             # . characters in the key
832 1029 100       5481 if ( $key =~ s/\.["'](.*)["']$// ) {
833 4         28 $name = $1;
834 4         16 $section = $key;
835             }
836             else {
837 1025         5343 $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         3305 ($section, $name) = map { _remove_balanced_quotes($_) } ($1, $2);
  2050         7643  
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         5710 $section =~ m/^([^.]+)(?:\.(.*))?$/;
848 1029         4826 ($section, $subsection) = ($1, $2);
849              
850 1029         4328 return ($section, $subsection, $name);
851             }
852              
853             sub group_set {
854 102     102 1 1535 my $self = shift;
855 102         322 my ($filename, $args_ref) = @_;
856              
857 102         450 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         603 for my $args_hash (@{$args_ref}) {
  102         423  
862 103         236 my %args = %{$args_hash};
  103         743  
863              
864 103         561 my ($section, $subsection, $name) = _split_key($args{key});
865              
866 103 100       499 die "No section given in key or invalid key $args{key}\n"
867             unless defined $section;
868              
869 99 100       575 die "Invalid variable name $name\n"
870             if $self->_invalid_variable_name($name);
871              
872 92 100       429 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         236 my $unescaped_subsection;
878 90 100       313 if ( defined $subsection ) {
879 5         21 $unescaped_subsection = $subsection;
880 5         24 $subsection =~ s{\\}{\\\\}g;
881 5         16 $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     724 ) if defined $args{value} && defined $args{as};
889              
890 89         266 my $new;
891             my @replace;
892              
893 89         489 my $key = $self->canonical_case( $args{key} );
894              
895             $args{multiple} = $self->is_multiple($key)
896 89 100       625 unless defined $args{multiple};
897              
898             # use this for comparison
899 89 100       373 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       350 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   2331 my %got = @_;
920 481 100       2109 return unless $got{section} eq $cmp_section;
921 204         8586 $new = $got{offset} + $got{length};
922 204 100       687 return unless defined $got{name};
923              
924 146         456 my $matched = 0;
925             # variable names are case-insensitive
926 146 100       525 if (lc $name eq lc $got{name}) {
927 28 100 66     186 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         20 my $filter = $args{filter};
932 8 50       75 if ($filter eq "!") {
    100          
    100          
933             # Never matches
934             }
935             elsif ($filter =~ s/^!//) {
936 3 100       33 $matched = 1 if ($got{value} !~ m/$filter/i);
937             }
938             elsif ($got{value} =~ m/$filter/i) {
939 3         13 $matched = 1;
940             }
941             }
942             else {
943 20         62 $matched = 1;
944             }
945             }
946              
947             push @replace, {offset => $got{offset}, length => $got{length}}
948 146 100       768 if $matched;
949             },
950             error => sub {
951             error_callback(@_, filename => $args{filename})
952 0     0   0 },
953 89         1384 );
954              
955             die "Multiple occurrences of non-multiple key?"
956 89 100 100     1646 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     506 @replace = ($replace[0]) if @replace and $args{value} and not $args{replace_all};
      100        
961              
962 88 100       301 if (defined $args{value}) {
963 83 100 100     544 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         26 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         30 my $last = pop @replace;
976              
977             # kill all values that are not last
978 11         50 ($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         277 );
1004             }
1005             else {
1006             # Adding a new section
1007 30         216 $c .= $self->format_section( section => $combined_section );
1008             $c .= $self->format_definition(
1009             key => $name,
1010             value => $args{value},
1011 30         199 );
1012             }
1013             }
1014             else {
1015             # Removing an existing value (unset / unset-all)
1016 5 100       41 die "No occurrence of $args{key} found to unset in $filename\n"
1017             unless @replace;
1018              
1019 4         18 ($c, undef) = _unset_variables(\@replace, $c, 0);
1020             }
1021             }
1022 86         473 return $self->_write_config( $filename, $c );
1023             }
1024              
1025             sub set {
1026 101     101 1 68235 my $self = shift;
1027 101         1189 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         334 my $filename = $args{filename};
1038 101         303 delete $args{filename};
1039              
1040 101         552 return $self->group_set( $filename, [ \%args ] );
1041             }
1042              
1043             sub _unset_variables {
1044 15     15   56 my ($variables, $c, $difference) = @_;
1045              
1046 15         35 for my $var (@{$variables}) {
  15         65  
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         20 my $newline = rindex($c, "\n", $var->{offset}-$difference);
1051             # need to add 1 here to not kill the ] too
1052 6         20 my $bracket = rindex($c, ']', $var->{offset}-$difference) + 1;
1053 6 100       16 my $start = $newline > $bracket ? $newline : $bracket;
1054              
1055             my $length =
1056 6         19 index($c, "\n", $var->{offset}-$difference+$var->{length})-$start;
1057              
1058 6         19 substr(
1059             $c,
1060             $start,
1061             $length,
1062             '',
1063             );
1064 6         17 $difference += $length;
1065             }
1066              
1067 15         71 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   409 my ($self, $name) = @_;
1087              
1088 99 100       3071 if ($self->compatible) {
1089 7         190 return $name !~ /^[a-z][0-9a-z-]*$/i;
1090             }
1091             else {
1092 92   100     2316 return $name !~ /^[^=\n\[][^=\n]*$/ || $name =~ /(?:^[ \t]+|[ \t+]$)/;
1093             }
1094             }
1095              
1096             # section, NOT subsection!
1097             sub _invalid_section_name {
1098 92     92   331 my ($self, $section) = @_;
1099              
1100 92 100       2479 if ($self->compatible) {
1101 3         79 return $section !~ /^[0-9a-z-.]+$/i;
1102             }
1103             else {
1104 89         1292 return $section =~ /\s|\[|\]|"/;
1105             }
1106             }
1107              
1108             # write config with locking
1109             sub _write_config {
1110 94     94   249 my $self = shift;
1111 94         348 my($filename, $content) = @_;
1112              
1113 94   100     3191 my $newlines = $self->newlines->{$filename} || "\n";
1114 94 50       1250 $content =~ s/\n/$newlines/g if $newlines ne "\n";
1115             # allow nested symlinks but only within reason
1116 94         231 my $max_depth = 5;
1117              
1118             # resolve symlinks
1119 94         332 while ($max_depth--) {
1120 470         4611 my $readlink = readlink $filename;
1121 470 100       2016 $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       10432 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       3400 if (my $encoding = $self->encoding) {
1130 1     1   90 binmode $fh, ":encoding($encoding)";
  1         15  
  1         2  
  1         10  
1131             }
1132 94         14979 print $fh $content;
1133 94         4746 close $fh;
1134              
1135             # atomic rename
1136 94 50       8150 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 4947 my $self = shift;
1142              
1143 5         48 my (%args) = (
1144             from => undef,
1145             to => undef,
1146             filename => undef,
1147             @_
1148             );
1149              
1150 5 50       28 die "No section to rename from given\n" unless defined $args{from};
1151              
1152 5         31 my $c = $self->_read_config($args{filename});
1153             # file couldn't be opened = nothing to rename
1154 5 50       30 return if !defined($c);
1155              
1156 8         33 ($args{from}, $args{to}) = map { _remove_balanced_quotes($_) }
1157 5         25 grep { defined $_ } ($args{from}, $args{to});
  10         40  
1158              
1159 5         15 my @replace;
1160 5         15 my $prev_matched = 0;
1161             $self->parse_content(
1162             content => $c,
1163             callback => sub {
1164 32     32   195 my %got = @_;
1165              
1166             $replace[-1]->{section_is_last} = 0
1167 32 100 100     172 if (@replace && !defined($got{name}));
1168              
1169 32 100       141 if (lc($got{section}) eq lc($args{from})) {
1170 14 100       58 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     57 if ($prev_matched && !defined $args{to} ) {
1176             $replace[-1]->{length} += ($got{offset} + $got{length})
1177 4         24 - ($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     54 if @replace && $prev_matched && !defined($args{to});
      100        
1190              
1191             push @replace, {offset => $got{offset}, length =>
1192 7         37 $got{length}, section_is_last => 1};
1193 7         28 $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     100 if @replace && $prev_matched && !defined($args{to});
      100        
1204 18         73 $prev_matched = 0;
1205             }
1206             },
1207             error => sub {
1208 0     0   0 error_callback( @_, filename => $args{filename} );
1209             },
1210 5         80 );
1211 5 100       104 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         13 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       30 $self->format_section( section => $args{to}, bare => 1 ) : '';
1223              
1224 4         29 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     63 $replace_with,
1233             );
1234 7         28 $difference += (length($replace_with) - $header->{length});
1235             }
1236              
1237 4         36 return $self->_write_config($args{filename}, $c);
1238             }
1239              
1240             sub remove_section {
1241 2     2 1 3116 my $self = shift;
1242              
1243 2         22 my (%args) = (
1244             section => undef,
1245             filename => undef,
1246             @_
1247             );
1248              
1249 2 50       17 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         22 $args{filename} );
1254             }
1255              
1256             sub add_comment {
1257 4     4 1 2812 my $self = shift;
1258 4         50 my (%args) = (
1259             comment => undef,
1260             filename => undef,
1261             indented => undef,
1262             semicolon => undef,
1263             @_
1264             );
1265              
1266 4 50       22 my $filename = $args{filename} or die "No filename passed to add_comment()";
1267 4 50       18 die "No comment to add\n" unless defined $args{comment};
1268              
1269             # Comment, preserving leading whitespace.
1270 4 100       14 my $chars = $args{indented} ? '[[:blank:]]*' : '';
1271 4 100       21 my $char = $args{semicolon} ? ';' : '#';
1272 4         105 (my $comment = $args{comment}) =~ s/^($chars)/$1$char /mg;
1273 4 50       33 $comment .= "\n" if $comment !~ /\n\z/;
1274              
1275 4         20 my $c = $self->_read_config($filename);
1276 4 100       24 $c = '' unless defined $c;
1277              
1278 4         33 return $self->_write_config( $filename, $c . $comment );
1279             }
1280              
1281             1;
1282              
1283             __END__