1 /* expand - convert tabs to spaces
2 Copyright (C) 1989, 1991 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 2, or (at your option)
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, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
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 spaces 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 tabstops given with
32 -i Only convert initial tabs on each line to spaces.
34 David MacKenzie <djm@gnu.ai.mit.edu> */
36 /* Get isblank from GNU libc. */
40 #define isblank(c) ((c) == ' ' || (c) == '\t')
44 #include <sys/types.h>
48 #define ISDIGIT(c) (isascii ((c)) && isdigit ((c)))
50 #define ISDIGIT(c) (isdigit ((c)))
53 /* The number of bytes added at a time to the amount of memory
54 allocated for the output line. */
55 #define OUTPUT_BLOCK 256
57 /* The number of bytes added at a time to the amount of memory
58 allocated for the list of tabstops. */
59 #define TABLIST_BLOCK 256
65 static FILE *next_file ();
66 static void add_tabstop ();
67 static void expand ();
68 static void parse_tabstops ();
70 static void validate_tabstops ();
72 /* If nonzero, convert blanks even after nonblank characters have been
74 static int convert_entire_line;
76 /* If nonzero, the size of all tab stops. If zero, use `tab_list' instead. */
79 /* Array of the explicit column numbers of the tab stops;
80 after `tab_list' is exhausted, each additional tab is replaced
81 by a space. The first column is column 0. */
84 /* The index of the first invalid element of `tab_list',
85 where the next element can be added. */
86 static int first_free_tab;
88 /* Null-terminated array of input filenames. */
89 static char **file_list;
91 /* Default for `file_list' if no files are given on the command line. */
92 static char *stdin_argv[] =
97 /* Nonzero if we have ever read standard input. */
98 static int have_read_stdin;
100 /* Status to return to the system. */
101 static int exit_status;
103 /* The name this program was run with. */
106 static struct option const longopts[] =
108 {"tabs", required_argument, NULL, 't'},
109 {"initial", no_argument, NULL, 'i'},
118 int tabval = -1; /* Value of tabstop being read, or -1. */
119 int c; /* Option character. */
123 convert_entire_line = 1;
126 program_name = argv[0];
128 while ((c = getopt_long (argc, argv, "it:,0123456789", longopts, (int *) 0))
136 convert_entire_line = 0;
139 parse_tabstops (optarg);
142 add_tabstop (tabval);
148 tabval = tabval * 10 + c - '0';
153 add_tabstop (tabval);
155 validate_tabstops (tab_list, first_free_tab);
157 if (first_free_tab == 0)
159 else if (first_free_tab == 1)
160 tab_size = tab_list[0];
165 file_list = stdin_argv;
167 file_list = &argv[optind];
171 if (have_read_stdin && fclose (stdin) == EOF)
172 error (1, errno, "-");
173 if (ferror (stdout) || fclose (stdout) == EOF)
174 error (1, 0, "write error");
179 /* Add the comma or blank separated list of tabstops STOPS
180 to the list of tabstops. */
183 parse_tabstops (stops)
188 for (; *stops; stops++)
190 if (*stops == ',' || isblank (*stops))
192 add_tabstop (tabval);
195 else if (ISDIGIT (*stops))
199 tabval = tabval * 10 + *stops - '0';
202 error (1, 0, "tab size contains an invalid character");
205 add_tabstop (tabval);
208 /* Add tab stop TABVAL to the end of `tab_list', except
209 if TABVAL is -1, do nothing. */
217 if (first_free_tab % TABLIST_BLOCK == 0)
218 tab_list = (int *) xrealloc (tab_list, first_free_tab + TABLIST_BLOCK);
219 tab_list[first_free_tab++] = tabval;
222 /* Check that the list of tabstops TABS, with ENTRIES entries,
223 contains only nonzero, ascending values. */
226 validate_tabstops (tabs, entries)
233 for (i = 0; i < entries; i++)
236 error (1, 0, "tab size cannot be 0");
237 if (tabs[i] <= prev_tab)
238 error (1, 0, "tab sizes must be ascending");
243 /* Change tabs to spaces, writing to stdout.
244 Read each file in `file_list', in order. */
249 FILE *fp; /* Input stream. */
250 int c; /* Each input character. */
251 int tab_index = 0; /* Index in `tab_list' of next tabstop. */
252 int column = 0; /* Column on screen of the next char. */
253 int next_tab_column; /* Column the next tab stop is on. */
254 int convert = 1; /* If nonzero, perform translations. */
256 fp = next_file ((FILE *) NULL);
264 break; /* No more files. */
276 else if (c == '\t' && convert)
280 /* Do not let tab_index == first_free_tab;
281 stop when it is 1 less. */
282 while (tab_index < first_free_tab - 1
283 && column >= tab_list[tab_index])
285 next_tab_column = tab_list[tab_index];
286 if (tab_index < first_free_tab - 1)
288 if (column >= next_tab_column)
289 next_tab_column = column + 1; /* Ran out of tab stops. */
293 next_tab_column = column + tab_size - column % tab_size;
295 while (column < next_tab_column)
313 if (convert_entire_line == 0)
322 /* Close the old stream pointer FP if it is non-NULL,
323 and return a new one opened to read the next input file.
324 Open a filename of `-' as the standard input.
325 Return NULL if there are no more input files. */
331 static char *prev_file;
338 error (0, errno, "%s", prev_file);
342 clearerr (fp); /* Also clear EOF. */
343 else if (fclose (fp) == EOF)
345 error (0, errno, "%s", prev_file);
350 while ((file = *file_list++) != NULL)
352 if (file[0] == '-' && file[1] == '\0')
358 fp = fopen (file, "r");
364 error (0, errno, "%s", file);
374 Usage: %s [-tab1[,tab2[,...]]] [-t tab1[,tab2[,...]]] [-i]\n\
375 [--tabs=tab1[,tab2[,...]]] [--initial] [file...]\n",