/* GNU SED, a batch stream editor.
Copyright (C) 1989-2018 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
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
along with this program; If not, see . */
#include "sed.h"
#include
#include
#include
#include
#include
#include
#include "binary-io.h"
#include "getopt.h"
#include "progname.h"
#include "version.h"
#include "xalloc.h"
#include "version-etc.h"
#define AUTHORS \
_("Jay Fenlason"), \
_("Tom Lord"), \
_("Ken Pizzini"), \
_("Paolo Bonzini"), \
_("Jim Meyering"), \
_("Assaf Gordon")
int extended_regexp_flags = 0;
/* one-byte buffer delimiter */
char buffer_delimiter = '\n';
/* If set, fflush(stdout) on every line output. */
bool unbuffered = false;
/* If set, don't write out the line unless explicitly told to */
bool no_default_output = false;
/* If set, reset line counts on every new file. */
bool separate_files = false;
/* If set, follow symlinks when processing in place */
bool follow_symlinks = false;
/* If set, opearate in 'sandbox' mode */
bool sandbox = false;
/* if set, print debugging information */
bool debug = false;
/* How do we edit files in-place? (we don't if NULL) */
char *in_place_extension = NULL;
/* The mode to use to read/write files, either "r"/"w" or "rb"/"wb". */
char const *read_mode = "r";
char const *write_mode = "w";
#if O_BINARY
/* Additional flag for binary mode on platforms with O_BINARY/O_TEXT. */
bool binary_mode = false;
#endif
/* Do we need to be pedantically POSIX compliant? */
enum posixicity_types posixicity;
/* How long should the `l' command's output line be? */
countT lcmd_out_line_len = 70;
/* The complete compiled SED program that we are going to run: */
static struct vector *the_program = NULL;
/* When we've created a temporary for an in-place update,
we may have to exit before the rename. This is the name
of the temporary that we'll have to unlink via an atexit-
registered cleanup function. */
static char const *G_file_to_unlink;
struct localeinfo localeinfo;
/* When exiting between temporary file creation and the rename
associated with a sed -i invocation, remove that file. */
static void
cleanup (void)
{
IF_LINT (free (in_place_extension));
if (G_file_to_unlink)
unlink (G_file_to_unlink);
}
/* Note that FILE must be removed upon exit. */
void
register_cleanup_file (char const *file)
{
G_file_to_unlink = file;
}
/* Clear the global file-to-unlink global. */
void
cancel_cleanup (void)
{
G_file_to_unlink = NULL;
}
static void usage (int);
static void
contact (int errmsg)
{
FILE *out = errmsg ? stderr : stdout;
fprintf (out, _("GNU sed home page: .\n\
General help using GNU software: .\n"));
/* Only print the bug report address for `sed --help', otherwise we'll
get reports for other people's bugs. */
if (!errmsg)
fprintf (out, _("E-mail bug reports to: <%s>.\n"), PACKAGE_BUGREPORT);
}
_Noreturn static void
usage (int status)
{
FILE *out = status ? stderr : stdout;
fprintf (out, _("\
Usage: %s [OPTION]... {script-only-if-no-other-script} [input-file]...\n\
\n"), program_name);
fprintf (out, _(" -n, --quiet, --silent\n\
suppress automatic printing of pattern space\n"));
fprintf (out, _(" --debug\n\
annotate program execution\n"));
fprintf (out, _(" -e script, --expression=script\n\
add the script to the commands to be executed\n"));
fprintf (out, _(" -f script-file, --file=script-file\n\
add the contents of script-file to the commands" \
" to be executed\n"));
#ifdef ENABLE_FOLLOW_SYMLINKS
fprintf (out, _(" --follow-symlinks\n\
follow symlinks when processing in place\n"));
#endif
fprintf (out, _(" -i[SUFFIX], --in-place[=SUFFIX]\n\
edit files in place (makes backup if SUFFIX supplied)\n"));
#if O_BINARY
fprintf (out, _(" -b, --binary\n\
open files in binary mode (CR+LFs are not" \
" processed specially)\n"));
#endif
fprintf (out, _(" -l N, --line-length=N\n\
specify the desired line-wrap length for the `l' command\n"));
fprintf (out, _(" --posix\n\
disable all GNU extensions.\n"));
fprintf (out, _(" -E, -r, --regexp-extended\n\
use extended regular expressions in the script\n\
(for portability use POSIX -E).\n"));
fprintf (out, _(" -s, --separate\n\
consider files as separate rather than as a single,\n\
continuous long stream.\n"));
fprintf (out, _(" --sandbox\n\
operate in sandbox mode (disable e/r/w commands).\n"));
fprintf (out, _(" -u, --unbuffered\n\
load minimal amounts of data from the input files and flush\n\
the output buffers more often\n"));
fprintf (out, _(" -z, --null-data\n\
separate lines by NUL characters\n"));
fprintf (out, _(" --help display this help and exit\n"));
fprintf (out, _(" --version output version information and exit\n"));
fprintf (out, _("\n\
If no -e, --expression, -f, or --file option is given, then the first\n\
non-option argument is taken as the sed script to interpret. All\n\
remaining arguments are names of input files; if no input files are\n\
specified, then the standard input is read.\n\
\n"));
contact (status);
ck_fclose (NULL);
exit (status);
}
int
main (int argc, char **argv)
{
#define SHORTOPTS "bsnrzuEe:f:l:i::V:"
enum { SANDBOX_OPTION = CHAR_MAX+1,
DEBUG_OPTION
};
static const struct option longopts[] = {
{"binary", 0, NULL, 'b'},
{"regexp-extended", 0, NULL, 'r'},
{"debug", 0, NULL, DEBUG_OPTION},
{"expression", 1, NULL, 'e'},
{"file", 1, NULL, 'f'},
{"in-place", 2, NULL, 'i'},
{"line-length", 1, NULL, 'l'},
{"null-data", 0, NULL, 'z'},
{"zero-terminated", 0, NULL, 'z'},
{"quiet", 0, NULL, 'n'},
{"posix", 0, NULL, 'p'},
{"silent", 0, NULL, 'n'},
{"sandbox", 0, NULL, SANDBOX_OPTION},
{"separate", 0, NULL, 's'},
{"unbuffered", 0, NULL, 'u'},
{"version", 0, NULL, 'v'},
{"help", 0, NULL, 'h'},
#ifdef ENABLE_FOLLOW_SYMLINKS
{"follow-symlinks", 0, NULL, 'F'},
#endif
{NULL, 0, NULL, 0}
};
int opt;
int return_code;
const char *cols = getenv ("COLS");
set_program_name (argv[0]);
initialize_main (&argc, &argv);
#if HAVE_SETLOCALE
/* Set locale according to user's wishes. */
setlocale (LC_ALL, "");
#endif
initialize_mbcs ();
init_localeinfo (&localeinfo);
/* Arrange to remove any un-renamed temporary file,
upon premature exit. */
atexit (cleanup);
#if ENABLE_NLS
/* Tell program which translations to use and where to find. */
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
#endif
if (getenv ("POSIXLY_CORRECT") != NULL)
posixicity = POSIXLY_CORRECT;
else
posixicity = POSIXLY_EXTENDED;
/* If environment variable `COLS' is set, use its value for
the baseline setting of `lcmd_out_line_len'. The "-1"
is to avoid gratuitous auto-line-wrap on ttys.
*/
if (cols)
{
countT t = atoi (cols);
if (t > 1)
lcmd_out_line_len = t-1;
}
while ((opt = getopt_long (argc, argv, SHORTOPTS, longopts, NULL)) != EOF)
{
switch (opt)
{
case 'n':
no_default_output = true;
break;
case 'e':
the_program = compile_string (the_program, optarg, strlen (optarg));
break;
case 'f':
the_program = compile_file (the_program, optarg);
break;
case 'z':
buffer_delimiter = 0;
break;
case 'F':
follow_symlinks = true;
break;
case 'i':
separate_files = true;
IF_LINT (free (in_place_extension));
if (optarg == NULL)
/* use no backups */
in_place_extension = xstrdup ("*");
else if (strchr (optarg, '*') != NULL)
in_place_extension = xstrdup (optarg);
else
{
in_place_extension = XCALLOC (strlen (optarg) + 2, char);
in_place_extension[0] = '*';
strcpy (in_place_extension + 1, optarg);
}
break;
case 'l':
lcmd_out_line_len = atoi (optarg);
break;
case 'p':
posixicity = POSIXLY_BASIC;
break;
case 'b':
read_mode = "rb";
write_mode = "wb";
#if O_BINARY
binary_mode = true;
#endif
break;
case 'E':
case 'r':
extended_regexp_flags = REG_EXTENDED;
break;
case 's':
separate_files = true;
break;
case SANDBOX_OPTION:
sandbox = true;
break;
case DEBUG_OPTION:
debug = true;
break;
case 'u':
unbuffered = true;
break;
case 'v':
version_etc (stdout, program_name, PACKAGE_NAME, Version,
AUTHORS, (char *) NULL);
contact (false);
ck_fclose (NULL);
exit (EXIT_SUCCESS);
case 'h':
usage (EXIT_SUCCESS);
default:
usage (EXIT_BAD_USAGE);
}
}
if (!the_program)
{
if (optind < argc)
{
char *arg = argv[optind++];
the_program = compile_string (the_program, arg, strlen (arg));
}
else
usage (EXIT_BAD_USAGE);
}
check_final_program (the_program);
#if O_BINARY
if (binary_mode)
{
if (set_binary_mode ( fileno (stdin), O_BINARY) == -1)
panic (_("failed to set binary mode on STDIN"));
if (set_binary_mode ( fileno (stdout), O_BINARY) == -1)
panic (_("failed to set binary mode on STDOUT"));
}
#endif
if (debug)
debug_print_program (the_program);
return_code = process_files (the_program, argv+optind);
finish_program (the_program);
ck_fclose (NULL);
return return_code;
}