/* unexpand - convert blanks to tabs                                            This is the unexpand utility
   Copyright (C) 1989-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
                                                                                
/* By default, convert only maximal strings of initial blanks and tabs          
   into tabs.                                                                   
   Preserves backspace characters in the output; they decrement the             
   column count for tab calculations.                                           
   The default action is equivalent to -8.                                      
                                                                                
   Options:                                                                     
   --tabs=tab1[,tab2[,...]]                                                     
   -t tab1[,tab2[,...]]                                                         
   -tab1[,tab2[,...]] If only one tab stop is given, set the tabs tab1          
                        columns apart instead of the default 8.  Otherwise,     
                        set the tabs at columns tab1, tab2, etc. (numbered from 
                        0); preserve any blanks beyond the tab stops given.     
   --all                                                                        
   -a   Use tabs wherever they would replace 2 or more blanks,                  
                        not just at the beginnings of lines.                    
                                                                                
   David MacKenzie <djm@gnu.ai.mit.edu> */                                      
                                                                                
#include <config.h>                                                             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 "xstrndup.h"                                                           ...!includes auto-comment...
                                                                                
#include "expand-common.h"                                                      ...!includes auto-comment...
                                                                                
/* The official name of this program (e.g., no 'g' prefix).  */                 
#define PROGRAM_NAME "unexpand"                                                 Line 48
                                                                                
#define AUTHORS proper_name ("David MacKenzie")                                 Line 50
                                                                                
                                                                                
                                                                                
/* 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 56
{                                                                               
  CONVERT_FIRST_ONLY_OPTION = CHAR_MAX + 1                                      Line 58
};                                                                              Block 1
                                                                                
static struct option const longopts[] =                                         Line 61
{                                                                               
  {"tabs", required_argument, NULL, 't'},                                       Line 63
  {"all", no_argument, NULL, 'a'},                                              Line 64
  {"first-only", no_argument, NULL, CONVERT_FIRST_ONLY_OPTION},                 Line 65
  {GETOPT_HELP_OPTION_DECL},                                                    Line 66
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 67
  {NULL, 0, NULL, 0}                                                            Line 68
};                                                                              Block 2
                                                                                
void                                                                            Line 71
usage (int status)                                                              Line 72
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 74
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 76
    {                                                                           
      printf (_("\                                                              Line 78
Usage: %s [OPTION]... [FILE]...\n\                                              Line 79
"),                                                                             Line 80
              program_name);                                                    Line 81
      fputs (_("\                                                               Line 82
Convert blanks in each FILE to tabs, writing to standard output.\n\             Line 83
"), stdout);                                                                    Line 84
                                                                                
      emit_stdin_note ();                                                       ...!common auto-comment...
      emit_mandatory_arg_note ();                                               ...!common auto-comment...
                                                                                
      fputs (_("\                                                               Line 89
  -a, --all        convert all blanks, instead of just initial blanks\n\        Line 90
      --first-only  convert only leading sequences of blanks (overrides -a)\n\  Line 91
  -t, --tabs=N     have tabs N characters apart instead of 8 (enables -a)\n\    Line 92
"), stdout);                                                                    Line 93
      emit_tab_list_info ();                                                    Line 94
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 95
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 96
      emit_ancillary_info (PROGRAM_NAME);                                       Line 97
    }                                                                           
  exit (status);                                                                Line 99
}                                                                               Block 3
                                                                                
/* Change blanks to tabs, writing to stdout.                                    
   Read each file in 'file_list', in order.  */                                 
                                                                                
