Updated with Tizen:Base source codes
[external/procps.git] / watch.c
1 /* watch -- execute a program repeatedly, displaying output fullscreen
2  *
3  * Based on the original 1991 'watch' by Tony Rems <rembo@unisoft.com>
4  * (with mods and corrections by Francois Pinard).
5  *
6  * Substantially reworked, new features (differences option, SIGWINCH
7  * handling, unlimited command length, long line handling) added Apr 1999 by
8  * Mike Coleman <mkc@acm.org>.
9  *
10  * Changes by Albert Cahalan, 2002-2003.
11  */
12
13 #define VERSION "0.2.0"
14
15 #include <ctype.h>
16 #include <getopt.h>
17 #include <signal.h>
18 #include <ncurses.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/ioctl.h>
23 #include <time.h>
24 #include <unistd.h>
25 #include <termios.h>
26 #include <locale.h>
27 #include "proc/procps.h"
28
29 #ifdef FORCE_8BIT
30 #undef isprint
31 #define isprint(x) ( (x>=' '&&x<='~') || (x>=0xa0) )
32 #endif
33
34 static struct option longopts[] = {
35         {"differences", optional_argument, 0, 'd'},
36         {"help", no_argument, 0, 'h'},
37         {"interval", required_argument, 0, 'n'},
38         {"no-title", no_argument, 0, 't'},
39         {"version", no_argument, 0, 'v'},
40         {0, 0, 0, 0}
41 };
42
43 static char usage[] =
44     "Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
45
46 static char *progname;
47
48 static int curses_started = 0;
49 static int height = 24, width = 80;
50 static int screen_size_changed = 0;
51 static int first_screen = 1;
52 static int show_title = 2;  // number of lines used, 2 or 0
53
54 #define min(x,y) ((x) > (y) ? (y) : (x))
55
56 static void do_usage(void) NORETURN;
57 static void do_usage(void)
58 {
59         fprintf(stderr, usage, progname);
60         exit(1);
61 }
62
63 static void do_exit(int status) NORETURN;
64 static void do_exit(int status)
65 {
66         if (curses_started)
67                 endwin();
68         exit(status);
69 }
70
71 /* signal handler */
72 static void die(int notused) NORETURN;
73 static void die(int notused)
74 {
75         (void) notused;
76         do_exit(0);
77 }
78
79 static void
80 winch_handler(int notused)
81 {
82         (void) notused;
83         screen_size_changed = 1;
84 }
85
86 static char env_col_buf[24];
87 static char env_row_buf[24];
88 static int incoming_cols;
89 static int incoming_rows;
90
91 static void
92 get_terminal_size(void)
93 {
94         struct winsize w;
95         if(!incoming_cols){  // have we checked COLUMNS?
96                 const char *s = getenv("COLUMNS");
97                 incoming_cols = -1;
98                 if(s && *s){
99                         long t;
100                         char *endptr;
101                         t = strtol(s, &endptr, 0);
102                         if(!*endptr && (t>0) && (t<(long)666)) incoming_cols = (int)t;
103                         width = incoming_cols;
104                         snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width);
105                         putenv(env_col_buf);
106                 }
107         }
108         if(!incoming_rows){  // have we checked LINES?
109                 const char *s = getenv("LINES");
110                 incoming_rows = -1;
111                 if(s && *s){
112                         long t;
113                         char *endptr;
114                         t = strtol(s, &endptr, 0);
115                         if(!*endptr && (t>0) && (t<(long)666)) incoming_rows = (int)t;
116                         height = incoming_rows;
117                         snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height);
118                         putenv(env_row_buf);
119                 }
120         }
121         if (incoming_cols<0 || incoming_rows<0){
122                 if (ioctl(2, TIOCGWINSZ, &w) == 0) {
123                         if (incoming_rows<0 && w.ws_row > 0){
124                                 height = w.ws_row;
125                                 snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height);
126                                 putenv(env_row_buf);
127                         }
128                         if (incoming_cols<0 && w.ws_col > 0){
129                                 width = w.ws_col;
130                                 snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width);
131                                 putenv(env_col_buf);
132                         }
133                 }
134         }
135 }
136
137 int
138 main(int argc, char *argv[])
139 {
140         int optc;
141         int option_differences = 0,
142             option_differences_cumulative = 0,
143             option_help = 0, option_version = 0;
144         double interval = 2;
145         char *command;
146         int command_length = 0; /* not including final \0 */
147
148         setlocale(LC_ALL, "");
149         progname = argv[0];
150
151         while ((optc = getopt_long(argc, argv, "+d::hn:vt", longopts, (int *) 0))
152                != EOF) {
153                 switch (optc) {
154                 case 'd':
155                         option_differences = 1;
156                         if (optarg)
157                                 option_differences_cumulative = 1;
158                         break;
159                 case 'h':
160                         option_help = 1;
161                         break;
162                 case 't':
163                         show_title = 0;
164                         break;
165                 case 'n':
166                         {
167                                 char *str;
168                                 interval = strtod(optarg, &str);
169                                 if (!*optarg || *str)
170                                         do_usage();
171                                 if(interval < 0.1)
172                                         interval = 0.1;
173                                 if(interval > ~0u/1000000)
174                                         interval = ~0u/1000000;
175                         }
176                         break;
177                 case 'v':
178                         option_version = 1;
179                         break;
180                 default:
181                         do_usage();
182                         break;
183                 }
184         }
185
186         if (option_version) {
187                 fprintf(stderr, "%s\n", VERSION);
188                 if (!option_help)
189                         exit(0);
190         }
191
192         if (option_help) {
193                 fprintf(stderr, usage, progname);
194                 fputs("  -d, --differences[=cumulative]\thighlight changes between updates\n", stderr);
195                 fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr);
196                 fputs("  -h, --help\t\t\t\tprint a summary of the options\n", stderr);
197                 fputs("  -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
198                 fputs("  -v, --version\t\t\t\tprint the version number\n", stderr);
199                 fputs("  -t, --no-title\t\t\tturns off showing the header\n", stderr);
200                 exit(0);
201         }
202
203         if (optind >= argc)
204                 do_usage();
205
206         command = strdup(argv[optind++]);
207         command_length = strlen(command);
208         for (; optind < argc; optind++) {
209                 char *endp;
210                 int s = strlen(argv[optind]);
211                 command = realloc(command, command_length + s + 2);     /* space and \0 */
212                 endp = command + command_length;
213                 *endp = ' ';
214                 memcpy(endp + 1, argv[optind], s);
215                 command_length += 1 + s;        /* space then string length */
216                 command[command_length] = '\0';
217         }
218
219         get_terminal_size();
220
221         /* Catch keyboard interrupts so we can put tty back in a sane state.  */
222         signal(SIGINT, die);
223         signal(SIGTERM, die);
224         signal(SIGHUP, die);
225         signal(SIGWINCH, winch_handler);
226
227         /* Set up tty for curses use.  */
228         curses_started = 1;
229         initscr();
230         nonl();
231         noecho();
232         cbreak();
233
234         for (;;) {
235                 time_t t = time(NULL);
236                 char *ts = ctime(&t);
237                 int tsl = strlen(ts);
238                 char *header;
239                 FILE *p;
240                 int x, y;
241                 int oldeolseen = 1;
242
243                 if (screen_size_changed) {
244                         get_terminal_size();
245                         resizeterm(height, width);
246                         clear();
247                         /* redrawwin(stdscr); */
248                         screen_size_changed = 0;
249                         first_screen = 1;
250                 }
251
252                 if (show_title) {
253                         // left justify interval and command,
254                         // right justify time, clipping all to fit window width
255                         asprintf(&header, "Every %.1fs: %.*s",
256                                 interval, min(width - 1, command_length), command);
257                         mvaddstr(0, 0, header);
258                         if (strlen(header) > (size_t) (width - tsl - 1))
259                                 mvaddstr(0, width - tsl - 4, "...  ");
260                         mvaddstr(0, width - tsl + 1, ts);
261                         free(header);
262                 }
263
264                 if (!(p = popen(command, "r"))) {
265                         perror("popen");
266                         do_exit(2);
267                 }
268
269                 for (y = show_title; y < height; y++) {
270                         int eolseen = 0, tabpending = 0;
271                         for (x = 0; x < width; x++) {
272                                 int c = ' ';
273                                 int attr = 0;
274
275                                 if (!eolseen) {
276                                         /* if there is a tab pending, just spit spaces until the
277                                            next stop instead of reading characters */
278                                         if (!tabpending)
279                                                 do
280                                                         c = getc(p);
281                                                 while (c != EOF && !isprint(c)
282                                                        && c != '\n'
283                                                        && c != '\t');
284                                         if (c == '\n')
285                                                 if (!oldeolseen && x == 0) {
286                                                         x = -1;
287                                                         continue;
288                                                 } else
289                                                         eolseen = 1;
290                                         else if (c == '\t')
291                                                 tabpending = 1;
292                                         if (c == EOF || c == '\n' || c == '\t')
293                                                 c = ' ';
294                                         if (tabpending && (((x + 1) % 8) == 0))
295                                                 tabpending = 0;
296                                 }
297                                 move(y, x);
298                                 if (option_differences) {
299                                         chtype oldch = inch();
300                                         char oldc = oldch & A_CHARTEXT;
301                                         attr = !first_screen
302                                             && ((char)c != oldc
303                                                 ||
304                                                 (option_differences_cumulative
305                                                  && (oldch & A_ATTRIBUTES)));
306                                 }
307                                 if (attr)
308                                         standout();
309                                 addch(c);
310                                 if (attr)
311                                         standend();
312                         }
313                         oldeolseen = eolseen;
314                 }
315
316                 pclose(p);
317
318                 first_screen = 0;
319                 refresh();
320                 usleep(interval * 1000000);
321         }
322
323         endwin();
324
325         return 0;
326 }