delta: harden external command invocations
No binary (build failed)
—
(log yok)
| @@ -34,8 +34,20 @@ use strict; | |
| use warnings; | |
| use POSIX; | |
| + | use IPC::Open3; |
| use Time::Piece; | |
| + | sub cmd { |
| + | my @out; |
| + | my $as_string = shift if($_[0] eq '$'); # return as string (vs. list of lines) |
| + | my $hideerr = shift if($_[0] eq '2>'); # 2>/dev/null |
| + | my $pid = open3(my $in, my $out, $hideerr ? '>/dev/null' : '>&STDERR', @_); |
| + | close $in; |
| + | push @out, <$out>; |
| + | waitpid($pid, 0); |
| + | return $as_string ? join('', @out) : @out; |
| + | } |
| + | |
| my $start = $ARGV[0] || ''; | |
| if($start eq "-h") { | |
| @@ -43,76 +55,92 @@ if($start eq "-h") { | |
| exit; | |
| } | |
| elsif($start eq "") { | |
| - | $start = `git tag --sort=taggerdate | grep "^curl-" | tail -1`; |
| + | my @tags = cmd('git', 'tag', '--sort=taggerdate', '--no-column', '--list', 'curl-*'); |
| + | $start = $tags[-1]; # latest tag |
| chomp $start; | |
| } | |
| - | my $commits = `git log --oneline $start.. | wc -l`; |
| - | my $committers = `git shortlog -s $start.. | wc -l`; |
| - | my $bcommitters = `git shortlog -s $start | wc -l`; |
| + | my $commits = cmd('$', 'git', 'rev-list', '--count', "$start.."); |
| + | my $committers = cmd('git', 'shortlog', '-s', "$start.."); |
| + | my $bcommitters = cmd('git', 'shortlog', '-s', $start); |
| - | my $acommits = `git log --oneline | wc -l`; |
| - | my $acommitters = `git shortlog -s | wc -l`; |
| + | my $acommits = cmd('$', 'git', 'rev-list', '--count', 'HEAD'); |
| + | my $acommitters = cmd('git', 'shortlog', '-s', 'HEAD'); |
| # delta from now compared to before | |
| my $ncommitters = $acommitters - $bcommitters; | |
| # number of contributors right now | |
| - | my $acontribs = `./scripts/contrithanks.sh stdout | wc -l`; |
| + | my $acontribs = cmd('./scripts/contrithanks.sh', 'stdout'); |
| # number when the tag was set | |
| - | my $bcontribs = `git show $start:docs/THANKS | grep -c '^[^ ]'`; |
| + | my $bcontribs = cmd('$', 'git', 'grep', '-h', '-c', '^[^ ]', "$start:docs/THANKS"); |
| # delta | |
| my $contribs = $acontribs - $bcontribs; | |
| # number of setops: | |
| sub setopts { | |
| - | my ($f)=@_; |
| - | open(H, $f); |
| - | my $opts; |
| - | while(<H>) { |
| - | if(/^ CURLOPT(|DEPRECATED)\(/ && ($_ !~ /OBSOLETE/)) { |
| - | $opts++; |
| + | my $mode = shift; |
| + | my $opts = 0; |
| + | if(open(H, $mode, @_)) { |
| + | while(<H>) { |
| + | if(/^ CURLOPT(|DEPRECATED)\(/ && ($_ !~ /OBSOLETE/)) { |
| + | $opts++; |
| + | } |
| } | |
| + | close(H); |
| + | } |
| + | else { |
| + | die join(' ', @_) . ": $!\n"; |
| } | |
| - | close(H); |
| return $opts; | |
| } | |
| - | my $asetopts = setopts("<include/curl/curl.h"); |
| - | my $bsetopts = setopts("git show $start:include/curl/curl.h|"); |
| + | my $asetopts = setopts('<', 'include/curl/curl.h'); |
| + | my $bsetopts = setopts('-|', 'git', 'show', "$start:include/curl/curl.h"); |
| my $nsetopts = $asetopts - $bsetopts; | |
| # Number of command line options: | |
| - | my $aoptions=`grep -c '{ *"....--' src/tool_listhelp.c`; |
| - | my $boptions=`git show $start:src/tool_listhelp.c 2>/dev/null | grep -c '{ *"....--'`; |
| - | my $noptions=$aoptions - $boptions; |
| + | my $aoptions = cmd('$', '2>', 'git', 'grep', '-h', '-c', '{ *"....--', 'src/tool_listhelp.c'); |
| + | my $boptions = cmd('$', '2>', 'git', 'grep', '-h', '-c', '{ *"....--', "$start:src/tool_listhelp.c"); |
| + | my $noptions = $aoptions - $boptions; |
| # current local branch | |
| - | my $branch=`git rev-parse --abbrev-ref HEAD 2>/dev/null`; |
| + | my $branch = cmd('$', '2>', 'git', 'rev-parse', '--abbrev-ref', 'HEAD'); |
| chomp $branch; | |
| # Number of files in git | |
| - | my $afiles=`git ls-files | wc -l`; |
| - | my $deletes=`git diff-tree --diff-filter=A -r --summary origin/$branch $start 2>/dev/null | wc -l`; |
| - | my $creates=`git diff-tree --diff-filter=D -r --summary origin/$branch $start 2>/dev/null | wc -l`; |
| + | my $afiles = cmd('git', 'ls-files'); |
| + | my $deletes = cmd('2>', 'git', 'diff-tree', '--diff-filter=A', '-r', '--summary', "origin/$branch", $start); |
| + | my $creates = cmd('2>', 'git', 'diff-tree', '--diff-filter=D', '-r', '--summary', "origin/$branch", $start); |
| # Time since that tag | |
| - | my $tagged=`git for-each-ref --format="%(refname:short) | %(taggerdate:unix)" refs/tags/* | grep ^$start | cut '-d|' -f2`; # Unix timestamp |
| - | my $taggednice=`git for-each-ref --format="%(refname:short) | %(creatordate)" refs/tags/* | grep ^$start | cut '-d|' -f2`; # human readable time |
| + | sub tagstamp { |
| + | my $col = shift; |
| + | foreach my $line (@_) { |
| + | if(index($line, "$start ") == 0) { |
| + | my @cols = split(/\|/, $line); |
| + | return $cols[$col - 1]; |
| + | } |
| + | } |
| + | } |
| + | my @tagged = cmd('git', 'for-each-ref', '--format=%(refname:short) | %(taggerdate:unix) | %(creatordate)', 'refs/tags/*'); |
| + | my $tagged = tagstamp(2, @tagged); # Unix timestamp |
| + | my $taggednice = tagstamp(3, @tagged); # human readable time |
| chomp $taggednice; | |
| - | my $now=POSIX::strftime("%s", localtime()); |
| - | my $elapsed=$now - $tagged; # number of seconds since tag |
| - | my $total=$now - Time::Piece->strptime('19980320', '%Y%m%d')->epoch; |
| - | my $totalhttpget=$now - Time::Piece->strptime('19961111', '%Y%m%d')->epoch; |
| + | my $now = POSIX::strftime("%s", localtime()); |
| + | my $elapsed = $now - $tagged; # number of seconds since tag |
| + | my $total = $now - Time::Piece->strptime('19980320', '%Y%m%d')->epoch; |
| + | my $totalhttpget = $now - Time::Piece->strptime('19961111', '%Y%m%d')->epoch; |
| # Number of public functions in libcurl | |
| - | my $apublic=`git grep ^CURL_EXTERN -- include/curl | wc -l`; |
| - | my $bpublic=`git grep ^CURL_EXTERN $start -- include/curl | wc -l`; |
| + | my $apublic = cmd('git', 'grep', '^CURL_EXTERN', '--', 'include/curl'); |
| + | my $bpublic = cmd('git', 'grep', '^CURL_EXTERN', $start, '--', 'include/curl'); |
| my $public = $apublic - $bpublic; | |
| # diffstat | |
| my ($fileschanged, $insertions, $deletions); | |
| - | my $diffstat=`git diff --stat $start.. | tail -1`; |
| + | my @diffstat = cmd('2>', 'git', 'diff', '--stat', "$start.."); |
| + | my $diffstat = $diffstat[-1]; |
| if($diffstat =~ /^ *(\d+) files changed, (\d+) insertions\(\+\), (\d+)/) { | |
| - | ($fileschanged, $insertions, $deletions)=($1, $2, $3); |
| + | ($fileschanged, $insertions, $deletions) = ($1, $2, $3); |
| } | |
| # Changes/bug-fixes currently logged | |
| @@ -123,16 +151,16 @@ open(F, "<RELEASE-NOTES"); | |
| my $mode = 0; | |
| while(<F>) { | |
| if($_ =~ /following changes:/) { | |
| - | $mode=1; |
| + | $mode = 1; |
| } | |
| elsif($_ =~ /following bugfixes:/) { | |
| - | $mode=2; |
| + | $mode = 2; |
| } | |
| elsif($_ =~ /known bugs:/) { | |
| - | $mode=3; |
| + | $mode = 3; |
| } | |
| elsif($_ =~ /like these:/) { | |
| - | $mode=4; |
| + | $mode = 4; |
| } | |
| if($_ =~ /^ o /) { | |
| if($mode == 1) { | |