From: Karel Zak Date: Wed, 2 Apr 2008 09:34:12 +0000 (+0200) Subject: scriptreplay: rewrite in C X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=18a706bdfd4ef7bef363c230baf0ec52eae6c1ac;p=util-linux scriptreplay: rewrite in C The dependence on Perl sucks... Co-Author: James Youngman Signed-off-by: Karel Zak Signed-off-by: James Youngman --- diff --git a/configure.ac b/configure.ac index 435f7683..75a112cb 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 5441b015..40610c14 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 whereis scriptreplay 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 98129fb4..b2a03a24 100644 --- a/misc-utils/scriptreplay.1 +++ b/misc-utils/scriptreplay.1 @@ -149,8 +149,18 @@ scriptreplay timingfile [typescript [divisor]] .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 +168,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 +189,23 @@ 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 +Copyright \(co 2008 Karel Zak +.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 +The original +.B scriptreplay +program was written by Joey Hess . +The program was re-written in C by James Youngman and Karel Zak . .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 00000000..2ae31a2a --- /dev/null +++ b/misc-utils/scriptreplay.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2008, Karel Zak + * Copyright (C) 2008, James Youngman + * + * This file 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 file 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. + * + * + * Based on scriptreplay.pl by Joey Hess + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nls.h" + +#define SCRIPT_MIN_DELAY 0.0001 /* from original sripreplay.pl */ + +void __attribute__((__noreturn__)) +usage(int rc) +{ + printf("%s [ []]\n", + program_invocation_short_name); + exit(rc); +} + +static double +getnum(const char *s) +{ + double d; + char *end; + + errno = 0; + d = strtod(s, &end); + + if (end && *end != '\0') + errx(EXIT_FAILURE, _("expected a number, but got '%s'"), s); + + if ((d == HUGE_VAL || d == -HUGE_VAL) && ERANGE == errno) + err(EXIT_FAILURE, _("divisor '%s'"), s); + + if (!(d==d)) { /* did they specify "nan"? */ + errno = EINVAL; + err(EXIT_FAILURE, _("divisor '%s'"), s); + } + return d; +} + +static void +delay_for(double delay) +{ +#ifdef HAVE_NANOSLEEP + struct timespec ts, remainder; + ts.tv_sec = (time_t) delay; + ts.tv_nsec = (delay - ts.tv_sec) * 1.0e9; + + while (-1 == nanosleep(&ts, &remainder)) { + if (EINTR == errno) + ts = remainder; + else + break; + } +#else + struct timeval tv; + tv.tv_sec = (long) delay; + tv.tv_usec = (delay - tv.tv_sec) * 1.0e6; + select(0, NULL, NULL, NULL, &tv); +#endif +} + +static void +emit(FILE *fd, const char *filename, size_t ct) +{ + char buf[BUFSIZ]; + + while(ct) { + size_t len, cc; + + cc = ct > sizeof(buf) ? sizeof(buf) : ct; + len = fread(buf, 1, cc, fd); + + if (!len) + break; + + ct -= len; + cc = write(STDOUT_FILENO, buf, len); + if (cc != len) + err(EXIT_FAILURE, "write to stdout failed"); + } + + if (!ct) + return; + if (feof(fd)) + errx(EXIT_FAILURE, _("unexpected end of file on %s"), filename); + + err(EXIT_FAILURE, _("failed to read typescript file %s"), filename); +} + + +int +main(int argc, char *argv[]) +{ + FILE *tfile, *sfile; + const char *sname, *tname; + double divi; + int c; + unsigned long line; + + /* 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, ""); + setlocale(LC_NUMERIC, "C"); + + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + if (argc < 2 && argc > 4) + usage(EXIT_FAILURE); + + tname = argv[1]; + sname = argc > 2 ? argv[2] : "typescript"; + divi = argc == 4 ? getnum(argv[3]) : 1; + + tfile = fopen(tname, "r"); + if (!tfile) + err(EXIT_FAILURE, _("cannot open timing file %s"), tname); + sfile = fopen(sname, "r"); + if (!sfile) + err(EXIT_FAILURE, _("cannot open typescript file %s"), sname); + + /* ignore the first typescript line */ + while((c = fgetc(sfile)) != EOF && c != '\n'); + + for(line = 0; ; line++) { + double delay; + size_t blk; + char nl; + + if ((fscanf(tfile, "%lf %zd%[\n]\n", &delay, &blk, &nl) != 3) || + (nl != '\n')) { + if (feof(tfile)) + break; + if (ferror(tfile)) + err(EXIT_FAILURE, + "failed to read timing file %s", tname); + errx(EXIT_FAILURE, + _("timings file %s: %lu: expected format"), + tname, line); + } + delay /= divi; + + if (delay > SCRIPT_MIN_DELAY) + delay_for(delay); + + emit(sfile, sname, blk); + } + + fclose(sfile); + fclose(tfile); + exit(EXIT_SUCCESS); +} diff --git a/misc-utils/scriptreplay.pl b/misc-utils/scriptreplay.pl deleted file mode 100755 index b445f6d6..00000000 --- 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 - - % 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. -; - -my $block; -my $oldblock=''; -while () { - 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 -