File Coverage

blib/lib/Lazy/Utils.pm
Criterion Covered Total %
statement 31 220 14.0
branch 0 106 0.0
condition 0 42 0.0
subroutine 10 29 34.4
pod 12 18 66.6
total 53 415 12.7


line stmt bran cond sub pod time code
1             package Lazy::Utils;
2             =head1 NAME
3              
4             Lazy::Utils - Utility functions
5              
6             =head1 VERSION
7              
8             version 1.20
9              
10             =head1 SYNOPSIS
11              
12             use Lazy::Utils;
13            
14             trim($str);
15             ltrim($str);
16             rtrim($str);
17             file_get_contents($path, $prefs);
18             file_put_contents($path, $contents, $prefs);
19             shellmeta($s, $nonquoted);
20             system2($cmd, @argv);
21             bash_readline($prompt);
22             cmdargs($prefs, @argv);
23             whereis($name, $path);
24             file_cache($tag, $expiry, $coderef);
25             get_pod_text($file_name, $section, $exclude_section);
26              
27             =head1 DESCRIPTION
28              
29             Collection of utility functions all of exported by default.
30              
31             =cut
32 1     1   13605 use strict;
  1         2  
  1         25  
33 1     1   4 use warnings;
  1         3  
  1         32  
34 1     1   23 use v5.10.1;
  1         17  
35 1     1   6 use feature qw(switch);
  1         1  
  1         99  
36 1     1   534 no if ($] >= 5.018), 'warnings' => 'experimental';
  1         10  
  1         5  
37 1     1   536 use FindBin;
  1         828  
  1         40  
38 1     1   464 use JSON;
  1         9008  
  1         5  
39 1     1   484 use Pod::Simple::Text;
  1         27351  
  1         87  
