2 * Copyright (c) 1980 Regents of the University of California.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
36 * - added Native Language Support
38 * 2000-07-30 Per Andreas Buer <per@linpro.no> - added "q"-option
50 #include <sys/ioctl.h>
65 #if HAVE_LIBUTIL && HAVE_PTY_H
69 #ifdef HAVE_LIBUTEMPTER
73 #define DEFAULT_OUTPUT "typescript"
83 void dooutput(FILE *timingfd
);
99 #if !HAVE_LIBUTIL || !HAVE_PTY_H
100 char line
[] = "/dev/ptyXX";
114 die_if_link(char *fn
) {
119 if (lstat(fn
, &s
) == 0 && (S_ISLNK(s
.st_mode
) || s
.st_nlink
> 1))
121 _("output file `%s' is a link\n"
122 "Use --force if you really want to use it.\n"
123 "Program not started."), fn
);
126 static void __attribute__((__noreturn__
))
129 fputs(_("\nUsage:\n"), out
);
131 _(" %s [options] [file]\n"), program_invocation_short_name
);
133 fputs(_("\nOptions:\n"), out
);
134 fputs(_(" -a, --append append the output\n"
135 " -c, --command <command> run command rather than interactive shell\n"
136 " -r, --return return exit code of the child process\n"
137 " -f, --flush run flush after each write\n"
138 " --force use output file even when it is a link\n"
139 " -q, --quiet be quiet\n"
140 " -t, --timing[=<file>] output timing data to stderr (or to FILE)\n"
141 " -V, --version output version information and exit\n"
142 " -h, --help display this help and exit\n\n"), out
);
144 exit(out
== stderr
? EXIT_FAILURE
: EXIT_SUCCESS
);
148 * script -t prints time delays as floating point numbers
149 * The example program (scriptreplay) that we provide to handle this
150 * timing output is a perl script, and does not handle numbers in
151 * locale format (not even when "use locale;" is added).
152 * So, since these numbers are not for human consumption, it seems
153 * easiest to set LC_NUMERIC here.
157 main(int argc
, char **argv
) {
158 sigset_t block_mask
, unblock_mask
;
162 FILE *timingfd
= stderr
;
164 enum { FORCE_OPTION
= CHAR_MAX
+ 1 };
166 static const struct option longopts
[] = {
167 { "append", no_argument
, NULL
, 'a' },
168 { "command", required_argument
, NULL
, 'c' },
169 { "return", no_argument
, NULL
, 'e' },
170 { "flush", no_argument
, NULL
, 'f' },
171 { "force", no_argument
, NULL
, FORCE_OPTION
, },
172 { "quiet", no_argument
, NULL
, 'q' },
173 { "timing", optional_argument
, NULL
, 't' },
174 { "version", no_argument
, NULL
, 'V' },
175 { "help", no_argument
, NULL
, 'h' },
179 setlocale(LC_ALL
, "");
180 setlocale(LC_NUMERIC
, "C"); /* see comment above */
181 bindtextdomain(PACKAGE
, LOCALEDIR
);
184 while ((ch
= getopt_long(argc
, argv
, "ac:efqt::Vh", longopts
, NULL
)) != -1)
206 if ((timingfd
= fopen(optarg
, "w")) == NULL
)
207 err(EXIT_FAILURE
, _("cannot open timing file %s"), optarg
);
211 printf(_("%s from %s\n"), program_invocation_short_name
,
228 fname
= DEFAULT_OUTPUT
;
231 if ((fscript
= fopen(fname
, aflg
? "a" : "w")) == NULL
) {
232 warn(_("open failed: %s"), fname
);
236 shell
= getenv("SHELL");
238 shell
= _PATH_BSHELL
;
242 printf(_("Script started, file is %s\n"), fname
);
245 #ifdef HAVE_LIBUTEMPTER
246 utempter_add_record(master
, NULL
);
248 /* setup SIGCHLD handler */
249 sigemptyset(&sa
.sa_mask
);
251 sa
.sa_handler
= finish
;
252 sigaction(SIGCHLD
, &sa
, NULL
);
254 /* init mask for SIGCHLD */
255 sigprocmask(SIG_SETMASK
, NULL
, &block_mask
);
256 sigaddset(&block_mask
, SIGCHLD
);
258 sigprocmask(SIG_SETMASK
, &block_mask
, &unblock_mask
);
260 sigprocmask(SIG_SETMASK
, &unblock_mask
, NULL
);
263 warn(_("fork failed"));
268 sigprocmask(SIG_SETMASK
, &block_mask
, NULL
);
269 subchild
= child
= fork();
270 sigprocmask(SIG_SETMASK
, &unblock_mask
, NULL
);
273 warn(_("fork failed"));
281 sa
.sa_handler
= resize
;
282 sigaction(SIGWINCH
, &sa
, NULL
);
298 if ((cc
= read(STDIN_FILENO
, ibuf
, BUFSIZ
)) > 0) {
299 ssize_t wrt
= write(master
, ibuf
, cc
);
301 warn (_("write failed"));
305 else if (cc
< 0 && errno
== EINTR
&& resized
)
314 #include <sys/wait.h>
317 finish(int dummy
__attribute__ ((__unused__
))) {
321 while ((pid
= wait3(&status
, WNOHANG
, 0)) > 0)
323 childstatus
= status
;
329 resize(int dummy
__attribute__ ((__unused__
))) {
331 /* transmit window change information to the child */
332 ioctl(STDIN_FILENO
, TIOCGWINSZ
, (char *)&win
);
333 ioctl(slave
, TIOCSWINSZ
, (char *)&win
);
337 * Stop extremely silly gcc complaint on %c:
338 * warning: `%c' yields only last 2 digits of year in some locales
341 my_strftime(char *buf
, size_t len
, const char *fmt
, const struct tm
*tm
) {
342 strftime(buf
, len
, fmt
, tm
);
346 dooutput(FILE *timingfd
) {
351 double oldtime
=time(NULL
), newtime
;
360 tvec
= time((time_t *)NULL
);
361 my_strftime(obuf
, sizeof obuf
, "%c\n", localtime(&tvec
));
362 fprintf(fscript
, _("Script started on %s"), obuf
);
365 if (die
&& flgs
== 0) {
366 /* ..child is dead, but it doesn't mean that there is
367 * nothing in buffers.
369 flgs
= fcntl(master
, F_GETFL
, 0);
370 if (fcntl(master
, F_SETFL
, (flgs
| O_NONBLOCK
)) == -1)
374 gettimeofday(&tv
, NULL
);
377 cc
= read(master
, obuf
, sizeof (obuf
));
379 if (die
&& errno
== EINTR
&& cc
<= 0)
380 /* read() has been interrupted by SIGCHLD, try it again
387 newtime
= tv
.tv_sec
+ (double) tv
.tv_usec
/ 1000000;
388 fprintf(timingfd
, "%f %zd\n", newtime
- oldtime
, cc
);
391 wrt
= write(STDOUT_FILENO
, obuf
, cc
);
393 warn (_("write failed"));
396 fwrt
= fwrite(obuf
, 1, cc
, fscript
);
398 warn (_("cannot write script file"));
406 fcntl(master
, F_SETFL
, flgs
);
417 t
= open(_PATH_DEV_TTY
, O_RDWR
);
419 ioctl(t
, TIOCNOTTY
, (char *)0);
427 dup2(slave
, STDIN_FILENO
);
428 dup2(slave
, STDOUT_FILENO
);
429 dup2(slave
, STDERR_FILENO
);
434 shname
= strrchr(shell
, '/');
441 execl(shell
, shname
, "-c", cflg
, NULL
);
443 execl(shell
, shname
, "-i", NULL
);
445 warn(_("failed to execute %s"), shell
);
455 rtt
.c_lflag
&= ~ECHO
;
456 tcsetattr(STDIN_FILENO
, TCSANOW
, &rtt
);
473 tvec
= time((time_t *)NULL
);
474 my_strftime(buf
, sizeof buf
, "%c\n", localtime(&tvec
));
475 fprintf(fscript
, _("\nScript done on %s"), buf
);
482 tcsetattr(STDIN_FILENO
, TCSADRAIN
, &tt
);
484 printf(_("Script done, file is %s\n"), fname
);
485 #ifdef HAVE_LIBUTEMPTER
487 utempter_remove_record(master
);
492 if (WIFSIGNALED(childstatus
))
493 exit(WTERMSIG(childstatus
) + 0x80);
495 exit(WEXITSTATUS(childstatus
));
502 #if HAVE_LIBUTIL && HAVE_PTY_H
503 tcgetattr(STDIN_FILENO
, &tt
);
504 ioctl(STDIN_FILENO
, TIOCGWINSZ
, (char *)&win
);
505 if (openpty(&master
, &slave
, NULL
, &tt
, &win
) < 0) {
506 warn(_("openpty failed"));
510 char *pty
, *bank
, *cp
;
513 pty
= &line
[strlen("/dev/ptyp")];
514 for (bank
= "pqrs"; *bank
; bank
++) {
515 line
[strlen("/dev/pty")] = *bank
;
517 if (stat(line
, &stb
) < 0)
519 for (cp
= "0123456789abcdef"; *cp
; cp
++) {
521 master
= open(line
, O_RDWR
);
523 char *tp
= &line
[strlen("/dev/")];
526 /* verify slave side is usable */
528 ok
= access(line
, R_OK
|W_OK
) == 0;
531 tcgetattr(STDIN_FILENO
, &tt
);
532 ioctl(STDIN_FILENO
, TIOCGWINSZ
,
542 warn(_("out of pty's"));
544 #endif /* not HAVE_LIBUTIL */
550 line
[strlen("/dev/")] = 't';
551 slave
= open(line
, O_RDWR
);
553 warn(_("open failed: %s"), line
);
556 tcsetattr(slave
, TCSANOW
, &tt
);
557 ioctl(slave
, TIOCSWINSZ
, (char *)&win
);
560 ioctl(slave
, TIOCSCTTY
, 0);