watch: refactor code, add output_header() and run_command()

The main() was starting to be quite long with deep indents.  Both
signs of clean up being welcome change.

FIXME: The comment also changes -g option to exit immediately,
when screen contents change.  This should be separated commit,
e.g. the stuff is not ready to be merged.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
This commit is contained in:
Sami Kerola 2012-03-10 13:29:34 +01:00
parent dbedc905fe
commit 85e0a23347

583
watch.c
View File

@ -62,6 +62,16 @@
# define isprint(x) ( (x>=' '&&x<='~') || (x>=0xa0) )
#endif
/* Boolean command line options */
static int flags;
#define WATCH_DIFF (1 << 1)
#define WATCH_CUMUL (1 << 2)
#define WATCH_EXEC (1 << 3)
#define WATCH_BEEP (1 << 4)
#define WATCH_COLOR (1 << 5)
#define WATCH_ERREXIT (1 << 6)
#define WATCH_CHGEXIT (1 << 7)
static int curses_started = 0;
static long height = 24, width = 80;
static int screen_size_changed = 0;
@ -285,15 +295,268 @@ wint_t my_getwc(FILE * s)
}
#endif /* WITH_WATCH8BIT */
void output_header(char *restrict command, double interval)
{
time_t t = time(NULL);
char *ts = ctime(&t);
int tsl = strlen(ts);
char *header;
/*
* left justify interval and command, right justify time,
* clipping all to fit window width
*/
int hlen = asprintf(&header, _("Every %.1fs: "), interval);
/*
* 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) {
free(header);
return;
}
if (tsl + hlen + 1 <= width) {
mvaddstr(0, 0, header);
if (tsl + hlen + 2 <= width) {
if (width < tsl + hlen + 4) {
mvaddstr(0, width - tsl - 4, "... ");
} else {
#ifdef WITH_WATCH8BIT
if (width < tsl + hlen + wcommand_columns) {
/* print truncated */
int available = width - tsl - hlen;
int in_use = wcommand_columns;
int wcomm_len = wcommand_characters;
while (available - 4 < in_use) {
wcomm_len--;
in_use = wcswidth(wcommand, wcomm_len);
}
mvaddnwstr(0, hlen, wcommand, wcomm_len);
mvaddstr(0, width - tsl - 4, "... ");
} else {
mvaddwstr(0, hlen, wcommand);
}
#else
mvaddnstr(0, hlen, command, width - tsl - hlen);
#endif /* WITH_WATCH8BIT */
}
}
}
mvaddstr(0, width - tsl + 1, ts);
free(header);
return;
}
int run_command(char *restrict command, char **restrict command_argv)
{
FILE *p;
int x, y;
int oldeolseen = 1;
int pipefd[2];
pid_t child;
int exit_early = 0;
int status;
/* allocate pipes */
if (pipe(pipefd) < 0)
xerr(7, _("unable to create IPC pipes"));
/* flush stdout and stderr, since we're about to do fd stuff */
fflush(stdout);
fflush(stderr);
/* fork to prepare to run command */
child = fork();
if (child < 0) { /* fork error */
xerr(2, _("unable to fork process"));
} 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 */
xerr(3, _("dup2 failed"));
}
dup2(1, 2); /* stderr should default to stdout */
if (flags & WATCH_EXEC) { /* pass command to exec instead of system */
if (execvp(command_argv[0], command_argv) == -1) {
xerr(4, _("unable to execute '%s'"),
command_argv[0]);
}
} else {
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));
}
}
}
/* otherwise, we're in parent */
close(pipefd[1]); /* close write side of pipe */
if ((p = fdopen(pipefd[0], "r")) == NULL)
xerr(5, _("fdopen"));
for (y = show_title; y < height; y++) {
int eolseen = 0, tabpending = 0;
#ifdef WITH_WATCH8BIT
wint_t carry = WEOF;
#endif
for (x = 0; x < width; x++) {
#ifdef WITH_WATCH8BIT
wint_t c = ' ';
#else
int c = ' ';
#endif
int attr = 0;
if (!eolseen) {
/* if there is a tab pending, just
* spit spaces until the next stop
* instead of reading characters */
if (!tabpending)
#ifdef WITH_WATCH8BIT
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'
|| !(flags & WATCH_COLOR)));
#else
do
c = getc(p);
while (c != EOF && !isprint(c)
&& c != '\n'
&& c != '\t'
&& (c != L'\033'
|| !(flags & WATCH_COLOR)));
#endif
if (c == L'\033' && (flags & WATCH_COLOR)) {
x--;
process_ansi(p);
continue;
}
if (c == L'\n')
if (!oldeolseen && x == 0) {
x = -1;
continue;
} else
eolseen = 1;
else if (c == L'\t')
tabpending = 1;
#ifdef WITH_WATCH8BIT
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' ';
#else
if (c == EOF || c == '\n' || c == '\t')
c = ' ';
#endif
if (tabpending && (((x + 1) % 8) == 0))
tabpending = 0;
}
move(y, x);
if (!first_screen && !exit_early && (flags & WATCH_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
}
if (flags & WATCH_DIFF) {
#ifdef WITH_WATCH8BIT
cchar_t oldc;
in_wch(&oldc);
attr = !first_screen
&& ((wchar_t) c != oldc.chars[0]
||
((flags & WATCH_CUMUL)
&& (oldc.attr & A_ATTRIBUTES)));
#else
chtype oldch = inch();
unsigned char oldc = oldch & A_CHARTEXT;
attr = !first_screen
&& ((unsigned char)c != oldc
||
((flags & WATCH_CUMUL)
&& (oldch & A_ATTRIBUTES)));
#endif
}
if (attr)
standout();
#ifdef WITH_WATCH8BIT
addnwstr((wchar_t *) & c, 1);
#else
addch(c);
#endif
if (attr)
standend();
#ifdef WITH_WATCH8BIT
if (wcwidth(c) == 0) {
x--;
}
if (wcwidth(c) == 2) {
x++;
}
#endif
}
oldeolseen = eolseen;
}
fclose(p);
/* harvest child process and get status, propagated from command */
if (waitpid(child, &status, 0) < 0)
xerr(8, _("waitpid"));
/* if child process exited in error, beep if option_beep is set */
if ((!WIFEXITED(status) || WEXITSTATUS(status))) {
if (flags & WATCH_BEEP)
beep();
if (flags & WATCH_ERREXIT) {
mvaddstr(height - 1, 0,
_("command exit with a non-zero status, press a key to exit"));
refresh();
fgetc(stdin);
endwin();
exit(8);
}
}
first_screen = 0;
refresh();
return exit_early;
}
int main(int argc, char *argv[])
{
int optc;
int option_differences = 0,
option_differences_cumulative = 0,
option_exec = 0,
option_beep = 0,
option_color = 0,
option_errexit = 0, option_chgexit = 0;
double interval = 2;
char *command;
char **command_argv;
@ -306,11 +569,6 @@ int main(int argc, char *argv[])
int wcommand_characters = 0; /* not including final \0 */
#endif /* WITH_WATCH8BIT */
int pipefd[2];
int status;
int exit_early = 0;
pid_t child;
static struct option longopts[] = {
{"color", no_argument, 0, 'c'},
{"differences", optional_argument, 0, 'd'},
@ -336,27 +594,27 @@ int main(int argc, char *argv[])
!= EOF) {
switch (optc) {
case 'b':
option_beep = 1;
flags |= WATCH_BEEP;
break;
case 'c':
option_color = 1;
flags |= WATCH_COLOR;
break;
case 'd':
option_differences = 1;
flags |= WATCH_DIFF;
if (optarg)
option_differences_cumulative = 1;
flags |= WATCH_CUMUL;
break;
case 'e':
option_errexit = 1;
flags |= WATCH_ERREXIT;
break;
case 'g':
option_chgexit = 1;
flags |= WATCH_CHGEXIT;
break;
case 't':
show_title = 0;
break;
case 'x':
option_exec = 1;
flags |= WATCH_EXEC;
break;
case 'n':
interval = strtod_or_err(optarg, _("failed to parse argument"));
@ -431,13 +689,14 @@ int main(int argc, char *argv[])
/* Set up tty for curses use. */
curses_started = 1;
initscr();
if (option_color) {
if (flags & WATCH_COLOR) {
if (has_colors()) {
start_color();
use_default_colors();
init_ansi_colors();
} else
option_color = 0;
} else {
flags |= WATCH_COLOR;
}
}
nonl();
noecho();
@ -446,15 +705,7 @@ int main(int argc, char *argv[])
if (precise_timekeeping)
next_loop = get_time_usec();
do {
time_t t = time(NULL);
char *ts = ctime(&t);
int tsl = strlen(ts);
char *header;
FILE *p;
int x, y;
int oldeolseen = 1;
while (1) {
if (screen_size_changed) {
get_terminal_size();
resizeterm(height, width);
@ -464,272 +715,11 @@ int main(int argc, char *argv[])
first_screen = 1;
}
if (show_title) {
/*
* left justify interval and command, right
* justify time, clipping all to fit window
* width
*/
int hlen = asprintf(&header, _("Every %.1fs: "), interval);
if (show_title)
output_header(command, interval);
/*
* 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) {
mvaddstr(0, 0, header);
if (width >= tsl + hlen + 2) {
if (width < tsl + hlen + 4) {
mvaddstr(0,
width - tsl -
4, "... ");
} else {
#ifdef WITH_WATCH8BIT
if (width <
tsl + hlen +
wcommand_columns) {
/* print truncated */
int avail_columns = width - tsl - hlen;
int using_columns = wcommand_columns;
int using_characters = wcommand_characters;
while (using_columns > avail_columns - 4) {
using_characters--;
using_columns
=
wcswidth
(wcommand,
using_characters);
}
mvaddnwstr(0,
hlen,
wcommand,
using_characters);
mvaddstr(0,
width -
tsl -
4,
"... ");
} else {
mvaddwstr(0,
hlen,
wcommand);
}
#else
mvaddnstr(0, hlen,
command,
width - tsl -
hlen);
#endif /* WITH_WATCH8BIT */
}
}
}
mvaddstr(0, width - tsl + 1, ts);
}
free(header);
}
/* allocate pipes */
if (pipe(pipefd) < 0)
xerr(7, _("unable to create IPC pipes"));
/* flush stdout and stderr, since we're about to do fd stuff */
fflush(stdout);
fflush(stderr);
/* fork to prepare to run command */
child = fork();
if (child < 0) { /* fork error */
xerr(2, _("unable to fork process"));
} 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 */
xerr(3, _("dup2 failed"));
}
dup2(1, 2); /* stderr should default to stdout */
if (option_exec) { /* pass command to exec instead of system */
if (execvp(command_argv[0], command_argv) == -1) {
xerr(4, _("unable to execute '%s'"), command_argv[0]);
}
} else {
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));
}
}
}
/* otherwise, we're in parent */
close(pipefd[1]); /* close write side of pipe */
if ((p = fdopen(pipefd[0], "r")) == NULL)
xerr(5, _("fdopen"));
for (y = show_title; y < height; y++) {
int eolseen = 0, tabpending = 0;
#ifdef WITH_WATCH8BIT
wint_t carry = WEOF;
#endif /* WITH_WATCH8BIT */
for (x = 0; x < width; x++) {
#ifdef WITH_WATCH8BIT
wint_t c = ' ';
#else
int c = ' ';
#endif /* WITH_WATCH8BIT */
int attr = 0;
if (!eolseen) {
/* if there is a tab pending, just
* spit spaces until the next stop
* instead of reading characters */
if (!tabpending)
#ifdef WITH_WATCH8BIT
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));
#else
do
c = getc(p);
while (c != EOF && !isprint(c)
&& c != '\n'
&& c != '\t'
&& (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')
if (!oldeolseen && x == 0) {
x = -1;
continue;
} else
eolseen = 1;
else if (c == L'\t')
tabpending = 1;
#ifdef WITH_WATCH8BIT
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' ';
#else
if (c == EOF || c == '\n' || c == '\t')
c = ' ';
#endif /* WITH_WATCH8BIT */
if (tabpending && (((x + 1) % 8) == 0))
tabpending = 0;
}
move(y, x);
if (!first_screen && !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 */
}
if (option_differences) {
#ifdef WITH_WATCH8BIT
cchar_t oldc;
in_wch(&oldc);
attr = !first_screen
&& ((wchar_t) c != oldc.chars[0]
||
(option_differences_cumulative
&& (oldc.
attr & A_ATTRIBUTES)));
#else
chtype oldch = inch();
unsigned char oldc = oldch & A_CHARTEXT;
attr = !first_screen
&& ((unsigned char)c != oldc
||
(option_differences_cumulative
&& (oldch & A_ATTRIBUTES)));
#endif /* WITH_WATCH8BIT */
}
if (attr)
standout();
#ifdef WITH_WATCH8BIT
addnwstr((wchar_t *) & c, 1);
#else
addch(c);
#endif /* WITH_WATCH8BIT */
if (attr)
standend();
#ifdef WITH_WATCH8BIT
if (wcwidth(c) == 0) {
x--;
}
if (wcwidth(c) == 2) {
x++;
}
#endif /* WITH_WATCH8BIT */
}
oldeolseen = eolseen;
}
fclose(p);
/* harvest child process and get status, propagated from command */
if (waitpid(child, &status, 0) < 0)
xerr(8, _("waitpid"));
/* if child process exited in error, beep if option_beep is set */
if ((!WIFEXITED(status) || WEXITSTATUS(status))) {
if (option_beep)
beep();
if (option_errexit) {
mvaddstr(height - 1, 0,
_("command exit with a non-zero status, press a key to exit"));
refresh();
fgetc(stdin);
endwin();
exit(8);
}
}
first_screen = 0;
refresh();
if (run_command(command, command_argv))
break;
if (precise_timekeeping) {
watch_usec_t cur_time = get_time_usec();
next_loop += USECS_PER_SEC * interval;
@ -737,9 +727,8 @@ int main(int argc, char *argv[])
usleep(next_loop - cur_time);
} else
usleep(interval * 1000000);
} while (!exit_early);
}
endwin();
return EXIT_SUCCESS;
}