Hi,
One nice feature of PLAYterm would be to display what recorder types
in his shell. So i quickly tried to hack ttyrec in order to produce a
ttyrecord.in that contains only 'input'. However, i noticed while
trying for instance a vim session a strange 'input', not initiated by
me:
$ ./ttyplay -t
vim.in
1317238613:366180 "\x1b[>1;2801;0c"
It seems to be a response from vim's query "\x1b[>1c" according to
quick digging with vte part (
http://git.gnome.org/browse/vte/tree/src/
caps.c#n469,
http://git.gnome.org/browse/vte/tree/src/vteseq.c#n2508).
I do think that such values should be ignored, but while testing
(unmodified, i.e. debian's version) ttyplay it 'stopped' due to this
sequence; ttyrec recorded such a "\1b[1c" sequence (i.e. CSI[>c) and
terminals (either xfce4-terminal or xterm) responded on ttyin with the
above sequence (in fact my debian version of ttyplay has been linked
to an older version of libvte, hence version (2801 means version) was
different), but tty was stuck, and i had to enter '1' to continue
ttyplay (strace show that it was selecting only 'fd 0', i.e. stdin,
not 'fd 4', i.e. ttyrecord).
Anyway. the simple patch below does the following:
- add a -i switch to ttyrec to create a <ttyrecord>.in with input
recordings with same format as ttyrecord
- add a -t switch to ttyplay do 'nicely' timing and value display
from a record (hence interest comes when feeding it with
ttyrecord.in).
Now, for PLAYterm, i do not know how difficult it would be to add a
'subtitle' track that would display 'user input' (ignoring a subset of
'pure-terminal' entries). My dream would of course be something like a
'karaoke' mode, i.e. scrolling available inputs, and highlighting them
when being 'active'.
Regarding the ttyrec patch, my original approach was "simpliest", i.e.
to add both input and output in the same file but after failing i
realized:
- that in fact ttyrec created 3 processes (in fact, i started to
code without even reading the whole code :(...)
- no version or header is supported in current ttyrec, hence older
version would just 'stuck'
- this approach was simplest (as not dealing with concurrent access
to a file, modified file format, handling different types of events
(for ttyplay))
- maybe PLAYterm integration would be even simple with a 2-pass
process, 1 for output, 2 for input.
What do you think ?
Cheers,
Marc.
Patch against debian's version (reason was just that 'cvs' version was
not compiling):
--- ttyrec-1.0.8/ttyplay.c 2011-09-28 22:20:27.000000000 +0200
+++ ttyrec-with-input/ttyplay.c 2011-09-28 22:26:19.000000000 +0200
@@ -38,18 +38,18 @@
#include <termios.h>
#include <sys/time.h>
#include <string.h>
+#include <ctype.h>
#include "ttyrec.h"
#include "io.h"
-typedef double (*WaitFunc) (struct timeval prev,
- struct timeval cur,
+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,
+typedef void (*WriteFunc) (const char *buf, const Header *h);
+typedef void (*ProcessFunc) (FILE *fp, double speed,
ReadFunc read_func, WaitFunc wait_func);
-
struct timeval
timeval_diff (struct timeval tv1, struct timeval tv2)
{
@@ -166,10 +166,11 @@ ttyread (FILE *fp, Header *h, char **buf
if (*buf == NULL) {
perror("malloc");
}
-
+
if (fread(*buf, 1, h->len, fp) == 0) {
goto err;
}
+
return 1;
err:
@@ -200,19 +201,35 @@ ttypread (FILE *fp, Header *h, char **bu
}
void
-ttywrite (char *buf, int len)
+ttywrite (const char *buf, const Header *h)
+{
+ fwrite(buf, 1, h->len, stdout);
+}
+
+void
+ttywrite_time_raw(const char *buf, const Header *h)
{
- fwrite(buf, 1, len, stdout);
+ int i;
+ fprintf(stdout, "%ld:%ld\t\"", h->tv.tv_sec, h->tv.tv_usec);
+ for(i = 0; i < h->len; i++) {
+ if (isprint(buf[i])) {
+ fprintf(stdout, "%c", buf[i]);
+ } else {
+ fprintf(stdout, "\\x%02hhx", buf[i]);
+ }
+ }
+ fprintf(stdout, "\"\n");
}
void
-ttynowrite (char *buf, int len)
+ttynowrite (const char *buf, const Header *h)
{
/* do nothing */
}
+
void
-ttyplay (FILE *fp, double speed, ReadFunc read_func,
+ttyplay (FILE *fp, double speed, ReadFunc read_func,
WriteFunc write_func, WaitFunc wait_func)
{
int first_time = 1;
@@ -224,8 +241,9 @@ ttyplay (FILE *fp, double speed, ReadFun
while (1) {
char *buf;
Header h;
+ int rc;
- if (read_func(fp, &h, &buf) == 0) {
+ if ((rc = read_func(fp, &h, &buf)) == 0) {
break;
}
@@ -236,7 +254,7 @@ ttyplay (FILE *fp, double speed, ReadFun
}
first_time = 0;
- write_func(buf, h.len);
+ write_func(buf, &h);
prev =
h.tv;
free(buf);
}
@@ -251,19 +269,25 @@ ttyskipall (FILE *fp)
ttyplay(fp, 0, ttyread, ttynowrite, ttynowait);
}
-void ttyplayback (FILE *fp, double speed,
+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,
+void ttypeek (FILE *fp, double speed,
ReadFunc read_func, WaitFunc wait_func)
{
ttyskipall(fp);
ttyplay(fp, speed, ttypread, ttywrite, ttynowait);
}
+void ttytime (FILE *fp, double speed,
+ ReadFunc read_func, WaitFunc wait_func)
+{
+ ttyplay(fp, speed, ttyread, ttywrite_time_raw, ttynowait);
+}
+
void
usage (void)
@@ -272,6 +296,7 @@ usage (void)
printf(" -s SPEED Set speed to SPEED [1.0]\n");
printf(" -n No wait mode\n");
printf(" -p Peek another person's ttyrecord\n");
+ printf(" -t Show timing info\n");
exit(EXIT_FAILURE);
}
@@ -287,7 +312,7 @@ input_from_stdin (void)
return efdopen(fd, "r");
}
-int
+int
main (int argc, char **argv)
{
double speed = 1.0;
@@ -299,7 +324,7 @@ main (int argc, char **argv)
set_progname(argv[0]);
while (1) {
- int ch = getopt(argc, argv, "s:np");
+ int ch = getopt(argc, argv, "s:npt");
if (ch == EOF) {
break;
}
@@ -317,6 +342,9 @@ main (int argc, char **argv)
case 'p':
process = ttypeek;
break;
+ case 't':
+ process = ttytime;
+ break;
default:
usage();
}
--- ttyrec-1.0.8/ttyrec.c 2011-09-28 22:20:27.000000000 +0200
+++ ttyrec-with-input/ttyrec.c 2011-09-28 22:18:55.000000000 +0200
@@ -95,6 +95,7 @@ void doshell(const char*);
char *shell;
FILE *fscript;
+FILE *fscript_in;
int master;
int slave;
int child;
@@ -110,8 +111,9 @@ int l;
char line[] = "/dev/ptyXX";
#endif
#endif /* !SVR4 */
-int aflg;
-int uflg;
+int aflg = 0;
+int uflg = 0;
+int in = 0;
static void
resize(int dummy) {
@@ -132,7 +134,7 @@ main(argc, argv)
char *getenv();
char *command = NULL;
- while ((ch = getopt(argc, argv, "aue:h?")) != EOF)
+ while ((ch = getopt(argc, argv, "aue:ih?")) != EOF)
switch((char)ch) {
case 'a':
aflg++;
@@ -140,13 +142,16 @@ main(argc, argv)
case 'u':
uflg++;
break;
+ case 'i':
+ in++;
+ break;
case 'e':
command = strdup(optarg);
break;
case 'h':
case '?':
default:
- fprintf(stderr, _("usage: ttyrec [-u] [-e command] [-a] [file]
\n"));
+ fprintf(stderr, _("usage: ttyrec [-u] [-e command] [-a] [-i] [file]
\n"));
exit(1);
}
argc -= optind;
@@ -162,6 +167,21 @@ main(argc, argv)
}
setbuf(fscript, NULL);
+ if (in) {
+ char *fname_in = malloc(strlen(fname) + sizeof(".in"));
+ if (!fname_in) {
+ perror("malloc");
+ fail();
+ }
+ strcpy(fname_in, fname);
+ strcat(fname_in, ".in");
+ if ((fscript_in = fopen(fname_in, aflg ? "a" : "w")) == NULL) {
+ perror(fname_in);
+ fail();
+ }
+ free(fname_in);
+ }
+
shell = getenv("SHELL");
if (shell == NULL)
shell = "/bin/sh";
@@ -179,6 +199,8 @@ main(argc, argv)
fail();
}
if (child == 0) {
+ if (fscript_in)
+ fclose(fscript_in);
subchild = child = fork();
if (child < 0) {
perror("fork");
@@ -199,18 +221,34 @@ main(argc, argv)
return 0;
}
+static int
+check_input(const char *str, int len)
+{
+ /* don't know (yet) when to 'forbid' input tracking */
+ return len;
+}
+
+
void
doinput()
{
register int cc;
char ibuf[BUFSIZ];
+ Header h;
(void) fclose(fscript);
#ifdef HAVE_openpty
(void) close(slave);
#endif
- while ((cc = read(0, ibuf, BUFSIZ)) > 0)
+ while ((cc = read(0, ibuf, BUFSIZ)) > 0) {
+ h.len = cc;
+ gettimeofday(&
h.tv, NULL);
(void) write(master, ibuf, cc);
+ if ((cc = check_input(ibuf, cc)) && fscript_in) {
+ (void) write_header(fscript_in, &h);
+ (void) fwrite(ibuf, 1, cc, fscript_in);
+ }
+ }
done();
}
@@ -252,8 +290,8 @@ check_line (const char *line)
} else {
int dummy; char dummy2[BUFSIZ];
if (sscanf(line, "begin %o %s", &dummy, dummy2) == 2) {
- /*
- * uuencode line found!
+ /*
+ * uuencode line found!
*/
uudecode = popen("uudecode", "w");
fprintf(uudecode, "%s", line);
@@ -288,6 +326,7 @@ uu_check_output(const char *str, int len
}
}
+
static int
check_output(char *str, int len)
{
@@ -358,7 +397,7 @@ doshell(const char* command)
if (!command) {
execl(shell, strrchr(shell, '/') + 1, "-i", NULL);
} else {
- execl(shell, strrchr(shell, '/') + 1, "-c", command, NULL);
+ execl(shell, strrchr(shell, '/') + 1, "-c", command, NULL);
}
perror(shell);
fail();
--- ttyrec-1.0.8/ttytime.c 2006-06-11 17:52:50.000000000 +0200
+++ ttyrec-with-input/ttytime.c 2011-09-28 22:18:55.000000000 +0200
@@ -43,6 +43,7 @@ calc_time (const char *filename)
{
Header start, end;
FILE *fp = efopen(filename, "r");
+ end.tv.tv_sec = 0;
read_header(fp, &start);
fseek(fp, start.len, SEEK_CUR);