summaryrefslogtreecommitdiff
path: root/src/csplit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/csplit.c')
-rw-r--r--src/csplit.c152
1 files changed, 93 insertions, 59 deletions
diff --git a/src/csplit.c b/src/csplit.c
index b0758102e..d1bc0ff8a 100644
--- a/src/csplit.c
+++ b/src/csplit.c
@@ -34,6 +34,11 @@
#include "safe-read.h"
#include "xstrtol.h"
+#ifndef SA_NOCLDSTOP
+# define sigprocmask(How, Set, Oset) /* empty */
+# define sigset_t int
+#endif
+
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "csplit"
@@ -139,19 +144,19 @@ static uintmax_t current_line = 0;
static bool have_read_eof = false;
/* Name of output files. */
-static char *filename_space = NULL;
+static char * volatile filename_space = NULL;
/* Prefix part of output file names. */
-static char *prefix = NULL;
+static char * volatile prefix = NULL;
/* Suffix part of output file names. */
-static char *suffix = NULL;
+static char * volatile suffix = NULL;
/* Number of digits to use in output file names. */
-static int digits = 2;
+static int volatile digits = 2;
/* Number of files created so far. */
-static unsigned int files_created = 0;
+static unsigned int volatile files_created = 0;
/* Number of bytes written to current file. */
static uintmax_t bytes_written;
@@ -169,7 +174,7 @@ static char **global_argv;
static bool suppress_count;
/* If true, remove output files on error. */
-static bool remove_files;
+static bool volatile remove_files;
/* If true, remove all output files which have a zero length. */
static bool elide_empty_files;
@@ -181,6 +186,9 @@ static struct control *controls;
/* Number of elements in `controls'. */
static size_t control_used;
+/* The set of signals that are caught. */
+static sigset_t caught_signals;
+
static struct option const longopts[] =
{
{"digits", required_argument, NULL, 'n'},
@@ -201,12 +209,16 @@ static struct option const longopts[] =
static void
cleanup (void)
{
+ sigset_t oldset;
+
close_output_file ();
- if (remove_files)
- delete_all_files ();
+ sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
+ delete_all_files ();
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
}
+static void cleanup_fatal (void) ATTRIBUTE_NORETURN;
static void
cleanup_fatal (void)
{
@@ -214,20 +226,16 @@ cleanup_fatal (void)
exit (EXIT_FAILURE);
}
-static RETSIGTYPE
+static void
interrupt_handler (int sig)
{
-#ifdef SA_NOCLDSTOP
- struct sigaction sigact;
+#ifndef SA_NOCLDSTOP
+ signal (sig, SIG_IGN);
+#endif
+
+ delete_all_files ();
- sigact.sa_handler = SIG_DFL;
- sigemptyset (&sigact.sa_mask);
- sigact.sa_flags = 0;
- sigaction (sig, &sigact, NULL);
-#else
signal (sig, SIG_DFL);
-#endif
- cleanup ();
raise (sig);
}
@@ -681,6 +689,8 @@ dump_rest_of_file (void)
/* Handle an attempt to read beyond EOF under the control of record P,
on iteration REPETITION if nonzero. */
+static void handle_line_error (const struct control *, uintmax_t)
+ ATTRIBUTE_NORETURN;
static void
handle_line_error (const struct control *p, uintmax_t repetition)
{
@@ -728,6 +738,7 @@ process_line_count (const struct control *p, uintmax_t repetition)
handle_line_error (p, repetition);
}
+static void regexp_error (struct control *, uintmax_t, bool) ATTRIBUTE_NORETURN;
static void
regexp_error (struct control *p, uintmax_t repetition, bool ignore)
{
@@ -902,30 +913,47 @@ make_filename (unsigned int num)
static void
create_output_file (void)
{
+ sigset_t oldset;
+ bool fopen_ok;
+ int fopen_errno;
+
output_filename = make_filename (files_created);
+
+ /* Create the output file in a critical section, to avoid races. */
+ sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
output_stream = fopen (output_filename, "w");
- if (output_stream == NULL)
+ fopen_ok = (output_stream != NULL);
+ fopen_errno = errno;
+ files_created += fopen_ok;
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
+
+ if (! fopen_ok)
{
- error (0, errno, "%s", output_filename);
+ error (0, fopen_errno, "%s", output_filename);
cleanup_fatal ();
}
- files_created++;
bytes_written = 0;
}
-/* Delete all the files we have created. */
+/* If requested, delete all the files we have created. This function
+ must be called only from critical sections. */
static void
delete_all_files (void)
{
unsigned int i;
+ if (! remove_files)
+ return;
+
for (i = 0; i < files_created; i++)
{
const char *name = make_filename (i);
if (unlink (name))
error (0, errno, "%s", name);
}
+
+ files_created = 0;
}
/* Close the current output file and print the count
@@ -950,9 +978,19 @@ close_output_file (void)
}
if (bytes_written == 0 && elide_empty_files)
{
- if (unlink (output_filename))
- error (0, errno, "%s", output_filename);
- files_created--;
+ sigset_t oldset;
+ bool unlink_ok;
+ int unlink_errno;
+
+ /* Remove the output file in a critical section, to avoid races. */
+ sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
+ unlink_ok = (unlink (output_filename) == 0);
+ unlink_errno = errno;
+ files_created -= unlink_ok;
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
+
+ if (! unlink_ok)
+ error (0, unlink_errno, "%s", output_filename);
}
else
{
@@ -1273,9 +1311,6 @@ main (int argc, char **argv)
{
int optc;
unsigned long int val;
-#ifdef SA_NOCLDSTOP
- struct sigaction oldact, newact;
-#endif
initialize_main (&argc, &argv);
program_name = argv[0];
@@ -1295,37 +1330,6 @@ main (int argc, char **argv)
/* Change the way xmalloc and xrealloc fail. */
xalloc_fail_func = cleanup;
-#ifdef SA_NOCLDSTOP
- newact.sa_handler = interrupt_handler;
- sigemptyset (&newact.sa_mask);
- newact.sa_flags = 0;
-
- sigaction (SIGHUP, NULL, &oldact);
- if (oldact.sa_handler != SIG_IGN)
- sigaction (SIGHUP, &newact, NULL);
-
- sigaction (SIGINT, NULL, &oldact);
- if (oldact.sa_handler != SIG_IGN)
- sigaction (SIGINT, &newact, NULL);
-
- sigaction (SIGQUIT, NULL, &oldact);
- if (oldact.sa_handler != SIG_IGN)
- sigaction (SIGQUIT, &newact, NULL);
-
- sigaction (SIGTERM, NULL, &oldact);
- if (oldact.sa_handler != SIG_IGN)
- sigaction (SIGTERM, &newact, NULL);
-#else
- if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
- signal (SIGHUP, interrupt_handler);
- if (signal (SIGINT, SIG_IGN) != SIG_IGN)
- signal (SIGINT, interrupt_handler);
- if (signal (SIGQUIT, SIG_IGN) != SIG_IGN)
- signal (SIGQUIT, interrupt_handler);
- if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
- signal (SIGTERM, interrupt_handler);
-#endif
-
while ((optc = getopt_long (argc, argv, "f:b:kn:sqz", longopts, NULL)) != -1)
switch (optc)
{
@@ -1383,6 +1387,36 @@ main (int argc, char **argv)
parse_patterns (argc, optind, argv);
+ {
+ int i;
+ static int const sig[] = { SIGHUP, SIGINT, SIGQUIT, SIGTERM };
+ enum { nsigs = sizeof sig / sizeof sig[0] };
+
+#ifdef SA_NOCLDSTOP
+ struct sigaction act;
+
+ sigemptyset (&caught_signals);
+ for (i = 0; i < nsigs; i++)
+ {
+ sigaction (sig[i], NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&caught_signals, sig[i]);
+ }
+
+ act.sa_handler = interrupt_handler;
+ act.sa_mask = caught_signals;
+ act.sa_flags = 0;
+
+ for (i = 0; i < nsigs; i++)
+ if (sigismember (&caught_signals, sig[i]))
+ sigaction (sig[i], &act, NULL);
+#else
+ for (i = 0; i < nsigs; i++)
+ if (signal (sig[i], SIG_IGN) != SIG_IGN)
+ signal (sig[i], interrupt_handler);
+#endif
+ }
+
split_file ();
if (close (input_desc) < 0)