commit 73b825713a0dd7be02a387423b032f940a5cf15f
Author: Antoine Amarilli <a3nm@a3nm.net>
Date: Wed, 3 Oct 2012 00:24:13 +0200
initial
Diffstat:
Makefile | | | 31 | +++++++++++++++++++++++++++++++ |
README | | | 27 | +++++++++++++++++++++++++++ |
io.c | | | 161 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
io.h | | | 13 | +++++++++++++ |
ttyplay.1 | | | 62 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
ttyplay.c | | | 312 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
ttyrec.1 | | | 76 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
ttyrec.c | | | 483 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
ttyrec.h | | | 12 | ++++++++++++ |
ttytime.1 | | | 27 | +++++++++++++++++++++++++++ |
ttytime.c | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
11 files changed, 1274 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,31 @@
+CC = gcc
+CFLAGS = -O2
+VERSION = 1.0.8
+
+TARGET = ttyrec ttyplay ttytime
+
+DIST = ttyrec.c ttyplay.c ttyrec.h io.c io.h ttytime.c\
+ README Makefile ttyrec.1 ttyplay.1 ttytime.1
+
+all: $(TARGET)
+
+ttyrec: ttyrec.o io.o
+ $(CC) $(CFLAGS) -o ttyrec ttyrec.o io.o
+
+ttyplay: ttyplay.o io.o
+ $(CC) $(CFLAGS) -o ttyplay ttyplay.o io.o
+
+ttytime: ttytime.o io.o
+ $(CC) $(CFLAGS) -o ttytime ttytime.o io.o
+
+clean:
+ rm -f *.o $(TARGET) ttyrecord *~
+
+dist:
+ rm -rf ttyrec-$(VERSION)
+ rm -f ttyrec-$(VERSION).tar.gz
+
+ mkdir ttyrec-$(VERSION)
+ cp $(DIST) ttyrec-$(VERSION)
+ tar zcf ttyrec-$(VERSION).tar.gz ttyrec-$(VERSION)
+ rm -rf ttyrec-$(VERSION)
diff --git a/README b/README
@@ -0,0 +1,27 @@
+ttyrec is a tty recorder. ttyplay is a tty player.
+
+Installation:
+
+ % make
+
+or if your system is SVR4 system (Solaris etc.),
+
+ % make CFLAGS=-DSVR4
+
+or if your system supports getpt(3),
+
+ % make CFLAGS=-DHAVE_getpt
+
+HAVE_getpt is required if your linux system uses devfs.
+
+
+Usage:
+
+ % ttyrec
+ (In the excuted shell, do whatever you want and exit)
+
+ % ttyplay ttyrecord
+
+Have fun!
+
+-- Satoru Takabayashi <satoru@namazu.org>
diff --git a/io.c b/io.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2000 Satoru Takabayashi <satoru@namazu.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ttyrec.h"
+
+#define SWAP_ENDIAN(val) ((unsigned int) ( \
+ (((unsigned int) (val) & (unsigned int) 0x000000ffU) << 24) | \
+ (((unsigned int) (val) & (unsigned int) 0x0000ff00U) << 8) | \
+ (((unsigned int) (val) & (unsigned int) 0x00ff0000U) >> 8) | \
+ (((unsigned int) (val) & (unsigned int) 0xff000000U) >> 24)))
+
+static int
+is_little_endian ()
+{
+ static int retval = -1;
+
+ if (retval == -1) {
+ int n = 1;
+ char *p = (char *)&n;
+ char x[] = {1, 0, 0, 0};
+
+ assert(sizeof(int) == 4);
+
+ if (memcmp(p, x, 4) == 0) {
+ retval = 1;
+ } else {
+ retval = 0;
+ }
+ }
+
+ return retval;
+}
+
+static int
+convert_to_little_endian (int x)
+{
+ if (is_little_endian()) {
+ return x;
+ } else {
+ return SWAP_ENDIAN(x);
+ }
+}
+
+int
+read_header (FILE *fp, Header *h)
+{
+ int buf[3];
+
+ if (fread(buf, sizeof(int), 3, fp) == 0) {
+ return 0;
+ }
+
+ h->tv.tv_sec = convert_to_little_endian(buf[0]);
+ h->tv.tv_usec = convert_to_little_endian(buf[1]);
+ h->len = convert_to_little_endian(buf[2]);
+
+ return 1;
+}
+
+int
+write_header (FILE *fp, Header *h)
+{
+ int buf[3];
+
+ buf[0] = convert_to_little_endian(h->tv.tv_sec);
+ buf[1] = convert_to_little_endian(h->tv.tv_usec);
+ buf[2] = convert_to_little_endian(h->len);
+
+ if (fwrite(buf, sizeof(int), 3, fp) == 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static char *progname = "";
+void
+set_progname (const char *name)
+{
+ progname = strdup(name);
+}
+
+FILE *
+efopen (const char *path, const char *mode)
+{
+ FILE *fp = fopen(path, mode);
+ if (fp == NULL) {
+ fprintf(stderr, "%s: %s: %s\n", progname, path, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ return fp;
+}
+
+int
+edup (int oldfd)
+{
+ int fd = dup(oldfd);
+ if (fd == -1) {
+ fprintf(stderr, "%s: dup failed: %s\n", progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ return fd;
+}
+
+int
+edup2 (int oldfd, int newfd)
+{
+ int fd = dup2(oldfd, newfd);
+ if (fd == -1) {
+ fprintf(stderr, "%s: dup2 failed: %s\n", progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ return fd;
+}
+
+FILE *
+efdopen (int fd, const char *mode)
+{
+ FILE *fp = fdopen(fd, mode);
+ if (fp == NULL) {
+ fprintf(stderr, "%s: fdopen failed: %s\n", progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
diff --git a/io.h b/io.h
@@ -0,0 +1,13 @@
+#ifndef __TTYREC_IO_H__
+#define __TTYREC_IO_H__
+
+#include "ttyrec.h"
+
+int read_header (FILE *fp, Header *h);
+int write_header (FILE *fp, Header *h);
+FILE* efopen (const char *path, const char *mode);
+int edup (int oldfd);
+int edup2 (int oldfd, int newfd);
+FILE* efdopen (int fd, const char *mode);
+
+#endif
diff --git a/ttyplay.1 b/ttyplay.1
@@ -0,0 +1,62 @@
+.\"
+.\" This manual page is written by NAKANO Takeo <nakano@webmasters.gr.jp>
+.\"
+.TH TTYPLAY 1
+.SH NAME
+ttyplay \- player of the tty session recorded by ttyrec
+.SH SYNOPSIS
+.br
+.B ttyplay
+.I [\-s SPEED] [\-n] [\-p] file
+.br
+.SH DESCRIPTION
+.B Ttyplay
+plays the tty session in
+.IR file ,
+which was recorded previously by
+.BR ttyrec (1).
+.PP
+When
+.B \-p
+option is given,
+.B ttyplay
+output the
+.I file
+as it grows.
+It means that you can see the "live" shell session
+running by another user.
+.PP
+If you hit any key during playback, it will go right to the next
+character typed. This is handy when examining sessions where a user
+spends a lot of time at a prompt.
+.PP
+Additionally, there are some special keys defined:
+.TP
+.BI + " or " f
+ double the speed of playback.
+.TP
+.BI \- " or " s
+ halve the speed of playback.
+.TP
+.BI 1
+set playback to speed 1.0 again.
+
+.SH OPTIONS
+.TP
+.BI \-s " SPEED"
+multiple the playing speed by
+.I SPEED
+(default is 1).
+.TP
+.B \-n
+no wait mode.
+Ignore the timing information in
+.IR file .
+.TP
+.B \-p
+peek another person's tty session.
+.SH "SEE ALSO"
+.BR script (1),
+.BR ttyrec (1),
+.BR ttytime (1)
+
diff --git a/ttyplay.c b/ttyplay.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2000 Satoru Takabayashi <satoru@namazu.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <termios.h>
+#include <sys/time.h>
+#include <string.h>
+
+#include "ttyrec.h"
+#include "io.h"
+
+typedef double (*WaitFunc) (struct timeval prev,
+ struct timeval cur,
+ double speed);
+typedef int (*ReadFunc) (FILE *fp, Header *h, char **buf);
+typedef void (*WriteFunc) (char *buf, int len);
+typedef void (*ProcessFunc) (FILE *fp, double speed,
+ ReadFunc read_func, WaitFunc wait_func);
+
+struct timeval
+timeval_diff (struct timeval tv1, struct timeval tv2)
+{
+ struct timeval diff;
+
+ diff.tv_sec = tv2.tv_sec - tv1.tv_sec;
+ diff.tv_usec = tv2.tv_usec - tv1.tv_usec;
+ if (diff.tv_usec < 0) {
+ diff.tv_sec--;
+ diff.tv_usec += 1000000;
+ }
+
+ return diff;
+}
+
+struct timeval
+timeval_div (struct timeval tv1, double n)
+{
+ double x = ((double)tv1.tv_sec + (double)tv1.tv_usec / 1000000.0) / n;
+ struct timeval div;
+
+ div.tv_sec = (int)x;
+ div.tv_usec = (x - (int)x) * 1000000;
+
+ return div;
+}
+
+double
+ttywait (struct timeval prev, struct timeval cur, double speed)
+{
+ static struct timeval drift = {0, 0};
+ struct timeval start;
+ struct timeval diff = timeval_diff(prev, cur);
+ fd_set readfs;
+
+ gettimeofday(&start, NULL);
+
+ assert(speed != 0);
+ diff = timeval_diff(drift, timeval_div(diff, speed));
+ if (diff.tv_sec < 0) {
+ diff.tv_sec = diff.tv_usec = 0;
+ }
+
+ FD_SET(STDIN_FILENO, &readfs);
+ /*
+ * We use select() for sleeping with subsecond precision.
+ * select() is also used to wait user's input from a keyboard.
+ *
+ * Save "diff" since select(2) may overwrite it to {0, 0}.
+ */
+ struct timeval orig_diff = diff;
+ select(1, &readfs, NULL, NULL, &diff);
+ diff = orig_diff; /* Restore the original diff value. */
+ if (FD_ISSET(0, &readfs)) { /* a user hits a character? */
+ char c;
+ read(STDIN_FILENO, &c, 1); /* drain the character */
+ switch (c) {
+ case '+':
+ case 'f':
+ speed *= 2;
+ break;
+ case '-':
+ case 's':
+ speed /= 2;
+ break;
+ case '1':
+ speed = 1.0;
+ break;
+ }
+ drift.tv_sec = drift.tv_usec = 0;
+ } else {
+ struct timeval stop;
+ gettimeofday(&stop, NULL);
+ /* Hack to accumulate the drift */
+ if (diff.tv_sec == 0 && diff.tv_usec == 0) {
+ diff = timeval_diff(drift, diff); // diff = 0 - drift.
+ }
+ drift = timeval_diff(diff, timeval_diff(start, stop));
+ }
+ return speed;
+}
+
+double
+ttynowait (struct timeval prev, struct timeval cur, double speed)
+{
+ /* do nothing */
+ return 0; /* Speed isn't important. */
+}
+
+int
+ttyread (FILE *fp, Header *h, char **buf)
+{
+ if (read_header(fp, h) == 0) {
+ return 0;
+ }
+
+ *buf = malloc(h->len);
+ if (*buf == NULL) {
+ perror("malloc");
+ }
+
+ if (fread(*buf, 1, h->len, fp) == 0) {
+ perror("fread");
+ }
+ return 1;
+}
+
+int
+ttypread (FILE *fp, Header *h, char **buf)
+{
+ /*
+ * Read persistently just like tail -f.
+ */
+ while (ttyread(fp, h, buf) == 0) {
+ struct timeval w = {0, 250000};
+ select(0, NULL, NULL, NULL, &w);
+ clearerr(fp);
+ }
+ return 1;
+}
+
+void
+ttywrite (char *buf, int len)
+{
+ fwrite(buf, 1, len, stdout);
+}
+
+void
+ttynowrite (char *buf, int len)
+{
+ /* do nothing */
+}
+
+void
+ttyplay (FILE *fp, double speed, ReadFunc read_func,
+ WriteFunc write_func, WaitFunc wait_func)
+{
+ int first_time = 1;
+ struct timeval prev;
+
+ setbuf(stdout, NULL);
+ setbuf(fp, NULL);
+
+ while (1) {
+ char *buf;
+ Header h;
+
+ if (read_func(fp, &h, &buf) == 0) {
+ break;
+ }
+
+ if (!first_time) {
+ speed = wait_func(prev, h.tv, speed);
+ }
+ first_time = 0;
+
+ write_func(buf, h.len);
+ prev = h.tv;
+ free(buf);
+ }
+}
+
+void
+ttyskipall (FILE *fp)
+{
+ /*
+ * Skip all records.
+ */
+ ttyplay(fp, 0, ttyread, ttynowrite, ttynowait);
+}
+
+void ttyplayback (FILE *fp, double speed,
+ ReadFunc read_func, WaitFunc wait_func)
+{
+ ttyplay(fp, speed, ttyread, ttywrite, wait_func);
+}
+
+void ttypeek (FILE *fp, double speed,
+ ReadFunc read_func, WaitFunc wait_func)
+{
+ ttyskipall(fp);
+ ttyplay(fp, speed, ttypread, ttywrite, ttynowait);
+}
+
+
+void
+usage (void)
+{
+ printf("Usage: ttyplay [OPTION] [FILE]\n");
+ printf(" -s SPEED Set speed to SPEED [1.0]\n");
+ printf(" -n No wait mode\n");
+ printf(" -p Peek another person's ttyrecord\n");
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * We do some tricks so that select(2) properly works on
+ * STDIN_FILENO in ttywait().
+ */
+FILE *
+input_from_stdin (void)
+{
+ FILE *fp;
+ int fd = edup(STDIN_FILENO);
+ edup2(STDOUT_FILENO, STDIN_FILENO);
+ return efdopen(fd, "r");
+}
+
+int
+main (int argc, char **argv)
+{
+ double speed = 1.0;
+ ReadFunc read_func = ttyread;
+ WaitFunc wait_func = ttywait;
+ ProcessFunc process = ttyplayback;
+ FILE *input = NULL;
+ struct termios old, new;
+
+ set_progname(argv[0]);
+ while (1) {
+ int ch = getopt(argc, argv, "s:np");
+ if (ch == EOF) {
+ break;
+ }
+ switch (ch) {
+ case 's':
+ if (optarg == NULL) {
+ perror("-s option requires an argument");
+ exit(EXIT_FAILURE);
+ }
+ sscanf(optarg, "%lf", &speed);
+ break;
+ case 'n':
+ wait_func = ttynowait;
+ break;
+ case 'p':
+ process = ttypeek;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (optind < argc) {
+ input = efopen(argv[optind], "r");
+ } else {
+ input = input_from_stdin();
+ }
+ assert(input != NULL);
+
+ tcgetattr(0, &old); /* Get current terminal state */
+ new = old; /* Make a copy */
+ new.c_lflag &= ~(ICANON | ECHO | ECHONL); /* unbuffered, no echo */
+ tcsetattr(0, TCSANOW, &new); /* Make it current */
+
+ process(input, speed, read_func, wait_func);
+ tcsetattr(0, TCSANOW, &old); /* Return terminal state */
+
+ return 0;
+}
diff --git a/ttyrec.1 b/ttyrec.1
@@ -0,0 +1,76 @@
+.\"
+.\" This manual page is written by NAKANO Takeo <nakano@webmasters.gr.jp>
+.\"
+.TH TTYREC 1
+.SH NAME
+ttyrec \- a tty recorder
+.SH SYNOPSIS
+.br
+.B ttyrec
+.I "[\-a][\-u] [file]"
+.br
+.SH DESCRIPTION
+.B Ttyrec
+is a tty recorder.
+It is a derivative of
+.BR script (1)
+command for recording timing information with microsecond accuracy as well.
+It can record emacs -nw, vi, lynx, or any programs running on tty.
+.PP
+.B Ttyrec
+invokes a shell and records the session until the shell exits.
+Recorded data can be played back with
+.BR ttyplay (1).
+If the argument
+.I file
+is given, the session will be recorded in that file.
+Otherwise,
+.I ttyrecord
+is used as default.
+.SH OPTIONS
+.TP
+.B \-a
+Append the output to
+.I file
+or
+.IR ttyrecord ,
+rather than overwriting it.
+.TP
+.B \-u
+With this option,
+.B ttyrec
+automatically calls
+.BR uudecode (1)
+and saves its output when uuencoded data appear on the session.
+It allow you to transfer files from remote host.
+You can call
+.B ttyrec
+with this option, login to the remote host
+and invoke
+.BR uuencode (1)
+on it for the file you want to transfer.
+.TP
+.BI \-e " command"
+Invoke
+.I command
+when ttyrec starts.
+
+
+.SH ENVIRONMENT
+.TP
+.I SHELL
+If the variable
+.I SHELL
+exists, the shell forked by
+.B ttyrec
+will be that shell.
+If it's not set,
+the Bourne shell is assumed.
+(Most shells set this variable automatically).
+.SH "SEE ALSO"
+.BR script (1),
+.BR ttyplay (1),
+.BR ttytime (1),
+.BR uuencode (1),
+.BR uudecode (1)
+
diff --git a/ttyrec.c b/ttyrec.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ */
+
+/* 2000-12-27 Satoru Takabayashi <satoru@namazu.org>
+ * - modify `script' to create `ttyrec'.
+ */
+
+/*
+ * script
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/signal.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if defined(SVR4)
+#include <fcntl.h>
+#include <stropts.h>
+#endif /* SVR4 */
+
+#include <sys/time.h>
+#include "ttyrec.h"
+#include "io.h"
+
+#define HAVE_inet_aton
+#define HAVE_scsi_h
+#define HAVE_kd_h
+
+#define _(FOO) FOO
+
+#ifdef HAVE_openpty
+#include <libutil.h>
+#endif
+
+#if defined(SVR4) && !defined(CDEL)
+#if defined(_POSIX_VDISABLE)
+#define CDEL _POSIX_VDISABLE
+#elif defined(CDISABLE)
+#define CDEL CDISABLE
+#else /* not _POSIX_VISIBLE && not CDISABLE */
+#define CDEL 255
+#endif /* not _POSIX_VISIBLE && not CDISABLE */
+#endif /* SVR4 && ! CDEL */
+
+void done(void);
+void fail(void);
+void fixtty(void);
+void getmaster(void);
+void getslave(void);
+void doinput(void);
+void dooutput(void);
+void doshell(const char*);
+
+char *shell;
+FILE *fscript;
+int master;
+int slave;
+int child;
+int subchild;
+char *fname;
+
+struct termios tt;
+struct winsize win;
+int lb;
+int l;
+#if !defined(SVR4)
+#ifndef HAVE_openpty
+char line[] = "/dev/ptyXX";
+#endif
+#endif /* !SVR4 */
+int aflg;
+int uflg;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ int ch;
+ void finish();
+ char *getenv();
+ char *command = NULL;
+
+ while ((ch = getopt(argc, argv, "aue:h?")) != EOF)
+ switch((char)ch) {
+ case 'a':
+ aflg++;
+ break;
+ case 'u':
+ uflg++;
+ break;
+ case 'e':
+ command = strdup(optarg);
+ break;
+ case 'h':
+ case '?':
+ default:
+ fprintf(stderr, _("usage: ttyrec [-u] [-e command] [-a] [file]\n"));
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ fname = argv[0];
+ else
+ fname = "ttyrecord";
+ if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
+ perror(fname);
+ fail();
+ }
+ setbuf(fscript, NULL);
+
+ shell = getenv("SHELL");
+ if (shell == NULL)
+ shell = "/bin/sh";
+
+ getmaster();
+ fixtty();
+
+ (void) signal(SIGCHLD, finish);
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail();
+ }
+ if (child == 0) {
+ subchild = child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail();
+ }
+ if (child)
+ dooutput();
+ else
+ doshell(command);
+ }
+ doinput();
+
+ return 0;
+}
+
+void
+doinput()
+{
+ register int cc;
+ char ibuf[BUFSIZ];
+
+ (void) fclose(fscript);
+#ifdef HAVE_openpty
+ (void) close(slave);
+#endif
+ while ((cc = read(0, ibuf, BUFSIZ)) > 0)
+ (void) write(master, ibuf, cc);
+ done();
+}
+
+#include <sys/wait.h>
+
+void
+finish()
+{
+#if defined(SVR4)
+ int status;
+#else /* !SVR4 */
+ union wait status;
+#endif /* !SVR4 */
+ register int pid;
+ register int die = 0;
+
+ while ((pid = wait3((int *)&status, WNOHANG, 0)) > 0)
+ if (pid == child)
+ die = 1;
+
+ if (die)
+ done();
+}
+
+struct linebuf {
+ char str[BUFSIZ + 1]; /* + 1 for an additional NULL character.*/
+ int len;
+};
+
+
+void
+check_line (const char *line)
+{
+ static int uuencode_mode = 0;
+ static FILE *uudecode;
+
+ if (uuencode_mode == 1) {
+ fprintf(uudecode, "%s", line);
+ if (strcmp(line, "end\n") == 0) {
+ pclose(uudecode);
+ uuencode_mode = 0;
+ }
+ } else {
+ int dummy; char dummy2[BUFSIZ];
+ if (sscanf(line, "begin %o %s", &dummy, dummy2) == 2) {
+ /*
+ * uuencode line found!
+ */
+ uudecode = popen("uudecode", "w");
+ fprintf(uudecode, "%s", line);
+ uuencode_mode = 1;
+ }
+ }
+}
+
+void
+check_output(const char *str, int len)
+{
+ static struct linebuf lbuf = {"", 0};
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (lbuf.len < BUFSIZ) {
+ lbuf.str[lbuf.len] = str[i];
+ if (lbuf.str[lbuf.len] == '\r') {
+ lbuf.str[lbuf.len] = '\n';
+ }
+ lbuf.len++;
+ if (lbuf.str[lbuf.len - 1] == '\n') {
+ if (lbuf.len > 1) { /* skip a blank line. */
+ lbuf.str[lbuf.len] = '\0';
+ check_line(lbuf.str);
+ }
+ lbuf.len = 0;
+ }
+ } else {/* buffer overflow */
+ lbuf.len = 0;
+ }
+ }
+}
+
+void
+dooutput()
+{
+ int cc;
+ char obuf[BUFSIZ];
+
+ setbuf(stdout, NULL);
+ (void) close(0);
+#ifdef HAVE_openpty
+ (void) close(slave);
+#endif
+ for (;;) {
+ Header h;
+
+ cc = read(master, obuf, BUFSIZ);
+ if (cc <= 0)
+ break;
+ if (uflg)
+ check_output(obuf, cc);
+ h.len = cc;
+ gettimeofday(&h.tv, NULL);
+ (void) write(1, obuf, cc);
+ (void) write_header(fscript, &h);
+ (void) fwrite(obuf, 1, cc, fscript);
+ }
+ done();
+}
+
+void
+doshell(const char* command)
+{
+ /***
+ int t;
+
+ t = open(_PATH_TTY, O_RDWR);
+ if (t >= 0) {
+ (void) ioctl(t, TIOCNOTTY, (char *)0);
+ (void) close(t);
+ }
+ ***/
+ getslave();
+ (void) close(master);
+ (void) fclose(fscript);
+ (void) dup2(slave, 0);
+ (void) dup2(slave, 1);
+ (void) dup2(slave, 2);
+ (void) close(slave);
+
+ if (!command) {
+ execl(shell, strrchr(shell, '/') + 1, "-i", 0);
+ } else {
+ execl(shell, strrchr(shell, '/') + 1, "-c", command, 0);
+ }
+ perror(shell);
+ fail();
+}
+
+void
+fixtty()
+{
+ struct termios rtt;
+
+ rtt = tt;
+#if defined(SVR4)
+ rtt.c_iflag = 0;
+ rtt.c_lflag &= ~(ISIG|ICANON|XCASE|ECHO|ECHOE|ECHOK|ECHONL);
+ rtt.c_oflag = OPOST;
+ rtt.c_cc[VINTR] = CDEL;
+ rtt.c_cc[VQUIT] = CDEL;
+ rtt.c_cc[VERASE] = CDEL;
+ rtt.c_cc[VKILL] = CDEL;
+ rtt.c_cc[VEOF] = 1;
+ rtt.c_cc[VEOL] = 0;
+#else /* !SVR4 */
+ cfmakeraw(&rtt);
+ rtt.c_lflag &= ~ECHO;
+#endif /* !SVR4 */
+ (void) tcsetattr(0, TCSAFLUSH, &rtt);
+}
+
+void
+fail()
+{
+
+ (void) kill(0, SIGTERM);
+ done();
+}
+
+void
+done()
+{
+ if (subchild) {
+ (void) fclose(fscript);
+ (void) close(master);
+ } else {
+ (void) tcsetattr(0, TCSAFLUSH, &tt);
+ }
+ exit(0);
+}
+
+void
+getmaster()
+{
+#if defined(SVR4)
+ (void) tcgetattr(0, &tt);
+ (void) ioctl(0, TIOCGWINSZ, (char *)&win);
+ if ((master = open("/dev/ptmx", O_RDWR)) < 0) {
+ perror("open(\"/dev/ptmx\", O_RDWR)");
+ fail();
+ }
+#else /* !SVR4 */
+#ifdef HAVE_openpty
+ (void) tcgetattr(0, &tt);
+ (void) ioctl(0, TIOCGWINSZ, (char *)&win);
+ if (openpty(&master, &slave, NULL, &tt, &win) < 0) {
+ fprintf(stderr, _("openpty failed\n"));
+ fail();
+ }
+#else
+#ifdef HAVE_getpt
+ if ((master = getpt()) < 0) {
+ perror("getpt()");
+ fail();
+ }
+#else
+ char *pty, *bank, *cp;
+ struct stat stb;
+
+ pty = &line[strlen("/dev/ptyp")];
+ for (bank = "pqrs"; *bank; bank++) {
+ line[strlen("/dev/pty")] = *bank;
+ *pty = '0';
+ if (stat(line, &stb) < 0)
+ break;
+ for (cp = "0123456789abcdef"; *cp; cp++) {
+ *pty = *cp;
+ master = open(line, O_RDWR);
+ if (master >= 0) {
+ char *tp = &line[strlen("/dev/")];
+ int ok;
+
+ /* verify slave side is usable */
+ *tp = 't';
+ ok = access(line, R_OK|W_OK) == 0;
+ *tp = 'p';
+ if (ok) {
+ (void) tcgetattr(0, &tt);
+ (void) ioctl(0, TIOCGWINSZ,
+ (char *)&win);
+ return;
+ }
+ (void) close(master);
+ }
+ }
+ }
+ fprintf(stderr, _("Out of pty's\n"));
+ fail();
+#endif /* not HAVE_getpt */
+#endif /* not HAVE_openpty */
+#endif /* !SVR4 */
+}
+
+void
+getslave()
+{
+#if defined(SVR4)
+ (void) setsid();
+ grantpt( master);
+ unlockpt(master);
+ if ((slave = open((const char *)ptsname(master), O_RDWR)) < 0) {
+ perror("open(fd, O_RDWR)");
+ fail();
+ }
+ if (isastream(slave)) {
+ if (ioctl(slave, I_PUSH, "ptem") < 0) {
+ perror("ioctl(fd, I_PUSH, ptem)");
+ fail();
+ }
+ if (ioctl(slave, I_PUSH, "ldterm") < 0) {
+ perror("ioctl(fd, I_PUSH, ldterm)");
+ fail();
+ }
+#ifndef _HPUX_SOURCE
+ if (ioctl(slave, I_PUSH, "ttcompat") < 0) {
+ perror("ioctl(fd, I_PUSH, ttcompat)");
+ fail();
+ }
+#endif
+ (void) ioctl(0, TIOCGWINSZ, (char *)&win);
+ }
+#else /* !SVR4 */
+#ifndef HAVE_openpty
+ line[strlen("/dev/")] = 't';
+ slave = open(line, O_RDWR);
+ if (slave < 0) {
+ perror(line);
+ fail();
+ }
+ (void) tcsetattr(slave, TCSAFLUSH, &tt);
+ (void) ioctl(slave, TIOCSWINSZ, (char *)&win);
+#endif
+ (void) setsid();
+ (void) ioctl(slave, TIOCSCTTY, 0);
+#endif /* SVR4 */
+}
diff --git a/ttyrec.h b/ttyrec.h
@@ -0,0 +1,12 @@
+#ifndef __TTYREC_H__
+#define __TTYREC_H__
+
+#include "sys/time.h"
+
+typedef struct header {
+ struct timeval tv;
+ int len;
+} Header;
+
+
+#endif
diff --git a/ttytime.1 b/ttytime.1
@@ -0,0 +1,27 @@
+.\"
+.\" This manual page is written by NAKANO Takeo <nakano@webmasters.gr.jp>
+.\"
+.TH TTYTIME 1
+.SH NAME
+ttytime \- print the time of the recorded session data by ttyrec(1)
+.SH SYNOPSIS
+.br
+.B ttytime
+.I file...
+.SH DESCRIPTION
+.B Ttytime
+tells you the time of recorded data in seconds.
+For example:
+.sp
+.RS
+.nf
+% ttytime *.tty
+ 173 foo.tty
+ 1832 bar.tty
+.fi
+.RE
+.SH "SEE ALSO"
+.BR script (1),
+.BR ttyrec (1),
+.BR ttyplay (1)
+
diff --git a/ttytime.c b/ttytime.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "io.h"
+#include "ttyrec.h"
+
+int
+calc_time (const char *filename)
+{
+ Header start, end;
+ FILE *fp = efopen(filename, "r");
+
+ read_header(fp, &start);
+ fseek(fp, start.len, SEEK_CUR);
+ while (1) {
+ Header h;
+ if (read_header(fp, &h) == 0) {
+ break;
+ }
+ end = h;
+ fseek(fp, h.len, SEEK_CUR);
+ }
+ return end.tv.tv_sec - start.tv.tv_sec;
+}
+
+int
+main (int argc, char **argv)
+{
+ int i;
+ set_progname(argv[0]);
+ for (i = 1; i < argc; i++) {
+ char *filename = argv[i];
+ printf("%7d %s\n", calc_time(filename), filename);
+ }
+ return 0;
+}