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