1 /* expand - convert tabs to spaces
2 Copyright (C) 1989-2024 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* By default, convert all tabs to spaces.
18 Preserves backspace characters in the output; they decrement the
19 column count for tab calculations.
20 The default action is equivalent to -8.
23 --tabs=tab1[,tab2[,...]]
25 -tab1[,tab2[,...]] If only one tab stop is given, set the tabs tab1
26 columns apart instead of the default 8. Otherwise,
27 set the tabs at columns tab1, tab2, etc. (numbered from
28 0); replace any tabs beyond the tab stops given with
31 -i Only convert initial tabs on each line to spaces.
33 David MacKenzie <djm@gnu.ai.mit.edu> */
40 #include <sys/types.h>
42 #include "expand-common.h"
44 /* The official name of this program (e.g., no 'g' prefix). */
45 #define PROGRAM_NAME "expand"
47 #define AUTHORS proper_name ("David MacKenzie")
49 static char const shortopts
[] = "it:0::1::2::3::4::5::6::7::8::9::";
51 static struct option
const longopts
[] =
53 {"tabs", required_argument
, nullptr, 't'},
54 {"initial", no_argument
, nullptr, 'i'},
55 {GETOPT_HELP_OPTION_DECL
},
56 {GETOPT_VERSION_OPTION_DECL
},
57 {nullptr, 0, nullptr, 0}
63 if (status
!= EXIT_SUCCESS
)
68 Usage: %s [OPTION]... [FILE]...\n\
72 Convert tabs in each FILE to spaces, writing to standard output.\n\
76 emit_mandatory_arg_note ();
79 -i, --initial do not convert tabs after non blanks\n\
80 -t, --tabs=N have tabs N characters apart, not 8\n\
82 emit_tab_list_info ();
83 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
84 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
85 emit_ancillary_info (PROGRAM_NAME
);
91 /* Change tabs to spaces, writing to stdout.
92 Read each file in 'file_list', in order. */
98 FILE *fp
= next_file (nullptr);
105 /* Input character, or EOF. */
108 /* If true, perform translations. */
112 /* The following variables have valid values only when CONVERT
115 /* Column of next input character. */
116 uintmax_t column
= 0;
118 /* Index in TAB_LIST of next tab stop to examine. */
119 size_t tab_index
= 0;
122 /* Convert a line of text. */
126 while ((c
= getc (fp
)) < 0 && (fp
= next_file (fp
)))
133 /* Column the next input tab stop is on. */
134 uintmax_t next_tab_column
;
137 next_tab_column
= get_next_tab_column (column
, &tab_index
,
141 next_tab_column
= column
+ 1;
143 if (next_tab_column
< column
)
144 error (EXIT_FAILURE
, 0, _("input line is too long"));
146 while (++column
< next_tab_column
)
147 if (putchar (' ') < 0)
154 /* Go back one column, and force recalculation of the
157 tab_index
-= !!tab_index
;
163 error (EXIT_FAILURE
, 0, _("input line is too long"));
166 convert
&= convert_entire_line
|| !! isblank (c
);
180 main (int argc
, char **argv
)
184 initialize_main (&argc
, &argv
);
185 set_program_name (argv
[0]);
186 setlocale (LC_ALL
, "");
187 bindtextdomain (PACKAGE
, LOCALEDIR
);
188 textdomain (PACKAGE
);
190 atexit (close_stdout
);
191 convert_entire_line
= true;
193 while ((c
= getopt_long (argc
, argv
, shortopts
, longopts
, nullptr)) != -1)
198 convert_entire_line
= false;
202 parse_tab_stops (optarg
);
205 case '0': case '1': case '2': case '3': case '4':
206 case '5': case '6': case '7': case '8': case '9':
208 parse_tab_stops (optarg
- 1);
214 parse_tab_stops (tab_stop
);
218 case_GETOPT_HELP_CHAR
;
220 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
223 usage (EXIT_FAILURE
);
227 finalize_tab_stops ();
229 set_file_list (optind
< argc
? &argv
[optind
] : nullptr);
233 cleanup_file_list_stdin ();