File Coverage

blib/lib/Shell/Config/Generate.pm
Criterion Covered Total %
statement 116 191 60.7
branch 43 86 50.0
condition 14 30 46.6
subroutine 22 30 73.3
pod 16 16 100.0
total 211 353 59.7


line stmt bran cond sub pod time code
1             package Shell::Config::Generate;
2              
3 11     11   2360777 use strict;
  11         81  
  11         313  
4 11     11   68 use warnings;
  11         20  
  11         298  
5 11     11   288 use 5.008001;
  11         42  
6 11     11   3289 use Shell::Guess;
  11         16493  
  11         333  
7 11     11   77 use Carp qw( croak );
  11         21  
  11         531  
8 11     11   67 use Exporter ();
  11         29  
  11         31387  
9              
10             # ABSTRACT: Portably generate config for any shell
11             our $VERSION = '0.33'; # VERSION
12              
13              
14             sub new
15             {
16 24     24 1 492586 my($class) = @_;
17 24         202 bless { commands => [], echo_off => 0 }, $class;
18             }
19              
20              
21             sub set
22             {
23 7     7 1 903 my($self, $name, $value) = @_;
24              
25 7         11 push @{ $self->{commands} }, ['set', $name, $value];
  7         25  
26              
27 7         29 $self;
28             }
29              
30              
31             sub set_path
32             {
33 4     4 1 2255 my($self, $name, @list) = @_;
34              
35 4         9 push @{ $self->{commands} }, [ 'set_path', $name, @list ];
  4         21  
36              
37 4         13 $self;
38             }
39              
40              
41             sub append_path
42             {
43 8     8 1 3980 my($self, $name, @list) = @_;
44              
45 8 50       50 push @{ $self->{commands} }, [ 'append_path', $name, @list ]
  8         31  
46             if @list > 0;
47              
48 8         24 $self;
49             }
50              
51              
52             sub prepend_path
53             {
54 8     8 1 3895 my($self, $name, @list) = @_;
55              
56 8 50       34 push @{ $self->{commands} }, [ 'prepend_path', $name, @list ]
  8         38  
57             if @list > 0;
58              
59 8         23 $self;
60             }
61              
62              
63             sub comment
64             {
65 4     4 1 931 my($self, @comments) = @_;
66              
67 4         10 push @{ $self->{commands} }, ['comment', $_] for @comments;
  5         17  
68              
69 4         10 $self;
70             }
71              
72              
73             sub shebang
74             {
75 4     4 1 11 my($self, $location) = @_;
76 4         14 $self->{shebang} = $location;
77 4         24 $self;
78             }
79              
80              
81             sub echo_off
82             {
83 3     3 1 7 my($self) = @_;
84 3         9 $self->{echo_off} = 1;
85 3         18 $self;
86             }
87              
88              
89             sub echo_on
90             {
91 1     1 1 3 my($self) = @_;
92 1         2 $self->{echo_off} = 0;
93 1         5 $self;
94             }
95              
96             sub _value_escape_csh
97             {
98 0     0   0 my $value = shift() . '';
99 0         0 $value =~ s/([\n!])/\\$1/g;
100 0         0 $value =~ s/(')/'"$1"'/g;
101 0         0 $value;
102             }
103              
104             sub _value_escape_fish
105             {
106 0     0   0 my $value = shift() . '';
107 0         0 $value =~ s/([\n])/\\$1/g;
108 0         0 $value =~ s/(')/'"$1"'/g;
109 0         0 $value;
110             }
111              
112             sub _value_escape_sh
113             {
114 86     86   256 my $value = shift() . '';
115 86         329 $value =~ s/(')/'"$1"'/g;
116 86         336 $value;
117             }
118              
119             sub _value_escape_win32
120             {
121 0     0   0 my $value = shift() . '';
122 0         0 $value =~ s/%/%%/g;
123 0         0 $value =~ s/([&^|<>()])/^$1/g;
124 0         0 $value =~ s/\n/^\n\n/g;
125 0         0 $value;
126             }
127              
128             # `0 Null
129             # `a Alert bell/beep
130             # `b Backspace
131             # `f Form feed (use with printer output)
132             # `n New line
133             # `r Carriage return
134             # `r`n Carriage return + New line
135             # `t Horizontal tab
136             # `v Vertical tab (use with printer output)
137              
138             my %ps = ( # microsoft would have to be different
139             "\0" => '`0',
140             "\a" => '`a',
141             "\b" => '`b',
142             "\f" => '`f',
143             "\r" => '`r',
144             "\n" => '`n',
145             "\t" => '`t',
146             #"\v" => '`v',
147             );
148              
149             sub _value_escape_powershell
150             {
151 3     3   11 my $value = shift() . '';
152 3         12 $value =~ s/(["'`\$#()])/`$1/g;
153 3         12 $value =~ s/([\0\a\b\f\r\n\t])/$ps{$1}/eg;
  0         0  
154 3         21 $value;
155             }
156              
157              
158             sub set_alias
159             {
160 1     1 1 625 my($self, $alias, $command) = @_;
161            
162 1         3 push @{ $self->{commands} }, ['alias', $alias, $command];
  1         9  
163             }
164              
165              
166             sub set_path_sep
167             {
168 9     9 1 5497 my($self, $sep) = @_;
169 9         37 push @{ $self->{commands} }, ['set_path_sep', $sep];
  9         74  
170             }
171              
172              
173             sub generate
174             {
175 54     54 1 616379 my($self, $shell) = @_;
176              
177 54 50       213 if(defined $shell)
178             {
179 54 100       310 if(ref($shell) eq '')
180             {
181 2         7 my $method = join '_', $shell, 'shell';
182 2 50       17 if(Shell::Guess->can($method))
183             {
184 2         10 $shell = Shell::Guess->$method;
185             }
186             else
187             {
188 0         0 croak("unknown shell type: $shell");
189             }
190             }
191             }
192             else
193             {
194 0         0 $shell = Shell::Guess->running_shell;
195             }
196            
197 54         323 $self->_generate($shell);
198             }
199              
200             sub _generate
201             {
202 50     50   158 my($self, $shell) = @_;
203            
204 50         222 my $buffer = '';
205 50 100       1352 my $sep = $shell->is_win32 ? ';' : ':';
206              
207 50 100 100     875 if(exists $self->{shebang} && $shell->is_unix)
208             {
209 3 100       34 if(defined $self->{shebang})
210 1         5 { $buffer .= "#!" . $self->{shebang} . "\n" }
211             else
212 2         8 { $buffer .= "#!" . $shell->default_location . "\n" }
213             }
214              
215 50 100 66     238 if($self->{echo_off} && ($shell->is_cmd || $shell->is_command))
      66        
216             {
217 1         10 $buffer .= '@echo off' . "\n";
218             }
219              
220 50         136 foreach my $args (map { [@$_] } @{ $self->{commands} })
  97         688  
  50         171  
221             {
222 97         228 my $command = shift @$args;
223              
224 97 100       258 if($command eq 'set_path_sep')
225             {
226 18         51 $sep = shift @$args;
227 18         66 next;
228             }
229            
230             # rewrite set_path as set
231 79 100       196 if($command eq 'set_path')
232             {
233 8         18 $command = 'set';
234 8         14 my $name = shift @$args;
235 8         63 $args = [$name, join $sep, @$args];
236             }
237              
238 79 100 100     414 if($command eq 'set')
    100          
    100          
    50          
239             {
240 22         57 my($name, $value) = @$args;
241 22 50 0     439 if($shell->is_c)
    50          
    50          
    0          
    0          
242             {
243 0         0 $value = _value_escape_csh($value);
244 0         0 $buffer .= "setenv $name '$value';\n";
245             }
246             elsif($shell->is_fish)
247             {
248 0         0 $value = _value_escape_fish($value);
249 0         0 $buffer .= "set -x $name '$value';\n";
250             }
251             elsif($shell->is_bourne)
252             {
253 22         1174 $value = _value_escape_sh($value);
254 22         80 $buffer .= "$name='$value';\n";
255 22         77 $buffer .= "export $name;\n";
256             }
257             elsif($shell->is_cmd || $shell->is_command)
258             {
259 0         0 $value = _value_escape_win32($value);
260 0         0 $buffer .= "set $name=$value\n";
261             }
262             elsif($shell->is_power)
263             {
264 0         0 $value = _value_escape_powershell($value);
265 0         0 $buffer .= "\$env:$name = \"$value\"\n";
266             }
267             else
268             {
269 0         0 croak 'don\'t know how to "set" with ' . $shell->name;
270             }
271             }
272              
273             elsif($command eq 'append_path' || $command eq 'prepend_path')
274             {
275 32         150 my($name, @values) = @$args;
276 32 50 0     734 if($shell->is_c)
    50 0        
    0          
    0          
277             {
278 0         0 my $value = join $sep, map { _value_escape_csh($_) } @values;
  0         0  
279 0         0 $buffer .= "test \"\$?$name\" = 0 && setenv $name '$value' || ";
280 0 0       0 if($command eq 'prepend_path')
281 0         0 { $buffer .= "setenv $name '$value$sep'\"\$$name\"" }
282             else
283 0         0 { $buffer .= "setenv $name \"\$$name\"'$sep$value'" }
284 0         0 $buffer .= ";\n";
285             }
286             elsif($shell->is_bourne)
287             {
288 32         1007 my $value = join $sep, map { _value_escape_sh($_) } @values;
  64         189  
289 32         124 $buffer .= "if [ -n \"\$$name\" ] ; then\n";
290 32 100       91 if($command eq 'prepend_path')
291 16         51 { $buffer .= " $name='$value$sep'\$$name;\n export $name;\n" }
292             else
293 16         49 { $buffer .= " $name=\$$name'$sep$value';\n export $name\n" }
294 32         61 $buffer .= "else\n";
295 32         100 $buffer .= " $name='$value';\n export $name;\n";
296 32         90 $buffer .= "fi;\n";
297             }
298             elsif($shell->is_fish)
299             {
300 0         0 my $value = join ' ', map { _value_escape_fish($_) } @values;
  0         0  
301 0         0 $buffer .= "if [ \"\$$name\" == \"\" ]; set -x $name $value; else; ";
302 0 0       0 if($command eq 'prepend_path')
303 0         0 { $buffer .= "set -x $name $value \$$name;" }
304             else
305 0         0 { $buffer .= "set -x $name \$$name $value;" }
306 0         0 $buffer .= "end\n";
307             }
308             elsif($shell->is_cmd || $shell->is_command || $shell->is_power)
309             {
310 0 0       0 my $value = join $sep, map { $shell->is_power ? _value_escape_powershell($_) : _value_escape_win32($_) } @values;
  0         0  
311 0 0       0 if($shell->is_power)
312             {
313 0         0 $buffer .= "if(\$env:$name) { ";
314 0 0       0 if($command eq 'prepend_path')
315 0         0 { $buffer .= "\$env:$name = \"$value$sep\" + \$env:$name" }
316             else
317 0         0 { $buffer .= "\$env:$name = \$env:$name + \"$sep$value\"" }
318 0         0 $buffer .= " } else { \$env:$name = \"$value\" }\n";
319             }
320             else
321             {
322 0         0 $buffer .= "if defined $name (set ";
323 0 0       0 if($command eq 'prepend_path')
324 0         0 { $buffer .= "$name=$value$sep%$name%" }
325             else
326 0         0 { $buffer .= "$name=%$name%$sep$value" }
327 0         0 $buffer .=") else (set $name=$value)\n";
328             }
329             }
330             else
331             {
332 0         0 croak 'don\'t know how to "append_path" with ' . $shell->name;
333             }
334             }
335              
336             elsif($command eq 'comment')
337             {
338 10 50 33     173 if($shell->is_unix || $shell->is_power)
    0 0        
339             {
340 10         73 $buffer .= "# $_\n" for map { split /\n/, } @$args;
  10         57  
341             }
342             elsif($shell->is_cmd || $shell->is_command)
343             {
344 0         0 $buffer .= "rem $_\n" for map { split /\n/, } @$args;
  0         0  
345             }
346             else
347             {
348 0         0 croak 'don\'t know how to "comment" with ' . $shell->name;
349             }
350             }
351            
352             elsif($command eq 'alias')
353             {
354 15 100 100     281 if($shell->is_bourne)
    100          
    100          
    100          
    50          
355             {
356 5         58 $buffer .= "alias $args->[0]=\"$args->[1]\";\n";
357             }
358             elsif($shell->is_c)
359             {
360 4         134 $buffer .= "alias $args->[0] $args->[1];\n";
361             }
362             elsif($shell->is_cmd || $shell->is_command)
363             {
364 2         159 $buffer .= "DOSKEY $args->[0]=$args->[1] \$*\n";
365             }
366             elsif($shell->is_power)
367             {
368 3         263 $buffer .= sprintf("function %s { %s \$args }\n", $args->[0], _value_escape_powershell($args->[1]));
369             }
370             elsif($shell->is_fish)
371             {
372 1         111 $buffer .= "alias $args->[0] '$args->[1]';\n";
373             }
374             else
375             {
376 0         0 croak 'don\'t know how to "alias" with ' . $shell->name;
377             }
378             }
379             }
380              
381 50         279 $buffer;
382             }
383              
384              
385             sub generate_file
386             {
387 0     0 1 0 my($self, $shell, $filename) = @_;
388 0         0 my $fh;
389 0 0       0 open($fh, '>', $filename) or die "cannot open $filename: $!";
390 0 0       0 print $fh $self->generate($shell) or die "cannot write $filename: $!";
391 0 0       0 close $fh or die "error closing $filename: $!";
392             }
393              
394             *import = \&Exporter::import;
395              
396             our @EXPORT_OK = qw( win32_space_be_gone cmd_escape_path powershell_escape_path );
397              
398              
399 0     0   0 *_win_to_posix_path = $^O =~ /^(cygwin|msys)$/ ? \&Cygwin::win_to_posix_path : sub { $_[0] };
400 0     0   0 *_posix_to_win_path = $^O =~ /^(cygwin|msys)$/ ? \&Cygwin::posix_to_win_path : sub { $_[0] };
401              
402             sub win32_space_be_gone
403             {
404 1 50   1 1 5655 return @_ if $^O !~ /^(MSWin32|cygwin|msys)$/;
405 0 0         map { /\s/ ? _win_to_posix_path(Win32::GetShortPathName(_posix_to_win_path($_))) : $_ } @_;
  0            
406             }
407              
408              
409             sub cmd_escape_path
410             {
411 0     0 1   my $path = shift() . '';
412 0           $path =~ s/%/%%/g;
413 0           $path =~ s/([&^|<>])/^$1/g;
414 0           $path =~ s/\n/^\n\n/g;
415 0           "\"$path\"";
416             }
417              
418              
419             sub powershell_escape_path
420             {
421 0     0 1   map { my $p = _value_escape_powershell($_); $p =~ s/ /` /g; $p } @_;
  0            
  0            
  0            
422             }
423              
424             1;
425              
426             __END__