| @@ -1,438 +1,438 @@ | | | @@ -1,438 +1,438 @@ |
1 | /* $NetBSD: script.c,v 1.19 2009/10/17 22:36:23 christos Exp $ */ | | 1 | /* $NetBSD: script.c,v 1.20 2011/06/08 13:51:13 yamt Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1980, 1992, 1993 | | 4 | * Copyright (c) 1980, 1992, 1993 |
5 | * The Regents of the University of California. All rights reserved. | | 5 | * The Regents of the University of California. All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. Neither the name of the University nor the names of its contributors | | 15 | * 3. Neither the name of the University nor the names of its contributors |
16 | * may be used to endorse or promote products derived from this software | | 16 | * may be used to endorse or promote products derived from this software |
17 | * without specific prior written permission. | | 17 | * without specific prior written permission. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | * SUCH DAMAGE. | | 29 | * SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | #include <sys/cdefs.h> | | 32 | #include <sys/cdefs.h> |
33 | #ifndef lint | | 33 | #ifndef lint |
34 | __COPYRIGHT("@(#) Copyright (c) 1980, 1992, 1993\ | | 34 | __COPYRIGHT("@(#) Copyright (c) 1980, 1992, 1993\ |
35 | The Regents of the University of California. All rights reserved."); | | 35 | The Regents of the University of California. All rights reserved."); |
36 | #endif /* not lint */ | | 36 | #endif /* not lint */ |
37 | | | 37 | |
38 | #ifndef lint | | 38 | #ifndef lint |
39 | #if 0 | | 39 | #if 0 |
40 | static char sccsid[] = "@(#)script.c 8.1 (Berkeley) 6/6/93"; | | 40 | static char sccsid[] = "@(#)script.c 8.1 (Berkeley) 6/6/93"; |
41 | #endif | | 41 | #endif |
42 | __RCSID("$NetBSD: script.c,v 1.19 2009/10/17 22:36:23 christos Exp $"); | | 42 | __RCSID("$NetBSD: script.c,v 1.20 2011/06/08 13:51:13 yamt Exp $"); |
43 | #endif /* not lint */ | | 43 | #endif /* not lint */ |
44 | | | 44 | |
45 | #include <sys/types.h> | | 45 | #include <sys/types.h> |
46 | #include <sys/wait.h> | | 46 | #include <sys/wait.h> |
47 | #include <sys/stat.h> | | 47 | #include <sys/stat.h> |
48 | #include <sys/ioctl.h> | | 48 | #include <sys/ioctl.h> |
49 | #include <sys/time.h> | | 49 | #include <sys/time.h> |
50 | #include <sys/param.h> | | 50 | #include <sys/param.h> |
51 | #include <sys/uio.h> | | 51 | #include <sys/uio.h> |
52 | | | 52 | |
53 | #include <err.h> | | 53 | #include <err.h> |
54 | #include <errno.h> | | 54 | #include <errno.h> |
55 | #include <fcntl.h> | | 55 | #include <fcntl.h> |
56 | #include <paths.h> | | 56 | #include <paths.h> |
57 | #include <signal.h> | | 57 | #include <signal.h> |
58 | #include <stdio.h> | | 58 | #include <stdio.h> |
59 | #include <stdlib.h> | | 59 | #include <stdlib.h> |
60 | #include <string.h> | | 60 | #include <string.h> |
61 | #include <termios.h> | | 61 | #include <termios.h> |
62 | #include <time.h> | | 62 | #include <time.h> |
63 | #include <tzfile.h> | | 63 | #include <tzfile.h> |
64 | #include <unistd.h> | | 64 | #include <unistd.h> |
65 | #include <util.h> | | 65 | #include <util.h> |
66 | | | 66 | |
67 | #define DEF_BUF 65536 | | 67 | #define DEF_BUF 65536 |
68 | | | 68 | |
69 | struct stamp { | | 69 | struct stamp { |
70 | uint64_t scr_len; /* amount of data */ | | 70 | uint64_t scr_len; /* amount of data */ |
71 | uint64_t scr_sec; /* time it arrived in seconds... */ | | 71 | uint64_t scr_sec; /* time it arrived in seconds... */ |
72 | uint32_t scr_usec; /* ...and microseconds */ | | 72 | uint32_t scr_usec; /* ...and microseconds */ |
73 | uint32_t scr_direction; /* 'i', 'o', etc (also indicates endianness) */ | | 73 | uint32_t scr_direction; /* 'i', 'o', etc (also indicates endianness) */ |
74 | }; | | 74 | }; |
75 | | | 75 | |
76 | FILE *fscript; | | 76 | FILE *fscript; |
77 | int master, slave; | | 77 | int master, slave; |
78 | int child, subchild; | | 78 | int child, subchild; |
79 | int outcc; | | 79 | int outcc; |
80 | int usesleep, rawout; | | 80 | int usesleep, rawout; |
81 | int quiet, flush; | | 81 | int quiet, flush; |
82 | const char *fname; | | 82 | const char *fname; |
83 | | | 83 | |
84 | struct termios tt; | | 84 | struct termios tt; |
85 | | | 85 | |
86 | void done(void); | | 86 | void done(void); |
87 | void dooutput(void); | | 87 | void dooutput(void); |
88 | void doshell(const char *); | | 88 | void doshell(const char *); |
89 | void fail(void); | | 89 | void fail(void); |
90 | void finish(int); | | 90 | void finish(int); |
91 | int main(int, char **); | | 91 | int main(int, char **); |
92 | void scriptflush(int); | | 92 | void scriptflush(int); |
93 | void record(FILE *, char *, size_t, int); | | 93 | void record(FILE *, char *, size_t, int); |
94 | void consume(FILE *, off_t, char *, int); | | 94 | void consume(FILE *, off_t, char *, int); |
95 | void playback(FILE *); | | 95 | void playback(FILE *); |
96 | | | 96 | |
97 | int | | 97 | int |
98 | main(int argc, char *argv[]) | | 98 | main(int argc, char *argv[]) |
99 | { | | 99 | { |
100 | int cc; | | 100 | int cc; |
101 | struct termios rtt; | | 101 | struct termios rtt; |
102 | struct winsize win; | | 102 | struct winsize win; |
103 | int aflg, pflg, ch; | | 103 | int aflg, pflg, ch; |
104 | char ibuf[BUFSIZ]; | | 104 | char ibuf[BUFSIZ]; |
105 | const char *command; | | 105 | const char *command; |
106 | | | 106 | |
107 | aflg = 0; | | 107 | aflg = 0; |
108 | pflg = 0; | | 108 | pflg = 0; |
109 | usesleep = 1; | | 109 | usesleep = 1; |
110 | rawout = 0; | | 110 | rawout = 0; |
111 | quiet = 0; | | 111 | quiet = 0; |
112 | flush = 0; | | 112 | flush = 0; |
113 | command = NULL; | | 113 | command = NULL; |
114 | while ((ch = getopt(argc, argv, "ac:dfpqr")) != -1) | | 114 | while ((ch = getopt(argc, argv, "ac:dfpqr")) != -1) |
115 | switch(ch) { | | 115 | switch(ch) { |
116 | case 'a': | | 116 | case 'a': |
117 | aflg = 1; | | 117 | aflg = 1; |
118 | break; | | 118 | break; |
119 | case 'c': | | 119 | case 'c': |
120 | command = optarg; | | 120 | command = optarg; |
121 | break; | | 121 | break; |
122 | case 'd': | | 122 | case 'd': |
123 | usesleep = 0; | | 123 | usesleep = 0; |
124 | break; | | 124 | break; |
125 | case 'f': | | 125 | case 'f': |
126 | flush = 1; | | 126 | flush = 1; |
127 | break; | | 127 | break; |
128 | case 'p': | | 128 | case 'p': |
129 | pflg = 1; | | 129 | pflg = 1; |
130 | break; | | 130 | break; |
131 | case 'q': | | 131 | case 'q': |
132 | quiet = 1; | | 132 | quiet = 1; |
133 | break; | | 133 | break; |
134 | case 'r': | | 134 | case 'r': |
135 | rawout = 1; | | 135 | rawout = 1; |
136 | break; | | 136 | break; |
137 | case '?': | | 137 | case '?': |
138 | default: | | 138 | default: |
139 | (void)fprintf(stderr, | | 139 | (void)fprintf(stderr, |
140 | "Usage: %s [-c <command>][-adfpqr] [file]\n", | | 140 | "Usage: %s [-c <command>][-adfpqr] [file]\n", |
141 | getprogname()); | | 141 | getprogname()); |
142 | exit(1); | | 142 | exit(1); |
143 | } | | 143 | } |
144 | argc -= optind; | | 144 | argc -= optind; |
145 | argv += optind; | | 145 | argv += optind; |
146 | | | 146 | |
147 | if (argc > 0) | | 147 | if (argc > 0) |
148 | fname = argv[0]; | | 148 | fname = argv[0]; |
149 | else | | 149 | else |
150 | fname = "typescript"; | | 150 | fname = "typescript"; |
151 | | | 151 | |
152 | if ((fscript = fopen(fname, pflg ? "r" : aflg ? "a" : "w")) == NULL) | | 152 | if ((fscript = fopen(fname, pflg ? "r" : aflg ? "a" : "w")) == NULL) |
153 | err(1, "fopen %s", fname); | | 153 | err(1, "fopen %s", fname); |
154 | | | 154 | |
155 | if (pflg) | | 155 | if (pflg) |
156 | playback(fscript); | | 156 | playback(fscript); |
157 | | | 157 | |
158 | (void)tcgetattr(STDIN_FILENO, &tt); | | 158 | (void)tcgetattr(STDIN_FILENO, &tt); |
159 | (void)ioctl(STDIN_FILENO, TIOCGWINSZ, &win); | | 159 | (void)ioctl(STDIN_FILENO, TIOCGWINSZ, &win); |
160 | if (openpty(&master, &slave, NULL, &tt, &win) == -1) | | 160 | if (openpty(&master, &slave, NULL, &tt, &win) == -1) |
161 | err(1, "openpty"); | | 161 | err(1, "openpty"); |
162 | | | 162 | |
163 | if (!quiet) | | 163 | if (!quiet) |
164 | (void)printf("Script started, output file is %s\n", fname); | | 164 | (void)printf("Script started, output file is %s\n", fname); |
165 | rtt = tt; | | 165 | rtt = tt; |
166 | cfmakeraw(&rtt); | | 166 | cfmakeraw(&rtt); |
167 | rtt.c_lflag &= ~ECHO; | | 167 | rtt.c_lflag &= ~ECHO; |
168 | (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt); | | 168 | (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt); |
169 | | | 169 | |
170 | (void)signal(SIGCHLD, finish); | | 170 | (void)signal(SIGCHLD, finish); |
171 | child = fork(); | | 171 | child = fork(); |
172 | if (child < 0) { | | 172 | if (child < 0) { |
173 | warn("fork"); | | 173 | warn("fork"); |
174 | fail(); | | 174 | fail(); |
175 | } | | 175 | } |
176 | if (child == 0) { | | 176 | if (child == 0) { |
177 | subchild = child = fork(); | | 177 | subchild = child = fork(); |
178 | if (child < 0) { | | 178 | if (child < 0) { |
179 | warn("fork"); | | 179 | warn("fork"); |
180 | fail(); | | 180 | fail(); |
181 | } | | 181 | } |
182 | if (child) | | 182 | if (child) |
183 | dooutput(); | | 183 | dooutput(); |
184 | else | | 184 | else |
185 | doshell(command); | | 185 | doshell(command); |
186 | } | | 186 | } |
187 | | | 187 | |
188 | if (!rawout) | | 188 | if (!rawout) |
189 | (void)fclose(fscript); | | 189 | (void)fclose(fscript); |
190 | while ((cc = read(STDIN_FILENO, ibuf, BUFSIZ)) > 0) { | | 190 | while ((cc = read(STDIN_FILENO, ibuf, BUFSIZ)) > 0) { |
191 | if (rawout) | | 191 | if (rawout) |
192 | record(fscript, ibuf, cc, 'i'); | | 192 | record(fscript, ibuf, cc, 'i'); |
193 | (void)write(master, ibuf, cc); | | 193 | (void)write(master, ibuf, cc); |
194 | } | | 194 | } |
195 | done(); | | 195 | done(); |
196 | /* NOTREACHED */ | | 196 | /* NOTREACHED */ |
197 | return (0); | | 197 | return (0); |
198 | } | | 198 | } |
199 | | | 199 | |
200 | void | | 200 | void |
201 | finish(int signo) | | 201 | finish(int signo) |
202 | { | | 202 | { |
203 | int die, pid, status; | | 203 | int die, pid, status; |
204 | | | 204 | |
205 | die = 0; | | 205 | die = 0; |
206 | while ((pid = wait3(&status, WNOHANG, 0)) > 0) | | 206 | while ((pid = wait3(&status, WNOHANG, 0)) > 0) |
207 | if (pid == child) | | 207 | if (pid == child) |
208 | die = 1; | | 208 | die = 1; |
209 | | | 209 | |
210 | if (die) | | 210 | if (die) |
211 | done(); | | 211 | done(); |
212 | } | | 212 | } |
213 | | | 213 | |
214 | void | | 214 | void |
215 | dooutput(void) | | 215 | dooutput(void) |
216 | { | | 216 | { |
217 | struct itimerval value; | | 217 | struct itimerval value; |
218 | int cc; | | 218 | int cc; |
219 | time_t tvec; | | 219 | time_t tvec; |
220 | char obuf[BUFSIZ]; | | 220 | char obuf[BUFSIZ]; |
221 | | | 221 | |
222 | (void)close(STDIN_FILENO); | | 222 | (void)close(STDIN_FILENO); |
223 | tvec = time(NULL); | | 223 | tvec = time(NULL); |
224 | if (rawout) | | 224 | if (rawout) |
225 | record(fscript, NULL, 0, 's'); | | 225 | record(fscript, NULL, 0, 's'); |
226 | else if (!quiet) | | 226 | else if (!quiet) |
227 | (void)fprintf(fscript, "Script started on %s", ctime(&tvec)); | | 227 | (void)fprintf(fscript, "Script started on %s", ctime(&tvec)); |
228 | | | 228 | |
229 | (void)signal(SIGALRM, scriptflush); | | 229 | (void)signal(SIGALRM, scriptflush); |
230 | value.it_interval.tv_sec = SECSPERMIN / 2; | | 230 | value.it_interval.tv_sec = SECSPERMIN / 2; |
231 | value.it_interval.tv_usec = 0; | | 231 | value.it_interval.tv_usec = 0; |
232 | value.it_value = value.it_interval; | | 232 | value.it_value = value.it_interval; |
233 | (void)setitimer(ITIMER_REAL, &value, NULL); | | 233 | (void)setitimer(ITIMER_REAL, &value, NULL); |
234 | for (;;) { | | 234 | for (;;) { |
235 | cc = read(master, obuf, sizeof (obuf)); | | 235 | cc = read(master, obuf, sizeof (obuf)); |
236 | if (cc <= 0) | | 236 | if (cc <= 0) |
237 | break; | | 237 | break; |
238 | (void)write(1, obuf, cc); | | 238 | (void)write(1, obuf, cc); |
239 | if (rawout) | | 239 | if (rawout) |
240 | record(fscript, obuf, cc, 'o'); | | 240 | record(fscript, obuf, cc, 'o'); |
241 | else | | 241 | else |
242 | (void)fwrite(obuf, 1, cc, fscript); | | 242 | (void)fwrite(obuf, 1, cc, fscript); |
243 | outcc += cc; | | 243 | outcc += cc; |
244 | if (flush) | | 244 | if (flush) |
245 | (void)fflush(fscript); | | 245 | (void)fflush(fscript); |
246 | } | | 246 | } |
247 | done(); | | 247 | done(); |
248 | } | | 248 | } |
249 | | | 249 | |
250 | void | | 250 | void |
251 | scriptflush(int signo) | | 251 | scriptflush(int signo) |
252 | { | | 252 | { |
253 | if (outcc) { | | 253 | if (outcc) { |
254 | (void)fflush(fscript); | | 254 | (void)fflush(fscript); |
255 | outcc = 0; | | 255 | outcc = 0; |
256 | } | | 256 | } |
257 | } | | 257 | } |
258 | | | 258 | |
259 | void | | 259 | void |
260 | doshell(const char *command) | | 260 | doshell(const char *command) |
261 | { | | 261 | { |
262 | const char *shell; | | 262 | const char *shell; |
263 | | | 263 | |
264 | (void)close(master); | | 264 | (void)close(master); |
265 | (void)fclose(fscript); | | 265 | (void)fclose(fscript); |
266 | login_tty(slave); | | 266 | login_tty(slave); |
267 | if (command == NULL) { | | 267 | if (command == NULL) { |
268 | shell = getenv("SHELL"); | | 268 | shell = getenv("SHELL"); |
269 | if (shell == NULL) | | 269 | if (shell == NULL) |
270 | shell = _PATH_BSHELL; | | 270 | shell = _PATH_BSHELL; |
271 | execl(shell, shell, "-i", NULL); | | 271 | execl(shell, shell, "-i", NULL); |
272 | warn("execl `%s'", shell); | | 272 | warn("execl `%s'", shell); |
273 | } else { | | 273 | } else { |
274 | if (system(command) == -1) | | 274 | if (system(command) == -1) |
275 | warn("system `%s'", command); | | 275 | warn("system `%s'", command); |
276 | } | | 276 | } |
277 | | | 277 | |
278 | fail(); | | 278 | fail(); |
279 | } | | 279 | } |
280 | | | 280 | |
281 | void | | 281 | void |
282 | fail() | | 282 | fail() |
283 | { | | 283 | { |
284 | | | 284 | |
285 | (void)kill(0, SIGTERM); | | 285 | (void)kill(0, SIGTERM); |
286 | done(); | | 286 | done(); |
287 | } | | 287 | } |
288 | | | 288 | |
289 | void | | 289 | void |
290 | done() | | 290 | done() |
291 | { | | 291 | { |
292 | time_t tvec; | | 292 | time_t tvec; |
293 | | | 293 | |
294 | if (subchild) { | | 294 | if (subchild) { |
295 | tvec = time(NULL); | | 295 | tvec = time(NULL); |
296 | if (rawout) | | 296 | if (rawout) |
297 | record(fscript, NULL, 0, 'e'); | | 297 | record(fscript, NULL, 0, 'e'); |
298 | else if (!quiet) | | 298 | else if (!quiet) |
299 | (void)fprintf(fscript,"\nScript done on %s", | | 299 | (void)fprintf(fscript,"\nScript done on %s", |
300 | ctime(&tvec)); | | 300 | ctime(&tvec)); |
301 | (void)fclose(fscript); | | 301 | (void)fclose(fscript); |
302 | (void)close(master); | | 302 | (void)close(master); |
303 | } else { | | 303 | } else { |
304 | (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt); | | 304 | (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt); |
305 | if (!quiet) | | 305 | if (!quiet) |
306 | (void)printf("Script done, output file is %s\n", fname); | | 306 | (void)printf("Script done, output file is %s\n", fname); |
307 | } | | 307 | } |
308 | exit(0); | | 308 | exit(0); |
309 | } | | 309 | } |
310 | | | 310 | |
311 | void | | 311 | void |
312 | record(FILE *fp, char *buf, size_t cc, int direction) | | 312 | record(FILE *fp, char *buf, size_t cc, int direction) |
313 | { | | 313 | { |
314 | struct iovec iov[2]; | | 314 | struct iovec iov[2]; |
315 | struct stamp stamp; | | 315 | struct stamp stamp; |
316 | struct timeval tv; | | 316 | struct timeval tv; |
317 | | | 317 | |
318 | (void)gettimeofday(&tv, NULL); | | 318 | (void)gettimeofday(&tv, NULL); |
319 | stamp.scr_len = cc; | | 319 | stamp.scr_len = cc; |
320 | stamp.scr_sec = tv.tv_sec; | | 320 | stamp.scr_sec = tv.tv_sec; |
321 | stamp.scr_usec = tv.tv_usec; | | 321 | stamp.scr_usec = tv.tv_usec; |
322 | stamp.scr_direction = direction; | | 322 | stamp.scr_direction = direction; |
323 | iov[0].iov_len = sizeof(stamp); | | 323 | iov[0].iov_len = sizeof(stamp); |
324 | iov[0].iov_base = &stamp; | | 324 | iov[0].iov_base = &stamp; |
325 | iov[1].iov_len = cc; | | 325 | iov[1].iov_len = cc; |
326 | iov[1].iov_base = buf; | | 326 | iov[1].iov_base = buf; |
327 | if (writev(fileno(fp), &iov[0], 2) == -1) | | 327 | if (writev(fileno(fp), &iov[0], 2) == -1) |
328 | err(1, "writev"); | | 328 | err(1, "writev"); |
329 | } | | 329 | } |
330 | | | 330 | |
331 | void | | 331 | void |
332 | consume(FILE *fp, off_t len, char *buf, int reg) | | 332 | consume(FILE *fp, off_t len, char *buf, int reg) |
333 | { | | 333 | { |
334 | size_t l; | | 334 | size_t l; |
335 | | | 335 | |
336 | if (reg) { | | 336 | if (reg) { |
337 | if (fseeko(fp, len, SEEK_CUR) == -1) | | 337 | if (fseeko(fp, len, SEEK_CUR) == -1) |
338 | err(1, NULL); | | 338 | err(1, NULL); |
339 | } | | 339 | } |
340 | else { | | 340 | else { |
341 | while (len > 0) { | | 341 | while (len > 0) { |
342 | l = MIN(DEF_BUF, len); | | 342 | l = MIN(DEF_BUF, len); |
343 | if (fread(buf, sizeof(char), l, fp) != l) | | 343 | if (fread(buf, sizeof(char), l, fp) != l) |
344 | err(1, "cannot read buffer"); | | 344 | err(1, "cannot read buffer"); |
345 | len -= l; | | 345 | len -= l; |
346 | } | | 346 | } |
347 | } | | 347 | } |
348 | } | | 348 | } |
349 | | | 349 | |
350 | #define swapstamp(stamp) do { \ | | 350 | #define swapstamp(stamp) do { \ |
351 | if (stamp.scr_direction > 0xff) { \ | | 351 | if (stamp.scr_direction > 0xff) { \ |
352 | stamp.scr_len = bswap64(stamp.scr_len); \ | | 352 | stamp.scr_len = bswap64(stamp.scr_len); \ |
353 | stamp.scr_sec = bswap64(stamp.scr_sec); \ | | 353 | stamp.scr_sec = bswap64(stamp.scr_sec); \ |
354 | stamp.scr_usec = bswap32(stamp.scr_usec); \ | | 354 | stamp.scr_usec = bswap32(stamp.scr_usec); \ |
355 | stamp.scr_direction = bswap32(stamp.scr_direction); \ | | 355 | stamp.scr_direction = bswap32(stamp.scr_direction); \ |
356 | } \ | | 356 | } \ |
357 | } while (0/*CONSTCOND*/) | | 357 | } while (0/*CONSTCOND*/) |
358 | | | 358 | |
359 | void | | 359 | void |
360 | playback(FILE *fp) | | 360 | playback(FILE *fp) |
361 | { | | 361 | { |
362 | struct timespec tsi, tso; | | 362 | struct timespec tsi, tso; |
363 | struct stamp stamp; | | 363 | struct stamp stamp; |
364 | struct stat pst; | | 364 | struct stat pst; |
365 | char buf[DEF_BUF]; | | 365 | char buf[DEF_BUF]; |
366 | off_t nread, save_len; | | 366 | off_t nread, save_len; |
367 | size_t l; | | 367 | size_t l; |
368 | time_t tclock; | | 368 | time_t tclock; |
369 | int reg; | | 369 | int reg; |
370 | | | 370 | |
371 | if (fstat(fileno(fp), &pst) == -1) | | 371 | if (fstat(fileno(fp), &pst) == -1) |
372 | err(1, "fstat failed"); | | 372 | err(1, "fstat failed"); |
373 | | | 373 | |
374 | reg = S_ISREG(pst.st_mode); | | 374 | reg = S_ISREG(pst.st_mode); |
375 | | | 375 | |
376 | for (nread = 0; !reg || nread < pst.st_size; nread += save_len) { | | 376 | for (nread = 0; !reg || nread < pst.st_size; nread += save_len) { |
377 | if (fread(&stamp, sizeof(stamp), 1, fp) != 1) { | | 377 | if (fread(&stamp, sizeof(stamp), 1, fp) != 1) { |
378 | if (reg) | | 378 | if (reg) |
379 | err(1, "reading playback header"); | | 379 | err(1, "reading playback header"); |
380 | else | | 380 | else |
381 | break; | | 381 | break; |
382 | } | | 382 | } |
383 | swapstamp(stamp); | | 383 | swapstamp(stamp); |
384 | save_len = sizeof(stamp); | | 384 | save_len = sizeof(stamp); |
385 | | | 385 | |
386 | if (reg && stamp.scr_len > | | 386 | if (reg && stamp.scr_len > |
387 | (uint64_t)(pst.st_size - save_len) - nread) | | 387 | (uint64_t)(pst.st_size - save_len) - nread) |
388 | err(1, "invalid stamp"); | | 388 | errx(1, "invalid stamp"); |
389 | | | 389 | |
390 | save_len += stamp.scr_len; | | 390 | save_len += stamp.scr_len; |
391 | tclock = stamp.scr_sec; | | 391 | tclock = stamp.scr_sec; |
392 | tso.tv_sec = stamp.scr_sec; | | 392 | tso.tv_sec = stamp.scr_sec; |
393 | tso.tv_nsec = stamp.scr_usec * 1000; | | 393 | tso.tv_nsec = stamp.scr_usec * 1000; |
394 | | | 394 | |
395 | switch (stamp.scr_direction) { | | 395 | switch (stamp.scr_direction) { |
396 | case 's': | | 396 | case 's': |
397 | if (!quiet) | | 397 | if (!quiet) |
398 | (void)printf("Script started on %s", | | 398 | (void)printf("Script started on %s", |
399 | ctime(&tclock)); | | 399 | ctime(&tclock)); |
400 | tsi = tso; | | 400 | tsi = tso; |
401 | (void)consume(fp, stamp.scr_len, buf, reg); | | 401 | (void)consume(fp, stamp.scr_len, buf, reg); |
402 | break; | | 402 | break; |
403 | case 'e': | | 403 | case 'e': |
404 | if (!quiet) | | 404 | if (!quiet) |
405 | (void)printf("\nScript done on %s", | | 405 | (void)printf("\nScript done on %s", |
406 | ctime(&tclock)); | | 406 | ctime(&tclock)); |
407 | (void)consume(fp, stamp.scr_len, buf, reg); | | 407 | (void)consume(fp, stamp.scr_len, buf, reg); |
408 | break; | | 408 | break; |
409 | case 'i': | | 409 | case 'i': |
410 | /* throw input away */ | | 410 | /* throw input away */ |
411 | (void)consume(fp, stamp.scr_len, buf, reg); | | 411 | (void)consume(fp, stamp.scr_len, buf, reg); |
412 | break; | | 412 | break; |
413 | case 'o': | | 413 | case 'o': |
414 | tsi.tv_sec = tso.tv_sec - tsi.tv_sec; | | 414 | tsi.tv_sec = tso.tv_sec - tsi.tv_sec; |
415 | tsi.tv_nsec = tso.tv_nsec - tsi.tv_nsec; | | 415 | tsi.tv_nsec = tso.tv_nsec - tsi.tv_nsec; |
416 | if (tsi.tv_nsec < 0) { | | 416 | if (tsi.tv_nsec < 0) { |
417 | tsi.tv_sec -= 1; | | 417 | tsi.tv_sec -= 1; |
418 | tsi.tv_nsec += 1000000000; | | 418 | tsi.tv_nsec += 1000000000; |
419 | } | | 419 | } |
420 | if (usesleep) | | 420 | if (usesleep) |
421 | (void)nanosleep(&tsi, NULL); | | 421 | (void)nanosleep(&tsi, NULL); |
422 | tsi = tso; | | 422 | tsi = tso; |
423 | while (stamp.scr_len > 0) { | | 423 | while (stamp.scr_len > 0) { |
424 | l = MIN(DEF_BUF, stamp.scr_len); | | 424 | l = MIN(DEF_BUF, stamp.scr_len); |
425 | if (fread(buf, sizeof(char), l, fp) != l) | | 425 | if (fread(buf, sizeof(char), l, fp) != l) |
426 | err(1, "cannot read buffer"); | | 426 | err(1, "cannot read buffer"); |
427 | | | 427 | |
428 | (void)write(STDOUT_FILENO, buf, l); | | 428 | (void)write(STDOUT_FILENO, buf, l); |
429 | stamp.scr_len -= l; | | 429 | stamp.scr_len -= l; |
430 | } | | 430 | } |
431 | break; | | 431 | break; |
432 | default: | | 432 | default: |
433 | err(1, "invalid direction"); | | 433 | errx(1, "invalid direction"); |
434 | } | | 434 | } |
435 | } | | 435 | } |
436 | (void)fclose(fp); | | 436 | (void)fclose(fp); |
437 | exit(0); | | 437 | exit(0); |
438 | } | | 438 | } |