watch.c 17 KB
Newer Older
1 2
/*
 * watch -- execute a program repeatedly, displaying output fullscreen
csmall's avatar
csmall committed
3 4 5 6 7
 *
 * Based on the original 1991 'watch' by Tony Rems <rembo@unisoft.com>
 * (with mods and corrections by Francois Pinard).
 *
 * Substantially reworked, new features (differences option, SIGWINCH
8 9
 * handling, unlimited command length, long line handling) added Apr
 * 1999 by Mike Coleman <mkc@acm.org>.
10
 *
albert's avatar
albert committed
11
 * Changes by Albert Cahalan, 2002-2003.
12
 * stderr handling, exec, and beep option added by Morty Abzug, 2008
Jarrod Lowe's avatar
Jarrod Lowe committed
13
 * Unicode Support added by Jarrod Lowe <procps@rrod.net> in 2009.
csmall's avatar
csmall committed
14 15
 */

Sami Kerola's avatar
Sami Kerola committed
16
#include "c.h"
17
#include "config.h"
Sami Kerola's avatar
Sami Kerola committed
18
#include "nls.h"
19
#include "proc/procps.h"
Sami Kerola's avatar
Sami Kerola committed
20
#include "strutils.h"
21
#include "xalloc.h"
csmall's avatar
csmall committed
22
#include <ctype.h>
23 24
#include <errno.h>
#include <errno.h>
csmall's avatar
csmall committed
25
#include <getopt.h>
26 27
#include <locale.h>
#include <locale.h>
csmall's avatar
csmall committed
28 29 30 31 32
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
33
#include <sys/time.h>
34
#include <sys/wait.h>
35 36
#include <termios.h>
#include <termios.h>
csmall's avatar
csmall committed
37 38
#include <time.h>
#include <unistd.h>
39
#ifdef WITH_WATCH8BIT
Sami Kerola's avatar
Sami Kerola committed
40 41
# include <wchar.h>
# include <ncursesw/ncurses.h>
42
#else
Sami Kerola's avatar
Sami Kerola committed
43
# include <ncurses.h>
44
#endif	/* WITH_WATCH8BIT */
csmall's avatar
csmall committed
45

albert's avatar
albert committed
46
#ifdef FORCE_8BIT
Sami Kerola's avatar
Sami Kerola committed
47 48
# undef isprint
# define isprint(x) ( (x>=' '&&x<='~') || (x>=0xa0) )
albert's avatar
albert committed
49 50
#endif

csmall's avatar
csmall committed
51
static int curses_started = 0;
albert's avatar
albert committed
52 53 54
static int height = 24, width = 80;
static int screen_size_changed = 0;
static int first_screen = 1;
55
static int show_title = 2;	/* number of lines used, 2 or 0 */
56
static int precise_timekeeping = 0;
csmall's avatar
csmall committed
57 58

#define min(x,y) ((x) > (y) ? (y) : (x))
59 60
#define MAX_ANSIBUF 10

61 62
static void __attribute__ ((__noreturn__))
    usage(FILE * out)
63
{
Sami Kerola's avatar
Sami Kerola committed
64
	fputs(USAGE_HEADER, out);
65
	fprintf(out,
66
              _(" %s [options] command\n"), program_invocation_short_name);
Sami Kerola's avatar
Sami Kerola committed
67
	fputs(USAGE_OPTIONS, out);
68 69
	fputs(_("  -b, --beep             beep if command has a non-zero exit\n"
		"  -c, --color            interpret ANSI color sequences\n"
70 71
		"  -d, --differences[=<permanent>]\n"
                "                         highlight changes between updates\n"
72
		"  -e, --errexit          exit if command has a non-zero exit\n"
73
		"  -g, --chgexit          exit when output from command changes\n"
74 75 76
		"  -n, --interval <secs>  seconds to wait between updates\n"
		"  -p, --precise          attempt run command in precise intervals\n"
		"  -t, --no-title         turn off header\n"
77
		"  -x, --exec             pass command to exec instead of \"sh -c\"\n"), out);
Sami Kerola's avatar
Sami Kerola committed
78 79 80 81
	fputs(USAGE_SEPARATOR, out);
	fputs(USAGE_HELP, out);
	fputs(_(" -v, --version  output version information and exit\n"), out);
	fprintf(out, USAGE_MAN_TAIL("watch(1)"));
82 83

	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
84 85
}

