Commit a96cd528dc for openssl.org

commit a96cd528dce1cb0caa378568e09aea18eaff7786
Author: Shreenidhi Shedi <yesshedi@gmail.com>
Date:   Thu Jul 9 16:24:09 2026 +0530

    apps/tsget: minor cleanups

    Add a progress() helper to centralise verbose output. Move usage()
    to the top. Rename @old_argv to @saved_argv. Replace foreach with
    for+lexical variable. Inline $output_base. Switch exists to defined
    for option checks. Remove the main-program comment block.

    Assisted-by: Claude:claude-sonnet-4-6
    Signed-off-by: Shreenidhi Shedi <yesshedi@gmail.com>

    Reviewed-by: Andrew Dinh <andrewd@openssl.org>
    Reviewed-by: Matt Caswell <matt@openssl.foundation>
    MergeDate: Fri Aug  7 13:29:39 2026
    (Merged from https://github.com/openssl/openssl/pull/31445)

diff --git a/apps/tsget.in b/apps/tsget.in
index 027d5666e5..32667ed411 100644
--- a/apps/tsget.in
+++ b/apps/tsget.in
@@ -12,18 +12,30 @@ use warnings;
 use IO::Handle;
 use Getopt::Std;
 use File::Basename;
+use File::Temp qw(tempfile);
 use Net::Curl::Easy qw(:constants);

 use vars qw(%options);

+sub usage {
+    print STDERR "usage: $0 -h <server_url> [-e <extension>] [-o <output>] ";
+    print STDERR "[-v] [-d] [-k <private_key.pem>] [-p <key_password>] ";
+    print STDERR "[-c <client_cert.pem>] [-C <CA_certs.pem>] [-P <CA_path>] ";
+    print STDERR "[-r <file:file...>] [-g <EGD_socket>] [<request>]...\n";
+    exit 1;
+}
+
+sub progress {
+    return unless $options{v};
+    STDERR->printflush(@_);
+}
+
 # Initialise a new Curl object.
 sub create_curl {
-    my $url = shift;
+    my ($url) = @_;

-    # Create Curl object.
     my $curl = Net::Curl::Easy->new();

-    # Error-handling related options.
     $curl->setopt(CURLOPT_VERBOSE, 1) if $options{d};
     $curl->setopt(CURLOPT_FAILONERROR, 1);
     $curl->setopt(CURLOPT_USERAGENT,
@@ -43,10 +55,10 @@ sub create_curl {
     $curl->setopt(CURLOPT_CAINFO, $options{C}) if defined($options{C});
     $curl->setopt(CURLOPT_CAPATH, $options{P}) if defined($options{P});

-    # CURLOPT_RANDOM_FILE and CURLOPT_EGDSOCKET were removed from libcurl;
-    # wrap in eval so we fail gracefully on newer versions.
-    eval { $curl->setopt(CURLOPT_RANDOM_FILE, $options{r}) if defined($options{r}); };
-    eval { $curl->setopt(CURLOPT_EGDSOCKET, $options{g}) if defined($options{g}); };
+    # CURLOPT_RANDOM_FILE and CURLOPT_EGDSOCKET are deprecated no-ops in libcurl;
+    # they still exist as constants and return success, so no eval is needed.
+    $curl->setopt(CURLOPT_RANDOM_FILE, $options{r}) if defined($options{r});
+    $curl->setopt(CURLOPT_EGDSOCKET, $options{g}) if defined($options{g});

     # Setting destination.
     $curl->setopt(CURLOPT_URL, $url);
@@ -91,57 +103,38 @@ sub send_request {
     return undef;
 }

-# Print usage information and exists.
-sub usage {
-
-    print STDERR "usage: $0 -h <server_url> [-e <extension>] [-o <output>] ";
-    print STDERR "[-v] [-d] [-k <private_key.pem>] [-p <key_password>] ";
-    print STDERR "[-c <client_cert.pem>] [-C <CA_certs.pem>] [-P <CA_path>] ";
-    print STDERR "[-r <file:file...>] [-g <EGD_socket>] [<request>]...\n";
-    exit 1;
-}
-
-# ----------------------------------------------------------------------
-#   Main program
-# ----------------------------------------------------------------------
+my $getopt_arg = "h:e:o:vdk:p:c:C:P:r:g:";

 # Getting command-line options (default comes from TSGET environment variable).
-my $getopt_arg =  "h:e:o:vdk:p:c:C:P:r:g:";
 if (exists $ENV{TSGET}) {
-    my @old_argv = @ARGV;
+    my @saved_argv = @ARGV;
     @ARGV = split /\s+/, $ENV{TSGET};
     getopts($getopt_arg, \%options) or usage;
-    @ARGV = @old_argv;
+    @ARGV = @saved_argv;
 }
 getopts($getopt_arg, \%options) or usage;

-# Checking argument consistency.
-if (!exists($options{h}) || (@ARGV == 0 && !exists($options{o}))
-    || (@ARGV > 1 && exists($options{o}))) {
+if (!defined($options{h}) || (@ARGV == 0 && !defined($options{o}))
+    || (@ARGV > 1 && defined($options{o}))) {
     print STDERR "Inconsistent command line options.\n";
     usage;
 }
-# Setting defaults.
-@ARGV = ("-") unless @ARGV != 0;
+@ARGV = ("-") unless @ARGV;
 $options{e} = ".tsr" unless defined($options{e});

-# Processing requests.
-my $curl = create_curl $options{h};
-undef $/;   # For reading whole files.
-REQUEST: foreach (@ARGV) {
-    my $input = $_;
+my $curl = create_curl($options{h});
+undef $/;
+
+REQUEST: for my $input (@ARGV) {
     my ($base, $path) = fileparse($input, '\.[^.]*');
-    my $output_base = $base . $options{e};
-    my $output = defined($options{o}) ? $options{o} : $path . $output_base;
+    my $output = defined($options{o}) ? $options{o} : $path . $base . $options{e};

-    STDERR->printflush("$input: ") if $options{v};
-    # Read request.
+    progress("$input: ");
     my $body;
     if ($input eq "-") {
         binmode STDIN;
         $body = <STDIN>;
     } else {
-        # Read the request from file.
         open my $in, '<', $input
             or warn("$input: could not open input file: $!\n"), next REQUEST;
         binmode $in;
@@ -150,13 +143,44 @@ REQUEST: foreach (@ARGV) {
             or warn("$input: could not close input file: $!\n"), next REQUEST;
     }

-    # Send request.
-    STDERR->printflush("sending request") if $options{v};
+    progress("sending request");

-    my $error = send_request($curl, $body, $output);
-    if (defined($error)) {
-        die "$input: fatal error: $error\n";
+    my $error;
+    if ($output eq "-") {
+        # Write to STDOUT directly.
+        binmode STDOUT;
+        $error = send_request($curl, $body, \*STDOUT);
+        die "$input: fatal error: $error\n" if defined($error);
+    } else {
+        # Write to a temp file first; rename to $output only on success so
+        # that a pre-existing output file is not clobbered on failure.
+        # Preserve the existing file's permissions; fall back to umask defaults.
+        my @st = stat($output);
+        my $mode = @st ? ($st[2] & 07777) : (0666 & ~umask());
+        my ($tmp_fh, $tmp_path) = eval {
+            tempfile(DIR => dirname($output), UNLINK => 1);
+        };
+        if (!defined($tmp_fh)) {
+            warn("$output: could not create temp file: $@\n");
+            next REQUEST;
+        }
+        chmod($mode, $tmp_path);
+        binmode $tmp_fh;
+
+        $error = send_request($curl, $body, $tmp_fh);
+        close $tmp_fh;
+
+        if (defined($error)) {
+            unlink $tmp_path;
+            die "$input: fatal error: $error\n";
+        }
+
+        rename($tmp_path, $output)
+            or do { unlink $tmp_path;
+                    warn("$output: could not rename temp file: $!\n");
+                    next REQUEST; };
     }
-    STDERR->printflush(", reply received") if $options{v};
-    STDERR->printflush(", $output written.\n") if $options{v};
+
+    progress(", reply received");
+    progress(", $output written.\n");
 }