/* Create a temporary file or directory, safely.                                This is the mktemp utility
   Copyright (C) 2007-2018 Free Software Foundation, Inc.                       
                                                                                
   This program is free software: you can redistribute it and/or modify         
   it under the terms of the GNU General Public License as published by         
   the Free Software Foundation, either version 3 of the License, or            
   (at your option) any later version.                                          
                                                                                
   This program is distributed in the hope that it will be useful,              
   but WITHOUT ANY WARRANTY; without even the implied warranty of               
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                
   GNU General Public License for more details.                                 
                                                                                
   You should have received a copy of the GNU General Public License            
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */   The GNUv3 license
                                                                                
/* Written by Jim Meyering and Eric Blake.  */                                  
                                                                                
#include <config.h>                                                             Provides system specific information
#include <sys/types.h>                                                          Provides system data types
#include <getopt.h>                                                             ...!includes auto-comment...
                                                                                
#include "system.h"                                                             ...!includes auto-comment...
                                                                                
#include "close-stream.h"                                                       ...!includes auto-comment...
#include "die.h"                                                                ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "filenamecat.h"                                                        ...!includes auto-comment...
#include "quote.h"                                                              ...!includes auto-comment...
#include "tempname.h"                                                           ...!includes auto-comment...
                                                                                
/* The official name of this program (e.g., no 'g' prefix).  */                 
#define PROGRAM_NAME "mktemp"                                                   Line 33
                                                                                
#define AUTHORS \                                                               Line 35
  proper_name ("Jim Meyering"), \                                               Line 36
  proper_name ("Eric Blake")                                                    Line 37
                                                                                
static const char *default_template = "tmp.XXXXXXXXXX";                         Line 39
                                                                                
/* For long options that have no equivalent short option, use a                 
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */      
enum                                                                            Line 43
{                                                                               
  SUFFIX_OPTION = CHAR_MAX + 1,                                                 Line 45
};                                                                              Block 1
                                                                                
static struct option const longopts[] =                                         Line 48
{                                                                               
  {"directory", no_argument, NULL, 'd'},                                        Line 50
  {"quiet", no_argument, NULL, 'q'},                                            Line 51
  {"dry-run", no_argument, NULL, 'u'},                                          Line 52
  {"suffix", required_argument, NULL, SUFFIX_OPTION},                           Line 53
  {"tmpdir", optional_argument, NULL, 'p'},                                     Line 54
  {GETOPT_HELP_OPTION_DECL},                                                    Line 55
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 56
  {NULL, 0, NULL, 0}                                                            Line 57
};                                                                              Block 2
                                                                                
void                                                                            Line 60
usage (int status)                                                              Line 61
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 63
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 65
    {                                                                           
      printf (_("Usage: %s [OPTION]... [TEMPLATE]\n"), program_name);           Line 67
      fputs (_("\                                                               Line 68
Create a temporary file or directory, safely, and print its name.\n\            Line 69
TEMPLATE must contain at least 3 consecutive 'X's in last component.\n\         Line 70
If TEMPLATE is not specified, use tmp.XXXXXXXXXX, and --tmpdir is implied.\n\   Line 71
"), stdout);                                                                    Line 72
      fputs (_("\                                                               Line 73
Files are created u+rw, and directories u+rwx, minus umask restrictions.\n\     Line 74
"), stdout);                                                                    Line 75
      fputs ("\n", stdout);                                                     Line 76
      fputs (_("\                                                               Line 77
  -d, --directory     create a directory, not a file\n\                         Line 78
  -u, --dry-run       do not create anything; merely print a name (unsafe)\n\   Line 79
  -q, --quiet         suppress diagnostics about file/dir-creation failure\n\   Line 80
"), stdout);                                                                    Line 81
      fputs (_("\                                                               Line 82
      --suffix=SUFF   append SUFF to TEMPLATE; SUFF must not contain a slash.\n\Line 83
                        This option is implied if TEMPLATE does not end in X\n\ Line 84
"), stdout);                                                                    Line 85
      fputs (_("\                                                               Line 86
  -p DIR, --tmpdir[=DIR]  interpret TEMPLATE relative to DIR; if DIR is not\n\  Line 87
                        specified, use $TMPDIR if set, else /tmp.  With\n\      Line 88
                        this option, TEMPLATE must not be an absolute name;\n\  Line 89
                        unlike with -t, TEMPLATE may contain slashes, but\n\    Line 90
                        mktemp creates only the final component\n\              Line 91
"), stdout);                                                                    Line 92
      fputs (_("\                                                               Line 93
  -t                  interpret TEMPLATE as a single file name component,\n\    Line 94
                        relative to a directory: $TMPDIR, if set; else the\n\   Line 95
                        directory specified via -p; else /tmp [deprecated]\n\   Line 96
"), stdout);                                                                    Line 97
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 98
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 99
      emit_ancillary_info (PROGRAM_NAME);                                       Line 100
    }                                                                           
                                                                                
  exit (status);                                                                Line 103
}                                                                               Block 3
                                                                                
static size_t                                                                   Line 106
count_consecutive_X_s (const char *s, size_t len)                               Line 107
{                                                                               
  size_t n = 0;                                                                 Line 109
  for ( ; len && s[len-1] == 'X'; len--)                                        Line 110
    ++n;                                                                        Line 111
  return n;                                                                     Line 112
}                                                                               Block 4
                                                                                
static int                                                                      Line 115
mkstemp_len (char *tmpl, size_t suff_len, size_t x_len, bool dry_run)           Line 116
{                                                                               
  return gen_tempname_len (tmpl, suff_len, 0, dry_run ? GT_NOCREATE : GT_FILE,  Line 118
                           x_len);                                              Line 119
}                                                                               Block 5
                                                                                
static int                                                                      Line 122
mkdtemp_len (char *tmpl, size_t suff_len, size_t x_len, bool dry_run)           Line 123
{                                                                               
  return gen_tempname_len (tmpl, suff_len, 0, dry_run ? GT_NOCREATE : GT_DIR,   Line 125
                           x_len);                                              Line 126
}                                                                               Block 6
                                                                                
/* True if we have already closed standard output.  */                          
static bool stdout_closed;                                                      Line 130
                                                                                
/* Avoid closing stdout twice.  Since we conditionally call                     
   close_stream (stdout) in order to decide whether to clean up a               
   temporary file, the exit hook needs to know whether to do all of             
   close_stdout or just the stderr half.  */                                    
static void                                                                     Line 136
maybe_close_stdout (void)                                                       Line 137
{                                                                               
  if (!stdout_closed)                                                           Line 139
    close_stdout ();                                                            Line 140
  else if (close_stream (stderr) != 0)                                          Line 141
    _exit (EXIT_FAILURE);                                                       Line 142
}                                                                               Block 7
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 146
{                                                                               
  char const *dest_dir;                                                         Line 148
  char const *dest_dir_arg = NULL;                                              Line 149
  bool suppress_file_err = false;                                               Line 150
  int c;                                                                        Line 151
  unsigned int n_args;                                                          Line 152
  char *template;                                                               Line 153
  char *suffix = NULL;                                                          Line 154
  bool use_dest_dir = false;                                                    Line 155
  bool deprecated_t_option = false;                                             Line 156
  bool create_directory = false;                                                Line 157
  bool dry_run = false;                                                         Line 158
  int status = EXIT_SUCCESS;                                                    Line 159
  size_t x_count;                                                               Line 160
  size_t suffix_len;                                                            Line 161
  char *dest_name;                                                              Line 162
                                                                                
  initialize_main (&argc, &argv);                                               VMS-specific entry point handling wildcard expansion
  set_program_name (argv[0]);                                                   Retains program name and discards path
  setlocale (LC_ALL, "");                                                       Sets up internationalization (i18n)
  bindtextdomain (PACKAGE, LOCALEDIR);                                          Assigns i18n directorySets text domain for _() [gettext()] function
  textdomain (PACKAGE);                                                         Sets text domain for _() [gettext()] function
                                                                                
  atexit (maybe_close_stdout);                                                  Close stdout on exit (see gnulib)
                                                                                
  while ((c = getopt_long (argc, argv, "dp:qtuV", longopts, NULL)) != -1)       Line 172
    {                                                                           
      switch (c)                                                                Line 174
        {                                                                       
        case 'd':                                                               Line 176
          create_directory = true;                                              Line 177
          break;                                                                Line 178
        case 'p':                                                               Line 179
          dest_dir_arg = optarg;                                                Line 180
          use_dest_dir = true;                                                  Line 181
          break;                                                                Line 182
        case 'q':                                                               Line 183
          suppress_file_err = true;                                             Line 184
          break;                                                                Line 185
        case 't':                                                               Line 186
          use_dest_dir = true;                                                  Line 187
          deprecated_t_option = true;                                           Line 188
          break;                                                                Line 189
        case 'u':                                                               Line 190
          dry_run = true;                                                       Line 191
          break;                                                                Line 192
                                                                                
        case SUFFIX_OPTION:                                                     Line 194
          suffix = optarg;                                                      Line 195
          break;                                                                Line 196
                                                                                
        case_GETOPT_HELP_CHAR;                                                  Line 198
                                                                                
        case 'V': /* Undocumented alias, for compatibility with the original    Line 200
                     mktemp program.  */                                        
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                       Line 202
        default:                                                                Line 203
          usage (EXIT_FAILURE);                                                 Line 204
        }                                                                       
    }                                                                           
                                                                                
  n_args = argc - optind;                                                       Line 208
  if (2 <= n_args)                                                              Line 209
    {                                                                           
      error (0, 0, _("too many templates"));                                    Line 211
      usage (EXIT_FAILURE);                                                     Line 212
    }                                                                           
                                                                                
  if (n_args == 0)                                                              Line 215
    {                                                                           
      use_dest_dir = true;                                                      Line 217
      template = (char *) default_template;                                     Line 218
    }                                                                           
  else                                                                          Line 220
    {                                                                           
      template = argv[optind];                                                  Line 222
    }                                                                           
                                                                                
  if (suffix)                                                                   Line 225
    {                                                                           
      size_t len = strlen (template);                                           Line 227
      if (!len || template[len - 1] != 'X')                                     Line 228
        {                                                                       
          die (EXIT_FAILURE, 0,                                                 Line 230
               _("with --suffix, template %s must end in X"),                   Line 231
               quote (template));                                               Line 232
        }                                                                       
      suffix_len = strlen (suffix);                                             Line 234
      dest_name = xcharalloc (len + suffix_len + 1);                            Line 235
      memcpy (dest_name, template, len);                                        Line 236
      memcpy (dest_name + len, suffix, suffix_len + 1);                         Line 237
      template = dest_name;                                                     Line 238
      suffix = dest_name + len;                                                 Line 239
    }                                                                           
  else                                                                          Line 241
    {                                                                           
      template = xstrdup (template);                                            Line 243
      suffix = strrchr (template, 'X');                                         Line 244
      if (!suffix)                                                              Line 245
        suffix = strchr (template, '\0');                                       Line 246
      else                                                                      Line 247
        suffix++;                                                               Line 248
      suffix_len = strlen (suffix);                                             Line 249
    }                                                                           
                                                                                
  /* At this point, template is malloc'd, and suffix points into template.  */  
  if (suffix_len && last_component (suffix) != suffix)                          Line 253
    {                                                                           
      die (EXIT_FAILURE, 0,                                                     Line 255
           _("invalid suffix %s, contains directory separator"),                Line 256
           quote (suffix));                                                     Line 257
    }                                                                           
  x_count = count_consecutive_X_s (template, suffix - template);                Line 259
  if (x_count < 3)                                                              Line 260
    die (EXIT_FAILURE, 0, _("too few X's in template %s"), quote (template));   Line 261
                                                                                
  if (use_dest_dir)                                                             Line 263
    {                                                                           
      if (deprecated_t_option)                                                  Line 265
        {                                                                       
          char *env = getenv ("TMPDIR");                                        Line 267
          if (env && *env)                                                      Line 268
            dest_dir = env;                                                     Line 269
          else if (dest_dir_arg && *dest_dir_arg)                               Line 270
            dest_dir = dest_dir_arg;                                            Line 271
          else                                                                  Line 272
            dest_dir = "/tmp";                                                  Line 273
                                                                                
          if (last_component (template) != template)                            Line 275
            die (EXIT_FAILURE, 0,                                               Line 276
                 _("invalid template, %s, contains directory separator"),       Line 277
                 quote (template));                                             Line 278
        }                                                                       
      else                                                                      Line 280
        {                                                                       
          if (dest_dir_arg && *dest_dir_arg)                                    Line 282
            dest_dir = dest_dir_arg;                                            Line 283
          else                                                                  Line 284
            {                                                                   
              char *env = getenv ("TMPDIR");                                    Line 286
              dest_dir = (env && *env ? env : "/tmp");                          Line 287
            }                                                                   
          if (IS_ABSOLUTE_FILE_NAME (template))                                 Line 289
            die (EXIT_FAILURE, 0,                                               Line 290
                 _("invalid template, %s; with --tmpdir,"                       Line 291
                   " it may not be absolute"),                                  Line 292
                 quote (template));                                             Line 293
        }                                                                       
                                                                                
      dest_name = file_name_concat (dest_dir, template, NULL);                  Line 296
      free (template);                                                          Line 297
      template = dest_name;                                                     Line 298
      /* Note that suffix is now invalid.  */                                   
    }                                                                           
                                                                                
  /* Make a copy to be used in case of diagnostic, since failing                
     mkstemp may leave the buffer in an undefined state.  */                    
  dest_name = xstrdup (template);                                               Line 304
                                                                                
  if (create_directory)                                                         Line 306
    {                                                                           
      int err = mkdtemp_len (dest_name, suffix_len, x_count, dry_run);          Line 308
      if (err != 0)                                                             Line 309
        {                                                                       
          if (!suppress_file_err)                                               Line 311
            error (0, errno, _("failed to create directory via template %s"),   Line 312
                   quote (template));                                           Line 313
          status = EXIT_FAILURE;                                                Line 314
        }                                                                       
    }                                                                           
  else                                                                          Line 317
    {                                                                           
      int fd = mkstemp_len (dest_name, suffix_len, x_count, dry_run);           Line 319
      if (fd < 0 || (!dry_run && close (fd) != 0))                              Line 320...!syscalls auto-comment...
        {                                                                       
          if (!suppress_file_err)                                               Line 322
            error (0, errno, _("failed to create file via template %s"),        Line 323
                   quote (template));                                           Line 324
          status = EXIT_FAILURE;                                                Line 325
        }                                                                       
    }                                                                           
                                                                                
  if (status == EXIT_SUCCESS)                                                   Line 329
    {                                                                           
      puts (dest_name);                                                         Line 331
      /* If we created a file, but then failed to output the file               
         name, we should clean up the mess before failing.  */                  
      if (!dry_run && ((stdout_closed = true), close_stream (stdout) != 0))     Line 334
        {                                                                       
          int saved_errno = errno;                                              Line 336
          remove (dest_name);                                                   Line 337
          if (!suppress_file_err)                                               Line 338
            error (0, saved_errno, _("write error"));                           Line 339
          status = EXIT_FAILURE;                                                Line 340
        }                                                                       
    }                                                                           
                                                                                
#ifdef lint                                                                     Line 344
  free (dest_name);                                                             Line 345
  free (template);                                                              Line 346
#endif                                                                          Line 347
                                                                                
  return status;                                                                Line 349
}                                                                               Block 8