86
static void init_ansi_colors(void)
87
{
88 89 90 91 92 93 94 95
	int i;
	short ncurses_colors[] = {
		COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE,
		COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE
	};

	for (i = 0; i < 8; i++)
		init_pair(i + 1, ncurses_colors[i], -1);
96 97
}

98
static void set_ansi_attribute(const int attrib)
99
{
100 101 102 103 104 105 106 107 108 109 110 111 112 113
	switch (attrib) {
	case -1:
		return;
	case 0:
		standend();
		return;
	case 1:
		attrset(A_BOLD);
		return;
	}
	if (attrib >= 30 && attrib <= 37) {
		color_set(attrib - 29, NULL);
		return;
	}
114
}
csmall's avatar
csmall committed
115

116
static void process_ansi(FILE * fp)
csmall's avatar
csmall committed
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
	int i, c, num1, num2;
	char buf[MAX_ANSIBUF];
	char *nextnum;

	c = getc(fp);
	if (c != '[') {
		ungetc(c, fp);
		return;
	}
	for (i = 0; i < MAX_ANSIBUF; i++) {
		c = getc(fp);
		/* COLOUR SEQUENCE ENDS in 'm' */
		if (c == 'm') {
			buf[i] = '\0';
			break;
		}
		if (c < '0' && c > '9' && c != ';') {
			while (--i >= 0)
				ungetc(buf[i], fp);
			return;
		}
		buf[i] = (char)c;
	}
	num1 = strtol(buf, &nextnum, 10);
	if (nextnum != buf && nextnum[0] != '\0')
		num2 = strtol(nextnum + 1, NULL, 10);
	else
		num2 = -1;
	set_ansi_attribute(num1);
	set_ansi_attribute(num2);
csmall's avatar
csmall committed
148 149
}

150
static void __attribute__ ((__noreturn__)) do_exit(int status)
albert's avatar
albert committed
151 152 153 154
{
	if (curses_started)
		endwin();
	exit(status);
csmall's avatar
csmall committed
155 156 157
}

/* signal handler */
158
static void die(int notused __attribute__ ((__unused__)))
csmall's avatar
csmall committed
159
{
160
	do_exit(EXIT_SUCCESS);
csmall's avatar
csmall committed
161 162
}

163
static void winch_handler(int notused __attribute__ ((__unused__)))
csmall's avatar
csmall committed
164
{
albert's avatar
albert committed
165
	screen_size_changed = 1;
csmall's avatar
csmall committed
166 167
}

albert's avatar
albert committed
168 169 170 171 172
static char env_col_buf[24];
static char env_row_buf[24];
static int incoming_cols;
static int incoming_rows;

173
static void get_terminal_size(void)
csmall's avatar
csmall committed
174
{
albert's avatar
albert committed
175
	struct winsize w;
176 177
	if (!incoming_cols) {
		/* have we checked COLUMNS? */
albert's avatar
albert committed
178 179
		const char *s = getenv("COLUMNS");
		incoming_cols = -1;
180
		if (s && *s) {
albert's avatar
albert committed
181 182 183
			long t;
			char *endptr;
			t = strtol(s, &endptr, 0);
184 185
			if (!*endptr && (t > 0) && (t < (long)666))
				incoming_cols = (int)t;
albert's avatar
albert committed
186
			width = incoming_cols;
187 188
			snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d",
				 width);
albert's avatar
albert committed
189 190 191
			putenv(env_col_buf);
		}
	}
192 193
	if (!incoming_rows) {
		/* have we checked LINES? */
albert's avatar
albert committed
194 195
		const char *s = getenv("LINES");
		incoming_rows = -1;
196
		if (s && *s) {
albert's avatar
albert committed
197 198 199
			long t;
			char *endptr;
			t = strtol(s, &endptr, 0);
200 201
			if (!*endptr && (t > 0) && (t < (long)666))
				incoming_rows = (int)t;
albert's avatar
albert committed
202
			height = incoming_rows;
203 204
			snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d",
				 height);
albert's avatar
albert committed
205 206 207
			putenv(env_row_buf);
		}
	}
