/* truncate -- truncate or extend the length of files.                          This is the truncate utility
   Copyright (C) 2008-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 Pádraig Brady                                                     
                                                                                
   This is backwards compatible with the FreeBSD utility, but is more           
   flexible wrt the size specifications and the use of long options,            
   to better fit the "GNU" environment.  */                                     
                                                                                
#include <config.h>             /* sets _FILE_OFFSET_BITS=64 etc. */            Provides system specific information
#include <stdio.h>                                                              Provides standard I/O capability
#include <getopt.h>                                                             ...!includes auto-comment...
#include <sys/types.h>                                                          Provides system data types
                                                                                
#include "system.h"                                                             ...!includes auto-comment...
#include "die.h"                                                                ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "quote.h"                                                              ...!includes auto-comment...
#include "stat-size.h"                                                          ...!includes auto-comment...
#include "xdectoint.h"                                                          ...!includes auto-comment...
                                                                                
/* The official name of this program (e.g., no 'g' prefix).  */                 
#define PROGRAM_NAME "truncate"                                                 Line 36
                                                                                
#define AUTHORS proper_name ("Padraig Brady")                                   Line 38
                                                                                
/* (-c) If true, don't create if not already there */                           
static bool no_create;                                                          Line 41
                                                                                
/* (-o) If true, --size refers to blocks not bytes */                           
static bool block_mode;                                                         Line 44
                                                                                
/* (-r) Reference file to use size from */                                      
static char const *ref_file;                                                    Line 47
                                                                                
static struct option const longopts[] =                                         Line 49
{                                                                               
  {"no-create", no_argument, NULL, 'c'},                                        Line 51
  {"io-blocks", no_argument, NULL, 'o'},                                        Line 52
  {"reference", required_argument, NULL, 'r'},                                  Line 53
  {"size", required_argument, NULL, 's'},                                       Line 54
  {GETOPT_HELP_OPTION_DECL},                                                    Line 55
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 56
  {NULL, 0, NULL, 0}                                                            Line 57
};                                                                              Block 1
                                                                                
typedef enum                                                                    Line 60
{ rm_abs = 0, rm_rel, rm_min, rm_max, rm_rdn, rm_rup } rel_mode_t;              Line 61
                                                                                
