2 * Copyright (C) 2008, 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>
33 #include "closestream.h"
37 #include "script-playutils.h"
39 #define SCRIPT_MIN_DELAY 0.0001 /* from original sripreplay.pl */
41 static void __attribute__((__noreturn__
))
45 fputs(USAGE_HEADER
, out
);
48 program_invocation_short_name
);
50 _(" %s [-t] timingfile [typescript] [divisor]\n"),
51 program_invocation_short_name
);
53 fputs(USAGE_SEPARATOR
, out
);
54 fputs(_("Play back terminal typescripts, using timing information.\n"), out
);
56 fputs(USAGE_OPTIONS
, out
);
57 fputs(_(" -t, --timing <file> script timing log file\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(_(" -s, --typescript <file> deprecated alist to -O\n"), out
);
63 fputs(USAGE_SEPARATOR
, out
);
64 fputs(_(" --summary display overview about recorded session and exit\n"), out
);
65 fputs(_(" -d, --divisor <num> speed up or slow down execution with time divisor\n"), out
);
66 fputs(_(" -m, --maxdelay <num> wait at most this many seconds between updates\n"), out
);
67 fputs(_(" -x, --stream <name> stream type (out, in, signal or info)\n"), out
);
68 fputs(_(" -c, --cr-mode <type> CR char mode (auto, never, always)\n"), out
);
69 printf(USAGE_HELP_OPTIONS(25));
71 printf(USAGE_MAN_TAIL("scriptreplay(1)"));
78 const double d
= strtod_or_err(s
, _("failed to parse number"));
82 err(EXIT_FAILURE
, "%s: %s", _("failed to parse number"), s
);
88 delay_for(double delay
)
91 struct timespec ts
, remainder
;
92 ts
.tv_sec
= (time_t) delay
;
93 ts
.tv_nsec
= (delay
- ts
.tv_sec
) * 1.0e9
;
95 DBG(TIMING
, ul_debug("going to sleep for %fs", delay
));
97 while (-1 == nanosleep(&ts
, &remainder
)) {
105 tv
.tv_sec
= (long) delay
;
106 tv
.tv_usec
= (delay
- tv
.tv_sec
) * 1.0e6
;
107 select(0, NULL
, NULL
, NULL
, &tv
);
111 static void appendchr(char *buf
, size_t bufsz
, int c
)
116 return; /* already in */
124 main(int argc
, char *argv
[])
126 struct replay_setup
*setup
= NULL
;
127 struct replay_step
*step
= NULL
;
128 char streams
[6] = {0}; /* IOSI - in, out, signal,info */
129 const char *log_out
= NULL
,
133 double divi
= 1, maxdelay
= 0;
134 int diviopt
= FALSE
, maxdelayopt
= FALSE
, idx
;
135 int ch
, rc
, crmode
= REPLAY_CRMODE_AUTO
, summary
= 0;
137 OPT_SUMMARY
= CHAR_MAX
+ 1
140 static const struct option longopts
[] = {
141 { "cr-mode", required_argument
, 0, 'c' },
142 { "timing", required_argument
, 0, 't' },
143 { "log-in", required_argument
, 0, 'I'},
144 { "log-out", required_argument
, 0, 'O'},
145 { "log-io", required_argument
, 0, 'B'},
146 { "typescript", required_argument
, 0, 's' },
147 { "divisor", required_argument
, 0, 'd' },
148 { "maxdelay", required_argument
, 0, 'm' },
149 { "stream", required_argument
, 0, 'x' },
150 { "summary", no_argument
, 0, OPT_SUMMARY
},
151 { "version", no_argument
, 0, 'V' },
152 { "help", no_argument
, 0, 'h' },
155 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
159 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
160 /* Because we use space as a separator, we can't afford to use any
161 * locale which tolerates a space in a number. In any case, script.c
162 * sets the LC_NUMERIC locale to C, anyway.
164 setlocale(LC_ALL
, "");
165 setlocale(LC_NUMERIC
, "C");
167 bindtextdomain(PACKAGE
, LOCALEDIR
);
169 close_stdout_atexit();
173 while ((ch
= getopt_long(argc
, argv
, "B:c:I:O:t:s:d:m:x:Vh", longopts
, NULL
)) != -1) {
175 err_exclusive_options(ch
, longopts
, excl
, excl_st
);
179 if (strcmp("auto", optarg
) == 0)
180 crmode
= REPLAY_CRMODE_AUTO
;
181 else if (strcmp("never", optarg
) == 0)
182 crmode
= REPLAY_CRMODE_NEVER
;
183 else if (strcmp("always", optarg
) == 0)
184 crmode
= REPLAY_CRMODE_ALWAYS
;
186 errx(EXIT_FAILURE
, _("unsupported mode name: '%s'"), optarg
);
203 divi
= getnum(optarg
);
207 maxdelay
= getnum(optarg
);
210 if (strcmp("in", optarg
) == 0)
211 appendchr(streams
, sizeof(streams
), 'I');
212 else if (strcmp("out", optarg
) == 0)
213 appendchr(streams
, sizeof(streams
), 'O');
214 else if (strcmp("signal", optarg
) == 0)
215 appendchr(streams
, sizeof(streams
), 'S');
216 else if (strcmp("info", optarg
) == 0)
217 appendchr(streams
, sizeof(streams
), 'H');
219 errx(EXIT_FAILURE
, _("unsupported stream name: '%s'"), optarg
);
225 print_version(EXIT_SUCCESS
);
229 errtryhelp(EXIT_FAILURE
);
237 streams
[0] = 'H', streams
[1] = '\0';
239 if (!log_tm
&& idx
< argc
)
240 log_tm
= argv
[idx
++];
241 if (!log_out
&& !log_in
&& !log_io
)
242 log_out
= idx
< argc
? argv
[idx
++] : "typescript";
245 divi
= idx
< argc
? getnum(argv
[idx
]) : 1;
250 errx(EXIT_FAILURE
, _("timing file not specified"));
251 if (!(log_out
|| log_in
|| log_io
))
252 errx(EXIT_FAILURE
, _("data log file not specified"));
254 setup
= replay_new_setup();
256 if (replay_set_timing_file(setup
, log_tm
) != 0)
257 err(EXIT_FAILURE
, _("cannot open %s"), log_tm
);
259 if (log_out
&& replay_associate_log(setup
, "O", log_out
) != 0)
260 err(EXIT_FAILURE
, _("cannot open %s"), log_out
);
262 if (log_in
&& replay_associate_log(setup
, "I", log_in
) != 0)
263 err(EXIT_FAILURE
, _("cannot open %s"), log_in
);
265 if (log_io
&& replay_associate_log(setup
, "IO", log_io
) != 0)
266 err(EXIT_FAILURE
, _("cannot open %s"), log_io
);
269 /* output is prefered default */
270 if (log_out
|| log_io
)
271 appendchr(streams
, sizeof(streams
), 'O');
273 appendchr(streams
, sizeof(streams
), 'I');
276 replay_set_default_type(setup
,
277 *streams
&& streams
[1] == '\0' ? *streams
: 'O');
278 replay_set_crmode(setup
, crmode
);
281 rc
= replay_get_next_step(setup
, streams
, &step
);
286 double delay
= replay_step_get_delay(step
);
289 if (maxdelayopt
&& delay
> maxdelay
)
291 if (delay
> SCRIPT_MIN_DELAY
)
295 rc
= replay_emit_step_data(setup
, step
, STDOUT_FILENO
);
299 err(EXIT_FAILURE
, _("%s: log file error"), replay_step_get_filename(step
));
301 err(EXIT_FAILURE
, _("%s: line %d: timing file error"),
302 replay_get_timing_file(setup
),
303 replay_get_timing_line(setup
));