1992-11-08 02:50:43 +00:00
|
|
|
/* expand - convert tabs to spaces
|
2009-03-06 10:49:18 +01:00
|
|
|
Copyright (C) 89, 91, 1995-2006, 2008-2009 Free Software Foundation, Inc.
|
1992-11-08 02:50:43 +00:00
|
|
|
|
2007-07-23 14:35:58 +02:00
|
|
|
This program is free software: you can redistribute it and/or modify
|
1992-11-08 02:50:43 +00:00
|
|
|
it under the terms of the GNU General Public License as published by
|
2007-07-23 14:35:58 +02:00
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
(at your option) any later version.
|
1992-11-08 02:50:43 +00:00
|
|
|
|
|
|
|
|
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
|
2007-07-23 14:35:58 +02:00
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
1992-11-08 02:50:43 +00:00
|
|
|
|
|
|
|
|
/* By default, convert all tabs to spaces.
|
|
|
|
|
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
|
2009-08-22 18:56:06 +02:00
|
|
|
columns apart instead of the default 8. Otherwise,
|
|
|
|
|
set the tabs at columns tab1, tab2, etc. (numbered from
|
|
|
|
|
0); replace any tabs beyond the tab stops given with
|
|
|
|
|
single spaces.
|
1992-11-08 02:50:43 +00:00
|
|
|
--initial
|
|
|
|
|
-i Only convert initial tabs on each line to spaces.
|
|
|
|
|
|
1992-11-19 21:03:49 +00:00
|
|
|
David MacKenzie <djm@gnu.ai.mit.edu> */
|
1992-11-08 02:50:43 +00:00
|
|
|
|
1993-10-21 22:08:53 +00:00
|
|
|
#include <config.h>
|
|
|
|
|
|
1992-11-08 02:50:43 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include "system.h"
|
1994-12-16 05:41:05 +00:00
|
|
|
#include "error.h"
|
2003-07-23 06:57:57 +00:00
|
|
|
#include "quote.h"
|
|
|
|
|
#include "xstrndup.h"
|
1992-11-08 02:50:43 +00:00
|
|
|
|
1999-04-03 05:01:48 +00:00
|
|
|
/* The official name of this program (e.g., no `g' prefix). */
|
|
|
|
|
#define PROGRAM_NAME "expand"
|
|
|
|
|
|
2008-05-19 16:24:27 +02:00
|
|
|
#define AUTHORS proper_name ("David MacKenzie")
|
1999-04-03 05:22:05 +00:00
|
|
|
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
/* If true, convert blanks even after nonblank characters have been
|
|
|
|
|
read on the line. */
|
|
|
|
|
static bool convert_entire_line;
|
1992-11-08 02:50:43 +00:00
|
|
|
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
/* If nonzero, the size of all tab stops. If zero, use `tab_list' instead. */
|
|
|
|
|
static uintmax_t tab_size;
|
1992-11-08 02:50:43 +00:00
|
|
|
|
|
|
|
|
/* Array of the explicit column numbers of the tab stops;
|
|
|
|
|
after `tab_list' is exhausted, each additional tab is replaced
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
by a space. The first column is column 0. */
|
|
|
|
|
static uintmax_t *tab_list;
|
|
|
|
|
|
|
|
|
|
/* The number of allocated entries in `tab_list'. */
|
|
|
|
|
static size_t n_tabs_allocated;
|
1992-11-08 02:50:43 +00:00
|
|
|
|
|
|
|
|
/* The index of the first invalid element of `tab_list',
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
where the next element can be added. */
|
2004-01-04 21:10:11 +00:00
|
|
|
static size_t first_free_tab;
|
1992-11-08 02:50:43 +00:00
|
|
|
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
/* Null-terminated array of input filenames. */
|
1992-11-08 20:19:58 +00:00
|
|
|
static char **file_list;
|
1992-11-08 02:50:43 +00:00
|
|
|
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
/* Default for `file_list' if no files are given on the command line. */
|
1992-11-08 20:19:58 +00:00
|
|
|
static char *stdin_argv[] =
|
1992-11-08 02:50:43 +00:00
|
|
|
{
|
2008-10-28 22:06:44 +01:00
|
|
|
(char *) "-", NULL
|
1992-11-08 02:50:43 +00:00
|
|
|
};
|
|
|
|
|
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
/* True if we have ever read standard input. */
|
|
|
|
|
static bool have_read_stdin;
|
1992-11-08 02:50:43 +00:00
|
|
|
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
/* The desired exit status. */
|
1992-11-08 20:19:58 +00:00
|
|
|
static int exit_status;
|
1992-11-08 02:50:43 +00:00
|
|
|
|
2005-04-26 16:41:03 +00:00
|
|
|
static char const shortopts[] = "it:0::1::2::3::4::5::6::7::8::9::";
|
|
|
|
|
|
remove redundant const directives
In 1463824d8e7f72c31f1d803d7cfe2b608ccafc5c, I added some
missing "const" directives, as well as some new, redundant ones.
This removes the redundant ones. Pointed out by Eric Blake.
* base64.c, cat.c, chcon.c, chgrp.c, chmod.c, chown.c, comm.c:
* cp.c, csplit.c, cut.c, date.c, dd.c, df.c, dircolors.c, du.c:
* env.c, expand.c, fmt.c, fold.c, groups.c, head.c, id.c:
* install.c, join.c, kill.c, ln.c, ls.c, md5sum.c, mkdir.c:
* mkfifo.c, mknod.c, mktemp.c, mv.c, nice.c, nl.c, od.c:
* paste.c, pathchk.c, pinky.c, pr.c, ptx.c, readlink.c, rm.c:
* rmdir.c, runcon.c, seq.c, shred.c, shuf.c, sort.c, split.c:
* stat.c, stty.c, su.c, sum.c, tac.c, tail.c, tee.c, timeout.c:
* touch.c, tr.c, truncate.c, tty.c, uname.c, unexpand.c, uniq.c:
* wc.c, who.c: Remove redundant const directives.
* maint.mk (sc_const_long_option): Don't require redundant "const".
2008-06-16 14:55:06 +02:00
|
|
|
static struct option const longopts[] =
|
1992-11-08 02:50:43 +00:00
|
|
|
{
|
1992-12-02 18:31:56 +00:00
|
|
|
{"tabs", required_argument, NULL, 't'},
|
|
|
|
|
{"initial", no_argument, NULL, 'i'},
|
1999-04-04 15:44:26 +00:00
|
|
|
{GETOPT_HELP_OPTION_DECL},
|
|
|
|
|
{GETOPT_VERSION_OPTION_DECL},
|
1992-11-08 02:50:43 +00:00
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
|
};
|
|
|
|
|
|
1999-01-14 18:25:16 +00:00
|
|
|
void
|
1995-10-17 13:47:48 +00:00
|
|
|
usage (int status)
|
1992-11-08 02:50:43 +00:00
|
|
|
{
|
2004-01-21 23:00:48 +00:00
|
|
|
if (status != EXIT_SUCCESS)
|
1995-10-17 13:43:00 +00:00
|
|
|
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
2009-08-22 18:56:06 +02:00
|
|
|
program_name);
|
1995-10-17 13:43:00 +00:00
|
|
|
else
|
1993-05-22 05:06:39 +00:00
|
|
|
{
|
1995-10-17 13:43:00 +00:00
|
|
|
printf (_("\
|
|
|
|
|
Usage: %s [OPTION]... [FILE]...\n\
|
|
|
|
|
"),
|
2009-08-22 18:56:06 +02:00
|
|
|
program_name);
|
2001-11-11 13:52:50 +00:00
|
|
|
fputs (_("\
|
1995-10-17 13:43:00 +00:00
|
|
|
Convert tabs in each FILE to spaces, writing to standard output.\n\
|
|
|
|
|
With no FILE, or when FILE is -, read standard input.\n\
|
|
|
|
|
\n\
|
2001-11-23 19:58:23 +00:00
|
|
|
"), stdout);
|
|
|
|
|
fputs (_("\
|
2001-11-04 09:43:16 +00:00
|
|
|
Mandatory arguments to long options are mandatory for short options too.\n\
|
2001-11-23 19:58:23 +00:00
|
|
|
"), stdout);
|
|
|
|
|
fputs (_("\
|
2004-08-24 07:38:04 +00:00
|
|
|
-i, --initial do not convert tabs after non blanks\n\
|
1995-10-17 13:43:00 +00:00
|
|
|
-t, --tabs=NUMBER have tabs NUMBER characters apart, not 8\n\
|
2001-11-11 13:52:50 +00:00
|
|
|
"), stdout);
|
|
|
|
|
fputs (_("\
|
1995-10-17 13:43:00 +00:00
|
|
|
-t, --tabs=LIST use comma separated list of explicit tab positions\n\
|
2001-11-23 19:58:23 +00:00
|
|
|
"), stdout);
|
2001-12-01 17:41:25 +00:00
|
|
|
fputs (HELP_OPTION_DESCRIPTION, stdout);
|
|
|
|
|
fputs (VERSION_OPTION_DESCRIPTION, stdout);
|
2009-09-18 23:06:21 +01:00
|
|
|
emit_ancillary_info ();
|
1993-05-22 05:06:39 +00:00
|
|
|
}
|
2004-01-21 23:00:48 +00:00
|
|
|
exit (status);
|
1995-10-17 13:43:00 +00:00
|
|
|
}
|
1993-04-29 05:26:22 +00:00
|
|
|
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
/* Add tab stop TABVAL to the end of `tab_list'. */
|
1992-11-08 02:50:43 +00:00
|
|
|
|
1995-10-17 13:43:00 +00:00
|
|
|
static void
|
2004-08-24 07:38:04 +00:00
|
|
|
add_tab_stop (uintmax_t tabval)
|
1995-10-17 13:43:00 +00:00
|
|
|
{
|
2004-01-04 21:10:11 +00:00
|
|
|
if (first_free_tab == n_tabs_allocated)
|
2005-08-12 07:16:25 +00:00
|
|
|
tab_list = X2NREALLOC (tab_list, &n_tabs_allocated);
|
1995-10-17 13:43:00 +00:00
|
|
|
tab_list[first_free_tab++] = tabval;
|
1992-11-08 02:50:43 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-24 07:38:04 +00:00
|
|
|
/* Add the comma or blank separated list of tab stops STOPS
|
|
|
|
|
to the list of tab stops. */
|
1992-11-08 02:50:43 +00:00
|
|
|
|
1992-11-08 20:19:58 +00:00
|
|
|
static void
|
2004-08-24 07:38:04 +00:00
|
|
|
parse_tab_stops (char const *stops)
|
1992-11-08 02:50:43 +00:00
|
|
|
{
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
bool have_tabval = false;
|
|
|
|
|
uintmax_t tabval IF_LINT (= 0);
|
2003-07-23 06:57:57 +00:00
|
|
|
char const *num_start IF_LINT (= NULL);
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
bool ok = true;
|
1992-11-08 02:50:43 +00:00
|
|
|
|
|
|
|
|
for (; *stops; stops++)
|
|
|
|
|
{
|
2006-07-09 17:02:53 +00:00
|
|
|
if (*stops == ',' || isblank (to_uchar (*stops)))
|
2009-08-22 18:56:06 +02:00
|
|
|
{
|
|
|
|
|
if (have_tabval)
|
|
|
|
|
add_tab_stop (tabval);
|
|
|
|
|
have_tabval = false;
|
|
|
|
|
}
|
1992-11-08 02:50:43 +00:00
|
|
|
else if (ISDIGIT (*stops))
|
2009-08-22 18:56:06 +02:00
|
|
|
{
|
|
|
|
|
if (!have_tabval)
|
|
|
|
|
{
|
|
|
|
|
tabval = 0;
|
|
|
|
|
have_tabval = true;
|
|
|
|
|
num_start = stops;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Detect overflow. */
|
|
|
|
|
if (!DECIMAL_DIGIT_ACCUMULATE (tabval, *stops - '0', uintmax_t))
|
|
|
|
|
{
|
|
|
|
|
size_t len = strspn (num_start, "0123456789");
|
|
|
|
|
char *bad_num = xstrndup (num_start, len);
|
|
|
|
|
error (0, 0, _("tab stop is too large %s"), quote (bad_num));
|
|
|
|
|
free (bad_num);
|
|
|
|
|
ok = false;
|
|
|
|
|
stops = num_start + len - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
1992-11-08 02:50:43 +00:00
|
|
|
else
|
2009-08-22 18:56:06 +02:00
|
|
|
{
|
|
|
|
|
error (0, 0, _("tab size contains invalid character(s): %s"),
|
|
|
|
|
quote (stops));
|
|
|
|
|
ok = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
1992-11-08 02:50:43 +00:00
|
|
|
}
|
|
|
|
|
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
if (!ok)
|
2003-07-23 06:57:57 +00:00
|
|
|
exit (EXIT_FAILURE);
|
|
|
|
|
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
if (have_tabval)
|
2004-08-24 07:38:04 +00:00
|
|
|
add_tab_stop (tabval);
|
1992-11-08 02:50:43 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-24 07:38:04 +00:00
|
|
|
/* Check that the list of tab stops TABS, with ENTRIES entries,
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
contains only nonzero, ascending values. */
|
1992-11-08 02:50:43 +00:00
|
|
|
|
1992-11-08 20:19:58 +00:00
|
|
|
static void
|
2004-08-24 07:38:04 +00:00
|
|
|
validate_tab_stops (uintmax_t const *tabs, size_t entries)
|
1992-11-08 02:50:43 +00:00
|
|
|
{
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
uintmax_t prev_tab = 0;
|
|
|
|
|
size_t i;
|
1993-10-21 17:19:34 +00:00
|
|
|
|
1992-11-08 02:50:43 +00:00
|
|
|
for (i = 0; i < entries; i++)
|
|
|
|
|
{
|
|
|
|
|
if (tabs[i] == 0)
|
2009-08-22 18:56:06 +02:00
|
|
|
error (EXIT_FAILURE, 0, _("tab size cannot be 0"));
|
1992-11-08 02:50:43 +00:00
|
|
|
if (tabs[i] <= prev_tab)
|
2009-08-22 18:56:06 +02:00
|
|
|
error (EXIT_FAILURE, 0, _("tab sizes must be ascending"));
|
1992-11-08 02:50:43 +00:00
|
|
|
prev_tab = tabs[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1995-10-17 13:43:00 +00:00
|
|
|
/* Close the old stream pointer FP if it is non-NULL,
|
|
|
|
|
and return a new one opened to read the next input file.
|
|
|
|
|
Open a filename of `-' as the standard input.
|
|
|
|
|
Return NULL if there are no more input files. */
|
|
|
|
|
|
|
|
|
|
static FILE *
|
1995-10-17 13:47:48 +00:00
|
|
|
next_file (FILE *fp)
|
1995-10-17 13:43:00 +00:00
|
|
|
{
|
|
|
|
|
static char *prev_file;
|
|
|
|
|
char *file;
|
|
|
|
|
|
|
|
|
|
if (fp)
|
|
|
|
|
{
|
1998-06-29 15:57:45 +00:00
|
|
|
if (ferror (fp))
|
2009-08-22 18:56:06 +02:00
|
|
|
{
|
|
|
|
|
error (0, errno, "%s", prev_file);
|
|
|
|
|
exit_status = EXIT_FAILURE;
|
|
|
|
|
}
|
2005-10-24 07:18:18 +00:00
|
|
|
if (STREQ (prev_file, "-"))
|
2009-08-22 18:56:06 +02:00
|
|
|
clearerr (fp); /* Also clear EOF. */
|
2004-08-24 07:38:04 +00:00
|
|
|
else if (fclose (fp) != 0)
|
2009-08-22 18:56:06 +02:00
|
|
|
{
|
|
|
|
|
error (0, errno, "%s", prev_file);
|
|
|
|
|
exit_status = EXIT_FAILURE;
|
|
|
|
|
}
|
1995-10-17 13:43:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while ((file = *file_list++) != NULL)
|
|
|
|
|
{
|
2005-10-24 07:18:18 +00:00
|
|
|
if (STREQ (file, "-"))
|
2009-08-22 18:56:06 +02:00
|
|
|
{
|
|
|
|
|
have_read_stdin = true;
|
|
|
|
|
prev_file = file;
|
|
|
|
|
return stdin;
|
|
|
|
|
}
|
1995-10-17 13:43:00 +00:00
|
|
|
fp = fopen (file, "r");
|
|
|
|
|
if (fp)
|
2009-08-22 18:56:06 +02:00
|
|
|
{
|
|
|
|
|
prev_file = file;
|
|
|
|
|
return fp;
|
|
|
|
|
}
|
1995-10-17 13:43:00 +00:00
|
|
|
error (0, errno, "%s", file);
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
exit_status = EXIT_FAILURE;
|
1995-10-17 13:43:00 +00:00
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
1992-11-08 02:50:43 +00:00
|
|
|
/* Change tabs to spaces, writing to stdout.
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
Read each file in `file_list', in order. */
|
1992-11-08 02:50:43 +00:00
|
|
|
|
1992-11-08 20:19:58 +00:00
|
|
|
static void
|
1995-10-17 13:47:48 +00:00
|
|
|
expand (void)
|
1992-11-08 02:50:43 +00:00
|
|
|
{
|
2004-08-24 07:38:04 +00:00
|
|
|
/* Input stream. */
|
|
|
|
|
FILE *fp = next_file (NULL);
|
|
|
|
|
|
|
|
|
|
if (!fp)
|
1994-03-25 23:38:00 +00:00
|
|
|
return;
|
1999-01-01 22:27:36 +00:00
|
|
|
|
1992-11-08 02:50:43 +00:00
|
|
|
for (;;)
|
|
|
|
|
{
|
2004-08-24 07:38:04 +00:00
|
|
|
/* Input character, or EOF. */
|
|
|
|
|
int c;
|
1992-11-08 02:50:43 +00:00
|
|
|
|
2004-08-24 07:38:04 +00:00
|
|
|
/* If true, perform translations. */
|
|
|
|
|
bool convert = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* The following variables have valid values only when CONVERT
|
2009-08-22 18:56:06 +02:00
|
|
|
is true: */
|
2004-08-24 07:38:04 +00:00
|
|
|
|
|
|
|
|
/* Column of next input character. */
|
|
|
|
|
uintmax_t column = 0;
|
|
|
|
|
|
|
|
|
|
/* Index in TAB_LIST of next tab stop to examine. */
|
|
|
|
|
size_t tab_index = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Convert a line of text. */
|
|
|
|
|
|
|
|
|
|
do
|
2009-08-22 18:56:06 +02:00
|
|
|
{
|
|
|
|
|
while ((c = getc (fp)) < 0 && (fp = next_file (fp)))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (convert)
|
|
|
|
|
{
|
|
|
|
|
if (c == '\t')
|
|
|
|
|
{
|
|
|
|
|
/* Column the next input tab stop is on. */
|
|
|
|
|
uintmax_t next_tab_column;
|
|
|
|
|
|
|
|
|
|
if (tab_size)
|
|
|
|
|
next_tab_column = column + (tab_size - column % tab_size);
|
|
|
|
|
else
|
|
|
|
|
for (;;)
|
|
|
|
|
if (tab_index == first_free_tab)
|
|
|
|
|
{
|
|
|
|
|
next_tab_column = column + 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
uintmax_t tab = tab_list[tab_index++];
|
|
|
|
|
if (column < tab)
|
|
|
|
|
{
|
|
|
|
|
next_tab_column = tab;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (next_tab_column < column)
|
|
|
|
|
error (EXIT_FAILURE, 0, _("input line is too long"));
|
|
|
|
|
|
|
|
|
|
while (++column < next_tab_column)
|
|
|
|
|
if (putchar (' ') < 0)
|
|
|
|
|
error (EXIT_FAILURE, errno, _("write error"));
|
|
|
|
|
|
|
|
|
|
c = ' ';
|
|
|
|
|
}
|
|
|
|
|
else if (c == '\b')
|
|
|
|
|
{
|
|
|
|
|
/* Go back one column, and force recalculation of the
|
|
|
|
|
next tab stop. */
|
|
|
|
|
column -= !!column;
|
|
|
|
|
tab_index -= !!tab_index;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
column++;
|
|
|
|
|
if (!column)
|
|
|
|
|
error (EXIT_FAILURE, 0, _("input line is too long"));
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-23 10:10:51 +01:00
|
|
|
convert &= convert_entire_line || !! isblank (c);
|
2009-08-22 18:56:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c < 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (putchar (c) < 0)
|
|
|
|
|
error (EXIT_FAILURE, errno, _("write error"));
|
|
|
|
|
}
|
2004-08-24 07:38:04 +00:00
|
|
|
while (c != '\n');
|
1992-11-08 02:50:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-21 22:41:04 +00:00
|
|
|
int
|
1995-10-17 13:47:48 +00:00
|
|
|
main (int argc, char **argv)
|
1992-11-08 02:50:43 +00:00
|
|
|
{
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
int c;
|
1992-11-08 02:50:43 +00:00
|
|
|
|
2003-06-17 18:13:23 +00:00
|
|
|
initialize_main (&argc, &argv);
|
2008-06-03 08:34:09 +02:00
|
|
|
set_program_name (argv[0]);
|
1996-03-09 20:19:13 +00:00
|
|
|
setlocale (LC_ALL, "");
|
|
|
|
|
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
|
|
|
textdomain (PACKAGE);
|
1995-10-17 13:43:00 +00:00
|
|
|
|
2000-05-20 22:06:38 +00:00
|
|
|
atexit (close_stdout);
|
|
|
|
|
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
have_read_stdin = false;
|
|
|
|
|
exit_status = EXIT_SUCCESS;
|
|
|
|
|
convert_entire_line = true;
|
|
|
|
|
tab_list = NULL;
|
|
|
|
|
first_free_tab = 0;
|
|
|
|
|
|
2005-04-26 16:41:03 +00:00
|
|
|
while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
|
1992-11-08 02:50:43 +00:00
|
|
|
{
|
1995-10-17 13:43:00 +00:00
|
|
|
switch (c)
|
2009-08-22 18:56:06 +02:00
|
|
|
{
|
|
|
|
|
case 'i':
|
|
|
|
|
convert_entire_line = false;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
|
parse_tab_stops (optarg);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
|
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
|
|
|
if (optarg)
|
|
|
|
|
parse_tab_stops (optarg - 1);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char tab_stop[2];
|
|
|
|
|
tab_stop[0] = c;
|
|
|
|
|
tab_stop[1] = '\0';
|
|
|
|
|
parse_tab_stops (tab_stop);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case_GETOPT_HELP_CHAR;
|
|
|
|
|
|
|
|
|
|
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
usage (EXIT_FAILURE);
|
|
|
|
|
}
|
1992-11-08 02:50:43 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-24 07:38:04 +00:00
|
|
|
validate_tab_stops (tab_list, first_free_tab);
|
1995-10-17 13:43:00 +00:00
|
|
|
|
|
|
|
|
if (first_free_tab == 0)
|
|
|
|
|
tab_size = 8;
|
|
|
|
|
else if (first_free_tab == 1)
|
|
|
|
|
tab_size = tab_list[0];
|
1993-10-23 15:37:19 +00:00
|
|
|
else
|
1995-10-17 13:43:00 +00:00
|
|
|
tab_size = 0;
|
|
|
|
|
|
2003-05-10 19:58:04 +00:00
|
|
|
file_list = (optind < argc ? &argv[optind] : stdin_argv);
|
1995-10-17 13:43:00 +00:00
|
|
|
|
|
|
|
|
expand ();
|
|
|
|
|
|
2004-08-24 07:38:04 +00:00
|
|
|
if (have_read_stdin && fclose (stdin) != 0)
|
1996-03-24 16:59:11 +00:00
|
|
|
error (EXIT_FAILURE, errno, "-");
|
1995-10-17 13:43:00 +00:00
|
|
|
|
(convert_entire_line, have_read_stdin, parse_tabstops,
next_file, expand, main):
Use bool for booleans.
(tab_size, tab_list, add_tabstop, parse_tabstops, validate_tabstops,
expand, main):
Use uintmax_t for column counts.
(add_tabstop): Don't reserve -1 (now UINTMAX_MAX) as a special value.
All callers changed.
(parse_tabstops): Don't pass a negative char to isblank.
Avoid memory leak with large tab stops.
(validate_tabstops, expand): Don't assume number of tab stops is
<= INT_MAX.
(next_file, main): Use EXIT_SUCCESS/EXIT_FAILURE rather than 0/1 when
storing values into exit_status.
(expand): Use same pattern as unexpand for reading chars.
Report an error when input line is too long, instead of silently
screwing up. Do not mishandle tab stops when backspacing left
over start of line.
2004-08-02 23:49:31 +00:00
|
|
|
exit (exit_status);
|
1992-11-08 02:50:43 +00:00
|
|
|
}
|