watch: Add --equexit option

This commit adds a new option called `--equexit` which is the opposite
of `--chgexit`. This option makes it possible to exit when the output
of the given command does not change for the given number of cycles.
A download operation could be given as a use-case since `watch` will
exit when the output does not change anymore, in other words, when
the download is completed.

References:
 procps-ng/procps#232

Signed-off-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
This commit is contained in:
Orhun Parmaksız 2022-03-31 22:36:57 +03:00 committed by Craig Small
parent 3f48e8bdfc
commit 3ac20bf536
2 changed files with 47 additions and 4 deletions

View File

@ -53,6 +53,11 @@ Exit when the output of
.I command .I command
changes. changes.
.TP .TP
\fB\-q\fR, \fB\-\-equexit\fR <cycles>
Exit when output of
.I command
does not change for the given number of cycles.
.TP
\fB\-c\fR, \fB\-\-color\fR \fB\-c\fR, \fB\-\-color\fR
Interpret ANSI color and style sequences. Interpret ANSI color and style sequences.
.TP .TP

46
watch.c
View File

@ -71,6 +71,7 @@ static int flags;
#define WATCH_COLOR (1 << 5) #define WATCH_COLOR (1 << 5)
#define WATCH_ERREXIT (1 << 6) #define WATCH_ERREXIT (1 << 6)
#define WATCH_CHGEXIT (1 << 7) #define WATCH_CHGEXIT (1 << 7)
#define WATCH_EQUEXIT (1 << 8)
static int curses_started = 0; static int curses_started = 0;
static long height = 24, width = 80; static long height = 24, width = 80;
@ -96,6 +97,8 @@ static void __attribute__ ((__noreturn__))
" highlight changes between updates\n"), out); " highlight changes between updates\n"), out);
fputs(_(" -e, --errexit exit if command has a non-zero exit\n"), out); fputs(_(" -e, --errexit exit if command has a non-zero exit\n"), out);
fputs(_(" -g, --chgexit exit when output from command changes\n"), out); fputs(_(" -g, --chgexit exit when output from command changes\n"), out);
fputs(_(" -q, --equexit <cycles>\n"
" exit when output from command does not change\n"), out);
fputs(_(" -n, --interval <secs> seconds to wait between updates\n"), out); fputs(_(" -n, --interval <secs> seconds to wait between updates\n"), out);
fputs(_(" -p, --precise attempt run command in precise intervals\n"), out); fputs(_(" -p, --precise attempt run command in precise intervals\n"), out);
fputs(_(" -t, --no-title turn off header\n"), out); fputs(_(" -t, --no-title turn off header\n"), out);
@ -561,6 +564,8 @@ static int run_command(char *restrict command, char **restrict command_argv)
int pipefd[2]; int pipefd[2];
pid_t child; pid_t child;
int exit_early = 0; int exit_early = 0;
int buffer_size = 0;
int unchanged_buffer = 0;
int status; int status;
/* allocate pipes */ /* allocate pipes */
@ -703,6 +708,20 @@ static int run_command(char *restrict command, char **restrict command_argv)
chtype oldch = inch(); chtype oldch = inch();
unsigned char oldc = oldch & A_CHARTEXT; unsigned char oldc = oldch & A_CHARTEXT;
exit_early = (unsigned char)c != oldc; exit_early = (unsigned char)c != oldc;
#endif
}
if (!first_screen && !exit_early && (flags & WATCH_EQUEXIT)) {
buffer_size++;
#ifdef WITH_WATCH8BIT
cchar_t oldc;
in_wch(&oldc);
if ((wchar_t) c == oldc.chars[0])
unchanged_buffer++;
#else
chtype oldch = inch();
unsigned char oldc = oldch & A_CHARTEXT;
if ((unsigned char)c == oldc)
unchanged_buffer++;
#endif #endif
} }
if (flags & WATCH_DIFF) { if (flags & WATCH_DIFF) {
@ -753,7 +772,6 @@ static int run_command(char *restrict command, char **restrict command_argv)
fclose(p); fclose(p);
/* harvest child process and get status, propagated from command */ /* harvest child process and get status, propagated from command */
if (waitpid(child, &status, 0) < 0) if (waitpid(child, &status, 0) < 0)
xerr(8, _("waitpid")); xerr(8, _("waitpid"));
@ -771,6 +789,10 @@ static int run_command(char *restrict command, char **restrict command_argv)
exit(8); exit(8);
} }
} }
if (unchanged_buffer == buffer_size && (flags & WATCH_EQUEXIT))
exit_early = 1;
first_screen = 0; first_screen = 0;
refresh(); refresh();
return exit_early; return exit_early;
@ -780,6 +802,8 @@ int main(int argc, char *argv[])
{ {
int optc; int optc;
double interval = 2; double interval = 2;
int max_cycles = 1;
int cycle_count = 0;
char *interval_string; char *interval_string;
char *command; char *command;
char **command_argv; char **command_argv;
@ -799,6 +823,7 @@ int main(int argc, char *argv[])
{"beep", no_argument, 0, 'b'}, {"beep", no_argument, 0, 'b'},
{"errexit", no_argument, 0, 'e'}, {"errexit", no_argument, 0, 'e'},
{"chgexit", no_argument, 0, 'g'}, {"chgexit", no_argument, 0, 'g'},
{"equexit", required_argument, 0, 'q'},
{"exec", no_argument, 0, 'x'}, {"exec", no_argument, 0, 'x'},
{"precise", no_argument, 0, 'p'}, {"precise", no_argument, 0, 'p'},
{"no-title", no_argument, 0, 't'}, {"no-title", no_argument, 0, 't'},
@ -820,7 +845,7 @@ int main(int argc, char *argv[])
interval = strtod_nol_or_err(interval_string, _("Could not parse interval from WATCH_INTERVAL")); interval = strtod_nol_or_err(interval_string, _("Could not parse interval from WATCH_INTERVAL"));
while ((optc = while ((optc =
getopt_long(argc, argv, "+bced::ghn:pvtwx", longopts, (int *)0)) getopt_long(argc, argv, "+bced::ghq:n:pvtwx", longopts, (int *)0))
!= EOF) { != EOF) {
switch (optc) { switch (optc) {
case 'b': case 'b':
@ -840,6 +865,10 @@ int main(int argc, char *argv[])
case 'g': case 'g':
flags |= WATCH_CHGEXIT; flags |= WATCH_CHGEXIT;
break; break;
case 'q':
flags |= WATCH_EQUEXIT;
max_cycles = strtod_nol_or_err(optarg, _("failed to parse argument"));
break;
case 't': case 't':
show_title = 0; show_title = 0;
break; break;
@ -955,9 +984,18 @@ int main(int argc, char *argv[])
output_header(command, interval); output_header(command, interval);
#endif /* WITH_WATCH8BIT */ #endif /* WITH_WATCH8BIT */
if (run_command(command, command_argv)) int exit = run_command(command, command_argv);
if (flags & WATCH_EQUEXIT) {
if (cycle_count == max_cycles && exit) {
break;
} else if (exit) {
cycle_count++;
} else {
cycle_count = 0;
}
} else if (exit) {
break; break;
}
if (precise_timekeeping) { if (precise_timekeeping) {
watch_usec_t cur_time = get_time_usec(); watch_usec_t cur_time = get_time_usec();