|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <config.h> |
|
|
|
|
|
#include <stdio.h> |
|
|
#include <getopt.h> |
|
|
#include <sys/types.h> |
|
|
|
|
|
#if HAVE_STROPTS_H |
|
|
# include <stropts.h> |
|
|
#endif |
|
|
#include <sys/ioctl.h> |
|
|
|
|
|
#include "system.h" |
|
|
#include "alignalloc.h" |
|
|
#include "ioblksize.h" |
|
|
#include "fadvise.h" |
|
|
#include "full-write.h" |
|
|
#include "xbinary-io.h" |
|
|
|
|
|
|
|
|
#define PROGRAM_NAME "cat" |
|
|
|
|
|
#define AUTHORS \ |
|
|
proper_name_lite ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \ |
|
|
proper_name ("Richard M. Stallman") |
|
|
|
|
|
|
|
|
static char const *infile; |
|
|
|
|
|
|
|
|
static int input_desc; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define LINE_COUNTER_BUF_LEN 20 |
|
|
static char line_buf[LINE_COUNTER_BUF_LEN] = |
|
|
{ |
|
|
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', |
|
|
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0', |
|
|
'\t', '\0' |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
static char *line_num_print = line_buf + LINE_COUNTER_BUF_LEN - 8; |
|
|
|
|
|
|
|
|
static char *line_num_start = line_buf + LINE_COUNTER_BUF_LEN - 3; |
|
|
|
|
|
|
|
|
static char *line_num_end = line_buf + LINE_COUNTER_BUF_LEN - 3; |
|
|
|
|
|
|
|
|
static int newlines2 = 0; |
|
|
|
|
|
|
|
|
static bool pending_cr = false; |
|
|
|
|
|
void |
|
|
usage (int status) |
|
|
{ |
|
|
if (status != EXIT_SUCCESS) |
|
|
emit_try_help (); |
|
|
else |
|
|
{ |
|
|
printf (_("\ |
|
|
Usage: %s [OPTION]... [FILE]...\n\ |
|
|
"), |
|
|
program_name); |
|
|
fputs (_("\ |
|
|
Concatenate FILE(s) to standard output.\n\ |
|
|
"), stdout); |
|
|
|
|
|
emit_stdin_note (); |
|
|
|
|
|
fputs (_("\ |
|
|
\n\ |
|
|
-A, --show-all equivalent to -vET\n\ |
|
|
-b, --number-nonblank number nonempty output lines, overrides -n\n\ |
|
|
-e equivalent to -vE\n\ |
|
|
-E, --show-ends display $ at end of each line\n\ |
|
|
-n, --number number all output lines\n\ |
|
|
-s, --squeeze-blank suppress repeated empty output lines\n\ |
|
|
"), stdout); |
|
|
fputs (_("\ |
|
|
-t equivalent to -vT\n\ |
|
|
-T, --show-tabs display TAB characters as ^I\n\ |
|
|
-u (ignored)\n\ |
|
|
-v, --show-nonprinting use ^ and M- notation, except for LFD and TAB\n\ |
|
|
"), stdout); |
|
|
fputs (HELP_OPTION_DESCRIPTION, stdout); |
|
|
fputs (VERSION_OPTION_DESCRIPTION, stdout); |
|
|
printf (_("\ |
|
|
\n\ |
|
|
Examples:\n\ |
|
|
%s f - g Output f's contents, then standard input, then g's contents.\n\ |
|
|
%s Copy standard input to standard output.\n\ |
|
|
"), |
|
|
program_name, program_name); |
|
|
emit_ancillary_info (PROGRAM_NAME); |
|
|
} |
|
|
exit (status); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
next_line_num (void) |
|
|
{ |
|
|
char *endp = line_num_end; |
|
|
do |
|
|
{ |
|
|
if ((*endp)++ < '9') |
|
|
return; |
|
|
*endp-- = '0'; |
|
|
} |
|
|
while (endp >= line_num_start); |
|
|
|
|
|
if (line_num_start > line_buf) |
|
|
*--line_num_start = '1'; |
|
|
else |
|
|
*line_buf = '>'; |
|
|
if (line_num_start < line_num_print) |
|
|
line_num_print--; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool |
|
|
simple_cat (char *buf, idx_t bufsize) |
|
|
{ |
|
|
|
|
|
|
|
|
while (true) |
|
|
{ |
|
|
|
|
|
|
|
|
ssize_t n_read = read (input_desc, buf, bufsize); |
|
|
if (n_read < 0) |
|
|
{ |
|
|
error (0, errno, "%s", quotef (infile)); |
|
|
return false; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (n_read == 0) |
|
|
return true; |
|
|
|
|
|
|
|
|
|
|
|
if (full_write (STDOUT_FILENO, buf, n_read) != n_read) |
|
|
write_error (); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline void |
|
|
write_pending (char *outbuf, char **bpout) |
|
|
{ |
|
|
idx_t n_write = *bpout - outbuf; |
|
|
if (0 < n_write) |
|
|
{ |
|
|
if (full_write (STDOUT_FILENO, outbuf, n_write) != n_write) |
|
|
write_error (); |
|
|
*bpout = outbuf; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool |
|
|
cat (char *inbuf, idx_t insize, char *outbuf, idx_t outsize, |
|
|
bool show_nonprinting, bool show_tabs, bool number, bool number_nonblank, |
|
|
bool show_ends, bool squeeze_blank) |
|
|
{ |
|
|
|
|
|
unsigned char ch; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int newlines = newlines2; |
|
|
|
|
|
#ifdef FIONREAD |
|
|
|
|
|
|
|
|
bool use_fionread = true; |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char *eob = inbuf; |
|
|
|
|
|
|
|
|
char *bpin = eob + 1; |
|
|
|
|
|
|
|
|
char *bpout = outbuf; |
|
|
|
|
|
while (true) |
|
|
{ |
|
|
do |
|
|
{ |
|
|
|
|
|
|
|
|
if (outbuf + outsize <= bpout) |
|
|
{ |
|
|
char *wp = outbuf; |
|
|
idx_t remaining_bytes; |
|
|
do |
|
|
{ |
|
|
if (full_write (STDOUT_FILENO, wp, outsize) != outsize) |
|
|
write_error (); |
|
|
wp += outsize; |
|
|
remaining_bytes = bpout - wp; |
|
|
} |
|
|
while (outsize <= remaining_bytes); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
memmove (outbuf, wp, remaining_bytes); |
|
|
bpout = outbuf + remaining_bytes; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (bpin > eob) |
|
|
{ |
|
|
bool input_pending = false; |
|
|
#ifdef FIONREAD |
|
|
int n_to_read = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (use_fionread |
|
|
&& ioctl (input_desc, FIONREAD, &n_to_read) < 0) |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (errno == EOPNOTSUPP || errno == ENOTTY |
|
|
|| errno == EINVAL || errno == ENODEV |
|
|
|| errno == ENOSYS) |
|
|
use_fionread = false; |
|
|
else |
|
|
{ |
|
|
error (0, errno, _("cannot do ioctl on %s"), |
|
|
quoteaf (infile)); |
|
|
newlines2 = newlines; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
if (n_to_read != 0) |
|
|
input_pending = true; |
|
|
#endif |
|
|
|
|
|
if (!input_pending) |
|
|
write_pending (outbuf, &bpout); |
|
|
|
|
|
|
|
|
|
|
|
ssize_t n_read = read (input_desc, inbuf, insize); |
|
|
if (n_read < 0) |
|
|
{ |
|
|
error (0, errno, "%s", quotef (infile)); |
|
|
write_pending (outbuf, &bpout); |
|
|
newlines2 = newlines; |
|
|
return false; |
|
|
} |
|
|
if (n_read == 0) |
|
|
{ |
|
|
write_pending (outbuf, &bpout); |
|
|
newlines2 = newlines; |
|
|
return true; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bpin = inbuf; |
|
|
eob = bpin + n_read; |
|
|
*eob = '\n'; |
|
|
} |
|
|
else |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (++newlines > 0) |
|
|
{ |
|
|
if (newlines >= 2) |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
newlines = 2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (squeeze_blank) |
|
|
{ |
|
|
ch = *bpin++; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (number && !number_nonblank) |
|
|
{ |
|
|
next_line_num (); |
|
|
bpout = stpcpy (bpout, line_num_print); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (show_ends) |
|
|
{ |
|
|
if (pending_cr) |
|
|
{ |
|
|
*bpout++ = '^'; |
|
|
*bpout++ = 'M'; |
|
|
pending_cr = false; |
|
|
} |
|
|
*bpout++ = '$'; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
*bpout++ = '\n'; |
|
|
} |
|
|
ch = *bpin++; |
|
|
} |
|
|
while (ch == '\n'); |
|
|
|
|
|
|
|
|
|
|
|
if (pending_cr) |
|
|
{ |
|
|
*bpout++ = '\r'; |
|
|
pending_cr = false; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (newlines >= 0 && number) |
|
|
{ |
|
|
next_line_num (); |
|
|
bpout = stpcpy (bpout, line_num_print); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (show_nonprinting) |
|
|
{ |
|
|
while (true) |
|
|
{ |
|
|
if (ch >= 32) |
|
|
{ |
|
|
if (ch < 127) |
|
|
*bpout++ = ch; |
|
|
else if (ch == 127) |
|
|
{ |
|
|
*bpout++ = '^'; |
|
|
*bpout++ = '?'; |
|
|
} |
|
|
else |
|
|
{ |
|
|
*bpout++ = 'M'; |
|
|
*bpout++ = '-'; |
|
|
if (ch >= 128 + 32) |
|
|
{ |
|
|
if (ch < 128 + 127) |
|
|
*bpout++ = ch - 128; |
|
|
else |
|
|
{ |
|
|
*bpout++ = '^'; |
|
|
*bpout++ = '?'; |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
*bpout++ = '^'; |
|
|
*bpout++ = ch - 128 + 64; |
|
|
} |
|
|
} |
|
|
} |
|
|
else if (ch == '\t' && !show_tabs) |
|
|
*bpout++ = '\t'; |
|
|
else if (ch == '\n') |
|
|
{ |
|
|
newlines = -1; |
|
|
break; |
|
|
} |
|
|
else |
|
|
{ |
|
|
*bpout++ = '^'; |
|
|
*bpout++ = ch + 64; |
|
|
} |
|
|
|
|
|
ch = *bpin++; |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
|
|
|
while (true) |
|
|
{ |
|
|
if (ch == '\t' && show_tabs) |
|
|
{ |
|
|
*bpout++ = '^'; |
|
|
*bpout++ = ch + 64; |
|
|
} |
|
|
else if (ch != '\n') |
|
|
{ |
|
|
if (ch == '\r' && *bpin == '\n' && show_ends) |
|
|
{ |
|
|
if (bpin == eob) |
|
|
pending_cr = true; |
|
|
else |
|
|
{ |
|
|
*bpout++ = '^'; |
|
|
*bpout++ = 'M'; |
|
|
} |
|
|
} |
|
|
else |
|
|
*bpout++ = ch; |
|
|
} |
|
|
else |
|
|
{ |
|
|
newlines = -1; |
|
|
break; |
|
|
} |
|
|
|
|
|
ch = *bpin++; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int |
|
|
copy_cat (void) |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (bool some_copied = false; ; some_copied = true) |
|
|
switch (copy_file_range (input_desc, nullptr, STDOUT_FILENO, nullptr, |
|
|
copy_max, 0)) |
|
|
{ |
|
|
case 0: |
|
|
return some_copied; |
|
|
|
|
|
case -1: |
|
|
if (errno == ENOSYS || is_ENOTSUP (errno) || errno == EINVAL |
|
|
|| errno == EBADF || errno == EXDEV || errno == ETXTBSY |
|
|
|| errno == EPERM) |
|
|
return 0; |
|
|
error (0, errno, "%s", quotef (infile)); |
|
|
return -1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
int |
|
|
main (int argc, char **argv) |
|
|
{ |
|
|
|
|
|
bool have_read_stdin = false; |
|
|
|
|
|
struct stat stat_buf; |
|
|
|
|
|
|
|
|
bool number = false; |
|
|
bool number_nonblank = false; |
|
|
bool squeeze_blank = false; |
|
|
bool show_ends = false; |
|
|
bool show_nonprinting = false; |
|
|
bool show_tabs = false; |
|
|
int file_open_mode = O_RDONLY; |
|
|
|
|
|
static struct option const long_options[] = |
|
|
{ |
|
|
{"number-nonblank", no_argument, nullptr, 'b'}, |
|
|
{"number", no_argument, nullptr, 'n'}, |
|
|
{"squeeze-blank", no_argument, nullptr, 's'}, |
|
|
{"show-nonprinting", no_argument, nullptr, 'v'}, |
|
|
{"show-ends", no_argument, nullptr, 'E'}, |
|
|
{"show-tabs", no_argument, nullptr, 'T'}, |
|
|
{"show-all", no_argument, nullptr, 'A'}, |
|
|
{GETOPT_HELP_OPTION_DECL}, |
|
|
{GETOPT_VERSION_OPTION_DECL}, |
|
|
{nullptr, 0, nullptr, 0} |
|
|
}; |
|
|
|
|
|
initialize_main (&argc, &argv); |
|
|
set_program_name (argv[0]); |
|
|
setlocale (LC_ALL, ""); |
|
|
bindtextdomain (PACKAGE, LOCALEDIR); |
|
|
textdomain (PACKAGE); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
atexit (close_stdout); |
|
|
|
|
|
|
|
|
|
|
|
int c; |
|
|
while ((c = getopt_long (argc, argv, "benstuvAET", long_options, nullptr)) |
|
|
!= -1) |
|
|
{ |
|
|
switch (c) |
|
|
{ |
|
|
case 'b': |
|
|
number = true; |
|
|
number_nonblank = true; |
|
|
break; |
|
|
|
|
|
case 'e': |
|
|
show_ends = true; |
|
|
show_nonprinting = true; |
|
|
break; |
|
|
|
|
|
case 'n': |
|
|
number = true; |
|
|
break; |
|
|
|
|
|
case 's': |
|
|
squeeze_blank = true; |
|
|
break; |
|
|
|
|
|
case 't': |
|
|
show_tabs = true; |
|
|
show_nonprinting = true; |
|
|
break; |
|
|
|
|
|
case 'u': |
|
|
|
|
|
break; |
|
|
|
|
|
case 'v': |
|
|
show_nonprinting = true; |
|
|
break; |
|
|
|
|
|
case 'A': |
|
|
show_nonprinting = true; |
|
|
show_ends = true; |
|
|
show_tabs = true; |
|
|
break; |
|
|
|
|
|
case 'E': |
|
|
show_ends = true; |
|
|
break; |
|
|
|
|
|
case 'T': |
|
|
show_tabs = true; |
|
|
break; |
|
|
|
|
|
case_GETOPT_HELP_CHAR; |
|
|
|
|
|
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); |
|
|
|
|
|
default: |
|
|
usage (EXIT_FAILURE); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (fstat (STDOUT_FILENO, &stat_buf) < 0) |
|
|
error (EXIT_FAILURE, errno, _("standard output")); |
|
|
|
|
|
|
|
|
idx_t outsize = io_blksize (&stat_buf); |
|
|
|
|
|
|
|
|
struct |
|
|
{ |
|
|
dev_t st_dev; |
|
|
ino_t st_ino; |
|
|
} out_id; |
|
|
int out_flags = -2; |
|
|
bool have_out_dev = ! (S_TYPEISSHM (&stat_buf) || S_TYPEISTMO (&stat_buf)); |
|
|
if (have_out_dev) |
|
|
{ |
|
|
out_id.st_dev = stat_buf.st_dev; |
|
|
out_id.st_ino = stat_buf.st_ino; |
|
|
} |
|
|
|
|
|
|
|
|
bool out_isreg = S_ISREG (stat_buf.st_mode) != 0; |
|
|
|
|
|
if (! (number || show_ends || squeeze_blank)) |
|
|
{ |
|
|
file_open_mode |= O_BINARY; |
|
|
xset_binary_mode (STDOUT_FILENO, O_BINARY); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
infile = "-"; |
|
|
int argind = optind; |
|
|
bool ok = true; |
|
|
idx_t page_size = getpagesize (); |
|
|
|
|
|
do |
|
|
{ |
|
|
if (argind < argc) |
|
|
infile = argv[argind]; |
|
|
|
|
|
bool reading_stdin = streq (infile, "-"); |
|
|
if (reading_stdin) |
|
|
{ |
|
|
have_read_stdin = true; |
|
|
input_desc = STDIN_FILENO; |
|
|
if (file_open_mode & O_BINARY) |
|
|
xset_binary_mode (STDIN_FILENO, O_BINARY); |
|
|
} |
|
|
else |
|
|
{ |
|
|
input_desc = open (infile, file_open_mode); |
|
|
if (input_desc < 0) |
|
|
{ |
|
|
error (0, errno, "%s", quotef (infile)); |
|
|
ok = false; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
|
|
|
if (fstat (input_desc, &stat_buf) < 0) |
|
|
{ |
|
|
error (0, errno, "%s", quotef (infile)); |
|
|
ok = false; |
|
|
goto contin; |
|
|
} |
|
|
|
|
|
|
|
|
idx_t insize = io_blksize (&stat_buf); |
|
|
|
|
|
fdadvise (input_desc, 0, 0, FADVISE_SEQUENTIAL); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (! (S_ISFIFO (stat_buf.st_mode) || S_ISSOCK (stat_buf.st_mode) |
|
|
|| S_TYPEISSHM (&stat_buf) || S_TYPEISTMO (&stat_buf)) |
|
|
&& have_out_dev |
|
|
&& SAME_INODE (stat_buf, out_id)) |
|
|
{ |
|
|
off_t in_pos = lseek (input_desc, 0, SEEK_CUR); |
|
|
if (0 <= in_pos) |
|
|
{ |
|
|
if (out_flags < -1) |
|
|
out_flags = fcntl (STDOUT_FILENO, F_GETFL); |
|
|
int whence = (0 <= out_flags && out_flags & O_APPEND |
|
|
? SEEK_END : SEEK_CUR); |
|
|
if (in_pos < lseek (STDOUT_FILENO, 0, whence)) |
|
|
{ |
|
|
error (0, 0, _("%s: input file is output file"), |
|
|
quotef (infile)); |
|
|
ok = false; |
|
|
goto contin; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
char *inbuf; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (! (number || show_ends || show_nonprinting |
|
|
|| show_tabs || squeeze_blank)) |
|
|
{ |
|
|
int copy_cat_status = |
|
|
out_isreg && S_ISREG (stat_buf.st_mode) ? copy_cat () : 0; |
|
|
if (copy_cat_status != 0) |
|
|
{ |
|
|
inbuf = nullptr; |
|
|
ok &= 0 < copy_cat_status; |
|
|
} |
|
|
else |
|
|
{ |
|
|
insize = MAX (insize, outsize); |
|
|
inbuf = xalignalloc (page_size, insize); |
|
|
ok &= simple_cat (inbuf, insize); |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
|
|
|
inbuf = xalignalloc (page_size, insize + 1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idx_t bufsize; |
|
|
if (ckd_mul (&bufsize, insize, 4) |
|
|
|| ckd_add (&bufsize, bufsize, outsize) |
|
|
|| ckd_add (&bufsize, bufsize, LINE_COUNTER_BUF_LEN - 1)) |
|
|
xalloc_die (); |
|
|
char *outbuf = xalignalloc (page_size, bufsize); |
|
|
|
|
|
ok &= cat (inbuf, insize, outbuf, outsize, show_nonprinting, |
|
|
show_tabs, number, number_nonblank, show_ends, |
|
|
squeeze_blank); |
|
|
|
|
|
alignfree (outbuf); |
|
|
} |
|
|
|
|
|
alignfree (inbuf); |
|
|
|
|
|
contin: |
|
|
if (!reading_stdin && close (input_desc) < 0) |
|
|
{ |
|
|
error (0, errno, "%s", quotef (infile)); |
|
|
ok = false; |
|
|
} |
|
|
} |
|
|
while (++argind < argc); |
|
|
|
|
|
if (pending_cr) |
|
|
{ |
|
|
if (full_write (STDOUT_FILENO, "\r", 1) != 1) |
|
|
write_error (); |
|
|
} |
|
|
|
|
|
if (have_read_stdin && close (STDIN_FILENO) < 0) |
|
|
error (EXIT_FAILURE, errno, _("closing standard input")); |
|
|
|
|
|
return ok ? EXIT_SUCCESS : EXIT_FAILURE; |
|
|
} |
|
|
|