2 * Copyright (C) 2008-2019, Karel Zak <kzak@redhat.com>
3 * Copyright (C) 2008, James Youngman <jay@gnu.org>
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.
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.
16 * Based on scriptreplay.pl by Joey Hess <joey@kitenet.net>
27 #include <sys/select.h>
34 #include "closestream.h"
38 #include "script-playutils.h"
40 static void __attribute__((__noreturn__
))
44 fputs(USAGE_HEADER
, out
);
47 program_invocation_short_name
);
49 _(" %s [-t] timingfile [typescript] [divisor]\n"),
50 program_invocation_short_name
);
52 fputs(USAGE_SEPARATOR
, out
);
53 fputs(_("Play back terminal typescripts, using timing information.\n"), out
);
55 fputs(USAGE_OPTIONS
, out
);
56 fputs(_(" -t, --timing <file> script timing log file\n"), out
);
57 fputs(_(" -T, --log-timing <file> aliast to -t\n"), out
);
58 fputs(_(" -I, --log-in <file> script stdin log file\n"), out
);
59 fputs(_(" -O, --log-out <file> script stdout log file (default)\n"), out
);
60 fputs(_(" -B, --log-io <file> script stdin and stdout log file\n"), out
);
61 fputs(USAGE_SEPARATOR
, out
);
62 fputs(_(" -s, --typescript <file> deprecated alist to -O\n"), out
);
64 fputs(USAGE_SEPARATOR
, out
);
65 fputs(_(" --summary display overview about recorded session and exit\n"), out
);
66 fputs(_(" -d, --divisor <num> speed up or slow down execution with time divisor\n"), out
);
67 fputs(_(" -m, --maxdelay <num> wait at most this many seconds between updates\n"), out
);
68 fputs(_(" -x, --stream <name> stream type (out, in, signal or info)\n"), out
);
69 fputs(_(" -c, --cr-mode <type> CR char mode (auto, never, always)\n"), out
);
70 printf(USAGE_HELP_OPTIONS(25));
72 printf(USAGE_MAN_TAIL("scriptreplay(1)"));
79 const double d
= strtod_or_err(s
, _("failed to parse number"));
83 err(EXIT_FAILURE
, "%s: %s", _("failed to parse number"), s
);
89 delay_for(struct timeval
*delay
)
92 struct timespec ts
, remainder
;
93 ts
.tv_sec
= (time_t) delay
->tv_sec
;
94 ts
.tv_nsec
= delay
->tv_usec
* 1000;
96 DBG(TIMING
, ul_debug("going to sleep for %ld.%06ld",
100 while (-1 == nanosleep(&ts
, &remainder
)) {
107 select(0, NULL
, NULL
, NULL
, delay
);
111 static void appendchr(char *buf
, size_t bufsz
, int c
)
116 return; /* already in */
124 main(int argc
, char *argv
[])
126 static const struct timeval mindelay
= { .tv_sec
= 0, .tv_usec
= 100 };
127 struct timeval maxdelay
;
129 struct replay_setup
*setup
= NULL
;
130 struct replay_step
*step
= NULL
;
131 char streams
[6] = {0}; /* IOSI - in, out, signal,info */
132 const char *log_out
= NULL
,
137 int diviopt
= FALSE
, idx
;
138 int ch
, rc
, crmode
= REPLAY_CRMODE_AUTO
, summary
= 0;
140 OPT_SUMMARY
= CHAR_MAX
+ 1
143 static const struct option longopts
[] = {
144 { "cr-mode", required_argument
, 0, 'c' },
145 { "timing", required_argument
, 0, 't' },
146 { "log-timing", required_argument
, 0, 'T' },
147 { "log-in", required_argument
, 0, 'I' },
148 { "log-out", required_argument
, 0, 'O' },
149 { "log-io", required_argument
, 0, 'B' },
150 { "typescript", required_argument
, 0, 's' },
151 { "divisor", required_argument
, 0, 'd' },
152 { "maxdelay", required_argument
, 0, 'm' },
153 { "stream", required_argument
, 0, 'x' },
154 { "summary", no_argument
, 0, OPT_SUMMARY
},
155 { "version", no_argument
, 0, 'V' },
156 { "help", no_argument
, 0, 'h' },
159 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
163 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
164 /* Because we use space as a separator, we can't afford to use any
165 * locale which tolerates a space in a number. In any case, script.c
166 * sets the LC_NUMERIC locale to C, anyway.
168 setlocale(LC_ALL
, "");
169 setlocale(LC_NUMERIC
, "C");
171 bindtextdomain(PACKAGE
, LOCALEDIR
);
173 close_stdout_atexit();
176 timerclear(&maxdelay
);
178 while ((ch
= getopt_long(argc
, argv
, "B:c:I:O:T:t:s:d:m:x:Vh", longopts
, NULL
)) != -1) {
180 err_exclusive_options(ch
, longopts
, excl
, excl_st
);
184 if (strcmp("auto", optarg
) == 0)
185 crmode
= REPLAY_CRMODE_AUTO
;
186 else if (strcmp("never", optarg
) == 0)
187 crmode
= REPLAY_CRMODE_NEVER
;
188 else if (strcmp("always", optarg
) == 0)
189 crmode
= REPLAY_CRMODE_ALWAYS
;
191 errx(EXIT_FAILURE
, _("unsupported mode name: '%s'"), optarg
);
209 divi
= getnum(optarg
);
212 strtotimeval_or_err(optarg
, &maxdelay
, _("failed to parse maximal delay argument"));
215 if (strcmp("in", optarg
) == 0)
216 appendchr(streams
, sizeof(streams
), 'I');
217 else if (strcmp("out", optarg
) == 0)
218 appendchr(streams
, sizeof(streams
), 'O');
219 else if (strcmp("signal", optarg
) == 0)
220 appendchr(streams
, sizeof(streams
), 'S');
221 else if (strcmp("info", optarg
) == 0)
222 appendchr(streams
, sizeof(streams
), 'H');
224 errx(EXIT_FAILURE
, _("unsupported stream name: '%s'"), optarg
);
230 print_version(EXIT_SUCCESS
);
234 errtryhelp(EXIT_FAILURE
);
242 streams
[0] = 'H', streams
[1] = '\0';
244 if (!log_tm
&& idx
< argc
)
245 log_tm
= argv
[idx
++];
246 if (!log_out
&& !summary
&& !log_in
&& !log_io
)
247 log_out
= idx
< argc
? argv
[idx
++] : "typescript";
250 divi
= idx
< argc
? getnum(argv
[idx
]) : 1;
253 errx(EXIT_FAILURE
, _("timing file not specified"));
254 if (!(log_out
|| log_in
|| log_io
) && !summary
)
255 errx(EXIT_FAILURE
, _("data log file not specified"));
257 setup
= replay_new_setup();
259 if (replay_set_timing_file(setup
, log_tm
) != 0)
260 err(EXIT_FAILURE
, _("cannot open %s"), log_tm
);
262 if (log_out
&& replay_associate_log(setup
, "O", log_out
) != 0)
263 err(EXIT_FAILURE
, _("cannot open %s"), log_out
);
265 if (log_in
&& replay_associate_log(setup
, "I", log_in
) != 0)
266 err(EXIT_FAILURE
, _("cannot open %s"), log_in
);
268 if (log_io
&& replay_associate_log(setup
, "IO", log_io
) != 0)
269 err(EXIT_FAILURE
, _("cannot open %s"), log_io
);
272 /* output is prefered default */
273 if (log_out
|| log_io
)
274 appendchr(streams
, sizeof(streams
), 'O');
276 appendchr(streams
, sizeof(streams
), 'I');
279 replay_set_default_type(setup
,
280 *streams
&& streams
[1] == '\0' ? *streams
: 'O');
281 replay_set_crmode(setup
, crmode
);
284 replay_set_delay_div(setup
, divi
);
285 if (timerisset(&maxdelay
))
286 replay_set_delay_max(setup
, &maxdelay
);
287 replay_set_delay_min(setup
, &mindelay
);
290 rc
= replay_get_next_step(setup
, streams
, &step
);
295 struct timeval
*delay
= replay_step_get_delay(step
);
297 if (delay
&& timerisset(delay
))
300 rc
= replay_emit_step_data(setup
, step
, STDOUT_FILENO
);
304 err(EXIT_FAILURE
, _("%s: log file error"), replay_step_get_filename(step
));
306 err(EXIT_FAILURE
, _("%s: line %d: timing file error"),
307 replay_get_timing_file(setup
),
308 replay_get_timing_line(setup
));
310 replay_free_setup(setup
);