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>
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(_(" -I, --log-in <file> script stdin log file\n"), out
);
58 fputs(_(" -O, --log-out <file> script stdout log file (default)\n"), out
);
59 fputs(_(" -B, --log-io <file> script stdin and stdout log file\n"), out
);
60 fputs(_(" -s, --typescript <file> deprecated alist to -O\n"), out
);
62 fputs(USAGE_SEPARATOR
, out
);
63 fputs(_(" --summary display overview about recorded session and exit\n"), out
);
64 fputs(_(" -d, --divisor <num> speed up or slow down execution with time divisor\n"), out
);
65 fputs(_(" -m, --maxdelay <num> wait at most this many seconds between updates\n"), out
);
66 fputs(_(" -x, --stream <name> stream type (out, in, signal or info)\n"), out
);
67 fputs(_(" -c, --cr-mode <type> CR char mode (auto, never, always)\n"), out
);
68 printf(USAGE_HELP_OPTIONS(25));
70 printf(USAGE_MAN_TAIL("scriptreplay(1)"));
77 const double d
= strtod_or_err(s
, _("failed to parse number"));
81 err(EXIT_FAILURE
, "%s: %s", _("failed to parse number"), s
);
87 delay_for(struct timeval
*delay
)
90 struct timespec ts
, remainder
;
91 ts
.tv_sec
= (time_t) delay
->tv_sec
;
92 ts
.tv_nsec
= delay
->tv_usec
* 1000;
94 DBG(TIMING
, ul_debug("going to sleep for %ld.%06ld",
98 while (-1 == nanosleep(&ts
, &remainder
)) {
105 select(0, NULL
, NULL
, NULL
, delay
);
109 static void appendchr(char *buf
, size_t bufsz
, int c
)
114 return; /* already in */
122 main(int argc
, char *argv
[])
124 static const struct timeval mindelay
= { .tv_sec
= 0, .tv_usec
= 100 };
125 struct timeval maxdelay
;
127 struct replay_setup
*setup
= NULL
;
128 struct replay_step
*step
= NULL
;
129 char streams
[6] = {0}; /* IOSI - in, out, signal,info */
130 const char *log_out
= NULL
,
135 int diviopt
= FALSE
, idx
;
136 int ch
, rc
, crmode
= REPLAY_CRMODE_AUTO
, summary
= 0;
138 OPT_SUMMARY
= CHAR_MAX
+ 1
141 static const struct option longopts
[] = {
142 { "cr-mode", required_argument
, 0, 'c' },
143 { "timing", required_argument
, 0, 't' },
144 { "log-in", required_argument
, 0, 'I'},
145 { "log-out", required_argument
, 0, 'O'},
146 { "log-io", required_argument
, 0, 'B'},
147 { "typescript", required_argument
, 0, 's' },
148 { "divisor", required_argument
, 0, 'd' },
149 { "maxdelay", required_argument
, 0, 'm' },
150 { "stream", required_argument
, 0, 'x' },
151 { "summary", no_argument
, 0, OPT_SUMMARY
},
152 { "version", no_argument
, 0, 'V' },
153 { "help", no_argument
, 0, 'h' },
156 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
160 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
161 /* Because we use space as a separator, we can't afford to use any
162 * locale which tolerates a space in a number. In any case, script.c
163 * sets the LC_NUMERIC locale to C, anyway.
165 setlocale(LC_ALL
, "");
166 setlocale(LC_NUMERIC
, "C");
168 bindtextdomain(PACKAGE
, LOCALEDIR
);
170 close_stdout_atexit();
173 timerclear(&maxdelay
);
175 while ((ch
= getopt_long(argc
, argv
, "B:c:I:O:t:s:d:m:x:Vh", longopts
, NULL
)) != -1) {
177 err_exclusive_options(ch
, longopts
, excl
, excl_st
);
181 if (strcmp("auto", optarg
) == 0)
182 crmode
= REPLAY_CRMODE_AUTO
;
183 else if (strcmp("never", optarg
) == 0)
184 crmode
= REPLAY_CRMODE_NEVER
;
185 else if (strcmp("always", optarg
) == 0)
186 crmode
= REPLAY_CRMODE_ALWAYS
;
188 errx(EXIT_FAILURE
, _("unsupported mode name: '%s'"), optarg
);
205 divi
= getnum(optarg
);
208 strtotimeval_or_err(optarg
, &maxdelay
, _("failed to parse maximal delay argument"));
211 if (strcmp("in", optarg
) == 0)
212 appendchr(streams
, sizeof(streams
), 'I');
213 else if (strcmp("out", optarg
) == 0)
214 appendchr(streams
, sizeof(streams
), 'O');
215 else if (strcmp("signal", optarg
) == 0)
216 appendchr(streams
, sizeof(streams
), 'S');
217 else if (strcmp("info", optarg
) == 0)
218 appendchr(streams
, sizeof(streams
), 'H');
220 errx(EXIT_FAILURE
, _("unsupported stream name: '%s'"), optarg
);
226 print_version(EXIT_SUCCESS
);
230 errtryhelp(EXIT_FAILURE
);
238 streams
[0] = 'H', streams
[1] = '\0';
240 if (!log_tm
&& idx
< argc
)
241 log_tm
= argv
[idx
++];
242 if (!log_out
&& !log_in
&& !log_io
)
243 log_out
= idx
< argc
? argv
[idx
++] : "typescript";
246 divi
= idx
< argc
? getnum(argv
[idx
]) : 1;
249 errx(EXIT_FAILURE
, _("timing file not specified"));
250 if (!(log_out
|| log_in
|| log_io
))
251 errx(EXIT_FAILURE
, _("data log file not specified"));
253 setup
= replay_new_setup();
255 if (replay_set_timing_file(setup
, log_tm
) != 0)
256 err(EXIT_FAILURE
, _("cannot open %s"), log_tm
);
258 if (log_out
&& replay_associate_log(setup
, "O", log_out
) != 0)
259 err(EXIT_FAILURE
, _("cannot open %s"), log_out
);
261 if (log_in
&& replay_associate_log(setup
, "I", log_in
) != 0)
262 err(EXIT_FAILURE
, _("cannot open %s"), log_in
);
264 if (log_io
&& replay_associate_log(setup
, "IO", log_io
) != 0)
265 err(EXIT_FAILURE
, _("cannot open %s"), log_io
);
268 /* output is prefered default */
269 if (log_out
|| log_io
)
270 appendchr(streams
, sizeof(streams
), 'O');
272 appendchr(streams
, sizeof(streams
), 'I');
275 replay_set_default_type(setup
,
276 *streams
&& streams
[1] == '\0' ? *streams
: 'O');
277 replay_set_crmode(setup
, crmode
);
280 replay_set_delay_div(setup
, divi
);
281 if (timerisset(&maxdelay
))
282 replay_set_delay_max(setup
, &maxdelay
);
283 replay_set_delay_min(setup
, &mindelay
);
286 rc
= replay_get_next_step(setup
, streams
, &step
);
291 struct timeval
*delay
= replay_step_get_delay(step
);
293 if (delay
&& timerisset(delay
))
296 rc
= replay_emit_step_data(setup
, step
, STDOUT_FILENO
);
300 err(EXIT_FAILURE
, _("%s: log file error"), replay_step_get_filename(step
));
302 err(EXIT_FAILURE
, _("%s: line %d: timing file error"),
303 replay_get_timing_file(setup
),
304 replay_get_timing_line(setup
));