Created
September 14, 2010 17:34
-
-
Save osamu/579435 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* tee - read from standard input and write to standard output and files. | |
Copyright (C) 1985, 1990-2006, 2008-2010 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 of the License, 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 <http://www.gnu.org/licenses/>. */ | |
/* Mike Parker, Richard M. Stallman, and David MacKenzie */ | |
#include <config.h> | |
#include <sys/types.h> | |
#include <signal.h> | |
#include <getopt.h> | |
#include <time.h> | |
#include "system.h" | |
#include "error.h" | |
#include "stdio--.h" | |
#include "xfreopen.h" | |
/* The official name of this program (e.g., no `g' prefix). */ | |
#define PROGRAM_NAME "tee" | |
#define AUTHORS \ | |
proper_name ("Mike Parker"), \ | |
proper_name ("Richard M. Stallman"), \ | |
proper_name ("David MacKenzie") | |
static bool tee_files (int nfiles, const char **files); | |
static bool tee_write_files(char *buffer, size_t bytes, FILE **descriptors, const char **files, int nfiles); | |
/* If true, append to output files rather than truncating them. */ | |
static bool append; | |
static bool with_timestamp; | |
static char timestamp_format[128] = "%H:%M:%S "; | |
/* If true, ignore interrupts. */ | |
static bool ignore_interrupts; | |
static struct option const long_options[] = | |
{ | |
{"append", no_argument, NULL, 'a'}, | |
{"times", optional_argument, NULL,'t'}, | |
{"ignore-interrupts", no_argument, NULL, 'i'}, | |
{GETOPT_HELP_OPTION_DECL}, | |
{GETOPT_VERSION_OPTION_DECL}, | |
{NULL, 0, NULL, 0} | |
}; | |
void | |
usage (int status) | |
{ | |
if (status != EXIT_SUCCESS) | |
fprintf (stderr, _("Try `%s --help' for more information.\n"), | |
program_name); | |
else | |
{ | |
printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name); | |
fputs (_("\ | |
Copy standard input to each FILE, and also to standard output.\n\ | |
\n\ | |
-a, --append append to the given FILEs, do not overwrite\n\ | |
-i, --ignore-interrupts ignore interrupt signals\n\ | |
-t, --time [FORMAT] insert timestamp to each line. if no format is specified, %H:%M:%S is used. \n\ | |
"), stdout); | |
fputs (HELP_OPTION_DESCRIPTION, stdout); | |
fputs (VERSION_OPTION_DESCRIPTION, stdout); | |
fputs (_("\ | |
\n\ | |
If a FILE is -, copy again to standard output.\n\ | |
"), stdout); | |
emit_ancillary_info (); | |
} | |
exit (status); | |
} | |
int | |
main (int argc, char **argv) | |
{ | |
bool ok; | |
int optc; | |
initialize_main (&argc, &argv); | |
set_program_name (argv[0]); | |
setlocale (LC_ALL, ""); | |
bindtextdomain (PACKAGE, LOCALEDIR); | |
textdomain (PACKAGE); | |
atexit (close_stdout); | |
append = false; | |
ignore_interrupts = false; | |
with_timestamp = false; | |
while ((optc = getopt_long (argc, argv, "ait:", long_options, NULL)) != -1) | |
{ | |
switch (optc) | |
{ | |
case 'a': | |
append = true; | |
break; | |
case 'i': | |
ignore_interrupts = true; | |
break; | |
case 't': | |
with_timestamp = true; | |
if( optarg ) | |
strcpy( timestamp_format, optarg); | |
break; | |
case_GETOPT_HELP_CHAR; | |
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); | |
default: | |
usage (EXIT_FAILURE); | |
} | |
} | |
if (ignore_interrupts) | |
signal (SIGINT, SIG_IGN); | |
/* Do *not* warn if tee is given no file arguments. | |
POSIX requires that it work when given no arguments. */ | |
ok = tee_files (argc - optind, (const char **) &argv[optind]); | |
if (close (STDIN_FILENO) != 0) | |
error (EXIT_FAILURE, errno, _("standard input")); | |
exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); | |
} | |
/* Copy the standard input into each of the NFILES files in FILES | |
and into the standard output. | |
Return true if successful. */ | |
static bool | |
tee_files (int nfiles, const char **files) | |
{ | |
FILE **descriptors; | |
char buffer[BUFSIZ]; | |
ssize_t bytes_read; | |
int i; | |
bool ok = true; | |
char const *mode_string = | |
(O_BINARY | |
? (append ? "ab" : "wb") | |
: (append ? "a" : "w")); | |
time_t t; | |
struct tm *lt; | |
char *line, *tbuffer, *p; | |
size_t line_len = 0, tbuffer_len = 0; | |
size_t tbuffer_maxsize = BUFSIZ + 1024; | |
descriptors = xnmalloc (nfiles + 1, sizeof *descriptors); | |
if(with_timestamp){ | |
tbuffer = xnmalloc( tbuffer_maxsize, sizeof(char)); | |
} | |
/* Move all the names `up' one in the argv array to make room for | |
the entry for standard output. This writes into argv[argc]. */ | |
for (i = nfiles; i >= 1; i--) | |
files[i] = files[i - 1]; | |
if (O_BINARY && ! isatty (STDIN_FILENO)) | |
xfreopen (NULL, "rb", stdin); | |
if (O_BINARY && ! isatty (STDOUT_FILENO)) | |
xfreopen (NULL, "wb", stdout); | |
/* In the array of NFILES + 1 descriptors, make | |
the first one correspond to standard output. */ | |
descriptors[0] = stdout; | |
files[0] = _("standard output"); | |
setvbuf (stdout, NULL, _IONBF, 0); | |
for (i = 1; i <= nfiles; i++) | |
{ | |
descriptors[i] = (STREQ (files[i], "-") | |
? stdout | |
: fopen (files[i], mode_string)); | |
if (descriptors[i] == NULL) | |
{ | |
error (0, errno, "%s", files[i]); | |
ok = false; | |
} | |
else | |
setvbuf (descriptors[i], NULL, _IONBF, 0); | |
} | |
while (1) | |
{ | |
line = buffer; | |
tbuffer_len = line_len = 0; | |
bytes_read = read (0, buffer, sizeof buffer); | |
#ifdef EINTR | |
if (bytes_read < 0 && errno == EINTR) | |
continue; | |
#endif | |
if (bytes_read <= 0) | |
break; | |
for( i = 0 ; (i < bytes_read) & with_timestamp ; i++) | |
{ | |
line_len += 1; | |
if (buffer[i] !='\n') | |
continue; | |
time(&t); | |
lt = localtime(&t); | |
tbuffer_len += strftime( tbuffer + tbuffer_len , | |
tbuffer_maxsize - tbuffer_len, | |
timestamp_format,lt); | |
if ( tbuffer_len + line_len > tbuffer_maxsize ) | |
{ | |
ok = tee_write_files(tbuffer, tbuffer_len, descriptors, files, nfiles); | |
tbuffer_len = 0; | |
} | |
memcpy(tbuffer + tbuffer_len, line, line_len); | |
tbuffer_len += line_len; | |
if ( i+1 < bytes_read ) | |
line = &(buffer[i+1]); | |
line_len = 0; | |
} | |
if (!with_timestamp ) | |
{ | |
tbuffer = buffer; | |
tbuffer_len = bytes_read; | |
} | |
ok = tee_write_files( tbuffer, tbuffer_len, descriptors, files, nfiles); | |
} | |
if (bytes_read == -1) | |
{ | |
error (0, errno, _("read error")); | |
ok = false; | |
} | |
/* Close the files, but not standard output. */ | |
for (i = 1; i <= nfiles; i++) | |
if (!STREQ (files[i], "-") | |
&& descriptors[i] && fclose (descriptors[i]) != 0) | |
{ | |
error (0, errno, "%s", files[i]); | |
ok = false; | |
} | |
free (descriptors); | |
return ok; | |
} | |
static bool tee_write_files(char *buffer, size_t bytes, FILE **descriptors, const char **files, int nfiles) | |
{ | |
bool ok = true; | |
int i; | |
/* Write to all NFILES + 1 descriptors. | |
Standard output is the first one. */ | |
for (i = 0; i <= nfiles; i++) | |
if (descriptors[i] | |
&& fwrite (buffer , bytes , 1, descriptors[i]) != 1) | |
{ | |
error (0, errno, "%s", files[i]); | |
descriptors[i] = NULL; | |
ok = false; | |
} | |
return ok; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment