/* 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; }