208
	if (incoming_cols < 0 || incoming_rows < 0) {
albert's avatar
albert committed
209
		if (ioctl(2, TIOCGWINSZ, &w) == 0) {
210
			if (incoming_rows < 0 && w.ws_row > 0) {
albert's avatar
albert committed
211
				height = w.ws_row;
212 213
				snprintf(env_row_buf, sizeof env_row_buf,
					 "LINES=%d", height);
albert's avatar
albert committed
214 215
				putenv(env_row_buf);
			}
216
			if (incoming_cols < 0 && w.ws_col > 0) {
albert's avatar
albert committed
217
				width = w.ws_col;
218 219
				snprintf(env_col_buf, sizeof env_col_buf,
					 "COLUMNS=%d", width);
albert's avatar
albert committed
220 221 222
				putenv(env_col_buf);
			}
		}
albert's avatar
albert committed
223
	}
csmall's avatar
csmall committed
224 225
}

226 227 228
/* get current time in usec */
typedef unsigned long long watch_usec_t;
#define USECS_PER_SEC (1000000ull)
229 230
watch_usec_t get_time_usec()
{
231 232
	struct timeval now;
	gettimeofday(&now, NULL);
233
	return USECS_PER_SEC * now.tv_sec + now.tv_usec;
234 235
}

236
#ifdef WITH_WATCH8BIT
237
/* read a wide character from a popen'd stream */
Jarrod Lowe's avatar
Jarrod Lowe committed
238
#define MAX_ENC_BYTES 16
239 240 241 242 243
wint_t my_getwc(FILE * s);
wint_t my_getwc(FILE * s)
{
	/* assuming no encoding ever consumes more than 16 bytes */
	char i[MAX_ENC_BYTES];
Jarrod Lowe's avatar
Jarrod Lowe committed
244 245 246 247
	int byte = 0;
	int convert;
	int x;
	wchar_t rval;
248
	while (1) {
Jarrod Lowe's avatar
Jarrod Lowe committed
249
		i[byte] = getc(s);
250 251 252
		if (i[byte] == EOF) {
			return WEOF;
		}
Jarrod Lowe's avatar
Jarrod Lowe committed
253 254 255 256 257
		byte++;
		errno = 0;
		mbtowc(NULL, NULL, 0);
		convert = mbtowc(&rval, i, byte);
		x = errno;
258 259 260 261 262 263 264 265 266 267 268
		if (convert > 0) {
			/* legal conversion */
			return rval;
		}
		if (byte == MAX_ENC_BYTES) {
			while (byte > 1) {
				/* at least *try* to fix up */
				ungetc(i[--byte], s);
			}
			errno = -EILSEQ;
			return WEOF;
Jarrod Lowe's avatar
Jarrod Lowe committed
269 270 271
		}
	}
}
272
#endif	/* WITH_WATCH8BIT */
Jarrod Lowe's avatar
Jarrod Lowe committed
273

