1 /* expand - convert tabs to spaces
2 Copyright (C) 1989, 1991, 1995-2006, 2008-2010 Free Software Foundation,
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 /* By default, convert all tabs to spaces.
19 Preserves backspace characters in the output; they decrement the
20 column count for tab calculations.
21 The default action is equivalent to -8.
24 --tabs=tab1[,tab2[,...]]
26 -tab1[,tab2[,...]] If only one tab stop is given, set the tabs tab1
27 columns apart instead of the default 8. Otherwise,
28 set the tabs at columns tab1, tab2, etc. (numbered from
29 0); replace any tabs beyond the tab stops given with
32 -i Only convert initial tabs on each line to spaces.
34 David MacKenzie <djm@gnu.ai.mit.edu> */
40 #include <sys/types.h>
46 /* The official name of this program (e.g., no `g' prefix). */
47 #define PROGRAM_NAME "expand"
49 #define AUTHORS proper_name ("David MacKenzie")
51 /* If true, convert blanks even after nonblank characters have been
53 static bool convert_entire_line
;
55 /* If nonzero, the size of all tab stops. If zero, use `tab_list' instead. */
56 static uintmax_t tab_size
;
58 /* Array of the explicit column numbers of the tab stops;
59 after `tab_list' is exhausted, each additional tab is replaced
60 by a space. The first column is column 0. */
61 static uintmax_t *tab_list
;
63 /* The number of allocated entries in `tab_list'. */
64 static size_t n_tabs_allocated
;
66 /* The index of the first invalid element of `tab_list',
67 where the next element can be added. */
68 static size_t first_free_tab
;
70 /* Null-terminated array of input filenames. */
71 static char **file_list
;
73 /* Default for `file_list' if no files are given on the command line. */
74 static char *stdin_argv
[] =
79 /* True if we have ever read standard input. */
80 static bool have_read_stdin
;
82 /* The desired exit status. */
83 static int exit_status
;
85 static char const shortopts
[] = "it:0::1::2::3::4::5::6::7::8::9::";
87 static struct option
const longopts
[] =
89 {"tabs", required_argument
, NULL
, 't'},
90 {"initial", no_argument
, NULL
, 'i'},
91 {GETOPT_HELP_OPTION_DECL
},
92 {GETOPT_VERSION_OPTION_DECL
},
99 if (status
!= EXIT_SUCCESS
)
100 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
105 Usage: %s [OPTION]... [FILE]...\n\
109 Convert tabs in each FILE to spaces, writing to standard output.\n\
110 With no FILE, or when FILE is -, read standard input.\n\
114 Mandatory arguments to long options are mandatory for short options too.\n\
117 -i, --initial do not convert tabs after non blanks\n\
118 -t, --tabs=NUMBER have tabs NUMBER characters apart, not 8\n\
121 -t, --tabs=LIST use comma separated list of explicit tab positions\n\
123 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
124 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
125 emit_ancillary_info ();
130 /* Add tab stop TABVAL to the end of `tab_list'. */
133 add_tab_stop (uintmax_t tabval
)
135 if (first_free_tab
== n_tabs_allocated
)
136 tab_list
= X2NREALLOC (tab_list
, &n_tabs_allocated
);
137 tab_list
[first_free_tab
++] = tabval
;
140 /* Add the comma or blank separated list of tab stops STOPS
141 to the list of tab stops. */
144 parse_tab_stops (char const *stops
)
146 bool have_tabval
= false;
147 uintmax_t tabval
IF_LINT (= 0);
148 char const *num_start
IF_LINT (= NULL
);
151 for (; *stops
; stops
++)
153 if (*stops
== ',' || isblank (to_uchar (*stops
)))
156 add_tab_stop (tabval
);
159 else if (ISDIGIT (*stops
))
168 /* Detect overflow. */
169 if (!DECIMAL_DIGIT_ACCUMULATE (tabval
, *stops
- '0', uintmax_t))
171 size_t len
= strspn (num_start
, "0123456789");
172 char *bad_num
= xstrndup (num_start
, len
);
173 error (0, 0, _("tab stop is too large %s"), quote (bad_num
));
176 stops
= num_start
+ len
- 1;
181 error (0, 0, _("tab size contains invalid character(s): %s"),
192 add_tab_stop (tabval
);
195 /* Check that the list of tab stops TABS, with ENTRIES entries,
196 contains only nonzero, ascending values. */
199 validate_tab_stops (uintmax_t const *tabs
, size_t entries
)
201 uintmax_t prev_tab
= 0;
204 for (i
= 0; i
< entries
; i
++)
207 error (EXIT_FAILURE
, 0, _("tab size cannot be 0"));
208 if (tabs
[i
] <= prev_tab
)
209 error (EXIT_FAILURE
, 0, _("tab sizes must be ascending"));
214 /* Close the old stream pointer FP if it is non-NULL,
215 and return a new one opened to read the next input file.
216 Open a filename of `-' as the standard input.
217 Return NULL if there are no more input files. */
222 static char *prev_file
;
229 error (0, errno
, "%s", prev_file
);
230 exit_status
= EXIT_FAILURE
;
232 if (STREQ (prev_file
, "-"))
233 clearerr (fp
); /* Also clear EOF. */
234 else if (fclose (fp
) != 0)
236 error (0, errno
, "%s", prev_file
);
237 exit_status
= EXIT_FAILURE
;
241 while ((file
= *file_list
++) != NULL
)
243 if (STREQ (file
, "-"))
245 have_read_stdin
= true;
249 fp
= fopen (file
, "r");
255 error (0, errno
, "%s", file
);
256 exit_status
= EXIT_FAILURE
;
261 /* Change tabs to spaces, writing to stdout.
262 Read each file in `file_list', in order. */
268 FILE *fp
= next_file (NULL
);
275 /* Input character, or EOF. */
278 /* If true, perform translations. */
282 /* The following variables have valid values only when CONVERT
285 /* Column of next input character. */
286 uintmax_t column
= 0;
288 /* Index in TAB_LIST of next tab stop to examine. */
289 size_t tab_index
= 0;
292 /* Convert a line of text. */
296 while ((c
= getc (fp
)) < 0 && (fp
= next_file (fp
)))
303 /* Column the next input tab stop is on. */
304 uintmax_t next_tab_column
;
307 next_tab_column
= column
+ (tab_size
- column
% tab_size
);
310 if (tab_index
== first_free_tab
)
312 next_tab_column
= column
+ 1;
317 uintmax_t tab
= tab_list
[tab_index
++];
320 next_tab_column
= tab
;
325 if (next_tab_column
< column
)
326 error (EXIT_FAILURE
, 0, _("input line is too long"));
328 while (++column
< next_tab_column
)
329 if (putchar (' ') < 0)
330 error (EXIT_FAILURE
, errno
, _("write error"));
336 /* Go back one column, and force recalculation of the
339 tab_index
-= !!tab_index
;
345 error (EXIT_FAILURE
, 0, _("input line is too long"));
348 convert
&= convert_entire_line
|| !! isblank (c
);
355 error (EXIT_FAILURE
, errno
, _("write error"));
362 main (int argc
, char **argv
)
366 initialize_main (&argc
, &argv
);
367 set_program_name (argv
[0]);
368 setlocale (LC_ALL
, "");
369 bindtextdomain (PACKAGE
, LOCALEDIR
);
370 textdomain (PACKAGE
);
372 atexit (close_stdout
);
374 have_read_stdin
= false;
375 exit_status
= EXIT_SUCCESS
;
376 convert_entire_line
= true;
380 while ((c
= getopt_long (argc
, argv
, shortopts
, longopts
, NULL
)) != -1)
385 convert_entire_line
= false;
389 parse_tab_stops (optarg
);
392 case '0': case '1': case '2': case '3': case '4':
393 case '5': case '6': case '7': case '8': case '9':
395 parse_tab_stops (optarg
- 1);
401 parse_tab_stops (tab_stop
);
405 case_GETOPT_HELP_CHAR
;
407 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
410 usage (EXIT_FAILURE
);
414 validate_tab_stops (tab_list
, first_free_tab
);
416 if (first_free_tab
== 0)
418 else if (first_free_tab
== 1)
419 tab_size
= tab_list
[0];
423 file_list
= (optind
< argc
? &argv
[optind
] : stdin_argv
);
427 if (have_read_stdin
&& fclose (stdin
) != 0)
428 error (EXIT_FAILURE
, errno
, "-");