Bump to procps-ng 3.3.16
[platform/upstream/procps-ng.git] / watch.c
1 /*
2  * watch -- execute a program repeatedly, displaying output fullscreen
3  *
4  * Based on the original 1991 'watch' by Tony Rems <rembo@unisoft.com>
5  * (with mods and corrections by Francois Pinard).
6  *
7  * Substantially reworked, new features (differences option, SIGWINCH
8  * handling, unlimited command length, long line handling) added Apr
9  * 1999 by Mike Coleman <mkc@acm.org>.
10  *
11  * Changes by Albert Cahalan, 2002-2003.
12  * stderr handling, exec, and beep option added by Morty Abzug, 2008
13  * Unicode Support added by Jarrod Lowe <procps@rrod.net> in 2009.
14  *
15  * This library is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU Lesser General Public
17  * License as published by the Free Software Foundation; either
18  * version 2.1 of the License, or (at your option) any later version.
19  *
20  * This library is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * Lesser General Public License for more details.
24  *
25  * You should have received a copy of the GNU Lesser General Public
26  * License along with this library; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
28  */
29
30 #include "c.h"
31 #include "config.h"
32 #include "fileutils.h"
33 #include "nls.h"
34 #include "strutils.h"
35 #include "xalloc.h"
36 #include <ctype.h>
37 #include <errno.h>
38 #include <getopt.h>
39 #include <locale.h>
40 #include <limits.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/ioctl.h>
46 #include <sys/time.h>
47 #include <sys/wait.h>
48 #include <termios.h>
49 #include <time.h>
50 #include <unistd.h>
51 #ifdef WITH_WATCH8BIT
52 # define _XOPEN_SOURCE_EXTENDED 1
53 # include <wchar.h>
54 # include <wctype.h>
55 #endif  /* WITH_WATCH8BIT */
56 #include <ncurses.h>
57
58 #ifdef FORCE_8BIT
59 # undef isprint
60 # define isprint(x) ( (x>=' '&&x<='~') || (x>=0xa0) )
61 #endif
62
63
64 /* Boolean command line options */
65 static int flags;
66 #define WATCH_DIFF      (1 << 1)
67 #define WATCH_CUMUL     (1 << 2)
68 #define WATCH_EXEC      (1 << 3)
69 #define WATCH_BEEP      (1 << 4)
70 #define WATCH_COLOR     (1 << 5)
71 #define WATCH_ERREXIT   (1 << 6)
72 #define WATCH_CHGEXIT   (1 << 7)
73
74 static int curses_started = 0;
75 static long height = 24, width = 80;
76 static int screen_size_changed = 0;
77 static int first_screen = 1;
78 static int show_title = 2;      /* number of lines used, 2 or 0 */
79 static int precise_timekeeping = 0;
80
81 #define min(x,y) ((x) > (y) ? (y) : (x))
82 #define MAX_ANSIBUF 100
83
84 static void __attribute__ ((__noreturn__))
85     usage(FILE * out)
86 {
87         fputs(USAGE_HEADER, out);
88         fprintf(out,
89               _(" %s [options] command\n"), program_invocation_short_name);
90         fputs(USAGE_OPTIONS, out);
91         fputs(_("  -b, --beep             beep if command has a non-zero exit\n"), out);
92         fputs(_("  -c, --color            interpret ANSI color and style sequences\n"), out);
93         fputs(_("  -d, --differences[=<permanent>]\n"
94                 "                         highlight changes between updates\n"), out);
95         fputs(_("  -e, --errexit          exit if command has a non-zero exit\n"), out);
96         fputs(_("  -g, --chgexit          exit when output from command changes\n"), out);
97         fputs(_("  -n, --interval <secs>  seconds to wait between updates\n"), out);
98         fputs(_("  -p, --precise          attempt run command in precise intervals\n"), out);
99         fputs(_("  -t, --no-title         turn off header\n"), out);
100         fputs(_("  -x, --exec             pass command to exec instead of \"sh -c\"\n"), out);
101         fputs(USAGE_SEPARATOR, out);
102         fputs(USAGE_HELP, out);
103         fputs(_(" -v, --version  output version information and exit\n"), out);
104         fprintf(out, USAGE_MAN_TAIL("watch(1)"));
105
106         exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
107 }
108
109 static int nr_of_colors;
110 static int attributes;
111 static int fg_col;
112 static int bg_col;
113
114
115 static void reset_ansi(void)
116 {
117         attributes = A_NORMAL;
118         fg_col = 0;
119         bg_col = 0;
120 }
121
122 static void init_ansi_colors(void)
123 {
124         short ncurses_colors[] = {
125                 -1, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW,
126                 COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE
127         };
128
129         nr_of_colors = sizeof(ncurses_colors) / sizeof(short);
130
131         for (bg_col = 0; bg_col < nr_of_colors; bg_col++)
132                 for (fg_col = 0; fg_col < nr_of_colors; fg_col++)
133                         init_pair(bg_col * nr_of_colors + fg_col + 1, ncurses_colors[fg_col], ncurses_colors[bg_col]);
134         reset_ansi();
135 }
136
137
138 static int set_ansi_attribute(const int attrib)
139 {
140         switch (attrib) {
141         case -1:        /* restore last settings */
142                 break;
143         case 0:         /* restore default settings */
144                 reset_ansi();
145                 break;
146         case 1:         /* set bold / increased intensity */
147                 attributes |= A_BOLD;
148                 break;
149         case 2:         /* set decreased intensity (if supported) */
150                 attributes |= A_DIM;
151                 break;
152 #ifdef A_ITALIC
153         case 3:         /* set italic (if supported) */
154                 attributes |= A_ITALIC;
155                 break;
156 #endif
157         case 4:         /* set underline */
158                 attributes |= A_UNDERLINE;
159                 break;
160         case 5:         /* set blinking */
161                 attributes |= A_BLINK;
162                 break;
163         case 7:         /* set inversed */
164                 attributes |= A_REVERSE;
165                 break;
166         case 21:        /* unset bold / increased intensity */
167                 attributes &= ~A_BOLD;
168                 break;
169         case 22:        /* unset bold / any intensity modifier */
170                 attributes &= ~(A_BOLD | A_DIM);
171                 break;
172 #ifdef A_ITALIC
173         case 23:        /* unset italic */
174                 attributes &= ~A_ITALIC;
175                 break;
176 #endif
177         case 24:        /* unset underline */
178                 attributes &= ~A_UNDERLINE;
179                 break;
180         case 25:        /* unset blinking */
181                 attributes &= ~A_BLINK;
182                 break;
183         case 27:        /* unset inversed */
184                 attributes &= ~A_REVERSE;
185                 break;
186     case 39:
187         fg_col = 0;
188         break;
189     case 49:
190         bg_col = 0;
191         break;
192         default:
193                 if (attrib >= 30 && attrib <= 37) {     /* set foreground color */
194                         fg_col = attrib - 30 + 1;
195                 } else if (attrib >= 40 && attrib <= 47) { /* set background color */
196                         bg_col = attrib - 40 + 1;
197                 } else {
198                         return 0; /* Not understood */
199                 }
200         }
201         attrset(attributes | COLOR_PAIR(bg_col * nr_of_colors + fg_col + 1));
202     return 1;
203 }
204
205 static void process_ansi(FILE * fp)
206 {
207         int i, c;
208         char buf[MAX_ANSIBUF];
209         char *numstart, *endptr;
210
211         c = getc(fp);
212         if (c != '[') {
213                 ungetc(c, fp);
214                 return;
215         }
216         for (i = 0; i < MAX_ANSIBUF; i++) {
217                 c = getc(fp);
218                 /* COLOUR SEQUENCE ENDS in 'm' */
219                 if (c == 'm') {
220                         buf[i] = '\0';
221                         break;
222                 }
223                 if ((c < '0' || c > '9') && c != ';') {
224                         return;
225                 }
226                 buf[i] = (char)c;
227         }
228         /*
229          * buf now contains a semicolon-separated list of decimal integers,
230          * each indicating an attribute to apply.
231          * For example, buf might contain "0;1;31", derived from the color
232          * escape sequence "<ESC>[0;1;31m". There can be 1 or more
233          * attributes to apply, but typically there are between 1 and 3.
234          */
235
236     /* Special case of <ESC>[m */
237     if (buf[0] == '\0')
238         set_ansi_attribute(0);
239
240     for (endptr = numstart = buf; *endptr != '\0'; numstart = endptr + 1) {
241         if (!set_ansi_attribute(strtol(numstart, &endptr, 10)))
242             break;
243     }
244 }
245
246 static void __attribute__ ((__noreturn__)) do_exit(int status)
247 {
248         if (curses_started)
249                 endwin();
250         exit(status);
251 }
252
253 /* signal handler */
254 static void die(int notused __attribute__ ((__unused__)))
255 {
256         do_exit(EXIT_SUCCESS);
257 }
258
259 static void winch_handler(int notused __attribute__ ((__unused__)))
260 {
261         screen_size_changed = 1;
262 }
263
264 static char env_col_buf[24];
265 static char env_row_buf[24];
266 static int incoming_cols;
267 static int incoming_rows;
268
269 static void get_terminal_size(void)
270 {
271         struct winsize w;
272         if (!incoming_cols) {
273                 /* have we checked COLUMNS? */
274                 const char *s = getenv("COLUMNS");
275                 incoming_cols = -1;
276                 if (s && *s) {
277                         long t;
278                         char *endptr;
279                         t = strtol(s, &endptr, 0);
280                         if (!*endptr && 0 < t)
281                                 incoming_cols = t;
282                         width = incoming_cols;
283                         snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%ld",
284                                  width);
285                         putenv(env_col_buf);
286                 }
287         }
288         if (!incoming_rows) {
289                 /* have we checked LINES? */
290                 const char *s = getenv("LINES");
291                 incoming_rows = -1;
292                 if (s && *s) {
293                         long t;
294                         char *endptr;
295                         t = strtol(s, &endptr, 0);
296                         if (!*endptr && 0 < t)
297                                 incoming_rows = t;
298                         height = incoming_rows;
299                         snprintf(env_row_buf, sizeof env_row_buf, "LINES=%ld",
300                                  height);
301                         putenv(env_row_buf);
302                 }
303         }
304         if (ioctl(STDERR_FILENO, TIOCGWINSZ, &w) == 0) {
305                 if (incoming_cols < 0 || incoming_rows < 0) {
306                         if (incoming_rows < 0 && w.ws_row > 0) {
307                                 height = w.ws_row;
308                                 snprintf(env_row_buf, sizeof env_row_buf,
309                                          "LINES=%ld", height);
310                                 putenv(env_row_buf);
311                         }
312                         if (incoming_cols < 0 && w.ws_col > 0) {
313                                 width = w.ws_col;
314                                 snprintf(env_col_buf, sizeof env_col_buf,
315                                          "COLUMNS=%ld", width);
316                                 putenv(env_col_buf);
317                         }
318                 }
319         }
320 }
321
322 /* get current time in usec */
323 typedef unsigned long long watch_usec_t;
324 #define USECS_PER_SEC (1000000ull)
325 static watch_usec_t get_time_usec()
326 {
327         struct timeval now;
328         gettimeofday(&now, NULL);
329         return USECS_PER_SEC * now.tv_sec + now.tv_usec;
330 }
331
332 #ifdef WITH_WATCH8BIT
333 /* read a wide character from a popen'd stream */
334 #define MAX_ENC_BYTES 16
335 wint_t my_getwc(FILE * s);
336 wint_t my_getwc(FILE * s)
337 {
338         /* assuming no encoding ever consumes more than 16 bytes */
339         char i[MAX_ENC_BYTES];
340         int byte = 0;
341         int convert;
342         int x;
343         wchar_t rval;
344         while (1) {
345                 i[byte] = getc(s);
346                 if (i[byte] == EOF) {
347                         return WEOF;
348                 }
349                 byte++;
350                 errno = 0;
351                 mbtowc(NULL, NULL, 0);
352                 convert = mbtowc(&rval, i, byte);
353                 x = errno;
354                 if (convert > 0) {
355                         /* legal conversion */
356                         return rval;
357                 }
358                 if (byte == MAX_ENC_BYTES) {
359                         while (byte > 1) {
360                                 /* at least *try* to fix up */
361                                 ungetc(i[--byte], s);
362                         }
363                         errno = -EILSEQ;
364                         return WEOF;
365                 }
366         }
367 }
368 #endif  /* WITH_WATCH8BIT */
369
370 #ifdef WITH_WATCH8BIT
371 static void output_header(wchar_t *restrict wcommand, int wcommand_characters, double interval)
372 #else
373 static void output_header(char *restrict command, double interval)
374 #endif  /* WITH_WATCH8BIT */
375 {
376         time_t t = time(NULL);
377         char *ts = ctime(&t);
378         char *header;
379         char *right_header;
380     int max_host_name_len = (int) sysconf(_SC_HOST_NAME_MAX);
381         char hostname[max_host_name_len + 1];
382         int command_columns = 0;        /* not including final \0 */
383
384         gethostname(hostname, sizeof(hostname));
385
386         /*
387          * left justify interval and command, right justify hostname and time,
388          * clipping all to fit window width
389          */
390         int hlen = asprintf(&header, _("Every %.1fs: "), interval);
391         int rhlen = asprintf(&right_header, _("%s: %s"), hostname, ts);
392
393         /*
394          * the rules:
395          *   width < rhlen : print nothing
396          *   width < rhlen + hlen + 1: print hostname, ts
397          *   width = rhlen + hlen + 1: print header, hostname, ts
398          *   width < rhlen + hlen + 4: print header, ..., hostname, ts
399          *   width < rhlen + hlen + wcommand_columns: print header,
400          *                           truncated wcommand, ..., hostname, ts
401          *   width > "": print header, wcomand, hostname, ts
402          * this is slightly different from how it used to be
403          */
404         if (width < rhlen) {
405                 free(header);
406                 free(right_header);
407                 return;
408         }
409         if (rhlen + hlen + 1 <= width) {
410                 mvaddstr(0, 0, header);
411                 if (rhlen + hlen + 2 <= width) {
412                         if (width < rhlen + hlen + 4) {
413                                 mvaddstr(0, width - rhlen - 4, "... ");
414                         } else {
415 #ifdef WITH_WATCH8BIT
416                     command_columns = wcswidth(wcommand, -1);
417                                 if (width < rhlen + hlen + command_columns) {
418                                         /* print truncated */
419                                         int available = width - rhlen - hlen;
420                                         int in_use = command_columns;
421                                         int wcomm_len = wcommand_characters;
422                                         while (available - 4 < in_use) {
423                                                 wcomm_len--;
424                                                 in_use = wcswidth(wcommand, wcomm_len);
425                                         }
426                                         mvaddnwstr(0, hlen, wcommand, wcomm_len);
427                                         mvaddstr(0, width - rhlen - 4, "... ");
428                                 } else {
429                                         mvaddwstr(0, hlen, wcommand);
430                                 }
431 #else
432                 command_columns = strlen(command);
433                 if (width < rhlen + hlen + command_columns) {
434                     /* print truncated */
435                     mvaddnstr(0, hlen, command, width - rhlen - hlen - 4);
436                     mvaddstr(0, width - rhlen - 4, "... ");
437                 } else {
438                     mvaddnstr(0, hlen, command, width - rhlen - hlen);
439                 }
440 #endif  /* WITH_WATCH8BIT */
441                         }
442                 }
443         }
444         mvaddstr(0, width - rhlen + 1, right_header);
445         free(header);
446         free(right_header);
447         return;
448 }
449
450 static int run_command(char *restrict command, char **restrict command_argv)
451 {
452         FILE *p;
453         int x, y;
454         int oldeolseen = 1;
455         int pipefd[2];
456         pid_t child;
457         int exit_early = 0;
458         int status;
459
460         /* allocate pipes */
461         if (pipe(pipefd) < 0)
462                 xerr(7, _("unable to create IPC pipes"));
463
464         /* flush stdout and stderr, since we're about to do fd stuff */
465         fflush(stdout);
466         fflush(stderr);
467
468         /* fork to prepare to run command */
469         child = fork();
470
471         if (child < 0) {                /* fork error */
472                 xerr(2, _("unable to fork process"));
473         } else if (child == 0) {        /* in child */
474                 close(pipefd[0]);               /* child doesn't need read side of pipe */
475                 close(1);                       /* prepare to replace stdout with pipe */
476                 if (dup2(pipefd[1], 1) < 0) {   /* replace stdout with write side of pipe */
477                         xerr(3, _("dup2 failed"));
478                 }
479                 close(pipefd[1]);               /* once duped, the write fd isn't needed */
480                 dup2(1, 2);                     /* stderr should default to stdout */
481
482                 if (flags & WATCH_EXEC) {       /* pass command to exec instead of system */
483                         if (execvp(command_argv[0], command_argv) == -1) {
484                                 xerr(4, _("unable to execute '%s'"),
485                                      command_argv[0]);
486                         }
487                 } else {
488                         status = system(command);       /* watch manpage promises sh quoting */
489                         /* propagate command exit status as child exit status */
490                         if (!WIFEXITED(status)) {       /* child exits nonzero if command does */
491                                 exit(EXIT_FAILURE);
492                         } else {
493                                 exit(WEXITSTATUS(status));
494                         }
495                 }
496         }
497
498         /* otherwise, we're in parent */
499         close(pipefd[1]);       /* close write side of pipe */
500         if ((p = fdopen(pipefd[0], "r")) == NULL)
501                 xerr(5, _("fdopen"));
502
503         reset_ansi();
504         for (y = show_title; y < height; y++) {
505                 int eolseen = 0, tabpending = 0, tabwaspending = 0;
506                 if (flags & WATCH_COLOR)
507                         set_ansi_attribute(-1);
508 #ifdef WITH_WATCH8BIT
509                 wint_t carry = WEOF;
510 #endif
511                 for (x = 0; x < width; x++) {
512 #ifdef WITH_WATCH8BIT
513                         wint_t c = ' ';
514 #else
515                         int c = ' ';
516 #endif
517                         int attr = 0;
518
519                         if (tabwaspending && (flags & WATCH_COLOR))
520                                 set_ansi_attribute(-1);
521                         tabwaspending = 0;
522
523                         if (!eolseen) {
524                                 /* if there is a tab pending, just
525                                  * spit spaces until the next stop
526                                  * instead of reading characters */
527                                 if (!tabpending)
528 #ifdef WITH_WATCH8BIT
529                                         do {
530                                                 if (carry == WEOF) {
531                                                         c = my_getwc(p);
532                                                 } else {
533                                                         c = carry;
534                                                         carry = WEOF;
535                                                 }
536                                         } while (c != WEOF && !iswprint(c)
537                                                  && c < 128
538                                                  && wcwidth(c) == 0
539                                                  && c != L'\n'
540                                                  && c != L'\t'
541                                                  && (c != L'\033'
542                                                      || !(flags & WATCH_COLOR)));
543 #else
544                                         do
545                                                 c = getc(p);
546                                         while (c != EOF && !isprint(c)
547                                                && c != '\n'
548                                                && c != '\t'
549                                                && (c != L'\033'
550                                                    || !(flags & WATCH_COLOR)));
551 #endif
552                                 if (c == L'\033' && (flags & WATCH_COLOR)) {
553                                         x--;
554                                         process_ansi(p);
555                                         continue;
556                                 }
557                                 if (c == L'\n')
558                                         if (!oldeolseen && x == 0) {
559                                                 x = -1;
560                                                 continue;
561                                         } else
562                                                 eolseen = 1;
563                                 else if (c == L'\t')
564                                         tabpending = 1;
565 #ifdef WITH_WATCH8BIT
566                                 if (x == width - 1 && wcwidth(c) == 2) {
567                                         y++;
568                                         x = -1;         /* process this double-width */
569                                         carry = c;      /* character on the next line */
570                                         continue;       /* because it won't fit here */
571                                 }
572                                 if (c == WEOF || c == L'\n' || c == L'\t') {
573                                         c = L' ';
574                                         if (flags & WATCH_COLOR)
575                                                 attrset(A_NORMAL);
576                                 }
577 #else
578                                 if (c == EOF || c == '\n' || c == '\t') {
579                                         c = ' ';
580                                         if (flags & WATCH_COLOR)
581                                                 attrset(A_NORMAL);
582                                 }
583 #endif
584                                 if (tabpending && (((x + 1) % 8) == 0)) {
585                                         tabpending = 0;
586                                         tabwaspending = 1;
587                                 }
588                         }
589                         move(y, x);
590
591                         if (!first_screen && !exit_early && (flags & WATCH_CHGEXIT)) {
592 #ifdef WITH_WATCH8BIT
593                                 cchar_t oldc;
594                                 in_wch(&oldc);
595                                 exit_early = (wchar_t) c != oldc.chars[0];
596 #else
597                                 chtype oldch = inch();
598                                 unsigned char oldc = oldch & A_CHARTEXT;
599                                 exit_early = (unsigned char)c != oldc;
600 #endif
601                         }
602                         if (flags & WATCH_DIFF) {
603 #ifdef WITH_WATCH8BIT
604                                 cchar_t oldc;
605                                 in_wch(&oldc);
606                                 attr = !first_screen
607                                     && ((wchar_t) c != oldc.chars[0]
608                                         ||
609                                         ((flags & WATCH_CUMUL)
610                                          && (oldc.attr & A_ATTRIBUTES)));
611 #else
612                                 chtype oldch = inch();
613                                 unsigned char oldc = oldch & A_CHARTEXT;
614                                 attr = !first_screen
615                                     && ((unsigned char)c != oldc
616                                         ||
617                                         ((flags & WATCH_CUMUL)
618                                          && (oldch & A_ATTRIBUTES)));
619 #endif
620                         }
621                         if (attr)
622                                 standout();
623 #ifdef WITH_WATCH8BIT
624                         addnwstr((wchar_t *) & c, 1);
625 #else
626                         addch(c);
627 #endif
628                         if (attr)
629                                 standend();
630 #ifdef WITH_WATCH8BIT
631                         if (wcwidth(c) == 0) {
632                                 x--;
633                         }
634                         if (wcwidth(c) == 2) {
635                                 x++;
636                         }
637 #endif
638                 }
639                 oldeolseen = eolseen;
640         }
641
642         fclose(p);
643
644
645         /* harvest child process and get status, propagated from command */
646         if (waitpid(child, &status, 0) < 0)
647                 xerr(8, _("waitpid"));
648
649         /* if child process exited in error, beep if option_beep is set */
650         if ((!WIFEXITED(status) || WEXITSTATUS(status))) {
651                 if (flags & WATCH_BEEP)
652                         beep();
653                 if (flags & WATCH_ERREXIT) {
654                         mvaddstr(height - 1, 0,
655                                  _("command exit with a non-zero status, press a key to exit"));
656                         refresh();
657                         fgetc(stdin);
658                         endwin();
659                         exit(8);
660                 }
661         }
662         first_screen = 0;
663         refresh();
664         return exit_early;
665 }
666
667 int main(int argc, char *argv[])
668 {
669         int optc;
670         double interval = 2;
671         char *command;
672         char **command_argv;
673         int command_length = 0; /* not including final \0 */
674         watch_usec_t next_loop; /* next loop time in us, used for precise time
675                                  * keeping only */
676 #ifdef WITH_WATCH8BIT
677         wchar_t *wcommand = NULL;
678         int wcommand_characters = 0;    /* not including final \0 */
679 #endif  /* WITH_WATCH8BIT */
680
681         static struct option longopts[] = {
682                 {"color", no_argument, 0, 'c'},
683                 {"differences", optional_argument, 0, 'd'},
684                 {"help", no_argument, 0, 'h'},
685                 {"interval", required_argument, 0, 'n'},
686                 {"beep", no_argument, 0, 'b'},
687                 {"errexit", no_argument, 0, 'e'},
688                 {"chgexit", no_argument, 0, 'g'},
689                 {"exec", no_argument, 0, 'x'},
690                 {"precise", no_argument, 0, 'p'},
691                 {"no-title", no_argument, 0, 't'},
692                 {"version", no_argument, 0, 'v'},
693                 {0, 0, 0, 0}
694         };
695
696 #ifdef HAVE_PROGRAM_INVOCATION_NAME
697         program_invocation_name = program_invocation_short_name;
698 #endif
699         setlocale(LC_ALL, "");
700         bindtextdomain(PACKAGE, LOCALEDIR);
701         textdomain(PACKAGE);
702         atexit(close_stdout);
703
704         while ((optc =
705                 getopt_long(argc, argv, "+bced::ghn:pvtx", longopts, (int *)0))
706                != EOF) {
707                 switch (optc) {
708                 case 'b':
709                         flags |= WATCH_BEEP;
710                         break;
711                 case 'c':
712                         flags |= WATCH_COLOR;
713                         break;
714                 case 'd':
715                         flags |= WATCH_DIFF;
716                         if (optarg)
717                                 flags |= WATCH_CUMUL;
718                         break;
719                 case 'e':
720                         flags |= WATCH_ERREXIT;
721                         break;
722                 case 'g':
723                         flags |= WATCH_CHGEXIT;
724                         break;
725                 case 't':
726                         show_title = 0;
727                         break;
728                 case 'x':
729                         flags |= WATCH_EXEC;
730                         break;
731                 case 'n':
732                         interval = strtod_nol_or_err(optarg, _("failed to parse argument"));
733                         if (interval < 0.1)
734                                 interval = 0.1;
735                         if (interval > UINT_MAX)
736                                 interval = UINT_MAX;
737                         break;
738                 case 'p':
739                         precise_timekeeping = 1;
740                         break;
741                 case 'h':
742                         usage(stdout);
743                         break;
744                 case 'v':
745                         printf(PROCPS_NG_VERSION);
746                         return EXIT_SUCCESS;
747                 default:
748                         usage(stderr);
749                         break;
750                 }
751         }
752
753         if (optind >= argc)
754                 usage(stderr);
755
756         /* save for later */
757         command_argv = &(argv[optind]);
758
759         command = xstrdup(argv[optind++]);
760         command_length = strlen(command);
761         for (; optind < argc; optind++) {
762                 char *endp;
763                 int s = strlen(argv[optind]);
764                 /* space and \0 */
765                 command = xrealloc(command, command_length + s + 2);
766                 endp = command + command_length;
767                 *endp = ' ';
768                 memcpy(endp + 1, argv[optind], s);
769                 /* space then string length */
770                 command_length += 1 + s;
771                 command[command_length] = '\0';
772         }
773
774 #ifdef WITH_WATCH8BIT
775         /* convert to wide for printing purposes */
776         /*mbstowcs(NULL, NULL, 0); */
777         wcommand_characters = mbstowcs(NULL, command, 0);
778         if (wcommand_characters < 0) {
779                 fprintf(stderr, _("unicode handling error\n"));
780                 exit(EXIT_FAILURE);
781         }
782         wcommand =
783             (wchar_t *) malloc((wcommand_characters + 1) * sizeof(wcommand));
784         if (wcommand == NULL) {
785                 fprintf(stderr, _("unicode handling error (malloc)\n"));
786                 exit(EXIT_FAILURE);
787         }
788         mbstowcs(wcommand, command, wcommand_characters + 1);
789 #endif  /* WITH_WATCH8BIT */
790
791         get_terminal_size();
792
793         /* Catch keyboard interrupts so we can put tty back in a sane
794          * state.  */
795         signal(SIGINT, die);
796         signal(SIGTERM, die);
797         signal(SIGHUP, die);
798         signal(SIGWINCH, winch_handler);
799
800         /* Set up tty for curses use.  */
801         curses_started = 1;
802         initscr();
803         if (flags & WATCH_COLOR) {
804                 if (has_colors()) {
805                         start_color();
806                         use_default_colors();
807                         init_ansi_colors();
808                 } else {
809                         flags &= ~WATCH_COLOR;
810                 }
811         }
812         nonl();
813         noecho();
814         cbreak();
815
816         if (precise_timekeeping)
817                 next_loop = get_time_usec();
818
819         while (1) {
820                 if (screen_size_changed) {
821                         get_terminal_size();
822                         resizeterm(height, width);
823                         clear();
824                         /* redrawwin(stdscr); */
825                         screen_size_changed = 0;
826                         first_screen = 1;
827                 }
828
829                 if (show_title)
830 #ifdef WITH_WATCH8BIT
831                         output_header(wcommand, wcommand_characters, interval);
832 #else
833                         output_header(command, interval);
834 #endif  /* WITH_WATCH8BIT */
835
836                 if (run_command(command, command_argv))
837                         break;
838
839
840                 if (precise_timekeeping) {
841                         watch_usec_t cur_time = get_time_usec();
842                         next_loop += USECS_PER_SEC * interval;
843                         if (cur_time < next_loop)
844                                 usleep(next_loop - cur_time);
845                 } else
846                         if (interval < UINT_MAX / USECS_PER_SEC)
847                                 usleep(interval * USECS_PER_SEC);
848                         else
849                                 sleep(interval);
850         }
851
852         endwin();
853         return EXIT_SUCCESS;
854 }