274
int main(int argc, char *argv[])
csmall's avatar
csmall committed
275
{
albert's avatar
albert committed
276 277 278
	int optc;
	int option_differences = 0,
	    option_differences_cumulative = 0,
279 280 281
	    option_exec = 0,
	    option_beep = 0,
	    option_color = 0,
282
	    option_errexit = 0, option_chgexit = 0;
283
	double interval = 2;
albert's avatar
albert committed
284
	char *command;
285
	char **command_argv;
albert's avatar
albert committed
286
	int command_length = 0;	/* not including final \0 */
287 288
	watch_usec_t next_loop;	/* next loop time in us, used for precise time
				 * keeping only */
289 290 291
#ifdef WITH_WATCH8BIT
	wchar_t *wcommand = NULL;
	int wcommand_columns = 0;	/* not including final \0 */
292 293
	int wcommand_characters = 0;	/* not including final \0 */
#endif	/* WITH_WATCH8BIT */
294

295 296
	int pipefd[2];
	int status;
297
	int exit_early = 0;
298
	pid_t child;
albert's avatar
albert committed
299

300 301 302 303 304 305 306
	static struct option longopts[] = {
		{"color", no_argument, 0, 'c'},
		{"differences", optional_argument, 0, 'd'},
		{"help", no_argument, 0, 'h'},
		{"interval", required_argument, 0, 'n'},
		{"beep", no_argument, 0, 'b'},
		{"errexit", no_argument, 0, 'e'},
307
		{"chgexit", no_argument, 0, 'g'},
308 309 310 311 312 313 314
		{"exec", no_argument, 0, 'x'},
		{"precise", no_argument, 0, 'p'},
		{"no-title", no_argument, 0, 't'},
		{"version", no_argument, 0, 'v'},
		{0, 0, 0, 0}
	};

315
	program_invocation_name = program_invocation_short_name;
albert's avatar
albert committed
316
	setlocale(LC_ALL, "");
317 318
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
albert's avatar
albert committed
319

320
	while ((optc =
321
		getopt_long(argc, argv, "+bced::ghn:pvtx", longopts, (int *)0))
albert's avatar
albert committed
322 323
	       != EOF) {
		switch (optc) {
324 325 326
		case 'b':
			option_beep = 1;
			break;
327 328 329
		case 'c':
			option_color = 1;
			break;
albert's avatar
albert committed
330 331 332 333 334
		case 'd':
			option_differences = 1;
			if (optarg)
				option_differences_cumulative = 1;
			break;
335 336
		case 'e':
			option_errexit = 1;
albert's avatar
albert committed
337
			break;
338 339 340
		case 'g':
			option_chgexit = 1;
			break;
albert's avatar
albert committed
341 342 343
		case 't':
			show_title = 0;
			break;
344
		case 'x':
345
			option_exec = 1;
346
			break;
albert's avatar
albert committed
347
		case 'n':
348 349 350 351 352
			interval = strtod_or_err(optarg, _("failed to parse argument"));
			if (interval < 0.1)
				interval = 0.1;
			if (interval > ~0u / 1000000)
				interval = ~0u / 1000000;
albert's avatar
albert committed
353
			break;
354 355 356
		case 'p':
			precise_timekeeping = 1;
			break;
357 358
		case 'h':
			usage(stdout);
albert's avatar
albert committed
359
			break;
360
		case 'v':
Sami Kerola's avatar
Sami Kerola committed
361
			printf(PROCPS_NG_VERSION);
362
			return EXIT_SUCCESS;
albert's avatar
albert committed
363
		default:
364
			usage(stderr);
albert's avatar
albert committed
365 366
			break;
		}
csmall's avatar
csmall committed
367
	}
albert's avatar
albert committed
368 369

	if (optind >= argc)
370
		usage(stderr);
albert's avatar
albert committed
371

372 373
	/* save for later */
	command_argv = &(argv[optind]);
374

375
	command = xstrdup(argv[optind++]);
albert's avatar
albert committed
376 377 378 379
	command_length = strlen(command);
	for (; optind < argc; optind++) {
		char *endp;
		int s = strlen(argv[optind]);
380
		/* space and \0 */
381
		command = xrealloc(command, command_length + s + 2);
albert's avatar
albert committed
382 383 384
		endp = command + command_length;
		*endp = ' ';
		memcpy(endp + 1, argv[optind], s);
385 386
		/* space then string length */
		command_length += 1 + s;
albert's avatar
albert committed
387
		command[command_length] = '\0';
csmall's avatar
csmall committed
388 389
	}

390
#ifdef WITH_WATCH8BIT
391 392
	/* convert to wide for printing purposes */
	/*mbstowcs(NULL, NULL, 0); */
Jarrod Lowe's avatar
Jarrod Lowe committed
393
	wcommand_characters = mbstowcs(NULL, command, 0);
394
	if (wcommand_characters < 0) {
395
		fprintf(stderr, _("unicode handling error\n"));
396
		exit(EXIT_FAILURE);
Jarrod Lowe's avatar
Jarrod Lowe committed
397
	}
398 399 400
	wcommand =
	    (wchar_t *) malloc((wcommand_characters + 1) * sizeof(wcommand));
	if (wcommand == NULL) {
401
		fprintf(stderr, _("unicode handling error (malloc)\n"));
402
		exit(EXIT_FAILURE);
Jarrod Lowe's avatar
Jarrod Lowe committed
403
	}
404
	mbstowcs(wcommand, command, wcommand_characters + 1);
Jarrod Lowe's avatar
Jarrod Lowe committed
405
	wcommand_columns = wcswidth(wcommand, -1);
406
#endif	/* WITH_WATCH8BIT */
Jarrod Lowe's avatar
Jarrod Lowe committed
407

albert's avatar
albert committed
408 409
	get_terminal_size();

410 411
	/* Catch keyboard interrupts so we can put tty back in a sane
	 * state.  */
albert's avatar
albert committed
412 413 414 415 416 417 418 419
	signal(SIGINT, die);
	signal(SIGTERM, die);
	signal(SIGHUP, die);
	signal(SIGWINCH, winch_handler);

	/* Set up tty for curses use.  */
	curses_started = 1;
	initscr();
420 421 422 423 424 425 426 427
	if (option_color) {
		if (has_colors()) {
			start_color();
			use_default_colors();
			init_ansi_colors();
		} else
			option_color = 0;
	}
albert's avatar
albert committed
428 429 430 431
	nonl();
	noecho();
	cbreak();

432 433 434
	if (precise_timekeeping)
		next_loop = get_time_usec();

435
	do {
albert's avatar
albert committed
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
		time_t t = time(NULL);
		char *ts = ctime(&t);
		int tsl = strlen(ts);
		char *header;
		FILE *p;
		int x, y;
		int oldeolseen = 1;

		if (screen_size_changed) {
			get_terminal_size();
			resizeterm(height, width);
			clear();
			/* redrawwin(stdscr); */
			screen_size_changed = 0;
			first_screen = 1;
csmall's avatar
csmall committed
451
		}
albert's avatar
albert committed
452

albert's avatar
albert committed
453
		if (show_title) {
454 455 456 457 458
			/*
			 * left justify interval and command, right
			 * justify time, clipping all to fit window
			 * width
			 */
Sami Kerola's avatar
Sami Kerola committed
459
			int hlen = asprintf(&header, _("Every %.1fs: "), interval);
Jarrod Lowe's avatar
Jarrod Lowe committed
460

461 462 463 464 465 466 467 468 469 470 471 472 473 474
			/*
			 * the rules:
			 *   width < tsl : print nothing
			 *   width < tsl + hlen + 1: print ts
			 *   width = tsl + hlen + 1: print header, ts
			 *   width < tsl + hlen + 4: print header, ..., ts
			 *   width < tsl + hlen +    wcommand_columns: print
			 *                           header, truncated wcommand,
			 *                           ..., ts
			 *   width > "": print header, wcomand, ts
			 * this is slightly different from how it used to be
			 */
			if (width >= tsl) {
				if (width >= tsl + hlen + 1) {
Jarrod Lowe's avatar
Jarrod Lowe committed
475
					mvaddstr(0, 0, header);
476 477 478 479 480 481
					if (width >= tsl + hlen + 2) {
						if (width < tsl + hlen + 4) {
							mvaddstr(0,
								 width - tsl -
								 4, "...  ");
						} else {
482
#ifdef WITH_WATCH8BIT
483 484 485 486
							if (width <
							    tsl + hlen +
							    wcommand_columns) {
								/* print truncated */
Jarrod Lowe's avatar
Jarrod Lowe committed
487 488 489
								int avail_columns = width - tsl - hlen;
								int using_columns = wcommand_columns;
								int using_characters = wcommand_characters;
490
								while (using_columns > avail_columns - 4) {
Jarrod Lowe's avatar
Jarrod Lowe committed
491
									using_characters--;
492 493 494 495 496
									using_columns
									    =
									    wcswidth
									    (wcommand,
									     using_characters);
Jarrod Lowe's avatar
Jarrod Lowe committed
497
								}
498 499 500 501 502 503 504 505 506 507 508 509 510
								mvaddnwstr(0,
									   hlen,
									   wcommand,
									   using_characters);
								mvaddstr(0,
									 width -
									 tsl -
									 4,
									 "... ");
							} else {
								mvaddwstr(0,
									  hlen,
									  wcommand);
Jarrod Lowe's avatar
Jarrod Lowe committed
511
							}
512
#else
513 514 515 516 517
							mvaddnstr(0, hlen,
								  command,
								  width - tsl -
								  hlen);
#endif	/* WITH_WATCH8BIT */
Jarrod Lowe's avatar
Jarrod Lowe committed
518 519 520 521 522 523
						}
					}
				}
				mvaddstr(0, width - tsl + 1, ts);
			}

albert's avatar
albert committed
524 525
			free(header);
		}
albert's avatar
albert committed
526

527
		/* allocate pipes */
528
		if (pipe(pipefd) < 0)
529
			xerr(7, _("unable to create IPC pipes"));
530 531 532 533 534 535

		/* flush stdout and stderr, since we're about to do fd stuff */
		fflush(stdout);
		fflush(stderr);

		/* fork to prepare to run command */
536 537 538
		child = fork();

		if (child < 0) {	/* fork error */
539
			xerr(2, _("unable to fork process"));
540 541 542 543
		} else if (child == 0) {	/* in child */
			close(pipefd[0]);	/* child doesn't need read side of pipe */
			close(1);		/* prepare to replace stdout with pipe */
			if (dup2(pipefd[1], 1) < 0) {	/* replace stdout with write side of pipe */
544
				xerr(3, _("dup2 failed"));
545
			}
546
			dup2(1, 2);	/* stderr should default to stdout */
547

548 549
			if (option_exec) {	/* pass command to exec instead of system */
				if (execvp(command_argv[0], command_argv) == -1) {
550
					xerr(4, _("unable to execute '%s'"), command_argv[0]);
551 552
				}
			} else {
553 554 555 556 557 558 559 560
				status = system(command);	/* watch manpage promises sh quoting */

				/* propagate command exit status as child exit status */
				if (!WIFEXITED(status)) {	/* child exits nonzero if command does */
					exit(EXIT_FAILURE);
				} else {
					exit(WEXITSTATUS(status));
				}
561 562 563 564 565
			}

		}

		/* otherwise, we're in parent */
566 567
		close(pipefd[1]);	/* close write side of pipe */
		if ((p = fdopen(pipefd[0], "r")) == NULL)
568
			xerr(5, _("fdopen"));
569

albert's avatar
albert committed
570
		for (y = show_title; y < height; y++) {
albert's avatar
albert committed
571
			int eolseen = 0, tabpending = 0;
572
#ifdef WITH_WATCH8BIT
Jarrod Lowe's avatar
Jarrod Lowe committed
573
			wint_t carry = WEOF;
574
#endif	/* WITH_WATCH8BIT */
albert's avatar
albert committed
575
			for (x = 0; x < width; x++) {
576
#ifdef WITH_WATCH8BIT
Jarrod Lowe's avatar
Jarrod Lowe committed
577
				wint_t c = ' ';
578 579
#else
				int c = ' ';
580
#endif	/* WITH_WATCH8BIT */
albert's avatar
albert committed
581 582 583
				int attr = 0;

				if (!eolseen) {
584 585 586 587
					/* if there is a tab pending, just
					 * spit spaces until the next stop
					 * instead of reading characters */
					if (!tabpending)
588
#ifdef WITH_WATCH8BIT
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
						do {
							if (carry == WEOF) {
								c = my_getwc(p);
							} else {
								c = carry;
								carry = WEOF;
							}
						} while (c != WEOF
							 && !isprint(c)
							 && c < 12
							 && wcwidth(c) == 0
							 && c != L'\n'
							 && c != L'\t'
							 && (c != L'\033'
							     || option_color !=
							     1));
605 606 607 608 609 610
#else
						do
							c = getc(p);
						while (c != EOF && !isprint(c)
						       && c != '\n'
						       && c != '\t'
611 612 613 614 615 616 617 618 619 620
						       && (c != L'\033'
							   || option_color !=
							   1));
#endif	/* WITH_WATCH8BIT */
					if (c == L'\033' && option_color == 1) {
						x--;
						process_ansi(p);
						continue;
					}
					if (c == L'\n')
albert's avatar
albert committed
621 622 623 624 625
						if (!oldeolseen && x == 0) {
							x = -1;
							continue;
						} else
							eolseen = 1;
Jarrod Lowe's avatar
Jarrod Lowe committed
626
					else if (c == L'\t')
albert's avatar
albert committed
627
						tabpending = 1;
628
#ifdef WITH_WATCH8BIT
629 630 631 632 633 634 635 636 637
					if (x == width - 1 && wcwidth(c) == 2) {
						y++;
						x = -1;		/* process this double-width */
						carry = c;	/* character on the next line */
						continue;	/* because it won't fit here */
					}
					if (c == WEOF || c == L'\n'
					    || c == L'\t')
						c = L' ';
638 639 640
#else
					if (c == EOF || c == '\n' || c == '\t')
						c = ' ';
641
#endif	/* WITH_WATCH8BIT */
albert's avatar
albert committed
642 643 644 645
					if (tabpending && (((x + 1) % 8) == 0))
						tabpending = 0;
				}
				move(y, x);
646 647 648 649 650 651 652 653 654 655 656
				if (!exit_early && option_chgexit) {
#ifdef WITH_WATCH8BIT
					cchar_t oldc;
					in_wch(&oldc);
					exit_early = (wchar_t) c != oldc.chars[0];
#else
					chtype oldch = inch();
					unsigned char oldc = oldch & A_CHARTEXT;
					exit_early = (unsigned char) c != oldc;
#endif	/* WITH_WATCH8BIT */
				}
albert's avatar
albert committed
657
				if (option_differences) {
658
#ifdef WITH_WATCH8BIT
659 660
					cchar_t oldc;
					in_wch(&oldc);
albert's avatar
albert committed
661
					attr = !first_screen
662
					    && ((wchar_t) c != oldc.chars[0]
albert's avatar
albert committed
663 664
						||
						(option_differences_cumulative
665 666
						 && (oldc.
						     attr & A_ATTRIBUTES)));
667 668 669 670 671 672 673 674
#else
					chtype oldch = inch();
					unsigned char oldc = oldch & A_CHARTEXT;
					attr = !first_screen
					    && ((unsigned char)c != oldc
						||
						(option_differences_cumulative
						 && (oldch & A_ATTRIBUTES)));
675
#endif	/* WITH_WATCH8BIT */
albert's avatar
albert committed
676 677 678
				}
				if (attr)
					standout();
679
#ifdef WITH_WATCH8BIT
680
				addnwstr((wchar_t *) & c, 1);
681 682
#else
				addch(c);
683
#endif	/* WITH_WATCH8BIT */
albert's avatar
albert committed
684 685
				if (attr)
					standend();
686
#ifdef WITH_WATCH8BIT
687 688 689 690 691 692 693
				if (wcwidth(c) == 0) {
					x--;
				}
				if (wcwidth(c) == 2) {
					x++;
				}
#endif	/* WITH_WATCH8BIT */
albert's avatar
albert committed
694 695 696
			}
			oldeolseen = eolseen;
		}
csmall's avatar
csmall committed
697

698 699 700
		fclose(p);

		/* harvest child process and get status, propagated from command */
701
		if (waitpid(child, &status, 0) < 0)
702
			xerr(8, _("waitpid"));
703 704 705

		/* if child process exited in error, beep if option_beep is set */
		if ((!WIFEXITED(status) || WEXITSTATUS(status))) {
706 707
			if (option_beep)
				beep();
708
			if (option_errexit) {
709
				mvaddstr(height - 1, 0,
710
					 _("command exit with a non-zero status, press a key to exit"));
711 712
				refresh();
				fgetc(stdin);
713
				endwin();
714
				exit(8);
715
			}
716
		}
albert's avatar
albert committed
717 718
		first_screen = 0;
		refresh();
719 720
		if (precise_timekeeping) {
			watch_usec_t cur_time = get_time_usec();
721
			next_loop += USECS_PER_SEC * interval;
722 723 724
			if (cur_time < next_loop)
				usleep(next_loop - cur_time);
		} else
725
			usleep(interval * 1000000);
726
	} while (!exit_early);
csmall's avatar
csmall committed
727

728
	return EXIT_FAILURE;
csmall's avatar
csmall committed
729
}