/* * 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, const char *my_program_invocation_short_name) { printf(_("%s [ []]\n"), my_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; size_t oldblk = 0; /* 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, argv[0]); 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: unexpected format"), tname, line); } delay /= divi; if (delay > SCRIPT_MIN_DELAY) delay_for(delay); if (oldblk) emit(sfile, sname, oldblk); oldblk = blk; } fclose(sfile); fclose(tfile); exit(EXIT_SUCCESS); }