]> git.ipfire.org Git - thirdparty/util-linux.git/blame - term-utils/scriptreplay.c
misc: consolidate version printing and close_stdout()
[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"
f0b3b904 33#include "strutils.h"
eb76ca98 34#include "c.h"
18a706bd
KZ
35
36#define SCRIPT_MIN_DELAY 0.0001 /* from original sripreplay.pl */
37
5769a938 38static void __attribute__((__noreturn__))
86be6a32 39usage(void)
18a706bd 40{
86be6a32 41 FILE *out = stdout;
db433bf7 42 fputs(USAGE_HEADER, out);
14cc9dda
KZ
43 fprintf(out,
44 _(" %s [-t] timingfile [typescript] [divisor]\n"),
45 program_invocation_short_name);
46
451dbcfa
BS
47 fputs(USAGE_SEPARATOR, out);
48 fputs(_("Play back terminal typescripts, using timing information.\n"), out);
49
db433bf7 50 fputs(USAGE_OPTIONS, out);
14cc9dda
KZ
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"
7f1d4836 54 " -m, --maxdelay <num> wait at most this many seconds between updates\n"
b3054454 55 ), out);
f45f3ec3 56 printf(USAGE_HELP_OPTIONS(25));
84adb5e6 57
f45f3ec3 58 printf(USAGE_MAN_TAIL("scriptreplay(1)"));
86be6a32 59 exit(EXIT_SUCCESS);
18a706bd
KZ
60}
61
62static double
63getnum(const char *s)
64{
f0b3b904 65 const double d = strtod_or_err(s, _("failed to parse number"));
18a706bd 66
f0b3b904 67 if (isnan(d)) {
18a706bd 68 errno = EINVAL;
f0b3b904 69 err(EXIT_FAILURE, "%s: %s", _("failed to parse number"), s);
18a706bd
KZ
70 }
71 return d;
72}
73
74static void
75delay_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
96static void
97emit(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)
3e6e4bf6 113 err(EXIT_FAILURE, _("write to stdout failed"));
18a706bd
KZ
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
125int
126main(int argc, char *argv[])
127{
128 FILE *tfile, *sfile;
0da871b5 129 const char *sname = NULL, *tname = NULL;
7f1d4836
JDN
130 double divi = 1, maxdelay = 0;
131 int c, diviopt = FALSE, maxdelayopt = FALSE, idx;
18a706bd 132 unsigned long line;
94757ece 133 int ch;
84adb5e6
SK
134
135 static const struct option longopts[] = {
0da871b5
SK
136 { "timing", required_argument, 0, 't' },
137 { "typescript", required_argument, 0, 's' },
138 { "divisor", required_argument, 0, 'd' },
7f1d4836 139 { "maxdelay", required_argument, 0, 'm' },
84adb5e6
SK
140 { "version", no_argument, 0, 'V' },
141 { "help", no_argument, 0, 'h' },
142 { NULL, 0, 0, 0 }
143 };
18a706bd
KZ
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);
2c308875 154 close_stdout_atexit();
18a706bd 155
7f1d4836 156 while ((ch = getopt_long(argc, argv, "t:s:d:m:Vh", longopts, NULL)) != -1)
84adb5e6 157 switch(ch) {
0da871b5
SK
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;
7f1d4836
JDN
168 case 'm':
169 maxdelayopt = TRUE;
170 maxdelay = getnum(optarg);
171 break;
2c308875 172
84adb5e6 173 case 'V':
2c308875 174 print_version(EXIT_SUCCESS);
84adb5e6 175 case 'h':
86be6a32 176 usage();
84adb5e6 177 default:
677ec86c 178 errtryhelp(EXIT_FAILURE);
84adb5e6
SK
179 }
180 argc -= optind;
181 argv += optind;
0da871b5 182 idx = 0;
84adb5e6 183
0da871b5 184 if ((argc < 1 && !tname) || argc > 3) {
84adb5e6 185 warnx(_("wrong number of arguments"));
677ec86c 186 errtryhelp(EXIT_FAILURE);
84adb5e6 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;
7f1d4836
JDN
194 if (maxdelay < 0)
195 maxdelay = 0;
18a706bd
KZ
196 tfile = fopen(tname, "r");
197 if (!tfile)
289dcc90 198 err(EXIT_FAILURE, _("cannot open %s"), tname);
18a706bd
KZ
199 sfile = fopen(sname, "r");
200 if (!sfile)
289dcc90 201 err(EXIT_FAILURE, _("cannot open %s"), sname);
18a706bd
KZ
202
203 /* ignore the first typescript line */
204 while((c = fgetc(sfile)) != EOF && c != '\n');
205
ba660b99 206 for(line = 1; ; line++) {
18a706bd
KZ
207 double delay;
208 size_t blk;
209 char nl;
66fc0665 210 if (fscanf(tfile, "%lf %zu%c\n", &delay, &blk, &nl) != 3 ||
739de076 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,
ba660b99 218 _("timing file %s: line %lu: unexpected format"),
18a706bd
KZ
219 tname, line);
220 }
221 delay /= divi;
222
7f1d4836
JDN
223 if (maxdelayopt && delay > maxdelay)
224 delay = maxdelay;
225
18a706bd
KZ
226 if (delay > SCRIPT_MIN_DELAY)
227 delay_for(delay);
228
a21f7ec9 229 emit(sfile, sname, blk);
18a706bd
KZ
230 }
231
232 fclose(sfile);
233 fclose(tfile);
f004a02d 234 printf("\n");
18a706bd
KZ
235 exit(EXIT_SUCCESS);
236}