static void                                                                     Line 105
unexpand (void)                                                                 Line 106
{                                                                               
  /* Input stream.  */                                                          
  FILE *fp = next_file (NULL);                                                  Line 109
                                                                                
  /* The array of pending blanks.  In non-POSIX locales, blanks can             
     include characters other than spaces, so the blanks must be                
     stored, not merely counted.  */                                            
  char *pending_blank;                                                          Line 114
                                                                                
  if (!fp)                                                                      Line 116
    return;                                                                     Line 117
                                                                                
  /* The worst case is a non-blank character, then one blank, then a            
     tab stop, then MAX_COLUMN_WIDTH - 1 blanks, then a non-blank; so           
     allocate MAX_COLUMN_WIDTH bytes to store the blanks.  */                   
  pending_blank = xmalloc (max_column_width);                                   Line 122
                                                                                
  while (true)                                                                  Line 124
    {                                                                           
      /* Input character, or EOF.  */                                           
      int c;                                                                    Line 127
                                                                                
      /* If true, perform translations.  */                                     
      bool convert = true;                                                      Line 130
                                                                                
                                                                                
      /* The following variables have valid values only when CONVERT            
         is true:  */                                                           
                                                                                
      /* Column of next input character.  */                                    
      uintmax_t column = 0;                                                     Line 137
                                                                                
      /* Column the next input tab stop is on.  */                              
      uintmax_t next_tab_column = 0;                                            Line 140
                                                                                
      /* Index in TAB_LIST of next tab stop to examine.  */                     
      size_t tab_index = 0;                                                     Line 143
                                                                                
      /* If true, the first pending blank came just before a tab stop.  */      
      bool one_blank_before_tab_stop = false;                                   Line 146
                                                                                
      /* If true, the previous input character was a blank.  This is            
         initially true, since initial strings of blanks are treated            
         as if the line was preceded by a blank.  */                            
      bool prev_blank = true;                                                   Line 151
                                                                                
      /* Number of pending columns of blanks.  */                               
      size_t pending = 0;                                                       Line 154
                                                                                
                                                                                
      /* Convert a line of text.  */                                            
                                                                                
      do                                                                        
        {                                                                       
          while ((c = getc (fp)) < 0 && (fp = next_file (fp)))                  Line 161
            continue;                                                           Line 162
                                                                                
          if (convert)                                                          Line 164
            {                                                                   
              bool blank = !! isblank (c);                                      Line 166
                                                                                
              if (blank)                                                        Line 168
                {                                                               
                  bool last_tab IF_LINT (=0);                                   Line 170
                                                                                
                  next_tab_column = get_next_tab_column (column, &tab_index,    Line 172
                                                         &last_tab);            Line 173
                                                                                
                  if (last_tab)                                                 Line 175
                    convert = false;                                            Line 176
                                                                                
                  if (convert)                                                  Line 178
                    {                                                           
                      if (next_tab_column < column)                             Line 180
                        die (EXIT_FAILURE, 0, _("input line is too long"));     Line 181
                                                                                
                      if (c == '\t')                                            Line 183
                        {                                                       
                          column = next_tab_column;                             Line 185
                                                                                
                          if (pending)                                          Line 187
                            pending_blank[0] = '\t';                            Line 188
                        }                                                       
                      else                                                      Line 190
                        {                                                       
                          column++;                                             Line 192
                                                                                
                          if (! (prev_blank && column == next_tab_column))      Line 194
                            {                                                   
                              /* It is not yet known whether the pending blanks 
                                 will be replaced by tabs.  */                  
                              if (column == next_tab_column)                    Line 198
                                one_blank_before_tab_stop = true;               Line 199
                              pending_blank[pending++] = c;                     Line 200
                              prev_blank = true;                                Line 201
                              continue;                                         Line 202
                            }                                                   
                                                                                
                          /* Replace the pending blanks by a tab or two.  */    
                          pending_blank[0] = c = '\t';                          Line 206
                        }                                                       
                                                                                
                      /* Discard pending blanks, unless it was a single         
                         blank just before the previous tab stop.  */           
                      pending = one_blank_before_tab_stop;                      Line 211
                    }                                                           
                }                                                               
              else if (c == '\b')                                               Line 214
                {                                                               
                  /* Go back one column, and force recalculation of the         
                     next tab stop.  */                                         
                  column -= !!column;                                           Line 218
                  next_tab_column = column;                                     Line 219
                  tab_index -= !!tab_index;                                     Line 220
                }                                                               
              else                                                              Line 222
                {                                                               
                  column++;                                                     Line 224
                  if (!column)                                                  Line 225
                    die (EXIT_FAILURE, 0, _("input line is too long"));         Line 226
                }                                                               
                                                                                
              if (pending)                                                      Line 229
                {                                                               
                  if (pending > 1 && one_blank_before_tab_stop)                 Line 231
                    pending_blank[0] = '\t';                                    Line 232
                  if (fwrite (pending_blank, 1, pending, stdout) != pending)    Line 233...!syscalls auto-comment...
                    die (EXIT_FAILURE, errno, _("write error"));                Line 234
                  pending = 0;                                                  Line 235
                  one_blank_before_tab_stop = false;                            Line 236
                }                                                               
                                                                                
              prev_blank = blank;                                               Line 239
              convert &= convert_entire_line || blank;                          Line 240
            }                                                                   
                                                                                
          if (c < 0)                                                            Line 243
            {                                                                   
              free (pending_blank);                                             Line 245
              return;                                                           Line 246
            }                                                                   
                                                                                
          if (putchar (c) < 0)                                                  Line 249
            die (EXIT_FAILURE, errno, _("write error"));                        Line 250
        }                                                                       
      while (c != '\n');                                                        Line 252
    }                                                                           
}                                                                               
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 257
{                                                                               
  bool have_tabval = false;                                                     Line 259
  uintmax_t tabval IF_LINT ( = 0);                                              Line 260
  int c;                                                                        Line 261
                                                                                
  /* If true, cancel the effect of any -a (explicit or implicit in -t),         
     so that only leading blanks will be considered.  */                        
  bool convert_first_only = false;                                              Line 265
                                                                                
  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, ",0123456789at:", longopts, NULL))       Line 275
         != -1)                                                                 Line 276
    {                                                                           
      switch (c)                                                                Line 278
        {                                                                       
        case '?':                                                               Line 280
          usage (EXIT_FAILURE);                                                 Line 281
        case 'a':                                                               Line 282
          convert_entire_line = true;                                           Line 283
          break;                                                                Line 284
        case 't':                                                               Line 285
          convert_entire_line = true;                                           Line 286
          parse_tab_stops (optarg);                                             Line 287
          break;                                                                Line 288
        case CONVERT_FIRST_ONLY_OPTION:                                         Line 289
          convert_first_only = true;                                            Line 290
          break;                                                                Line 291
        case ',':                                                               Line 292
          if (have_tabval)                                                      Line 293
            add_tab_stop (tabval);                                              Line 294
          have_tabval = false;                                                  Line 295
          break;                                                                Line 296
        case_GETOPT_HELP_CHAR;                                                  Line 297
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                       Line 298
        default:                                                                Line 299
          if (!have_tabval)                                                     Line 300
            {                                                                   
              tabval = 0;                                                       Line 302
              have_tabval = true;                                               Line 303
            }                                                                   
          if (!DECIMAL_DIGIT_ACCUMULATE (tabval, c - '0', uintmax_t))           Line 305
            die (EXIT_FAILURE, 0, _("tab stop value is too large"));            Line 306
          break;                                                                Line 307
        }                                                                       
    }                                                                           
                                                                                
  if (convert_first_only)                                                       Line 311
    convert_entire_line = false;                                                Line 312
                                                                                
  if (have_tabval)                                                              Line 314
    add_tab_stop (tabval);                                                      Line 315
                                                                                
  finalize_tab_stops ();                                                        Line 317
                                                                                
  set_file_list ( (optind < argc) ? &argv[optind] : NULL);                      Line 319
                                                                                
  unexpand ();                                                                  Line 321
                                                                                
  cleanup_file_list_stdin ();                                                   Line 323
                                                                                
  return exit_status;                                                           Line 325
}                                                                               Block 5