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