]> git.ipfire.org Git - thirdparty/util-linux.git/blame - term-utils/scriptreplay.c
term-utils: verify writing to streams was successful
[thirdparty/util-linux.git] / term-utils / scriptreplay.c
CommitLineData
18a706bd
KZ
1/*
2 * Copyright (C) 2008, Karel Zak <kzak@redhat.com>
3 * Copyright (C) 2008, James Youngman <jay@gnu.org>
4 *
5 * This file is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This file is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 *
16 * Based on scriptreplay.pl by Joey Hess <joey@kitenet.net>
17 */
18
19#include <stdio.h>
20#include <stdarg.h>
21#include <stdlib.h>
22#include <string.h>
23#include <errno.h>
24#include <time.h>
25#include <limits.h>
26#include <math.h>
27#include <sys/select.h>
28#include <unistd.h>
84adb5e6 29#include <getopt.h>
18a706bd 30
cdd2a8c3 31#include "closestream.h"
18a706bd 32#include "nls.h"
eb76ca98 33#include "c.h"
18a706bd
KZ
34
35#define SCRIPT_MIN_DELAY 0.0001 /* from original sripreplay.pl */
36
37void __attribute__((__noreturn__))
84adb5e6 38usage(FILE *out)
18a706bd 39{
14cc9dda
KZ
40 fputs(_("\nUsage:\n"), out);
41 fprintf(out,
42 _(" %s [-t] timingfile [typescript] [divisor]\n"),
43 program_invocation_short_name);
44
45 fputs(_("\nOptions:\n"), out);
46 fputs(_(" -t, --timing <file> script timing output file\n"
47 " -s, --typescript <file> script terminal session output file\n"
48 " -d, --divisor <num> speed up or slow down execution with time divisor\n"
84adb5e6 49 " -V, --version output version information and exit\n"
14cc9dda 50 " -h, --help display this help and exit\n\n"), out);
84adb5e6
SK
51
52 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
18a706bd
KZ
53}
54
55static double
56getnum(const char *s)
57{
58 double d;
59 char *end;
60
61 errno = 0;
62 d = strtod(s, &end);
63
64 if (end && *end != '\0')
65 errx(EXIT_FAILURE, _("expected a number, but got '%s'"), s);
66
67 if ((d == HUGE_VAL || d == -HUGE_VAL) && ERANGE == errno)
68 err(EXIT_FAILURE, _("divisor '%s'"), s);
69
70 if (!(d==d)) { /* did they specify "nan"? */
71 errno = EINVAL;
72 err(EXIT_FAILURE, _("divisor '%s'"), s);
73 }
74 return d;
75}
76
77static void
78delay_for(double delay)
79{
80#ifdef HAVE_NANOSLEEP
81 struct timespec ts, remainder;
82 ts.tv_sec = (time_t) delay;
83 ts.tv_nsec = (delay - ts.tv_sec) * 1.0e9;
84
85 while (-1 == nanosleep(&ts, &remainder)) {
86 if (EINTR == errno)
87 ts = remainder;
88 else
89 break;
90 }
91#else
92 struct timeval tv;
93 tv.tv_sec = (long) delay;
94 tv.tv_usec = (delay - tv.tv_sec) * 1.0e6;
95 select(0, NULL, NULL, NULL, &tv);
96#endif
97}
98
99static void
100emit(FILE *fd, const char *filename, size_t ct)
101{
102 char buf[BUFSIZ];
103
104 while(ct) {
105 size_t len, cc;
106
107 cc = ct > sizeof(buf) ? sizeof(buf) : ct;
108 len = fread(buf, 1, cc, fd);
109
110 if (!len)
111 break;
112
113 ct -= len;
114 cc = write(STDOUT_FILENO, buf, len);
115 if (cc != len)
3e6e4bf6 116 err(EXIT_FAILURE, _("write to stdout failed"));
18a706bd
KZ
117 }
118
119 if (!ct)
120 return;
121 if (feof(fd))
122 errx(EXIT_FAILURE, _("unexpected end of file on %s"), filename);
123
124 err(EXIT_FAILURE, _("failed to read typescript file %s"), filename);
125}
126
127
128int
129main(int argc, char *argv[])
130{
131 FILE *tfile, *sfile;
0da871b5
SK
132 const char *sname = NULL, *tname = NULL;
133 double divi = 1;
134 int c, diviopt = FALSE, idx;
18a706bd 135 unsigned long line;
0c723f34 136 size_t oldblk = 0;
84adb5e6
SK
137 char ch;
138
139 static const struct option longopts[] = {
0da871b5
SK
140 { "timing", required_argument, 0, 't' },
141 { "typescript", required_argument, 0, 's' },
142 { "divisor", required_argument, 0, 'd' },
84adb5e6
SK
143 { "version", no_argument, 0, 'V' },
144 { "help", no_argument, 0, 'h' },
145 { NULL, 0, 0, 0 }
146 };
18a706bd
KZ
147
148 /* Because we use space as a separator, we can't afford to use any
149 * locale which tolerates a space in a number. In any case, script.c
150 * sets the LC_NUMERIC locale to C, anyway.
151 */
152 setlocale(LC_ALL, "");
153 setlocale(LC_NUMERIC, "C");
154
155 bindtextdomain(PACKAGE, LOCALEDIR);
156 textdomain(PACKAGE);
cdd2a8c3 157 atexit(close_stdout);
18a706bd 158
0da871b5 159 while ((ch = getopt_long(argc, argv, "t:s:d:Vh", longopts, NULL)) != -1)
84adb5e6 160 switch(ch) {
0da871b5
SK
161 case 't':
162 tname = optarg;
163 break;
164 case 's':
165 sname = optarg;
166 break;
167 case 'd':
168 diviopt = TRUE;
169 divi = getnum(optarg);
170 break;
84adb5e6
SK
171 case 'V':
172 printf(_("%s from %s\n"), program_invocation_short_name,
173 PACKAGE_STRING);
174 exit(EXIT_SUCCESS);
175 case 'h':
176 usage(stdout);
177 default:
178 usage(stderr);
179 }
180 argc -= optind;
181 argv += optind;
0da871b5 182 idx = 0;
84adb5e6 183
0da871b5 184 if ((argc < 1 && !tname) || argc > 3) {
84adb5e6
SK
185 warnx(_("wrong number of arguments"));
186 usage(stderr);
187 }
0da871b5
SK
188 if (!tname)
189 tname = argv[idx++];
190 if (!sname)
191 sname = idx < argc ? argv[idx++] : "typescript";
192 if (!diviopt)
193 divi = idx < argc ? getnum(argv[idx]) : 1;
18a706bd
KZ
194
195 tfile = fopen(tname, "r");
196 if (!tfile)
197 err(EXIT_FAILURE, _("cannot open timing file %s"), tname);
198 sfile = fopen(sname, "r");
199 if (!sfile)
200 err(EXIT_FAILURE, _("cannot open typescript file %s"), sname);
201
202 /* ignore the first typescript line */
203 while((c = fgetc(sfile)) != EOF && c != '\n');
204
205 for(line = 0; ; line++) {
206 double delay;
207 size_t blk;
208 char nl;
209
739de076
SK
210 if (fscanf(tfile, "%lf %zd%c\n", &delay, &blk, &nl) != 3 ||
211 nl != '\n') {
18a706bd
KZ
212 if (feof(tfile))
213 break;
214 if (ferror(tfile))
215 err(EXIT_FAILURE,
3e6e4bf6 216 _("failed to read timing file %s"), tname);
18a706bd 217 errx(EXIT_FAILURE,
91b72e09 218 _("timings file %s: %lu: unexpected format"),
18a706bd
KZ
219 tname, line);
220 }
221 delay /= divi;
222
223 if (delay > SCRIPT_MIN_DELAY)
224 delay_for(delay);
225
0c723f34
KZ
226 if (oldblk)
227 emit(sfile, sname, oldblk);
228 oldblk = blk;
18a706bd
KZ
229 }
230
231 fclose(sfile);
232 fclose(tfile);
f004a02d 233 printf("\n");
18a706bd
KZ
234 exit(EXIT_SUCCESS);
235}