Signed-off-by: James Youngman <jay@xxxxxxx> --- configure.ac | 1 - misc-utils/Makefile.am | 6 +- misc-utils/scriptreplay.1 | 47 +++++++--- misc-utils/scriptreplay.c | 226 ++++++++++++++++++++++++++++++++++++++++++++ misc-utils/scriptreplay.pl | 83 ---------------- 5 files changed, 264 insertions(+), 99 deletions(-) create mode 100644 misc-utils/scriptreplay.c delete mode 100755 misc-utils/scriptreplay.pl diff --git a/configure.ac b/configure.ac index 435f768..75a112c 100644 --- a/configure.ac +++ b/configure.ac @@ -693,7 +693,6 @@ tests/Makefile tests/helpers/Makefile tests/commands.sh misc-utils/chkdupexe:misc-utils/chkdupexe.pl -misc-utils/scriptreplay:misc-utils/scriptreplay.pl ]) AC_OUTPUT diff --git a/misc-utils/Makefile.am b/misc-utils/Makefile.am index 5441b01..800566d 100644 --- a/misc-utils/Makefile.am +++ b/misc-utils/Makefile.am @@ -5,14 +5,14 @@ EXTRA_DIST = README.flushb bin_PROGRAMS = usrbinexec_PROGRAMS = cal ddate logger look mcookie \ - namei script whereis + namei script scriptreplay whereis EXTRA_DIST += README.cal README.ddate README.namei README.namei2 mcookie_SOURCES = mcookie.c ../lib/md5.c -usrbinexec_SCRIPTS = chkdupexe scriptreplay +usrbinexec_SCRIPTS = chkdupexe -CLEANFILES = chkdupexe scriptreplay +CLEANFILES = chkdupexe dist_man_MANS = cal.1 chkdupexe.1 ddate.1 logger.1 look.1 mcookie.1 \ namei.1 script.1 whereis.1 scriptreplay.1 diff --git a/misc-utils/scriptreplay.1 b/misc-utils/scriptreplay.1 index 98129fb..bb923e5 100644 --- a/misc-utils/scriptreplay.1 +++ b/misc-utils/scriptreplay.1 @@ -1,5 +1,5 @@ -.\" Automatically generated by Pod::Man version 1.02 -.\" Mon Sep 3 02:16:06 2001 +.\" Automatically generated by Pod::Man version 1.02 but now +.\" maintained by hand, since the source file is not Perl any more. .\" .\" Standard preamble: .\" ====================================================================== @@ -138,19 +138,28 @@ .\" ====================================================================== .\" .IX Title "REPLAY 1" -.TH REPLAY 1 "perl v5.6.0" "2001-09-03" "User Contributed Perl Documentation" -.UC +.TH SCRIPTREPLAY 1 "2008-04-06" "Util-linux-ng 2.60" "User Commands" .SH "NAME" scriptreplay \- play back typescripts, using timing information .SH "SYNOPSIS" .IX Header "SYNOPSIS" -scriptreplay timingfile [typescript [divisor]] +scriptreplay timingfile [typescript [speedup]] .SH "DESCRIPTION" .IX Header "DESCRIPTION" This program replays a typescript, using timing information to ensure that output happens at the same speed as it originally appeared when the script -was recorded. It is only guaranteed to work properly if run on the same -terminal the script was recorded on. +was recorded. +.PP +The replay simply displays the information again; the programs +that were run when the typescript was being recorded are not run again. +Since the same information is simply being displayed, +.B scriptreplay +is only guaranteed to work properly if run on the same type of +terminal the typescript was recorded on. Otherwise, any escape characters +in the typescript may be interpreted differently by the terminal to +which +.B scriptreplay +is sending its output. .PP The timings information is what script outputs to standard error if it is run with the \-t parameter. @@ -158,8 +167,11 @@ run with the \-t parameter. By default, the typescript to display is assumed to be named \*(L"typescript\*(R", but other filenames may be specified, as the second parameter. .PP -If the third parameter exits, it is used as a time divisor. For example, -specifying a divisor of 2 makes the script be replayed twice as fast. +If the third parameter is specified, it is used as a speed-up multiplier. For +example, a speed-up of 2 makes +.B scriptreplay +go twice as fast and a speed-up of 0.1 makes it go ten times slower +than the original session. .SH "EXAMPLE" .IX Header "EXAMPLE" .Vb 7 @@ -176,10 +188,21 @@ specifying a divisor of 2 makes the script be replayed twice as fast. .BR script (1) .SH "COPYRIGHT" .IX Header "COPYRIGHT" -This program is in the public domain. +Copyright \(co 2008 James Youngman. +.PP +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. +.PP +Released under the GNU General Public License version 2 or later. .SH "AUTHOR" .IX Header "AUTHOR" -Joey Hess <joey@xxxxxxxxxxx> +The original +.B scriptreplay +program was written by Joey Hess <joey@xxxxxxxxxxx>. +The program was re-written in C by James Youngman <jay@xxxxxxx>. .SH AVAILABILITY -The scriptreplay command is part of the util-linux-ng package and is available from +The +.B scriptreplay +command is part of the util-linux-ng package and is available from ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/. diff --git a/misc-utils/scriptreplay.c b/misc-utils/scriptreplay.c new file mode 100644 index 0000000..bd3f8f7 --- /dev/null +++ b/misc-utils/scriptreplay.c @@ -0,0 +1,226 @@ +/* + scriptreplay.c - replay a typescript with timings. + + Copyright (C) 2008 James Youngman. + + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Written by James Youngman, <jay@xxxxxxx>, based on scriptreplay.pl by + Joey Hess <joey@xxxxxxxxxxx>. +*/ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <limits.h> +#include <math.h> +#include <sys/select.h> +#include <unistd.h> + +#include "nls.h" + +static double divisor = 1.0; +static const char *progname; +enum {ChunkSize = 1} ; +static char buf[ChunkSize]; + +static void +usage(void) { + printf (_("usage: %s timingfile [typescript [divisor]]\n"), progname); +} + +static void +error(int rv, int err, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (err) { + fprintf(stderr, ": %s", strerror(err)); + } + putc('\n', stderr); + if (rv) { + exit(rv); + } +} + +static FILE* +open_or_die(const char *name) +{ + FILE *f; + f = fopen(name, "r"); + if (NULL == f) { + error(1, errno, name); + } + return f; +} + +static int +getnum(const char *s, double *ret) { + double d; + char *end = (char*)s; + errno = 0; + d = strtod(s, &end); + if (0.0 == d && end == s) { + error(1, 0, _("Expected a number, but got '%s'\n"), s); + return 0; + } else if (d == HUGE_VAL || d == -HUGE_VAL) { + if (ERANGE == errno) { + perror(s); + return 0; + } + } else if (!(d==d)) { /* did they specify "nan"? */ + errno = EINVAL; + perror(s); + return 0; + } + *ret = d; + return 1; +} + +static void +eatline(FILE *f) { + int ch; + while ((ch=getc(f)) != EOF) { + if ('\n' == ch) + return; + } +} + +static void +delay_for(double delay) { + double whole, frac; + frac = modf(delay, &whole); + + if (delay > 0.0) { +#ifdef HAVE_NANOSLEEP + struct timespec ts, remainder; + ts.tv_sec = whole; + ts.tv_nsec = frac * 1.0e9; + while (-1 == nanosleep(&ts, &remainder)) { + if (EINTR == errno) { + ts = remainder; + } else { + break; + } + } +#elif HAVE_USLEEP + unsigned int sec = whole; + useconds_t us = frac * 1.0e6; + sleep(whole); + usleep(frac * 1.0e6); +#else + struct timeval tv; + tv.tv_sec = whole; + tv.tv_usec = frac * 1.0e6; + select(0, NULL, NULL, NULL, &tv); +#endif + } +} + +static void +emit(unsigned long bytecount, + FILE *in, FILE *out, + const char *input_file_name, + const char *output_file_name) { + while (bytecount) { + size_t to_read, actually_read, actually_written; + + to_read = (bytecount < ChunkSize) ? bytecount : ChunkSize; + actually_read = fread(buf, 1, to_read, in); + if (0 == actually_read) { + if (ferror(in)) { + error(1, errno, "%s", input_file_name); + } else { + error(1, 0, _("Unexpected EOF on %s"), input_file_name); + } + } else { + actually_written = fwrite(buf, 1, actually_read, out); + if (actually_written != actually_read) { + error(1, 0, _("%s: short write"), output_file_name); + } + } + bytecount -= actually_read; + } + fflush(out); +} + +int +main (int argc, char *argv[]) { + const char *typescript_file_name = "typescript"; + FILE *timings, *script; + unsigned long bytecount, line; + double delta; + char newline; + int nf; + + /* Because we use space as a separator, we can't + * afford to use any locale which tolerates a space in + * a number. In any case, script.c sets the LC_NUMERIC + * locale to C, anyway. + */ + setlocale(LC_ALL, ""); /* Ese environment settings for most locale info */ + setlocale(LC_NUMERIC, "C"); /* do not remove; see comment above */ + + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + progname = (argc>0 && argv[0]) ? argv[0] : "scriptreplay"; + if (argc < 2 || argc > 4) { + usage(); + return 1; + } + if (argc > 2) { + typescript_file_name = argv[2]; + } + if (argc > 3) { + if (!getnum(argv[3], &divisor)) + return 1; + } + timings = open_or_die(argv[1]); + script = open_or_die(typescript_file_name); + + line = 0; + eatline(script); /* Eat "Script started...." */ + while ((nf = fscanf(timings, "%lf %lu%[\n]", + &delta, &bytecount, &newline)) != -1) { + if ((nf < 3) || (newline != '\n')) { + fprintf(stderr, + _("Timings file %s line %lu is not " + "in the expected format (nf=%d, nl=%d)\n"), + argv[1], line, nf, (int)newline); + return 1; + } else { + ++line; + } + delay_for(delta / divisor); + emit(bytecount, script, stdout, + typescript_file_name, "stdout"); + } + fclose(timings); + fclose(script); +} + +/* + * Local Variables: + * mode: c + * c-style: linux + * c-basic-offset: 8 + * c-auto-newline: nil + * End: + */ diff --git a/misc-utils/scriptreplay.pl b/misc-utils/scriptreplay.pl deleted file mode 100755 index b445f6d..0000000 --- a/misc-utils/scriptreplay.pl +++ /dev/null @@ -1,83 +0,0 @@ -#!@PERL@ -w - -# "script -t" will output a typescript with timings -# this script "scriptreplay" replays it -# run pod2man on it to get a man page - -=head1 NAME - -scriptreplay - play back typescripts, using timing information - -=head1 SYNOPSIS - -scriptreplay timingfile [typescript [divisor]] - -=head1 DESCRIPTION - -This program replays a typescript, using timing information to ensure that -output happens at the same speed as it originally appeared when the script -was recorded. It is only guaranteed to work properly if run on the same -terminal the script was recorded on. - -The timings information is what script outputs to standard error if it is -run with the -t parameter. - -By default, the typescript to display is assumed to be named "typescript", -but other filenames may be specified, as the second parameter. - -If the third parameter exits, it is used as a time divisor. For example, -specifying a divisor of 2 makes the script be replayed twice as fast. - -=head1 EXAMPLE - - % script -t 2> timingfile - Script started, file is typescript - % ls - <etc, etc> - % exit - Script done, file is typescript - % scriptreplay timingfile - -=cut - -use strict; -$|=1; -open (TIMING, shift) - or die "cannot read timing info: $!"; -open (TYPESCRIPT, shift || 'typescript') - or die "cannot read typescript: $!"; -my $divisor=shift || 1; - -# Read starting timestamp line and ignore. -<TYPESCRIPT>; - -my $block; -my $oldblock=''; -while (<TIMING>) { - my ($delay, $blocksize)=split ' ', $_, 2; - # Sleep, unless the delay is really tiny. Really tiny delays cannot - # be accurately done, because the system calls in this loop will - # have more overhead. The 0.0001 is arbitrary, but works fairly well. - if ($delay / $divisor > 0.0001) { - select(undef, undef, undef, $delay / $divisor - 0.0001); - } - - read(TYPESCRIPT, $block, $blocksize) - or die "read failure on typescript: $!"; - print $oldblock; - $oldblock=$block; -} -print $oldblock; - -=head1 SEE ALSO - -script(1) - -=head1 COPYRIGHT - -This program is in the public domain. - -=head1 AUTHOR - -Joey Hess <joey@xxxxxxxxxxx> - -- 1.5.3.8 -- To unsubscribe from this list: send the line "unsubscribe util-linux-ng" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html