void                                                                            Line 63
usage (int status)                                                              Line 64
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 66
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 68
    {                                                                           
      printf (_("Usage: %s OPTION... FILE...\n"), program_name);                Line 70
      fputs (_("\                                                               Line 71
Shrink or extend the size of each FILE to the specified size\n\                 Line 72
\n\                                                                             
A FILE argument that does not exist is created.\n\                              Line 74
\n\                                                                             
If a FILE is larger than the specified size, the extra data is lost.\n\         Line 76
If a FILE is shorter, it is extended and the extended part (hole)\n\            Line 77
reads as zero bytes.\n\                                                         Line 78
"), stdout);                                                                    Line 79
                                                                                
      emit_mandatory_arg_note ();                                               ...!common auto-comment...
                                                                                
      fputs (_("\                                                               Line 83
  -c, --no-create        do not create any files\n\                             Line 84
"), stdout);                                                                    Line 85
      fputs (_("\                                                               Line 86
  -o, --io-blocks        treat SIZE as number of IO blocks instead of bytes\n\  Line 87
"), stdout);                                                                    Line 88
      fputs (_("\                                                               Line 89
  -r, --reference=RFILE  base size on RFILE\n\                                  Line 90
  -s, --size=SIZE        set or adjust the file size by SIZE bytes\n"), stdout);Line 91
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 92
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 93
      emit_size_note ();                                                        Line 94
      fputs (_("\n\                                                             Line 95
SIZE may also be prefixed by one of the following modifying characters:\n\      Line 96
'+' extend by, '-' reduce by, '<' at most, '>' at least,\n\                     Line 97
'/' round down to multiple of, '%' round up to multiple of.\n"), stdout);       Line 98
      emit_ancillary_info (PROGRAM_NAME);                                       Line 99
    }                                                                           
  exit (status);                                                                Line 101
}                                                                               Block 3
                                                                                
/* return true on success, false on error.  */                                  
static bool                                                                     Line 105
do_ftruncate (int fd, char const *fname, off_t ssize, off_t rsize,              Line 106...!syscalls auto-comment...
              rel_mode_t rel_mode)                                              Line 107
{                                                                               
  struct stat sb;                                                               Line 109
  off_t nsize;                                                                  Line 110
                                                                                
  if ((block_mode || (rel_mode && rsize < 0)) && fstat (fd, &sb) != 0)          Line 112...!syscalls auto-comment......!syscalls auto-comment...
    {                                                                           
      error (0, errno, _("cannot fstat %s"), quoteaf (fname));                  Line 114
      return false;                                                             Line 115
    }                                                                           
  if (block_mode)                                                               Line 117
    {                                                                           
      off_t const blksize = ST_BLKSIZE (sb);                                    Line 119
      if (ssize < OFF_T_MIN / blksize || ssize > OFF_T_MAX / blksize)           Line 120
        {                                                                       
          error (0, 0,                                                          Line 122
                 _("overflow in %" PRIdMAX                                      Line 123
                   " * %" PRIdMAX " byte blocks for file %s"),                  Line 124
                 (intmax_t) ssize, (intmax_t) blksize,                          Line 125
                 quoteaf (fname));                                              Line 126
          return false;                                                         Line 127
        }                                                                       
      ssize *= blksize;                                                         Line 129
    }                                                                           
  if (rel_mode)                                                                 Line 131
    {                                                                           
      uintmax_t fsize;                                                          Line 133
                                                                                
      if (0 <= rsize)                                                           Line 135
        fsize = rsize;                                                          Line 136
      else                                                                      Line 137
        {                                                                       
          off_t file_size;                                                      Line 139
          if (usable_st_size (&sb))                                             Line 140
            {                                                                   
              file_size = sb.st_size;                                           Line 142
              if (file_size < 0)                                                Line 143
                {                                                               
                  /* Sanity check.  Overflow is the only reason I can think     
                     this would ever go negative. */                            
                  error (0, 0, _("%s has unusable, apparently negative size"),  Line 147
                         quoteaf (fname));                                      Line 148
                  return false;                                                 Line 149
                }                                                               
            }                                                                   
          else                                                                  Line 152
            {                                                                   
              file_size = lseek (fd, 0, SEEK_END);                              Line 154
              if (file_size < 0)                                                Line 155
                {                                                               
                  error (0, errno, _("cannot get the size of %s"),              Line 157
                         quoteaf (fname));                                      Line 158
                  return false;                                                 Line 159
                }                                                               
            }                                                                   
          fsize = file_size;                                                    Line 162
        }                                                                       
                                                                                
      if (rel_mode == rm_min)                                                   Line 165
        nsize = MAX (fsize, (uintmax_t) ssize);                                 Line 166
      else if (rel_mode == rm_max)                                              Line 167
        nsize = MIN (fsize, (uintmax_t) ssize);                                 Line 168
      else if (rel_mode == rm_rdn)                                              Line 169
        /* 0..ssize-1 -> 0 */                                                   
        nsize = (fsize / ssize) * ssize;                                        Line 171
      else if (rel_mode == rm_rup)                                              Line 172
        /* 1..ssize -> ssize */                                                 
        {                                                                       
          /* Here ssize>=1 && fsize>=0 */                                       
          uintmax_t const overflow = ((fsize + ssize - 1) / ssize) * ssize;     Line 176
          if (overflow > OFF_T_MAX)                                             Line 177
            {                                                                   
              error (0, 0, _("overflow rounding up size of file %s"),           Line 179
                     quoteaf (fname));                                          Line 180
              return false;                                                     Line 181
            }                                                                   
          nsize = overflow;                                                     Line 183
        }                                                                       
      else                                                                      Line 185
        {                                                                       
          if (ssize > OFF_T_MAX - (off_t)fsize)                                 Line 187
            {                                                                   
              error (0, 0, _("overflow extending size of file %s"),             Line 189
                     quoteaf (fname));                                          Line 190
              return false;                                                     Line 191
            }                                                                   
          nsize = fsize + ssize;                                                Line 193
        }                                                                       
    }                                                                           
  else                                                                          Line 196
    nsize = ssize;                                                              Line 197
  if (nsize < 0)                                                                Line 198
    nsize = 0;                                                                  Line 199
                                                                                
  if (ftruncate (fd, nsize) == -1)      /* note updates mtime & ctime */        Line 201...!syscalls auto-comment...
    {                                                                           
      error (0, errno,                                                          Line 203
             _("failed to truncate %s at %" PRIdMAX " bytes"), quoteaf (fname), Line 204
             (intmax_t) nsize);                                                 Line 205
      return false;                                                             Line 206
    }                                                                           
                                                                                
  return true;                                                                  Line 209
}                                                                               Block 4
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 213
{                                                                               
  bool got_size = false;                                                        Line 215
  bool errors = false;                                                          Line 216
  off_t size IF_LINT ( = 0);                                                    Line 217
  off_t rsize = -1;                                                             Line 218
  rel_mode_t rel_mode = rm_abs;                                                 Line 219
  int c, fd = -1, oflags;                                                       Line 220
  char const *fname;                                                            Line 221
                                                                                
  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 (close_stdout);                                                        Close stdout on exit (see gnulib)
                                                                                
  while ((c = getopt_long (argc, argv, "cor:s:", longopts, NULL)) != -1)        Line 231
    {                                                                           
      switch (c)                                                                Line 233
        {                                                                       
        case 'c':                                                               Line 235
          no_create = true;                                                     Line 236
          break;                                                                Line 237
                                                                                
        case 'o':                                                               Line 239
          block_mode = true;                                                    Line 240
          break;                                                                Line 241
                                                                                
        case 'r':                                                               Line 243
          ref_file = optarg;                                                    Line 244
          break;                                                                Line 245
                                                                                
        case 's':                                                               Line 247
          /* skip any whitespace */                                             
          while (isspace (to_uchar (*optarg)))                                  Line 249
            optarg++;                                                           Line 250
          switch (*optarg)                                                      Line 251
            {                                                                   
            case '<':                                                           Line 253
              rel_mode = rm_max;                                                Line 254
              optarg++;                                                         Line 255
              break;                                                            Line 256
            case '>':                                                           Line 257
              rel_mode = rm_min;                                                Line 258
              optarg++;                                                         Line 259
              break;                                                            Line 260
            case '/':                                                           Line 261
              rel_mode = rm_rdn;                                                Line 262
              optarg++;                                                         Line 263
              break;                                                            Line 264
            case '%':                                                           Line 265
              rel_mode = rm_rup;                                                Line 266
              optarg++;                                                         Line 267
              break;                                                            Line 268
            }                                                                   
          /* skip any whitespace */                                             
          while (isspace (to_uchar (*optarg)))                                  Line 271
            optarg++;                                                           Line 272
          if (*optarg == '+' || *optarg == '-')                                 Line 273
            {                                                                   
              if (rel_mode)                                                     Line 275
                {                                                               
                  error (0, 0, _("multiple relative modifiers specified"));     Line 277
                  /* Note other combinations are flagged as invalid numbers */  
                  usage (EXIT_FAILURE);                                         Line 279
                }                                                               
              rel_mode = rm_rel;                                                Line 281
            }                                                                   
          /* Support dd BLOCK size suffixes + lowercase g,t,m for bsd compat.   
             Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats. */
          size = xdectoimax (optarg, OFF_T_MIN, OFF_T_MAX, "EgGkKmMPtTYZ0",     Line 285
                             _("Invalid number"), 0);                           Line 286
          /* Rounding to multiple of 0 is nonsensical */                        
          if ((rel_mode == rm_rup || rel_mode == rm_rdn) && size == 0)          Line 288
            die (EXIT_FAILURE, 0, _("division by zero"));                       Line 289
          got_size = true;                                                      Line 290
          break;                                                                Line 291
                                                                                
        case_GETOPT_HELP_CHAR;                                                  Line 293
                                                                                
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                       Line 295
                                                                                
        default:                                                                Line 297
          usage (EXIT_FAILURE);                                                 Line 298
        }                                                                       
    }                                                                           
                                                                                
  argv += optind;                                                               Line 302
  argc -= optind;                                                               Line 303
                                                                                
  /* must specify either size or reference file */                              
  if (!ref_file && !got_size)                                                   Line 306
    {                                                                           
      error (0, 0, _("you must specify either %s or %s"),                       Line 308
             quote_n (0, "--size"), quote_n (1, "--reference"));                Line 309
      usage (EXIT_FAILURE);                                                     Line 310
    }                                                                           
  /* must specify a relative size with a reference file */                      
  if (ref_file && got_size && !rel_mode)                                        Line 313
    {                                                                           
      error (0, 0, _("you must specify a relative %s with %s"),                 Line 315
             quote_n (0, "--size"), quote_n (1, "--reference"));                Line 316
      usage (EXIT_FAILURE);                                                     Line 317
    }                                                                           
  /* block_mode without size is not valid */                                    
  if (block_mode && !got_size)                                                  Line 320
    {                                                                           
      error (0, 0, _("%s was specified but %s was not"),                        Line 322
             quote_n (0, "--io-blocks"), quote_n (1, "--size"));                Line 323
      usage (EXIT_FAILURE);                                                     Line 324
    }                                                                           
  /* must specify at least 1 file */                                            
  if (argc < 1)                                                                 Line 327
    {                                                                           
      error (0, 0, _("missing file operand"));                                  Line 329
      usage (EXIT_FAILURE);                                                     Line 330
    }                                                                           
                                                                                
  if (ref_file)                                                                 Line 333
    {                                                                           
      struct stat sb;                                                           Line 335
      off_t file_size = -1;                                                     Line 336
      if (stat (ref_file, &sb) != 0)                                            Line 337...!syscalls auto-comment...
        die (EXIT_FAILURE, errno, _("cannot stat %s"), quoteaf (ref_file));     Line 338
      if (usable_st_size (&sb))                                                 Line 339
        file_size = sb.st_size;                                                 Line 340
      else                                                                      Line 341
        {                                                                       
          int ref_fd = open (ref_file, O_RDONLY);                               Line 343...!syscalls auto-comment...
          if (0 <= ref_fd)                                                      Line 344
            {                                                                   
              off_t file_end = lseek (ref_fd, 0, SEEK_END);                     Line 346
              int saved_errno = errno;                                          Line 347
              close (ref_fd); /* ignore failure */                              Line 348...!syscalls auto-comment...
              if (0 <= file_end)                                                Line 349
                file_size = file_end;                                           Line 350
              else                                                              Line 351
                {                                                               
                  /* restore, in case close clobbered it. */                    
                  errno = saved_errno;                                          Line 354
                }                                                               
            }                                                                   
        }                                                                       
      if (file_size < 0)                                                        Line 358
        die (EXIT_FAILURE, errno, _("cannot get the size of %s"),               Line 359
             quoteaf (ref_file));                                               Line 360
      if (!got_size)                                                            Line 361
        size = file_size;                                                       Line 362
      else                                                                      Line 363
        rsize = file_size;                                                      Line 364
    }                                                                           
                                                                                
  oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK;                   Line 367
                                                                                
  while ((fname = *argv++) != NULL)                                             Line 369
    {                                                                           
      if ((fd = open (fname, oflags, MODE_RW_UGO)) == -1)                       Line 371...!syscalls auto-comment...
        {                                                                       
          /* 'truncate -s0 -c no-such-file'  shouldn't gen error                
             'truncate -s0 no-such-dir/file' should gen ENOENT error            
             'truncate -s0 no-such-dir/' should gen EISDIR error                
             'truncate -s0 .' should gen EISDIR error */                        
          if (!(no_create && errno == ENOENT))                                  Line 377
            {                                                                   
              error (0, errno, _("cannot open %s for writing"),                 Line 379
                     quoteaf (fname));                                          Line 380
              errors = true;                                                    Line 381
            }                                                                   
          continue;                                                             Line 383
        }                                                                       
                                                                                
                                                                                
      if (fd != -1)                                                             Line 387
        {                                                                       
          errors |= !do_ftruncate (fd, fname, size, rsize, rel_mode);           Line 389...!syscalls auto-comment...
          if (close (fd) != 0)                                                  Line 390...!syscalls auto-comment...
            {                                                                   
              error (0, errno, _("failed to close %s"), quoteaf (fname));       Line 392
              errors = true;                                                    Line 393
            }                                                                   
        }                                                                       
    }                                                                           
                                                                                
  return errors ? EXIT_FAILURE : EXIT_SUCCESS;                                  Line 398
}                                                                               Block 5