File Coverage

bin/stopwatch
Criterion Covered Total %
statement 59 115 51.3
branch 32 52 61.5
condition 3 3 100.0
subroutine 8 12 66.6
pod n/a
total 102 182 56.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             # PODNAME: stopwatch
4              
5              
6 2     2   1534 use strict;
  2         4  
  2         92  
7 2     2   11 use warnings FATAL => 'all';
  2         4  
  2         98  
8              
9 2     2   19 use Carp;
  2         3  
  2         204  
10 2     2   1937 use POSIX qw(floor);
  2         16950  
  2         17  
11 2     2   5128 use Pod::Usage;
  2         124282  
  2         2840  
12              
13             my $true = 1;
14             my $false = '';
15              
16             sub clear {
17             # '01:02:12' is 8 symbols
18 0     0   0 my $char_count = 8;
19              
20 0         0 print "\r" x $char_count;
21 0         0 return '';
22             }
23              
24             sub print_seconds {
25 0     0   0 my ($seconds) = @_;
26              
27 0         0 my ($h, $m, $s) = get_h_m_s($seconds);
28              
29 0         0 printf("%02d:%02d:%02d", $h, $m, $s);
30              
31 0         0 return '';
32             }
33              
34             sub get_h_m_s {
35 3     3   960 my ($seconds) = @_;
36              
37 3         28 my $h = floor($seconds / (60*60));
38 3 50       10 croak 'Number is too big' if $h > 99;
39              
40 3         11 my $m = floor( ($seconds - $h*60*60) / 60 );
41 3         7 my $s = $seconds - $h*60*60 - $m*60;
42              
43 3         24 return $h, $m, $s;
44             }
45              
46              
47             sub get_seconds_from_string {
48 7     7   1181 my ($string) = @_;
49              
50 7 100       51 if ($string =~ /^([1-9][0-9]*)\s*s$/i) {
    100          
51 2         12 return $1;
52             } elsif ($string =~ /^([1-9][0-9]*)\s*m$/i) {
53 2         14 return $1 * 60;
54             } else {
55 3 50       14 $string = '' if not defined $string;
56 3         983 croak "Can't parse string '$string'";
57             }
58             }
59              
60             sub parse_argv {
61 16     16   485 my (@argv) = @_;
62              
63             # options that can't be used with other options
64 16 100       38 if (@argv == 0) {
  33 100       147  
    100          
65             return {
66 1         13 error => 0,
67             actions => [],
68             };
69             } elsif (grep {$_ eq '--help' or $_ eq '-h'} @argv) {
70              
71 7 100       15 if (@argv == 1) {
72             return {
73 2         24 error => 0,
74             actions => ['help'],
75             };
76             } else {
77             return {
78 5         41 error => 1,
79             error_actions => ['help_cant_be_used_with_other_options'],
80             };
81             }
82              
83             }
84              
85             # options that can be used with other options
86 8         9 my @actions;
87             my %result;
88              
89 0         0 my @run_options;
90              
91 8         16 my @original_argv = @argv;
92 8         12 @argv = ();
93              
94 8         11 my $run_position;
95             my $run_got_all_options;
96 0         0 my $run_time;
97 0         0 my $run_cmd;
98              
99             # --run
100 8         19 for (my $i = 0; $i < @original_argv; $i++) {
101 21 100       38 if ($original_argv[$i] eq '--run') {
102 5         6 $run_position = $i;
103 5         7 $run_got_all_options = $false;
104             }
105              
106 21 100       34 if (defined($run_position)) {
107 15 100       34 if ($run_position == $i) {
    100          
    100          
108             # do nothing
109             } elsif ($run_position + 1 == $i) {
110 5         7 $run_time = $original_argv[$i];
111             } elsif ($run_position + 2 == $i) {
112 4         5 $run_cmd = $original_argv[$i];
113 4         13 push @run_options, {
114             time => $run_time,
115             cmd => $run_cmd,
116             };
117 4         16 $run_got_all_options = $true;
118             } else {
119 1         2 $run_position = undef;
120             }
121             }
122              
123 21 100       51 if (not defined $run_position) {
124 7         20 push @argv, $original_argv[$i];
125             }
126             }
127              
128 8 100 100     31 if ((defined $run_got_all_options) and (not $run_got_all_options)) {
129             return {
130 1         9 error => 1,
131             error_actions => ['incorrect_run_usage'],
132             };
133             }
134              
135 7 100       13 if (@run_options) {
136 3         5 push @actions, 'run';
137 3         7 $result{run_options} = \@run_options;
138             }
139              
140 7 100       13 if (not @argv) {
141             # we parsed all the options
142             return {
143 2         26 error => 0,
144             actions => \@actions,
145             %result,
146             }
147             } else {
148             # some options are left
149             return {
150 5         57 error => 1,
151             error_actions => ['unknown_option'],
152             error_options => \@argv,
153             };
154             }
155              
156             }
157              
158             sub print_help {
159 0     0     pod2usage();
160             }
161              
162             sub main {
163              
164 0     0     my $options = parse_argv(@ARGV);
165              
166 0           my %second2cmd;
167              
168 0 0         if (not $options->{error}) {
169 0 0         if (grep { $_ eq 'help'} @{$options->{actions}}) {
  0            
  0            
170 0           print_help();
171 0           exit 0;
172             }
173              
174 0 0         if (grep { $_ eq 'run'} @{$options->{actions}}) {
  0            
  0            
175 0           foreach my $run_option (@{$options->{run_options}}) {
  0            
176 0           my $s;
177              
178 0           eval {
179 0           $s = get_seconds_from_string($run_option->{time});
180             };
181 0 0         if ($@) {
182 0           print "Error. `--run` option '$run_option->{time}' is incorrect.\n";
183 0           exit 1;
184             }
185 0           $second2cmd{$s} = $run_option->{cmd};
186             }
187             }
188              
189             } else {
190 0 0         if (grep { $_ eq 'help_cant_be_used_with_other_options'} @{$options->{error_actions}}) {
  0 0          
  0 0          
  0            
191 0           print "Error. Help can't be used with other options.\n";
192 0           exit 1;
193 0           } elsif (grep { $_ eq 'incorrect_run_usage'} @{$options->{error_actions}}) {
  0            
194 0           print "Error. `--run` should be used with 2 parameters.\n";
195 0           exit 1;
196 0           } elsif (grep { $_ eq 'unknown_option'} @{$options->{error_actions}}) {
197 0 0         my $postfix = @{$options->{error_options}} > 1 ? 's' : '';
  0            
198 0           print "Error. Got unknown option$postfix: '"
199 0           . (join "', '", @{$options->{error_options}})
200             . "'.\n"
201             ;
202 0           exit 1;
203             }
204             }
205              
206             # unbuffer STDOUT
207 0           $|++;
208              
209 0           my $seconds = 0;
210              
211 0           while ($true) {
212              
213 0           clear();
214 0           print_seconds($seconds);
215 0 0         if ($second2cmd{$seconds}) {
216 0           `$second2cmd{$seconds} 2> /dev/null &`;
217             }
218              
219 0           sleep 1;
220 0           $seconds++;
221              
222             }
223              
224             }
225             main() if not caller;
226              
227             1;
228              
229             __END__