2011-06-05 18:35:47 +05:30
|
|
|
|
/*
|
|
|
|
|
* watch -- execute a program repeatedly, displaying output fullscreen
|
2002-02-02 04:17:29 +05:30
|
|
|
|
*
|
|
|
|
|
* 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
|
2011-06-05 18:35:47 +05:30
|
|
|
|
* handling, unlimited command length, long line handling) added Apr
|
|
|
|
|
* 1999 by Mike Coleman <mkc@acm.org>.
|
2002-12-15 06:00:17 +05:30
|
|
|
|
*
|
2003-03-18 05:12:00 +05:30
|
|
|
|
* Changes by Albert Cahalan, 2002-2003.
|
2009-11-24 05:30:43 +05:30
|
|
|
|
* stderr handling, exec, and beep option added by Morty Abzug, 2008
|
2009-11-04 00:54:27 +05:30
|
|
|
|
* Unicode Support added by Jarrod Lowe <procps@rrod.net> in 2009.
|
2012-03-02 17:59:36 +05:30
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library 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
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
2002-02-02 04:17:29 +05:30
|
|
|
|
*/
|
|
|
|
|
|
2011-10-09 16:42:04 +05:30
|
|
|
|
#include "c.h"
|
2011-12-20 16:42:37 +05:30
|
|
|
|
#include "config.h"
|
2012-03-23 18:02:24 +05:30
|
|
|
|
#include "fileutils.h"
|
2011-10-09 16:42:04 +05:30
|
|
|
|
#include "nls.h"
|
2012-01-04 23:53:19 +05:30
|
|
|
|
#include "strutils.h"
|
2011-10-10 00:59:26 +05:30
|
|
|
|
#include "xalloc.h"
|
2002-02-02 04:17:29 +05:30
|
|
|
|
#include <ctype.h>
|
2011-06-05 18:35:47 +05:30
|
|
|
|
#include <errno.h>
|
2002-02-02 04:17:29 +05:30
|
|
|
|
#include <getopt.h>
|
2011-06-05 18:35:47 +05:30
|
|
|
|
#include <locale.h>
|
2013-08-25 13:13:20 +05:30
|
|
|
|
#include <limits.h>
|
2002-02-02 04:17:29 +05:30
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
2009-11-24 05:30:46 +05:30
|
|
|
|
#include <sys/time.h>
|
2012-01-24 11:06:15 +05:30
|
|
|
|
#include <sys/wait.h>
|
2011-06-05 18:35:47 +05:30
|
|
|
|
#include <termios.h>
|
2002-02-02 04:17:29 +05:30
|
|
|
|
#include <time.h>
|
|
|
|
|
#include <unistd.h>
|
2011-12-20 16:42:37 +05:30
|
|
|
|
#ifdef WITH_WATCH8BIT
|
2015-07-06 01:07:48 +05:30
|
|
|
|
# define _XOPEN_SOURCE_EXTENDED 1
|
2011-10-09 16:42:04 +05:30
|
|
|
|
# include <wchar.h>
|
2012-05-30 01:09:57 +05:30
|
|
|
|
# include <wctype.h>
|
2011-06-05 18:35:47 +05:30
|
|
|
|
#endif /* WITH_WATCH8BIT */
|
2016-08-07 15:05:20 +05:30
|
|
|
|
#include <ncurses.h>
|
2002-02-02 04:17:29 +05:30
|
|
|
|
|
2004-08-27 19:53:36 +05:30
|
|
|
|
#ifdef FORCE_8BIT
|
2011-10-09 16:42:04 +05:30
|
|
|
|
# undef isprint
|
|
|
|
|
# define isprint(x) ( (x>=' '&&x<='~') || (x>=0xa0) )
|
2004-08-27 19:53:36 +05:30
|
|
|
|
#endif
|
|
|
|
|
|
2016-07-11 04:11:21 +05:30
|
|
|
|
|
2012-03-10 17:59:34 +05:30
|
|
|
|
/* 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)
|
|
|
|
|
|
2002-02-02 04:17:29 +05:30
|
|
|
|
static int curses_started = 0;
|
2012-03-09 03:11:23 +05:30
|
|
|
|
static long height = 24, width = 80;
|
2002-11-29 04:39:48 +05:30
|
|
|
|
static int screen_size_changed = 0;
|
|
|
|
|
static int first_screen = 1;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
static int show_title = 2; /* number of lines used, 2 or 0 */
|
2009-11-24 05:30:46 +05:30
|
|
|
|
static int precise_timekeeping = 0;
|
2020-10-19 16:33:44 +05:30
|
|
|
|
static int line_wrap = 1;
|
2002-02-02 04:17:29 +05:30
|
|
|
|
|
|
|
|
|
#define min(x,y) ((x) > (y) ? (y) : (x))
|
2014-08-20 22:44:13 +05:30
|
|
|
|
#define MAX_ANSIBUF 100
|
2010-03-01 11:47:11 +05:30
|
|
|
|
|
2011-06-05 18:35:47 +05:30
|
|
|
|
static void __attribute__ ((__noreturn__))
|
2020-07-13 08:54:53 +05:30
|
|
|
|
usage(FILE * out)
|
2010-03-01 11:47:11 +05:30
|
|
|
|
{
|
2011-10-09 16:42:04 +05:30
|
|
|
|
fputs(USAGE_HEADER, out);
|
2011-06-05 18:35:47 +05:30
|
|
|
|
fprintf(out,
|
2011-12-17 23:02:47 +05:30
|
|
|
|
_(" %s [options] command\n"), program_invocation_short_name);
|
2011-10-09 16:42:04 +05:30
|
|
|
|
fputs(USAGE_OPTIONS, out);
|
2013-12-28 03:55:39 +05:30
|
|
|
|
fputs(_(" -b, --beep beep if command has a non-zero exit\n"), out);
|
2014-09-06 14:16:27 +05:30
|
|
|
|
fputs(_(" -c, --color interpret ANSI color and style sequences\n"), out);
|
2013-12-28 03:55:39 +05:30
|
|
|
|
fputs(_(" -d, --differences[=<permanent>]\n"
|
|
|
|
|
" highlight changes between updates\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(_(" -n, --interval <secs> seconds to wait between updates\n"), out);
|
|
|
|
|
fputs(_(" -p, --precise attempt run command in precise intervals\n"), out);
|
|
|
|
|
fputs(_(" -t, --no-title turn off header\n"), out);
|
2020-10-19 16:33:44 +05:30
|
|
|
|
fputs(_(" -w, --no-wrap turn off line wrapping\n"), out);
|
2013-12-28 03:55:39 +05:30
|
|
|
|
fputs(_(" -x, --exec pass command to exec instead of \"sh -c\"\n"), out);
|
2011-10-09 16:42:04 +05:30
|
|
|
|
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)"));
|
2011-06-05 18:35:47 +05:30
|
|
|
|
|
|
|
|
|
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
|
2010-03-01 11:47:11 +05:30
|
|
|
|
}
|
|
|
|
|
|
2014-09-06 14:16:27 +05:30
|
|
|
|
static int nr_of_colors;
|
|
|
|
|
static int attributes;
|
|
|
|
|
static int fg_col;
|
|
|
|
|
static int bg_col;
|
2020-07-13 07:38:26 +05:30
|
|
|
|
static int more_colors;
|
2014-09-06 14:16:27 +05:30
|
|
|
|
|
2014-09-08 19:20:24 +05:30
|
|
|
|
|
|
|
|
|
static void reset_ansi(void)
|
|
|
|
|
{
|
|
|
|
|
attributes = A_NORMAL;
|
|
|
|
|
fg_col = 0;
|
|
|
|
|
bg_col = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-05 18:35:47 +05:30
|
|
|
|
static void init_ansi_colors(void)
|
2010-03-01 11:47:11 +05:30
|
|
|
|
{
|
2011-06-05 18:35:47 +05:30
|
|
|
|
short ncurses_colors[] = {
|
2014-09-08 19:20:24 +05:30
|
|
|
|
-1, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW,
|
|
|
|
|
COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE
|
2011-06-05 18:35:47 +05:30
|
|
|
|
};
|
2014-09-06 14:16:27 +05:30
|
|
|
|
nr_of_colors = sizeof(ncurses_colors) / sizeof(short);
|
|
|
|
|
|
2020-07-13 07:38:26 +05:30
|
|
|
|
more_colors = (COLORS >= 16) && (COLOR_PAIRS >= 16 * 16);
|
|
|
|
|
|
|
|
|
|
// Initialize ncurses colors. -1 is terminal default
|
|
|
|
|
// 0-7 are auto created standard colors initialized by ncurses
|
|
|
|
|
if (more_colors) {
|
|
|
|
|
// Initialize using ANSI SGR 8-bit specified colors
|
|
|
|
|
// 8-15 are bright colors
|
|
|
|
|
init_color(8, 333, 333, 333); // Bright black
|
|
|
|
|
init_color(9, 1000, 333, 333); // Bright red
|
|
|
|
|
init_color(10, 333, 1000, 333); // Bright green
|
|
|
|
|
init_color(11, 1000, 1000, 333); // Bright yellow
|
|
|
|
|
init_color(12, 333, 333, 1000); // Bright blue
|
|
|
|
|
init_color(13, 1000, 333, 1000); // Bright magenta
|
|
|
|
|
init_color(14, 333, 1000, 1000); // Bright cyan
|
|
|
|
|
// Often ncurses is built with only 256 colors, so lets
|
|
|
|
|
// stop here - so we can support the -1 terminal default
|
|
|
|
|
//init_color(15, 1000, 1000, 1000); // Bright white
|
|
|
|
|
nr_of_colors += 7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize all color pairs with ncurses
|
2014-09-06 14:16:27 +05:30
|
|
|
|
for (bg_col = 0; bg_col < nr_of_colors; bg_col++)
|
|
|
|
|
for (fg_col = 0; fg_col < nr_of_colors; fg_col++)
|
2020-07-13 07:38:26 +05:30
|
|
|
|
init_pair(bg_col * nr_of_colors + fg_col + 1, fg_col - 1, bg_col - 1);
|
|
|
|
|
|
2014-09-08 19:20:24 +05:30
|
|
|
|
reset_ansi();
|
2010-03-01 11:47:11 +05:30
|
|
|
|
}
|
|
|
|
|
|
2014-09-06 14:16:27 +05:30
|
|
|
|
|
2020-07-13 06:29:29 +05:30
|
|
|
|
static int process_ansi_color_escape_sequence(char** escape_sequence) {
|
2020-07-13 07:38:26 +05:30
|
|
|
|
// process SGR ANSI color escape sequence
|
|
|
|
|
// Eg 8-bit
|
|
|
|
|
// 38;5;⟨n⟩ (set fg color to n)
|
|
|
|
|
// 48;5;⟨n⟩ (set bg color to n)
|
|
|
|
|
//
|
|
|
|
|
// Eg 24-bit (not yet implemented)
|
|
|
|
|
// ESC[ 38;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB foreground color
|
|
|
|
|
// ESC[ 48;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB background color
|
|
|
|
|
int num;
|
2020-07-02 11:35:24 +05:30
|
|
|
|
|
2021-05-06 20:00:54 +05:30
|
|
|
|
if (!escape_sequence)
|
|
|
|
|
return 0; /* avoid NULLPTR dereference, return "not understood" */
|
|
|
|
|
|
2020-07-13 07:38:26 +05:30
|
|
|
|
if ((*escape_sequence)[0] != ';')
|
|
|
|
|
return 0; /* not understood */
|
2020-07-02 11:35:24 +05:30
|
|
|
|
|
2020-07-13 07:38:26 +05:30
|
|
|
|
if ((*escape_sequence)[1] == '5') {
|
|
|
|
|
// 8 bit! ANSI specifies a predefined set of 256 colors here.
|
|
|
|
|
if ((*escape_sequence)[2] != ';')
|
|
|
|
|
return 0; /* not understood */
|
|
|
|
|
num = strtol((*escape_sequence) + 3, escape_sequence, 10);
|
|
|
|
|
if (num >= 0 && num <= 7) {
|
|
|
|
|
// 0-7 are standard colors same as SGR 30-37
|
|
|
|
|
return num + 1;
|
|
|
|
|
}
|
|
|
|
|
if (num >= 8 && num <= 15) {
|
|
|
|
|
// 8-15 are standard colors same as SGR 90-97
|
|
|
|
|
return more_colors ? num + 1 : num - 8 + 1;
|
|
|
|
|
}
|
2020-07-02 11:35:24 +05:30
|
|
|
|
|
2020-07-13 07:38:26 +05:30
|
|
|
|
// Remainder aren't yet implemented
|
|
|
|
|
// 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
|
|
|
|
|
// 232-255: grayscale from black to white in 24 steps
|
|
|
|
|
}
|
2020-07-02 11:35:24 +05:30
|
|
|
|
|
2020-07-13 07:38:26 +05:30
|
|
|
|
return 0; /* not understood */
|
2020-07-02 11:35:24 +05:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-07-02 13:15:29 +05:30
|
|
|
|
static int set_ansi_attribute(const int attrib, char** escape_sequence)
|
2010-03-01 11:47:11 +05:30
|
|
|
|
{
|
2011-06-05 18:35:47 +05:30
|
|
|
|
switch (attrib) {
|
2014-09-06 14:16:27 +05:30
|
|
|
|
case -1: /* restore last settings */
|
|
|
|
|
break;
|
|
|
|
|
case 0: /* restore default settings */
|
2014-09-08 19:20:24 +05:30
|
|
|
|
reset_ansi();
|
2014-09-06 14:16:27 +05:30
|
|
|
|
break;
|
|
|
|
|
case 1: /* set bold / increased intensity */
|
|
|
|
|
attributes |= A_BOLD;
|
|
|
|
|
break;
|
|
|
|
|
case 2: /* set decreased intensity (if supported) */
|
|
|
|
|
attributes |= A_DIM;
|
|
|
|
|
break;
|
|
|
|
|
#ifdef A_ITALIC
|
|
|
|
|
case 3: /* set italic (if supported) */
|
|
|
|
|
attributes |= A_ITALIC;
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
case 4: /* set underline */
|
|
|
|
|
attributes |= A_UNDERLINE;
|
|
|
|
|
break;
|
|
|
|
|
case 5: /* set blinking */
|
|
|
|
|
attributes |= A_BLINK;
|
|
|
|
|
break;
|
|
|
|
|
case 7: /* set inversed */
|
|
|
|
|
attributes |= A_REVERSE;
|
|
|
|
|
break;
|
|
|
|
|
case 21: /* unset bold / increased intensity */
|
|
|
|
|
attributes &= ~A_BOLD;
|
|
|
|
|
break;
|
|
|
|
|
case 22: /* unset bold / any intensity modifier */
|
|
|
|
|
attributes &= ~(A_BOLD | A_DIM);
|
|
|
|
|
break;
|
|
|
|
|
#ifdef A_ITALIC
|
|
|
|
|
case 23: /* unset italic */
|
2014-09-06 14:24:34 +05:30
|
|
|
|
attributes &= ~A_ITALIC;
|
2014-09-06 14:16:27 +05:30
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
case 24: /* unset underline */
|
|
|
|
|
attributes &= ~A_UNDERLINE;
|
|
|
|
|
break;
|
|
|
|
|
case 25: /* unset blinking */
|
|
|
|
|
attributes &= ~A_BLINK;
|
|
|
|
|
break;
|
|
|
|
|
case 27: /* unset inversed */
|
|
|
|
|
attributes &= ~A_REVERSE;
|
|
|
|
|
break;
|
2020-07-02 11:35:24 +05:30
|
|
|
|
case 38:
|
2020-07-13 06:29:29 +05:30
|
|
|
|
fg_col = process_ansi_color_escape_sequence(escape_sequence);
|
2020-07-02 11:35:24 +05:30
|
|
|
|
if (fg_col == 0) {
|
|
|
|
|
return 0; /* not understood */
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-07-13 08:50:23 +05:30
|
|
|
|
case 39:
|
|
|
|
|
fg_col = 0;
|
|
|
|
|
break;
|
2020-07-02 11:35:24 +05:30
|
|
|
|
case 48:
|
2020-07-13 06:29:29 +05:30
|
|
|
|
bg_col = process_ansi_color_escape_sequence(escape_sequence);
|
2020-07-02 11:35:24 +05:30
|
|
|
|
if (bg_col == 0) {
|
|
|
|
|
return 0; /* not understood */
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 49:
|
|
|
|
|
bg_col = 0;
|
|
|
|
|
break;
|
2014-09-06 14:16:27 +05:30
|
|
|
|
default:
|
|
|
|
|
if (attrib >= 30 && attrib <= 37) { /* set foreground color */
|
2014-09-08 19:20:24 +05:30
|
|
|
|
fg_col = attrib - 30 + 1;
|
2014-09-06 14:16:27 +05:30
|
|
|
|
} else if (attrib >= 40 && attrib <= 47) { /* set background color */
|
2014-09-08 19:20:24 +05:30
|
|
|
|
bg_col = attrib - 40 + 1;
|
2020-07-13 07:38:26 +05:30
|
|
|
|
} else if (attrib >= 90 && attrib <= 97) { /* set bright fg color */
|
|
|
|
|
fg_col = more_colors ? attrib - 90 + 9 : attrib - 90 + 1;
|
|
|
|
|
} else if (attrib >= 100 && attrib <= 107) { /* set bright bg color */
|
|
|
|
|
bg_col = more_colors ? attrib - 100 + 9 : attrib - 100 + 1;
|
2016-07-09 10:22:54 +05:30
|
|
|
|
} else {
|
|
|
|
|
return 0; /* Not understood */
|
2014-09-06 14:16:27 +05:30
|
|
|
|
}
|
2011-06-05 18:35:47 +05:30
|
|
|
|
}
|
2020-07-13 07:38:26 +05:30
|
|
|
|
attr_set(attributes, bg_col * nr_of_colors + fg_col + 1, NULL);
|
2020-07-13 08:54:53 +05:30
|
|
|
|
return 1;
|
2010-03-01 11:47:11 +05:30
|
|
|
|
}
|
2002-02-02 04:17:29 +05:30
|
|
|
|
|
2011-06-05 18:35:47 +05:30
|
|
|
|
static void process_ansi(FILE * fp)
|
2002-02-02 04:17:29 +05:30
|
|
|
|
{
|
2013-10-18 11:34:15 +05:30
|
|
|
|
int i, c;
|
2020-07-02 11:35:24 +05:30
|
|
|
|
int ansi_attribute;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
char buf[MAX_ANSIBUF];
|
2013-10-18 11:34:15 +05:30
|
|
|
|
char *numstart, *endptr;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
|
|
|
|
|
c = getc(fp);
|
2018-04-27 18:57:17 +05:30
|
|
|
|
|
|
|
|
|
if (c == '(') {
|
|
|
|
|
c = getc(fp);
|
|
|
|
|
c = getc(fp);
|
|
|
|
|
}
|
2011-06-05 18:35:47 +05:30
|
|
|
|
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;
|
|
|
|
|
}
|
2016-07-08 12:59:59 +05:30
|
|
|
|
if ((c < '0' || c > '9') && c != ';') {
|
2011-06-05 18:35:47 +05:30
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
buf[i] = (char)c;
|
|
|
|
|
}
|
2013-10-18 11:34:15 +05:30
|
|
|
|
/*
|
|
|
|
|
* buf now contains a semicolon-separated list of decimal integers,
|
|
|
|
|
* each indicating an attribute to apply.
|
|
|
|
|
* For example, buf might contain "0;1;31", derived from the color
|
|
|
|
|
* escape sequence "<ESC>[0;1;31m". There can be 1 or more
|
|
|
|
|
* attributes to apply, but typically there are between 1 and 3.
|
|
|
|
|
*/
|
2015-02-25 23:10:53 +05:30
|
|
|
|
|
2020-07-13 08:50:23 +05:30
|
|
|
|
/* Special case of <ESC>[m */
|
|
|
|
|
if (buf[0] == '\0')
|
|
|
|
|
set_ansi_attribute(0, NULL);
|
2015-02-25 23:10:53 +05:30
|
|
|
|
|
2020-07-13 08:50:23 +05:30
|
|
|
|
for (endptr = numstart = buf; *endptr != '\0'; numstart = endptr + 1) {
|
|
|
|
|
ansi_attribute = strtol(numstart, &endptr, 10);
|
|
|
|
|
if (!set_ansi_attribute(ansi_attribute, &endptr))
|
|
|
|
|
break;
|
|
|
|
|
}
|
2002-02-02 04:17:29 +05:30
|
|
|
|
}
|
|
|
|
|
|
2011-06-05 18:35:47 +05:30
|
|
|
|
static void __attribute__ ((__noreturn__)) do_exit(int status)
|
2002-11-29 04:39:48 +05:30
|
|
|
|
{
|
|
|
|
|
if (curses_started)
|
|
|
|
|
endwin();
|
|
|
|
|
exit(status);
|
2002-02-02 04:17:29 +05:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* signal handler */
|
2011-06-05 18:35:47 +05:30
|
|
|
|
static void die(int notused __attribute__ ((__unused__)))
|
2002-02-02 04:17:29 +05:30
|
|
|
|
{
|
2011-06-05 18:35:47 +05:30
|
|
|
|
do_exit(EXIT_SUCCESS);
|
2002-02-02 04:17:29 +05:30
|
|
|
|
}
|
|
|
|
|
|
2011-06-05 18:35:47 +05:30
|
|
|
|
static void winch_handler(int notused __attribute__ ((__unused__)))
|
2002-02-02 04:17:29 +05:30
|
|
|
|
{
|
2002-11-29 04:39:48 +05:30
|
|
|
|
screen_size_changed = 1;
|
2002-02-02 04:17:29 +05:30
|
|
|
|
}
|
|
|
|
|
|
2004-08-27 19:53:36 +05:30
|
|
|
|
static char env_col_buf[24];
|
|
|
|
|
static char env_row_buf[24];
|
|
|
|
|
static int incoming_cols;
|
|
|
|
|
static int incoming_rows;
|
|
|
|
|
|
2011-06-05 18:35:47 +05:30
|
|
|
|
static void get_terminal_size(void)
|
2002-02-02 04:17:29 +05:30
|
|
|
|
{
|
2002-11-29 04:39:48 +05:30
|
|
|
|
struct winsize w;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
if (!incoming_cols) {
|
|
|
|
|
/* have we checked COLUMNS? */
|
2004-08-27 19:53:36 +05:30
|
|
|
|
const char *s = getenv("COLUMNS");
|
|
|
|
|
incoming_cols = -1;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
if (s && *s) {
|
2004-08-27 19:53:36 +05:30
|
|
|
|
long t;
|
|
|
|
|
char *endptr;
|
|
|
|
|
t = strtol(s, &endptr, 0);
|
2012-03-09 03:11:23 +05:30
|
|
|
|
if (!*endptr && 0 < t)
|
|
|
|
|
incoming_cols = t;
|
2004-08-27 19:53:36 +05:30
|
|
|
|
width = incoming_cols;
|
2012-03-09 03:11:23 +05:30
|
|
|
|
snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%ld",
|
2011-06-05 18:35:47 +05:30
|
|
|
|
width);
|
2004-08-27 19:53:36 +05:30
|
|
|
|
putenv(env_col_buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-06-05 18:35:47 +05:30
|
|
|
|
if (!incoming_rows) {
|
|
|
|
|
/* have we checked LINES? */
|
2004-08-27 19:53:36 +05:30
|
|
|
|
const char *s = getenv("LINES");
|
|
|
|
|
incoming_rows = -1;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
if (s && *s) {
|
2004-08-27 19:53:36 +05:30
|
|
|
|
long t;
|
|
|
|
|
char *endptr;
|
|
|
|
|
t = strtol(s, &endptr, 0);
|
2012-03-09 03:11:23 +05:30
|
|
|
|
if (!*endptr && 0 < t)
|
|
|
|
|
incoming_rows = t;
|
2004-08-27 19:53:36 +05:30
|
|
|
|
height = incoming_rows;
|
2012-03-09 03:11:23 +05:30
|
|
|
|
snprintf(env_row_buf, sizeof env_row_buf, "LINES=%ld",
|
2011-06-05 18:35:47 +05:30
|
|
|
|
height);
|
2004-08-27 19:53:36 +05:30
|
|
|
|
putenv(env_row_buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-03-09 03:11:23 +05:30
|
|
|
|
if (ioctl(STDERR_FILENO, TIOCGWINSZ, &w) == 0) {
|
|
|
|
|
if (incoming_cols < 0 || incoming_rows < 0) {
|
2011-06-05 18:35:47 +05:30
|
|
|
|
if (incoming_rows < 0 && w.ws_row > 0) {
|
2004-08-27 19:53:36 +05:30
|
|
|
|
height = w.ws_row;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
snprintf(env_row_buf, sizeof env_row_buf,
|
2012-03-09 03:11:23 +05:30
|
|
|
|
"LINES=%ld", height);
|
2004-08-27 19:53:36 +05:30
|
|
|
|
putenv(env_row_buf);
|
|
|
|
|
}
|
2011-06-05 18:35:47 +05:30
|
|
|
|
if (incoming_cols < 0 && w.ws_col > 0) {
|
2004-08-27 19:53:36 +05:30
|
|
|
|
width = w.ws_col;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
snprintf(env_col_buf, sizeof env_col_buf,
|
2012-03-09 03:11:23 +05:30
|
|
|
|
"COLUMNS=%ld", width);
|
2004-08-27 19:53:36 +05:30
|
|
|
|
putenv(env_col_buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-11-29 04:39:48 +05:30
|
|
|
|
}
|
2002-02-02 04:17:29 +05:30
|
|
|
|
}
|
|
|
|
|
|
2009-11-24 05:30:46 +05:30
|
|
|
|
/* get current time in usec */
|
|
|
|
|
typedef unsigned long long watch_usec_t;
|
|
|
|
|
#define USECS_PER_SEC (1000000ull)
|
2012-11-02 23:20:58 +05:30
|
|
|
|
static watch_usec_t get_time_usec()
|
2011-06-05 18:35:47 +05:30
|
|
|
|
{
|
2009-11-24 05:30:46 +05:30
|
|
|
|
struct timeval now;
|
|
|
|
|
gettimeofday(&now, NULL);
|
2011-06-05 18:35:47 +05:30
|
|
|
|
return USECS_PER_SEC * now.tv_sec + now.tv_usec;
|
2009-11-24 05:30:46 +05:30
|
|
|
|
}
|
|
|
|
|
|
2011-12-20 16:42:37 +05:30
|
|
|
|
#ifdef WITH_WATCH8BIT
|
2011-06-05 18:35:47 +05:30
|
|
|
|
/* read a wide character from a popen'd stream */
|
2009-11-04 00:54:27 +05:30
|
|
|
|
#define MAX_ENC_BYTES 16
|
2011-06-05 18:35:47 +05:30
|
|
|
|
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];
|
2009-11-04 00:54:27 +05:30
|
|
|
|
int byte = 0;
|
|
|
|
|
int convert;
|
|
|
|
|
int x;
|
|
|
|
|
wchar_t rval;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
while (1) {
|
2009-11-04 00:54:27 +05:30
|
|
|
|
i[byte] = getc(s);
|
2011-06-05 18:35:47 +05:30
|
|
|
|
if (i[byte] == EOF) {
|
|
|
|
|
return WEOF;
|
|
|
|
|
}
|
2009-11-04 00:54:27 +05:30
|
|
|
|
byte++;
|
|
|
|
|
errno = 0;
|
|
|
|
|
mbtowc(NULL, NULL, 0);
|
|
|
|
|
convert = mbtowc(&rval, i, byte);
|
|
|
|
|
x = errno;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
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;
|
2009-11-04 00:54:27 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-06-05 18:35:47 +05:30
|
|
|
|
#endif /* WITH_WATCH8BIT */
|
2009-11-04 00:54:27 +05:30
|
|
|
|
|
2012-05-20 12:38:29 +05:30
|
|
|
|
#ifdef WITH_WATCH8BIT
|
2016-07-02 11:37:56 +05:30
|
|
|
|
static void output_header(wchar_t *restrict wcommand, int wcommand_characters, double interval)
|
2012-05-20 12:38:29 +05:30
|
|
|
|
#else
|
2012-11-02 23:20:58 +05:30
|
|
|
|
static void output_header(char *restrict command, double interval)
|
2012-05-20 12:38:29 +05:30
|
|
|
|
#endif /* WITH_WATCH8BIT */
|
2012-03-10 17:59:34 +05:30
|
|
|
|
{
|
|
|
|
|
time_t t = time(NULL);
|
|
|
|
|
char *ts = ctime(&t);
|
|
|
|
|
char *header;
|
2016-07-02 11:15:36 +05:30
|
|
|
|
char *right_header;
|
2020-07-13 08:54:53 +05:30
|
|
|
|
int max_host_name_len = (int) sysconf(_SC_HOST_NAME_MAX);
|
2018-03-03 13:06:44 +05:30
|
|
|
|
char hostname[max_host_name_len + 1];
|
2016-07-02 11:37:56 +05:30
|
|
|
|
int command_columns = 0; /* not including final \0 */
|
2016-07-02 11:15:36 +05:30
|
|
|
|
|
|
|
|
|
gethostname(hostname, sizeof(hostname));
|
2012-03-10 17:59:34 +05:30
|
|
|
|
|
|
|
|
|
/*
|
2016-07-02 11:15:36 +05:30
|
|
|
|
* left justify interval and command, right justify hostname and time,
|
2012-03-10 17:59:34 +05:30
|
|
|
|
* clipping all to fit window width
|
|
|
|
|
*/
|
|
|
|
|
int hlen = asprintf(&header, _("Every %.1fs: "), interval);
|
2016-07-02 11:15:36 +05:30
|
|
|
|
int rhlen = asprintf(&right_header, _("%s: %s"), hostname, ts);
|
2012-03-10 17:59:34 +05:30
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* the rules:
|
2016-07-02 11:15:36 +05:30
|
|
|
|
* width < rhlen : print nothing
|
|
|
|
|
* width < rhlen + hlen + 1: print hostname, ts
|
|
|
|
|
* width = rhlen + hlen + 1: print header, hostname, ts
|
|
|
|
|
* width < rhlen + hlen + 4: print header, ..., hostname, ts
|
|
|
|
|
* width < rhlen + hlen + wcommand_columns: print header,
|
|
|
|
|
* truncated wcommand, ..., hostname, ts
|
|
|
|
|
* width > "": print header, wcomand, hostname, ts
|
2012-03-10 17:59:34 +05:30
|
|
|
|
* this is slightly different from how it used to be
|
|
|
|
|
*/
|
2016-07-02 11:15:36 +05:30
|
|
|
|
if (width < rhlen) {
|
2012-03-10 17:59:34 +05:30
|
|
|
|
free(header);
|
2016-07-02 11:15:36 +05:30
|
|
|
|
free(right_header);
|
2012-03-10 17:59:34 +05:30
|
|
|
|
return;
|
|
|
|
|
}
|
2016-07-02 11:15:36 +05:30
|
|
|
|
if (rhlen + hlen + 1 <= width) {
|
2012-03-10 17:59:34 +05:30
|
|
|
|
mvaddstr(0, 0, header);
|
2016-07-02 11:15:36 +05:30
|
|
|
|
if (rhlen + hlen + 2 <= width) {
|
|
|
|
|
if (width < rhlen + hlen + 4) {
|
|
|
|
|
mvaddstr(0, width - rhlen - 4, "... ");
|
2012-03-10 17:59:34 +05:30
|
|
|
|
} else {
|
|
|
|
|
#ifdef WITH_WATCH8BIT
|
2020-07-13 08:54:53 +05:30
|
|
|
|
command_columns = wcswidth(wcommand, -1);
|
2016-07-02 11:37:56 +05:30
|
|
|
|
if (width < rhlen + hlen + command_columns) {
|
2012-03-10 17:59:34 +05:30
|
|
|
|
/* print truncated */
|
2016-07-02 11:15:36 +05:30
|
|
|
|
int available = width - rhlen - hlen;
|
2016-07-02 11:37:56 +05:30
|
|
|
|
int in_use = command_columns;
|
2012-03-10 17:59:34 +05:30
|
|
|
|
int wcomm_len = wcommand_characters;
|
|
|
|
|
while (available - 4 < in_use) {
|
|
|
|
|
wcomm_len--;
|
|
|
|
|
in_use = wcswidth(wcommand, wcomm_len);
|
|
|
|
|
}
|
|
|
|
|
mvaddnwstr(0, hlen, wcommand, wcomm_len);
|
2016-07-02 11:15:36 +05:30
|
|
|
|
mvaddstr(0, width - rhlen - 4, "... ");
|
2012-03-10 17:59:34 +05:30
|
|
|
|
} else {
|
|
|
|
|
mvaddwstr(0, hlen, wcommand);
|
|
|
|
|
}
|
|
|
|
|
#else
|
2020-07-13 08:54:53 +05:30
|
|
|
|
command_columns = strlen(command);
|
|
|
|
|
if (width < rhlen + hlen + command_columns) {
|
|
|
|
|
/* print truncated */
|
|
|
|
|
mvaddnstr(0, hlen, command, width - rhlen - hlen - 4);
|
|
|
|
|
mvaddstr(0, width - rhlen - 4, "... ");
|
|
|
|
|
} else {
|
|
|
|
|
mvaddnstr(0, hlen, command, width - rhlen - hlen);
|
|
|
|
|
}
|
2012-03-10 17:59:34 +05:30
|
|
|
|
#endif /* WITH_WATCH8BIT */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-07-02 11:15:36 +05:30
|
|
|
|
mvaddstr(0, width - rhlen + 1, right_header);
|
2012-03-10 17:59:34 +05:30
|
|
|
|
free(header);
|
2016-07-02 11:15:36 +05:30
|
|
|
|
free(right_header);
|
2012-03-10 17:59:34 +05:30
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-19 16:33:44 +05:30
|
|
|
|
static void find_eol(FILE *p)
|
|
|
|
|
{
|
|
|
|
|
int c;
|
|
|
|
|
#ifdef WITH_WATCH8BIT
|
|
|
|
|
do {
|
|
|
|
|
c = my_getwc(p);
|
|
|
|
|
} while (c != WEOF
|
|
|
|
|
&& c!= L'\n');
|
|
|
|
|
#else
|
|
|
|
|
do {
|
|
|
|
|
c = getc(p);
|
|
|
|
|
} while (c != EOF
|
|
|
|
|
&& c != '\n');
|
|
|
|
|
#endif /* WITH_WATCH8BIT */
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 23:20:58 +05:30
|
|
|
|
static int run_command(char *restrict command, char **restrict command_argv)
|
2012-03-10 17:59:34 +05:30
|
|
|
|
{
|
|
|
|
|
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"));
|
|
|
|
|
}
|
2014-02-04 23:16:58 +05:30
|
|
|
|
close(pipefd[1]); /* once duped, the write fd isn't needed */
|
2012-03-10 17:59:34 +05:30
|
|
|
|
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"));
|
|
|
|
|
|
2014-09-08 19:20:24 +05:30
|
|
|
|
reset_ansi();
|
2012-03-10 17:59:34 +05:30
|
|
|
|
for (y = show_title; y < height; y++) {
|
2014-09-08 21:54:28 +05:30
|
|
|
|
int eolseen = 0, tabpending = 0, tabwaspending = 0;
|
2014-09-08 21:58:08 +05:30
|
|
|
|
if (flags & WATCH_COLOR)
|
2020-07-02 11:35:24 +05:30
|
|
|
|
set_ansi_attribute(-1, NULL);
|
2012-03-10 17:59:34 +05:30
|
|
|
|
#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;
|
|
|
|
|
|
2014-09-06 14:16:27 +05:30
|
|
|
|
if (tabwaspending && (flags & WATCH_COLOR))
|
2020-07-02 11:35:24 +05:30
|
|
|
|
set_ansi_attribute(-1, NULL);
|
2014-09-06 14:16:27 +05:30
|
|
|
|
tabwaspending = 0;
|
|
|
|
|
|
2012-03-10 17:59:34 +05:30
|
|
|
|
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;
|
|
|
|
|
}
|
2012-05-30 01:09:57 +05:30
|
|
|
|
} while (c != WEOF && !iswprint(c)
|
|
|
|
|
&& c < 128
|
2012-03-10 17:59:34 +05:30
|
|
|
|
&& 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 */
|
|
|
|
|
}
|
2014-09-06 14:16:27 +05:30
|
|
|
|
if (c == WEOF || c == L'\n' || c == L'\t') {
|
2012-03-10 17:59:34 +05:30
|
|
|
|
c = L' ';
|
2014-09-06 14:16:27 +05:30
|
|
|
|
if (flags & WATCH_COLOR)
|
|
|
|
|
attrset(A_NORMAL);
|
|
|
|
|
}
|
2012-03-10 17:59:34 +05:30
|
|
|
|
#else
|
2014-09-06 14:16:27 +05:30
|
|
|
|
if (c == EOF || c == '\n' || c == '\t') {
|
2012-03-10 17:59:34 +05:30
|
|
|
|
c = ' ';
|
2014-09-06 14:16:27 +05:30
|
|
|
|
if (flags & WATCH_COLOR)
|
|
|
|
|
attrset(A_NORMAL);
|
|
|
|
|
}
|
2012-03-10 17:59:34 +05:30
|
|
|
|
#endif
|
2014-09-06 14:16:27 +05:30
|
|
|
|
if (tabpending && (((x + 1) % 8) == 0)) {
|
2012-03-10 17:59:34 +05:30
|
|
|
|
tabpending = 0;
|
2014-09-06 14:16:27 +05:30
|
|
|
|
tabwaspending = 1;
|
|
|
|
|
}
|
2012-03-10 17:59:34 +05:30
|
|
|
|
}
|
|
|
|
|
move(y, x);
|
2014-09-06 14:16:27 +05:30
|
|
|
|
|
2012-03-10 17:59:34 +05:30
|
|
|
|
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;
|
2020-10-19 16:33:44 +05:30
|
|
|
|
if (!line_wrap) {
|
|
|
|
|
reset_ansi();
|
|
|
|
|
if (flags & WATCH_COLOR)
|
|
|
|
|
attrset(A_NORMAL);
|
|
|
|
|
find_eol(p);
|
|
|
|
|
}
|
2012-03-10 17:59:34 +05:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(p);
|
|
|
|
|
|
2014-09-06 14:16:27 +05:30
|
|
|
|
|
2012-03-10 17:59:34 +05:30
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-05 18:35:47 +05:30
|
|
|
|
int main(int argc, char *argv[])
|
2002-02-02 04:17:29 +05:30
|
|
|
|
{
|
2002-11-29 04:39:48 +05:30
|
|
|
|
int optc;
|
2006-06-17 10:22:42 +05:30
|
|
|
|
double interval = 2;
|
2018-04-23 16:04:08 +05:30
|
|
|
|
char *interval_string;
|
2002-11-29 04:39:48 +05:30
|
|
|
|
char *command;
|
2009-11-24 05:30:43 +05:30
|
|
|
|
char **command_argv;
|
2002-11-29 04:39:48 +05:30
|
|
|
|
int command_length = 0; /* not including final \0 */
|
2011-06-05 18:35:47 +05:30
|
|
|
|
watch_usec_t next_loop; /* next loop time in us, used for precise time
|
|
|
|
|
* keeping only */
|
2011-12-20 16:42:37 +05:30
|
|
|
|
#ifdef WITH_WATCH8BIT
|
|
|
|
|
wchar_t *wcommand = NULL;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
int wcommand_characters = 0; /* not including final \0 */
|
|
|
|
|
#endif /* WITH_WATCH8BIT */
|
2011-12-20 16:42:37 +05:30
|
|
|
|
|
2011-06-05 18:35:47 +05:30
|
|
|
|
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'},
|
2012-01-26 20:12:37 +05:30
|
|
|
|
{"chgexit", no_argument, 0, 'g'},
|
2011-06-05 18:35:47 +05:30
|
|
|
|
{"exec", no_argument, 0, 'x'},
|
|
|
|
|
{"precise", no_argument, 0, 'p'},
|
|
|
|
|
{"no-title", no_argument, 0, 't'},
|
2020-10-19 16:33:44 +05:30
|
|
|
|
{"no-wrap", no_argument, 0, 'w'},
|
2011-06-05 18:35:47 +05:30
|
|
|
|
{"version", no_argument, 0, 'v'},
|
|
|
|
|
{0, 0, 0, 0}
|
|
|
|
|
};
|
|
|
|
|
|
2013-02-20 23:01:48 +05:30
|
|
|
|
#ifdef HAVE_PROGRAM_INVOCATION_NAME
|
2012-01-03 13:18:43 +05:30
|
|
|
|
program_invocation_name = program_invocation_short_name;
|
2013-02-20 23:01:48 +05:30
|
|
|
|
#endif
|
2002-11-29 04:39:48 +05:30
|
|
|
|
setlocale(LC_ALL, "");
|
2011-12-07 17:57:21 +05:30
|
|
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
|
|
|
textdomain(PACKAGE);
|
2012-03-23 18:02:24 +05:30
|
|
|
|
atexit(close_stdout);
|
2002-11-29 04:39:48 +05:30
|
|
|
|
|
2018-04-23 16:04:08 +05:30
|
|
|
|
interval_string = getenv("WATCH_INTERVAL");
|
2020-05-12 14:52:48 +05:30
|
|
|
|
if(interval_string != NULL)
|
2018-04-23 16:04:08 +05:30
|
|
|
|
interval = strtod_nol_or_err(interval_string, _("Could not parse interval from WATCH_INTERVAL"));
|
|
|
|
|
|
2011-06-05 18:35:47 +05:30
|
|
|
|
while ((optc =
|
2020-10-19 16:33:44 +05:30
|
|
|
|
getopt_long(argc, argv, "+bced::ghn:pvtwx", longopts, (int *)0))
|
2002-11-29 04:39:48 +05:30
|
|
|
|
!= EOF) {
|
|
|
|
|
switch (optc) {
|
2009-11-24 05:30:43 +05:30
|
|
|
|
case 'b':
|
2012-03-10 17:59:34 +05:30
|
|
|
|
flags |= WATCH_BEEP;
|
2009-11-24 05:30:43 +05:30
|
|
|
|
break;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
case 'c':
|
2012-03-10 17:59:34 +05:30
|
|
|
|
flags |= WATCH_COLOR;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
break;
|
2002-11-29 04:39:48 +05:30
|
|
|
|
case 'd':
|
2012-03-10 17:59:34 +05:30
|
|
|
|
flags |= WATCH_DIFF;
|
2002-11-29 04:39:48 +05:30
|
|
|
|
if (optarg)
|
2012-03-10 17:59:34 +05:30
|
|
|
|
flags |= WATCH_CUMUL;
|
2002-11-29 04:39:48 +05:30
|
|
|
|
break;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
case 'e':
|
2012-03-10 17:59:34 +05:30
|
|
|
|
flags |= WATCH_ERREXIT;
|
2002-11-29 04:39:48 +05:30
|
|
|
|
break;
|
2012-01-26 20:12:37 +05:30
|
|
|
|
case 'g':
|
2012-03-10 17:59:34 +05:30
|
|
|
|
flags |= WATCH_CHGEXIT;
|
2012-01-26 20:12:37 +05:30
|
|
|
|
break;
|
2003-02-09 12:57:16 +05:30
|
|
|
|
case 't':
|
|
|
|
|
show_title = 0;
|
|
|
|
|
break;
|
2020-10-19 16:33:44 +05:30
|
|
|
|
case 'w':
|
|
|
|
|
line_wrap = 0;
|
|
|
|
|
break;
|
2009-11-24 05:30:43 +05:30
|
|
|
|
case 'x':
|
2012-03-10 17:59:34 +05:30
|
|
|
|
flags |= WATCH_EXEC;
|
2009-11-24 05:30:43 +05:30
|
|
|
|
break;
|
2002-11-29 04:39:48 +05:30
|
|
|
|
case 'n':
|
2016-07-03 11:44:36 +05:30
|
|
|
|
interval = strtod_nol_or_err(optarg, _("failed to parse argument"));
|
2002-11-29 04:39:48 +05:30
|
|
|
|
break;
|
2009-11-24 05:30:46 +05:30
|
|
|
|
case 'p':
|
|
|
|
|
precise_timekeeping = 1;
|
|
|
|
|
break;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
case 'h':
|
|
|
|
|
usage(stdout);
|
2002-11-29 04:39:48 +05:30
|
|
|
|
break;
|
2011-06-05 18:35:47 +05:30
|
|
|
|
case 'v':
|
2011-10-09 16:42:04 +05:30
|
|
|
|
printf(PROCPS_NG_VERSION);
|
2011-06-05 18:35:47 +05:30
|
|
|
|
return EXIT_SUCCESS;
|
2002-11-29 04:39:48 +05:30
|
|
|
|
default:
|
2011-06-05 18:35:47 +05:30
|
|
|
|
usage(stderr);
|
2002-11-29 04:39:48 +05:30
|
|
|
|
break;
|
|
|
|
|
}
|
2002-02-02 04:17:29 +05:30
|
|
|
|
}
|
2002-11-29 04:39:48 +05:30
|
|
|
|
|
2018-04-23 16:04:08 +05:30
|
|
|
|
if (interval < 0.1)
|
|
|
|
|
interval = 0.1;
|
|
|
|
|
if (interval > UINT_MAX)
|
|
|
|
|
interval = UINT_MAX;
|
|
|
|
|
|
2002-11-29 04:39:48 +05:30
|
|
|
|
if (optind >= argc)
|
2011-06-05 18:35:47 +05:30
|
|
|
|
usage(stderr);
|
2002-11-29 04:39:48 +05:30
|
|
|
|
|
2011-06-05 18:35:47 +05:30
|
|
|
|
/* save for later */
|
|
|
|
|
command_argv = &(argv[optind]);
|
2009-11-24 05:30:43 +05:30
|
|
|
|
|
2011-10-10 00:59:26 +05:30
|
|
|
|
command = xstrdup(argv[optind++]);
|
2002-11-29 04:39:48 +05:30
|
|
|
|
command_length = strlen(command);
|
|
|
|
|
for (; optind < argc; optind++) {
|
|
|
|
|
char *endp;
|
|
|
|
|
int s = strlen(argv[optind]);
|
2011-06-05 18:35:47 +05:30
|
|
|
|
/* space and \0 */
|
2011-10-10 00:59:26 +05:30
|
|
|
|
command = xrealloc(command, command_length + s + 2);
|
2002-11-29 04:39:48 +05:30
|
|
|
|
endp = command + command_length;
|
|
|
|
|
*endp = ' ';
|
|
|
|
|
memcpy(endp + 1, argv[optind], s);
|
2011-06-05 18:35:47 +05:30
|
|
|
|
/* space then string length */
|
|
|
|
|
command_length += 1 + s;
|
2002-11-29 04:39:48 +05:30
|
|
|
|
command[command_length] = '\0';
|
2002-02-02 04:17:29 +05:30
|
|
|
|
}
|
|
|
|
|
|
2011-12-20 16:42:37 +05:30
|
|
|
|
#ifdef WITH_WATCH8BIT
|
2011-06-05 18:35:47 +05:30
|
|
|
|
/* convert to wide for printing purposes */
|
|
|
|
|
/*mbstowcs(NULL, NULL, 0); */
|
2009-11-04 00:54:27 +05:30
|
|
|
|
wcommand_characters = mbstowcs(NULL, command, 0);
|
2011-06-05 18:35:47 +05:30
|
|
|
|
if (wcommand_characters < 0) {
|
2012-01-14 03:08:47 +05:30
|
|
|
|
fprintf(stderr, _("unicode handling error\n"));
|
2011-06-05 18:35:47 +05:30
|
|
|
|
exit(EXIT_FAILURE);
|
2009-11-04 00:54:27 +05:30
|
|
|
|
}
|
2011-06-05 18:35:47 +05:30
|
|
|
|
wcommand =
|
|
|
|
|
(wchar_t *) malloc((wcommand_characters + 1) * sizeof(wcommand));
|
|
|
|
|
if (wcommand == NULL) {
|
2012-01-14 03:08:47 +05:30
|
|
|
|
fprintf(stderr, _("unicode handling error (malloc)\n"));
|
2011-06-05 18:35:47 +05:30
|
|
|
|
exit(EXIT_FAILURE);
|
2009-11-04 00:54:27 +05:30
|
|
|
|
}
|
2011-06-05 18:35:47 +05:30
|
|
|
|
mbstowcs(wcommand, command, wcommand_characters + 1);
|
|
|
|
|
#endif /* WITH_WATCH8BIT */
|
2009-11-04 00:54:27 +05:30
|
|
|
|
|
2002-11-29 04:39:48 +05:30
|
|
|
|
get_terminal_size();
|
|
|
|
|
|
2011-06-05 18:35:47 +05:30
|
|
|
|
/* Catch keyboard interrupts so we can put tty back in a sane
|
|
|
|
|
* state. */
|
2002-11-29 04:39:48 +05:30
|
|
|
|
signal(SIGINT, die);
|
|
|
|
|
signal(SIGTERM, die);
|
|
|
|
|
signal(SIGHUP, die);
|
|
|
|
|
signal(SIGWINCH, winch_handler);
|
|
|
|
|
|
|
|
|
|
/* Set up tty for curses use. */
|
|
|
|
|
curses_started = 1;
|
|
|
|
|
initscr();
|
2012-03-10 17:59:34 +05:30
|
|
|
|
if (flags & WATCH_COLOR) {
|
2011-06-05 18:35:47 +05:30
|
|
|
|
if (has_colors()) {
|
|
|
|
|
start_color();
|
|
|
|
|
use_default_colors();
|
|
|
|
|
init_ansi_colors();
|
2012-03-10 17:59:34 +05:30
|
|
|
|
} else {
|
2019-09-21 11:33:28 +05:30
|
|
|
|
flags &= ~WATCH_COLOR;
|
2012-03-10 17:59:34 +05:30
|
|
|
|
}
|
2011-06-05 18:35:47 +05:30
|
|
|
|
}
|
2002-11-29 04:39:48 +05:30
|
|
|
|
nonl();
|
|
|
|
|
noecho();
|
|
|
|
|
cbreak();
|
|
|
|
|
|
2009-11-24 05:30:46 +05:30
|
|
|
|
if (precise_timekeeping)
|
|
|
|
|
next_loop = get_time_usec();
|
|
|
|
|
|
2012-03-10 17:59:34 +05:30
|
|
|
|
while (1) {
|
2002-11-29 04:39:48 +05:30
|
|
|
|
if (screen_size_changed) {
|
|
|
|
|
get_terminal_size();
|
|
|
|
|
resizeterm(height, width);
|
|
|
|
|
clear();
|
|
|
|
|
/* redrawwin(stdscr); */
|
|
|
|
|
screen_size_changed = 0;
|
|
|
|
|
first_screen = 1;
|
2002-02-02 04:17:29 +05:30
|
|
|
|
}
|
2002-11-29 04:39:48 +05:30
|
|
|
|
|
2012-03-10 17:59:34 +05:30
|
|
|
|
if (show_title)
|
2012-05-20 12:38:29 +05:30
|
|
|
|
#ifdef WITH_WATCH8BIT
|
2016-07-09 08:57:23 +05:30
|
|
|
|
output_header(wcommand, wcommand_characters, interval);
|
2012-05-20 12:38:29 +05:30
|
|
|
|
#else
|
2012-03-10 17:59:34 +05:30
|
|
|
|
output_header(command, interval);
|
2012-05-20 12:38:29 +05:30
|
|
|
|
#endif /* WITH_WATCH8BIT */
|
2002-02-02 04:17:29 +05:30
|
|
|
|
|
2012-03-10 17:59:34 +05:30
|
|
|
|
if (run_command(command, command_argv))
|
|
|
|
|
break;
|
2014-09-06 14:16:27 +05:30
|
|
|
|
|
|
|
|
|
|
2009-11-24 05:30:46 +05:30
|
|
|
|
if (precise_timekeeping) {
|
|
|
|
|
watch_usec_t cur_time = get_time_usec();
|
2011-06-05 18:35:47 +05:30
|
|
|
|
next_loop += USECS_PER_SEC * interval;
|
2009-11-24 05:30:46 +05:30
|
|
|
|
if (cur_time < next_loop)
|
|
|
|
|
usleep(next_loop - cur_time);
|
|
|
|
|
} else
|
2013-08-25 13:13:20 +05:30
|
|
|
|
if (interval < UINT_MAX / USECS_PER_SEC)
|
|
|
|
|
usleep(interval * USECS_PER_SEC);
|
|
|
|
|
else
|
|
|
|
|
sleep(interval);
|
2012-03-10 17:59:34 +05:30
|
|
|
|
}
|
2002-02-02 04:17:29 +05:30
|
|
|
|
|
2012-03-11 06:05:08 +05:30
|
|
|
|
endwin();
|
|
|
|
|
return EXIT_SUCCESS;
|
2002-02-02 04:17:29 +05:30
|
|
|
|
}
|