40              
41              
42             BEGIN
43             {
44 1     1   8 require Exporter;
45 1         3 our $VERSION = '1.20';
46 1         7 our @ISA = qw(Exporter);
47 1         5 our @EXPORT = qw(trim ltrim rtrim file_get_contents file_put_contents shellmeta system2 _system
48             bash_readline bashReadLine cmdargs commandArgs cmdArgs whereis whereisBin file_cache fileCache
49             get_pod_text getPodText);
50 1         488 our @EXPORT_OK = qw();
51             }
52              
53              
54             =head1 FUNCTIONS
55              
56             =head2 trim($str)
57              
58             trims given string
59              
60             $str: I
61              
62             return value: I
63              
64             =cut
65             sub trim
66             {
67 0     0 1   my ($s) = @_;
68 0           $s =~ s/^\s+|\s+$//g;
69 0           return $s
70             }
71              
72             =head2 ltrim($str)
73              
74             trims left given string
75              
76             $str: I
77              
78             return value: I
79              
80             =cut
81             sub ltrim
82             {
83 0     0 1   my ($s) = @_;
84 0           $s =~ s/^\s+//;
85 0           return $s
86             }
87              
88             =head2 rtrim($str)
89              
90             trims right given string
91              
92             $str: I
93              
94             return value: I
95              
96             =cut
97             sub rtrim
98             {
99 0     0 1   my ($s) = @_;
100 0           $s =~ s/\s+$//;
101 0           return $s
102             }
103              
104             =head2 file_get_contents($path, $prefs)
105              
106             gets all contents of file in string type
107              
108             $path: I
109              
110             $prefs: I
111              
112             =over
113              
114             utf8: I
115              
116             =back
117              
118             return value: I
119              
120             =cut
121             sub file_get_contents
122             {
123 0     0 1   my ($path, $prefs) = @_;
124 0 0         $prefs = {} unless ref($prefs) eq 'HASH';
125             my $result = do
126 0           {
127 0           local $/ = undef;
128 0           my $mode = "";
129 0 0         $mode .= " :utf8" if $prefs->{utf8};
130 0 0         open my $fh, "<$mode", $path or return;
131 0           my $result = <$fh>;
132 0           close $fh;
133 0           $result;
134             };
135 0           return $result;
136             }
137              
138             =head2 file_put_contents($path, $contents, $prefs)
139              
140             puts all contents of file in string type
141              
142             $path: I
143              
144             $contents: I
145              
146             $prefs: I
147              
148             =over
149              
150             utf8: I
151              
152             =back
153              
154             return value: I
155              
156             =cut
157             sub file_put_contents
158             {
159 0     0 1   my ($path, $contents, $prefs) = @_;
160 0 0 0       return if not defined($contents) or ref($contents);
161 0 0         $prefs = {} unless ref($prefs) eq 'HASH';
162             my $result = do
163 0           {
164 0           local $\ = undef;
165 0           my $mode = "";
166 0 0         $mode .= " :utf8" if $prefs->{utf8};
167 0 0         open my $fh, ">$mode", $path or return;
168 0           my $result = print $fh $contents;
169 0           close $fh;
170 0           $result;
171             };
172 0           return $result;
173             }
174              
175             =head2 shellmeta($s, $nonquoted)
176              
177             escapes metacharacters of interpolated shell string
178              
179             $s: I
180              
181             $nonquoted: I
182              
183             return value: I
184              
185             =cut
186             sub shellmeta
187             {
188 0     0 1   my ($s, $nonquoted) = @_;
189 0 0         return unless defined $s;
190 0           $s =~ s/(\\|\"|\$)/\\$1/g;
191 0 0         $s =~ s/(\s|\*)/\\$1/g if $nonquoted;
192 0           return $s;
193             }
194              
195             =head2 system2($cmd, @argv)
196              
197             B<_system($cmd, @argv)> I
198              
199             alternative implementation of perls core system subroutine that executes a system command
200              
201             $cmd: I
202              
203             @argv: I
204              
205             return value: I
206              
207             returned $?: I
208              
209             returned $!: I
210              
211             =cut
212             sub system2
213             {
214             eval
215 0     0 1   {
216 0           my $pid;
217 0 0         if (not defined($pid = fork))
218             {
219 0           return -1;
220             }
221 0 0         if (not $pid)
222             {
223 1     1   7 no warnings FATAL => 'exec';
  1         2  
  1         1488  
224 0           exec(@_);
225 0           die $!;
226             }
227 0 0         if (waitpid($pid, 0) <= 0)
228             {
229 0           return -1;
230             }
231             };
232 0 0         return -1 if $@;
233 0           return $? >> 8;
234             }
235             sub _system
236             {
237 0     0     return system2(@_);
238             }
239              
240             =head2 bash_readline($prompt)
241              
242             B I
243              
244             reads a line from STDIN using Bash
245              
246             $prompt: I
247              
248             return value: I
249              
250             =cut
251             sub bash_readline
252             {
253 0     0 1   my ($prompt) = @_;
254 0 0         $prompt = "" unless defined($prompt);
255 0           my $in = \*STDIN;
256 0 0         unless (-t $in)
257             {
258 0           my $line = <$in>;
259 0 0         chomp $line if defined $line;
260 0           return $line;
261             }
262 0           local $/ = "\n";
263 0           my $cmd = '/usr/bin/env bash -c "read -p \"'.shellmeta(shellmeta($prompt)).'\" -r -e && echo -n \"\$REPLY\" 2>/dev/null"';
264 0           $_ = `$cmd`;
265 0 0         return (not $?)? $_: undef;
266             }
267             sub bashReadLine
268             {
269 0     0 0   return bash_readline(@_);
270             }
271              
272             =head2 cmdargs([$prefs, ]@argv)
273              
274             B I
275              
276             B I
277              
278             resolves command line arguments
279              
280             $prefs: I
281              
282             =over
283              
284             valuableArgs: I
285              
286             noCommand: I
287              
288             optionAtAll: I
289              
290             =back
291              
292             @argv: I
293              
294             -a -b=c -d e --f g --h --i=j k l -- m n
295              
296             by default, return value:
297              
298             { -a => '', -b => 'c', -d => '', --f => '', --h => '', --i => 'j', command => 'e', parameters => ['g', 'k', 'l'], late_parameters => ['m', 'n'] }
299              
300             if valuableArgs is on, return value;
301              
302             { -a => '', -b => 'c', -d => 'e', --f => 'g', --h => '', --i => 'j', command => 'k', parameters => ['l'], late_parameters => ['m', 'n'] }
303              
304             if noCommand is on, return value:
305              
306             { -a => '', -b => 'c', -d => '', --f => '', --h => '', --i => 'j', command => undef, parameters => ['e', 'g', 'k', 'l'], late_parameters => ['m', 'n'] }
307              
308             if optionAtAll is off, return value:
309              
310             { -a => '', -b => 'c', -d => '', command => 'e', parameters => ['--f', 'g', '--h', '--i=j', 'k', 'l', '--','m', 'n'], late_parameters => [] }
311              
312             =cut
313             sub cmdargs
314             {
315 0     0 1   my $prefs = {};
316 0 0 0       $prefs = shift if @_ >= 1 and ref($_[0]) eq 'HASH';
317 0           my @argv = @_;
318 0           my %result;
319 0           $result{command} = undef;
320 0           $result{parameters} = undef;
321              
322 0           my @parameters;
323             my @late_parameters;
324 0           my $late;
325 0           my $opt;
326 0           while (@argv)
327             {
328 0           my $argv = shift @argv;
329 0 0 0       next unless defined($argv) and not ref($argv);
330              
331 0 0 0       if (not (not defined($prefs->{optionAtAll}) or $prefs->{optionAtAll}) and @parameters)
      0        
332             {
333 0           push @parameters, $argv;
334 0           next;
335             }
336              
337 0 0         if ($late)
338             {
339 0           push @late_parameters, $argv;
340 0           next;
341             }
342              
343 0 0         if (substr($argv, 0, 2) eq '--')
344             {
345 0           $opt = undef;
346 0 0         if (length($argv) == 2)
347             {
348 0           $late = 1;
349 0           next;
350             }
351 0           my @arg = split('=', $argv, 2);
352 0           $result{$arg[0]} = $arg[1];
353 0 0         unless (defined($result{$arg[0]}))
354             {
355 0           $result{$arg[0]} = "";
356 0           $opt = $arg[0];
357             }
358 0           next;
359             }
360              
361 0 0 0       if (substr($argv, 0, 1) eq '-' and length($argv) != 1)
362             {
363 0           $opt = undef;
364 0           my @arg = split('=', $argv, 2);
365 0           $result{$arg[0]} = $arg[1];
366 0 0         unless (defined($result{$arg[0]}))
367             {
368 0           $result{$arg[0]} = "";
369 0           $opt = $arg[0];
370             }
371 0           next;
372             }
373              
374 0 0 0       if ($prefs->{valuableArgs} and $opt)
375             {
376 0           $result{$opt} = $argv;
377 0           $opt = undef;
378 0           next;
379             }
380 0           $opt = undef;
381              
382 0           push @parameters, $argv;
383             }
384              
385 0 0         $result{command} = shift @parameters unless $prefs->{noCommand};
386 0           $result{parameters} = \@parameters;
387 0           $result{late_parameters} = \@late_parameters;
388              
389 0           return \%result;
390             }
391             sub commandArgs
392             {
393 0     0 0   return cmdargs(@_);
394             }
395             sub cmdArgs
396             {
397 0     0 0   return cmdargs(@_);
398             }
399              
400             =head2 whereis($name, $path)
401              
402             B I
403              
404             searches valid binary in search path
405              
406             $name: I
407              
408             $path: I
409              
410             return value: I
411              
412             =cut
413             sub whereis
414             {
415 0     0 1   my ($name, $path) = @_;
416 0 0         return () unless $name;
417 0 0         $path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" unless $path;
418 0           return grep(-x $_, map("$_/$name", split(":", $path)));
419             }
420             sub whereisBin
421             {
422 0     0 0   return whereis(@_);
423             }
424              
425             =head2 file_cache($tag, $expiry, $coderef)
426              
427             B I
428              
429             gets most recent cached value in file cache by given tag and caller function if there is cached value in expiry period. Otherwise tries to get current value using $coderef, puts value in cache and cleanups old cache values.
430              
431             $tag: I
432              
433             $expiry: I
434              
435             =over
436              
437             E0: I
438              
439             =0: I
440              
441             E0: I
442              
443             =back
444              
445             $coderef: I
446              
447             return value: I
448              
449             =cut
450             sub file_cache
451             {
452 0     0 1   my ($tag, $expiry, $coderef) = @_;
453 0           my $result;
454 0           my $now = time();
455 0           my @cleanup;
456 0           my $caller = (caller(1))[3];
457 0 0         $caller = (caller(0))[0] unless $caller;
458 0           $caller = (caller(0))[3].",$caller";
459 0           my $tag_encoded = "";
460 0           for (0..(bytes::length($tag)-1))
461             {
462 0           my $c = bytes::substr($tag, $_, 1);
463 0 0         if ($c =~ /\W/)
464             {
465 0           $c = uc(sprintf("%%%x", bytes::ord($c)));
466             }
467 0           $tag_encoded .= $c;
468             }
469 0           my $tmp_base = "/tmp/";
470 0           my $tmp_prefix = $caller;
471 0           $tmp_prefix =~ s/\Q::\E/-/g;
472 0           $tmp_prefix .= ".$tag_encoded,";
473 0           for my $tmp_path (sort {$b cmp $a} glob("${tmp_base}$tmp_prefix*"))
  0            
474             {
475 0 0         if (my ($epoch, $pid) = $tmp_path =~ /^\Q${tmp_base}$tmp_prefix\E(\d*)\.(\d*)/)
476             {
477 0 0 0       if ($expiry < 0 or ($expiry > 0 and $now-$epoch < $expiry))
      0        
478             {
479 0 0         if (not defined($result))
480             {
481 0           my $tmp;
482 0           $tmp = file_get_contents($tmp_path);
483 0 0         if ($tmp)
484             {
485 0 0         if ($tmp =~ /^SCALAR\n(.*)/)
486             {
487 0           $result = $1;
488             } else
489             {
490 0           eval { $result = from_json($tmp, {utf8 => 1}) };
  0            
491             }
492             }
493             }
494 0           next;
495             }
496             }
497 0           unshift @cleanup, $tmp_path;
498             }
499 0 0         if (not defined($result))
500             {
501 0 0         $result = $coderef->() if ref($coderef) eq 'CODE';
502 0 0         if (defined($result))
503             {
504 0           my $tmp;
505 0 0         unless (ref($result))
506             {
507 0           $tmp = "SCALAR\n$result";
508             } else
509             {
510 0 0 0       eval { $tmp = to_json($result, {utf8 => 1, pretty => 1}) } if ref($result) eq "ARRAY" or ref($result) eq "HASH";
  0            
511             }
512 0 0 0       if ($tmp and file_put_contents("${tmp_base}tmp.$tmp_prefix$now.$$", $tmp) and rename("${tmp_base}tmp.$tmp_prefix$now.$$", "${tmp_base}$tmp_prefix$now.$$"))
      0        
513             {
514 0           pop @cleanup;
515 0           for (@cleanup)
516             {
517 0           unlink($_);
518             }
519             }
520             }
521             }
522 0           return $result;
523             }
524             sub fileCache
525             {
526 0     0 0   return file_cache(@_);
527             }
528              
529             =head2 get_pod_text($file_name, $section, $exclude_section)
530              
531             B I
532              
533             gets a text of pod contents in given file
534              
535             $file_name: I
536              
537             $section: I
538              
539             $exclude_section: I
540              
541             return value: I
542              
543             =cut
544             sub get_pod_text
545             {
546 0     0 1   my ($file_name, $section, $exclude_section) = @_;
547 0 0         $file_name = "$FindBin::Bin/$FindBin::Script" unless $file_name;
548 0 0         return unless -e $file_name;
549 0           my $parser = Pod::Simple::Text->new();
550 0           my $text;
551 0           $parser->output_string(\$text);
552 0           eval { $parser->parse_file($file_name) };
  0            
553 0 0         return if $@;
554 0           utf8::decode($text);
555 0 0         $section = ltrim($section) if $section;
556 0           my @text = split(/^/m, $text);
557 0           my $result;
558             my @result;
559 0           for my $line (@text)
560             {
561 0           chomp $line;
562 0 0 0       if (defined($section) and not defined($result))
563             {
564 0 0         if ($line eq $section)
565             {
566 0 0         unless ($exclude_section)
567             {
568 0           $result = "$line\n";
569 0           push @result, $line;
570             } else
571             {
572 0           $result = "";
573             }
574             }
575 0           next;
576             }
577 0 0 0       last if defined($section) and $line =~ /^\S+/;
578 0 0         $result = "" unless defined($result);
579 0           $result .= "$line\n";
580 0           push @result, $line;
581             }
582 0 0         return @result if wantarray;
583 0           return $result;
584             }
585             sub getPodText
586             {
587 0     0 0   return get_pod_text(@_);
588             }
589              
590              
591             1;
592             __END__