File: view.c

package info (click to toggle)
termdebug 2.2+dfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: buster, stretch
  • size: 456 kB
  • ctags: 685
  • sloc: ansic: 5,008; sh: 560; makefile: 48; lex: 46
file content (173 lines) | stat: -rw-r--r-- 4,609 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* Copyright (C) 2010,2013 G.P. Halkes
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 3, as
   published by the Free Software Foundation.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <float.h>
#include <time.h>
#include <errno.h>

#include "common.h"
#include "optionMacros.h"
#include "replay.h"
#include "input.h"

/* TODO:
- perhaps we should empty the input buffer as well and even set no-echo and
  non-canonical mode
- wait for signal before continuing after set_window_size (if we do the
  actual resize that is). However, we first must check whether we actually get
  a signal!
*/

static int key_delay;
static double key_delay_scale = 1.0;
static const char *input;
static td_bool resize_capable;

static void printUsage(void) {
	printf("Usage: tdview [OPTIONS] <recording>\n");
	printf("  -k<delay>, --key-delay=<delay>      Set key delay\n");
	printf("  -V, --version                       Print version and copyright information\n");
	exit(EXIT_SUCCESS);
}

static PARSE_FUNCTION(parse_options)
	OPTIONS
		OPTION('k', "key-delay", REQUIRED_ARG)
			while (*optArg && isspace(*optArg))
				optArg++;
			if (optArg[0] == '/') {
				optArg++;
				PARSE_DOUBLE(key_delay_scale, DBL_MIN, DBL_MAX);
				key_delay = 0;
			} else if (isdigit(*optArg)) {
				PARSE_INT(key_delay, 1, INT_MAX);
				key_delay_scale = 1.0;
			} else {
				fatal("Invalid argument for key-delay option: %s\n", optArg);
			}
		END_OPTION
		OPTION('h', "help", NO_ARG)
			printUsage();
		END_OPTION
		OPTION('V', "version", NO_ARG)
			fputs("tdview " VERSION_STRING "\nCopyright (C) 2010,2013 G.P. Halkes\nLicensed under the GNU General Public License version 3\n", stdout); /* @copyright */
			exit(0);
		END_OPTION

		DOUBLE_DASH
			NO_MORE_OPTIONS;
		END_OPTION

		fatal("Unknown option " OPTFMT "\n", OPTPRARG);
	NO_OPTION
		if (input != NULL)
			fatal("More than one recording specified\n");
		input = optcurrent;
	END_OPTIONS
	if (input == NULL)
		fatal("No recording specified\n");
END_FUNCTION


static void print_string_list(StringListNode *string) {
	for (; string != NULL; string = string->next)
		safe_write(STDOUT_FILENO, string->string, string->length);
}

static void do_delay(int delay) {
	struct timespec delay_spec;

	if (delay == 0)
		return;
	else if (key_delay)
		delay = key_delay;
	else
		delay = delay / key_delay_scale;

	delay_spec.tv_nsec = (long) (delay % 1000) * 1000000;
	delay_spec.tv_sec = delay / 1000;
	while (nanosleep(&delay_spec, &delay_spec) < 0) {
		if (errno == EINTR)
			continue;
		fatal("Error sleeping: %s\n", strerror(errno));
	}
}


static void set_window_size(ExpNode *size) {
	if (resize_capable) {
		char buffer[64];
		size_t result;
		/* FIXME: this should use tparm or similar to ensure proper padding etc. */
		result = sprintf(buffer, "\x1b[8;%d;%dt", size->un.size.rows, size->un.size.columns);
		safe_write(STDOUT_FILENO, buffer, result);
		/* FIXME: wait for terminal to respond because otherwise the next updates may fail. */
	}
}

int tdview_main(int argc, char *argv[]) {

	parse_options(argc, argv);

	if (!isatty(STDIN_FILENO))
		fatal("Can only replay to a terminal devices\n");
	resize_capable = detect_resize_capable();

	reset_lexer(input);
	parse();

	save_tty();
	copy_attrs_to_terminal(STDIN_FILENO);

	while (script != NULL) {
		switch (script->type) {
			case EXP_START:
			case EXP_INTERACT:
			case EXP_ENV:
				/* Ignore stuff that isn't relavant to simply viewing the replay. */
				break;
			case EXP_EXPECT:
				print_string_list(script->un.args);
				break;
			case EXP_SEND: {
				StringListNode *key;

				for (key = script->un.args; key != NULL; key = key->next)
					do_delay(key->delay);
				break;
			}
			case EXP_EXPECT_EXIT:
				exit(EXIT_SUCCESS);
				break;
			case EXP_WINDOW_SIZE:
				do_delay(script->un.size.delay);
				set_window_size(script);
				break;
			case EXP_EXPECT_SUSPEND:
				printf("Process has stopped. Will continue in 3 seconds\n");
				sleep(3);
				break;
			case EXP_REQUIRE_VERSION:
				break;
			default:
				fatal("Internal error\n");
		}
		script = script->next;
	}
	return EXIT_SUCCESS;
}