]> git.ipfire.org Git - thirdparty/util-linux.git/blob - term-utils/scriptreplay.c
Merge branch 'chcpu' of git://git.kernel.org/pub/scm/linux/kernel/git/heiko/util...
[thirdparty/util-linux.git] / term-utils / scriptreplay.c
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>
29 #include <getopt.h>
30
31 #include "nls.h"
32 #include "c.h"
33
34 #define SCRIPT_MIN_DELAY 0.0001 /* from original sripreplay.pl */
35
36 void __attribute__((__noreturn__))
37 usage(FILE *out)
38 {
39 fputs(_("\nUsage:\n"), out);
40 fprintf(out,
41 _(" %s [-t] timingfile [typescript] [divisor]\n"),
42 program_invocation_short_name);
43
44 fputs(_("\nOptions:\n"), out);
45 fputs(_(" -t, --timing <file> script timing output file\n"
46 " -s, --typescript <file> script terminal session output file\n"
47 " -d, --divisor <num> speed up or slow down execution with time divisor\n"
48 " -V, --version output version information and exit\n"
49 " -h, --help display this help and exit\n\n"), out);
50
51 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
52 }
53
54 static double
55 getnum(const char *s)
56 {
57 double d;
58 char *end;
59
60 errno = 0;
61 d = strtod(s, &end);
62
63 if (end && *end != '\0')
64 errx(EXIT_FAILURE, _("expected a number, but got '%s'"), s);
65
66 if ((d == HUGE_VAL || d == -HUGE_VAL) && ERANGE == errno)
67 err(EXIT_FAILURE, _("divisor '%s'"), s);
68
69 if (!(d==d)) { /* did they specify "nan"? */
70 errno = EINVAL;
71 err(EXIT_FAILURE, _("divisor '%s'"), s);
72 }
73 return d;
74 }
75
76 static void
77 delay_for(double delay)
78 {
79 #ifdef HAVE_NANOSLEEP
80 struct timespec ts, remainder;
81 ts.tv_sec = (time_t) delay;
82 ts.tv_nsec = (delay - ts.tv_sec) * 1.0e9;
83
84 while (-1 == nanosleep(&ts, &remainder)) {
85 if (EINTR == errno)
86 ts = remainder;
87 else
88 break;
89 }
90 #else
91 struct timeval tv;
92 tv.tv_sec = (long) delay;
93 tv.tv_usec = (delay - tv.tv_sec) * 1.0e6;
94 select(0, NULL, NULL, NULL, &tv);
95 #endif
96 }
97
98 static void
99 emit(FILE *fd, const char *filename, size_t ct)
100 {
101 char buf[BUFSIZ];
102
103 while(ct) {
104 size_t len, cc;
105
106 cc = ct > sizeof(buf) ? sizeof(buf) : ct;
107 len = fread(buf, 1, cc, fd);
108
109 if (!len)
110 break;
111
112 ct -= len;
113 cc = write(STDOUT_FILENO, buf, len);
114 if (cc != len)
115 err(EXIT_FAILURE, _("write to stdout failed"));
116 }
117
118 if (!ct)
119 return;
120 if (feof(fd))
121 errx(EXIT_FAILURE, _("unexpected end of file on %s"), filename);
122
123 err(EXIT_FAILURE, _("failed to read typescript file %s"), filename);
124 }
125
126
127 int
128 main(int argc, char *argv[])
129 {
130 FILE *tfile, *sfile;
131 const char *sname = NULL, *tname = NULL;
132 double divi = 1;
133 int c, diviopt = FALSE, idx;
134 unsigned long line;
135 size_t oldblk = 0;
136 char ch;
137
138 static const struct option longopts[] = {
139 { "timing", required_argument, 0, 't' },
140 { "typescript", required_argument, 0, 's' },
141 { "divisor", required_argument, 0, 'd' },
142 { "version", no_argument, 0, 'V' },
143 { "help", no_argument, 0, 'h' },
144 { NULL, 0, 0, 0 }
145 };
146
147 /* Because we use space as a separator, we can't afford to use any
148 * locale which tolerates a space in a number. In any case, script.c
149 * sets the LC_NUMERIC locale to C, anyway.
150 */
151 setlocale(LC_ALL, "");
152 setlocale(LC_NUMERIC, "C");
153
154 bindtextdomain(PACKAGE, LOCALEDIR);
155 textdomain(PACKAGE);
156
157 while ((ch = getopt_long(argc, argv, "t:s:d:Vh", longopts, NULL)) != -1)
158 switch(ch) {
159 case 't':
160 tname = optarg;
161 break;
162 case 's':
163 sname = optarg;
164 break;
165 case 'd':
166 diviopt = TRUE;
167 divi = getnum(optarg);
168 break;
169 case 'V':
170 printf(_("%s from %s\n"), program_invocation_short_name,
171 PACKAGE_STRING);
172 exit(EXIT_SUCCESS);
173 case 'h':
174 usage(stdout);
175 default:
176 usage(stderr);
177 }
178 argc -= optind;
179 argv += optind;
180 idx = 0;
181
182 if ((argc < 1 && !tname) || argc > 3) {
183 warnx(_("wrong number of arguments"));
184 usage(stderr);
185 }
186 if (!tname)
187 tname = argv[idx++];
188 if (!sname)
189 sname = idx < argc ? argv[idx++] : "typescript";
190 if (!diviopt)
191 divi = idx < argc ? getnum(argv[idx]) : 1;
192
193 tfile = fopen(tname, "r");
194 if (!tfile)
195 err(EXIT_FAILURE, _("cannot open timing file %s"), tname);
196 sfile = fopen(sname, "r");
197 if (!sfile)
198 err(EXIT_FAILURE, _("cannot open typescript file %s"), sname);
199
200 /* ignore the first typescript line */
201 while((c = fgetc(sfile)) != EOF && c != '\n');
202
203 for(line = 0; ; line++) {
204 double delay;
205 size_t blk;
206 char nl;
207
208 if (fscanf(tfile, "%lf %zd%c\n", &delay, &blk, &nl) != 3 ||
209 nl != '\n') {
210 if (feof(tfile))
211 break;
212 if (ferror(tfile))
213 err(EXIT_FAILURE,
214 _("failed to read timing file %s"), tname);
215 errx(EXIT_FAILURE,
216 _("timings file %s: %lu: unexpected format"),
217 tname, line);
218 }
219 delay /= divi;
220
221 if (delay > SCRIPT_MIN_DELAY)
222 delay_for(delay);
223
224 if (oldblk)
225 emit(sfile, sname, oldblk);
226 oldblk = blk;
227 }
228
229 fclose(sfile);
230 fclose(tfile);
231 printf("\n");
232 exit(EXIT_SUCCESS);
233 }