xref: /dflybsd-src/usr.bin/dsynth/ncurses.c (revision ea37671df32972e5ab85505aad1eaaf6a307b013)
1*ea37671dSMatthew Dillon /*
2*ea37671dSMatthew Dillon  * Copyright (c) 2019 The DragonFly Project.  All rights reserved.
3*ea37671dSMatthew Dillon  *
4*ea37671dSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
5*ea37671dSMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
6*ea37671dSMatthew Dillon  *
7*ea37671dSMatthew Dillon  * This code uses concepts and configuration based on 'synth', by
8*ea37671dSMatthew Dillon  * John R. Marino <draco@marino.st>, which was written in ada.
9*ea37671dSMatthew Dillon  *
10*ea37671dSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
11*ea37671dSMatthew Dillon  * modification, are permitted provided that the following conditions
12*ea37671dSMatthew Dillon  * are met:
13*ea37671dSMatthew Dillon  *
14*ea37671dSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
15*ea37671dSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
16*ea37671dSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
17*ea37671dSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
18*ea37671dSMatthew Dillon  *    the documentation and/or other materials provided with the
19*ea37671dSMatthew Dillon  *    distribution.
20*ea37671dSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
21*ea37671dSMatthew Dillon  *    contributors may be used to endorse or promote products derived
22*ea37671dSMatthew Dillon  *    from this software without specific, prior written permission.
23*ea37671dSMatthew Dillon  *
24*ea37671dSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25*ea37671dSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26*ea37671dSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27*ea37671dSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
28*ea37671dSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29*ea37671dSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30*ea37671dSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31*ea37671dSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32*ea37671dSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33*ea37671dSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34*ea37671dSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35*ea37671dSMatthew Dillon  * SUCH DAMAGE.
36*ea37671dSMatthew Dillon  */
37*ea37671dSMatthew Dillon #include "dsynth.h"
38*ea37671dSMatthew Dillon 
39*ea37671dSMatthew Dillon #include <curses.h>
40*ea37671dSMatthew Dillon 
41*ea37671dSMatthew Dillon /*
42*ea37671dSMatthew Dillon  * ncurses - LINES, COLS are the main things we care about
43*ea37671dSMatthew Dillon  */
44*ea37671dSMatthew Dillon static WINDOW *CWin;
45*ea37671dSMatthew Dillon static WINDOW *CMon;
46*ea37671dSMatthew Dillon static const char *Line0 = " Total -       Built -      Ignored -      "
47*ea37671dSMatthew Dillon 			   "Load -      Pkg/hour -               ";
48*ea37671dSMatthew Dillon static const char *Line1 = "  Left -      Failed -      Skipped -      "
49*ea37671dSMatthew Dillon 			   "Swap -       Impulse -      --:--:-- ";
50*ea37671dSMatthew Dillon static const char *LineB = "==========================================="
51*ea37671dSMatthew Dillon 			   "====================================";
52*ea37671dSMatthew Dillon static const char *LineI = " ID  Duration  Build Phase      Origin     "
53*ea37671dSMatthew Dillon 			   "                               Lines";
54*ea37671dSMatthew Dillon 
55*ea37671dSMatthew Dillon static int LastReduce;
56*ea37671dSMatthew Dillon static off_t MonitorLogOff;
57*ea37671dSMatthew Dillon static int MonitorLogLines;
58*ea37671dSMatthew Dillon static int MonitorBufBeg;
59*ea37671dSMatthew Dillon static int MonitorBufEnd;
60*ea37671dSMatthew Dillon static int MonitorBufScan;
61*ea37671dSMatthew Dillon static int MonitorBufDiscardMode;
62*ea37671dSMatthew Dillon static char MonitorBuf[1024];
63*ea37671dSMatthew Dillon 
64*ea37671dSMatthew Dillon static int guireadline(int fd, char **bufp);
65*ea37671dSMatthew Dillon 
66*ea37671dSMatthew Dillon #define TOTAL_COL	7
67*ea37671dSMatthew Dillon #define BUILT_COL	21
68*ea37671dSMatthew Dillon #define IGNORED_COL	36
69*ea37671dSMatthew Dillon #define LOAD_COL	48
70*ea37671dSMatthew Dillon #define GPKGRATE_COL	64
71*ea37671dSMatthew Dillon #define REDUCE_COL	71
72*ea37671dSMatthew Dillon 
73*ea37671dSMatthew Dillon #define LEFT_COL	7
74*ea37671dSMatthew Dillon #define FAILED_COL	21
75*ea37671dSMatthew Dillon #define SKIPPED_COL	36
76*ea37671dSMatthew Dillon #define SWAP_COL	48
77*ea37671dSMatthew Dillon #define IMPULSE_COL	64
78*ea37671dSMatthew Dillon #define TIME_COL	71
79*ea37671dSMatthew Dillon 
80*ea37671dSMatthew Dillon #define ID_COL		1
81*ea37671dSMatthew Dillon #define DURATION_COL	5
82*ea37671dSMatthew Dillon #define BUILD_PHASE_COL	15
83*ea37671dSMatthew Dillon #define ORIGIN_COL	32
84*ea37671dSMatthew Dillon #define LINES_COL	73
85*ea37671dSMatthew Dillon 
86*ea37671dSMatthew Dillon /*
87*ea37671dSMatthew Dillon  * The row that the worker list starts on, and the row that the log starts
88*ea37671dSMatthew Dillon  * on.
89*ea37671dSMatthew Dillon  */
90*ea37671dSMatthew Dillon #define WORKER_START	5
91*ea37671dSMatthew Dillon #define LOG_START	(WORKER_START + MaxWorkers + 1)
92*ea37671dSMatthew Dillon 
93*ea37671dSMatthew Dillon static void NCursesReset(void);
94*ea37671dSMatthew Dillon 
95*ea37671dSMatthew Dillon static void
96*ea37671dSMatthew Dillon NCursesInit(void)
97*ea37671dSMatthew Dillon {
98*ea37671dSMatthew Dillon 	if (UseNCurses == 0)
99*ea37671dSMatthew Dillon 		return;
100*ea37671dSMatthew Dillon 
101*ea37671dSMatthew Dillon 	CWin = initscr();
102*ea37671dSMatthew Dillon 	NCursesReset();
103*ea37671dSMatthew Dillon 
104*ea37671dSMatthew Dillon 	intrflush(stdscr, FALSE);
105*ea37671dSMatthew Dillon 	nonl();
106*ea37671dSMatthew Dillon 	noecho();
107*ea37671dSMatthew Dillon 	cbreak();
108*ea37671dSMatthew Dillon 
109*ea37671dSMatthew Dillon 	start_color();
110*ea37671dSMatthew Dillon 	use_default_colors();
111*ea37671dSMatthew Dillon 	init_pair(1, COLOR_RED, -1);
112*ea37671dSMatthew Dillon 	init_pair(2, COLOR_GREEN, -1);
113*ea37671dSMatthew Dillon 	init_pair(3, -1, -1);
114*ea37671dSMatthew Dillon }
115*ea37671dSMatthew Dillon 
116*ea37671dSMatthew Dillon static void
117*ea37671dSMatthew Dillon NCursesReset(void)
118*ea37671dSMatthew Dillon {
119*ea37671dSMatthew Dillon 	int i;
120*ea37671dSMatthew Dillon 
121*ea37671dSMatthew Dillon 	if (UseNCurses == 0)
122*ea37671dSMatthew Dillon 		return;
123*ea37671dSMatthew Dillon 
124*ea37671dSMatthew Dillon 	if (CMon) {
125*ea37671dSMatthew Dillon 		delwin(CMon);
126*ea37671dSMatthew Dillon 		CMon = NULL;
127*ea37671dSMatthew Dillon 	}
128*ea37671dSMatthew Dillon 
129*ea37671dSMatthew Dillon 	werase(CWin);
130*ea37671dSMatthew Dillon 	curs_set(0);
131*ea37671dSMatthew Dillon 	redrawwin(CWin);
132*ea37671dSMatthew Dillon 	wrefresh(CWin);
133*ea37671dSMatthew Dillon 	mvwprintw(CWin, 0, 0, "%s", Line0);
134*ea37671dSMatthew Dillon 	mvwprintw(CWin, 1, 0, "%s", Line1);
135*ea37671dSMatthew Dillon 	mvwprintw(CWin, 2, 0, "%s", LineB);
136*ea37671dSMatthew Dillon 	mvwprintw(CWin, 3, 0, "%s", LineI);
137*ea37671dSMatthew Dillon 	mvwprintw(CWin, 4, 0, "%s", LineB);
138*ea37671dSMatthew Dillon 
139*ea37671dSMatthew Dillon 	for (i = 0; i < MaxWorkers; ++i) {
140*ea37671dSMatthew Dillon 		mvwprintw(CWin, WORKER_START + i, ID_COL, "%02d", i);
141*ea37671dSMatthew Dillon 		mvwprintw(CWin, WORKER_START + i, DURATION_COL, "--:--:--");
142*ea37671dSMatthew Dillon 		mvwprintw(CWin, WORKER_START + i, BUILD_PHASE_COL, "Idle");
143*ea37671dSMatthew Dillon 		mvwprintw(CWin, WORKER_START + i, ORIGIN_COL, "%39.39s", "");
144*ea37671dSMatthew Dillon 		mvwprintw(CWin, WORKER_START + i, LINES_COL, "%6.6s", "");
145*ea37671dSMatthew Dillon 	}
146*ea37671dSMatthew Dillon 	mvwprintw(CWin, WORKER_START + MaxWorkers, 0, "%s", LineB);
147*ea37671dSMatthew Dillon 	wrefresh(CWin);
148*ea37671dSMatthew Dillon 
149*ea37671dSMatthew Dillon 	CMon = subwin(CWin, 0, 0, LOG_START, 0);
150*ea37671dSMatthew Dillon 	scrollok(CMon, 1);
151*ea37671dSMatthew Dillon 	MonitorLogLines = LINES - LOG_START;
152*ea37671dSMatthew Dillon 	MonitorLogOff = 0;
153*ea37671dSMatthew Dillon 	MonitorBufBeg = 0;
154*ea37671dSMatthew Dillon 	MonitorBufEnd = 0;
155*ea37671dSMatthew Dillon 	MonitorBufScan = 0;
156*ea37671dSMatthew Dillon 	MonitorBufDiscardMode = 0;
157*ea37671dSMatthew Dillon 	nodelay(CMon, 1);
158*ea37671dSMatthew Dillon 
159*ea37671dSMatthew Dillon 	LastReduce = -1;
160*ea37671dSMatthew Dillon }
161*ea37671dSMatthew Dillon 
162*ea37671dSMatthew Dillon static void
163*ea37671dSMatthew Dillon NCursesUpdateTop(topinfo_t *info)
164*ea37671dSMatthew Dillon {
165*ea37671dSMatthew Dillon 	if (UseNCurses == 0)
166*ea37671dSMatthew Dillon 		return;
167*ea37671dSMatthew Dillon 
168*ea37671dSMatthew Dillon 	mvwprintw(CWin, 0, TOTAL_COL, "%-5d", info->total);
169*ea37671dSMatthew Dillon 	mvwprintw(CWin, 0, BUILT_COL, "%-5d", info->successful);
170*ea37671dSMatthew Dillon 	mvwprintw(CWin, 0, IGNORED_COL, "%-5d", info->ignored);
171*ea37671dSMatthew Dillon 	if (info->dload[0] > 999.9)
172*ea37671dSMatthew Dillon 		mvwprintw(CWin, 0, LOAD_COL, "%5.0f", info->dload[0]);
173*ea37671dSMatthew Dillon 	else
174*ea37671dSMatthew Dillon 		mvwprintw(CWin, 0, LOAD_COL, "%5.1f", info->dload[0]);
175*ea37671dSMatthew Dillon 	mvwprintw(CWin, 0, GPKGRATE_COL, "%-5d", info->pkgrate);
176*ea37671dSMatthew Dillon 
177*ea37671dSMatthew Dillon 	/*
178*ea37671dSMatthew Dillon 	 * If dynamic worker reduction is active include a field,
179*ea37671dSMatthew Dillon 	 * Otherwise blank the field.
180*ea37671dSMatthew Dillon 	 */
181*ea37671dSMatthew Dillon 	if (LastReduce != DynamicMaxWorkers) {
182*ea37671dSMatthew Dillon 		LastReduce = DynamicMaxWorkers;
183*ea37671dSMatthew Dillon 		if (MaxWorkers == LastReduce)
184*ea37671dSMatthew Dillon 			mvwprintw(CWin, 0, REDUCE_COL, "        ");
185*ea37671dSMatthew Dillon 		else
186*ea37671dSMatthew Dillon 			mvwprintw(CWin, 0, REDUCE_COL, "Limit %-2d",
187*ea37671dSMatthew Dillon 				  LastReduce);
188*ea37671dSMatthew Dillon 	}
189*ea37671dSMatthew Dillon 
190*ea37671dSMatthew Dillon 	mvwprintw(CWin, 1, LEFT_COL, "%-5d", info->remaining);
191*ea37671dSMatthew Dillon 	mvwprintw(CWin, 1, FAILED_COL, "%-5d", info->failed);
192*ea37671dSMatthew Dillon 	mvwprintw(CWin, 1, SKIPPED_COL, "%-5d", info->skipped);
193*ea37671dSMatthew Dillon 	if (info->noswap)
194*ea37671dSMatthew Dillon 		mvwprintw(CWin, 1, SWAP_COL, "-   ");
195*ea37671dSMatthew Dillon 	else
196*ea37671dSMatthew Dillon 		mvwprintw(CWin, 1, SWAP_COL, "%5.1f", info->dswap);
197*ea37671dSMatthew Dillon 	mvwprintw(CWin, 1, IMPULSE_COL, "%-5d", info->pkgimpulse);
198*ea37671dSMatthew Dillon 	if (info->h > 99)
199*ea37671dSMatthew Dillon 		mvwprintw(CWin, 1, TIME_COL-1, "%3d:%02d:%02d",
200*ea37671dSMatthew Dillon 			  info->h, info->m, info->s);
201*ea37671dSMatthew Dillon 	else
202*ea37671dSMatthew Dillon 		mvwprintw(CWin, 1, TIME_COL, "%02d:%02d:%02d",
203*ea37671dSMatthew Dillon 			  info->h, info->m, info->s);
204*ea37671dSMatthew Dillon }
205*ea37671dSMatthew Dillon 
206*ea37671dSMatthew Dillon static void
207*ea37671dSMatthew Dillon NCursesUpdateLogs(void)
208*ea37671dSMatthew Dillon {
209*ea37671dSMatthew Dillon 	int fd = dlog00_fd();
210*ea37671dSMatthew Dillon 	char *ptr;
211*ea37671dSMatthew Dillon 	char c;
212*ea37671dSMatthew Dillon 	ssize_t n;
213*ea37671dSMatthew Dillon 	int w;
214*ea37671dSMatthew Dillon 
215*ea37671dSMatthew Dillon 	if (UseNCurses == 0)
216*ea37671dSMatthew Dillon 		return;
217*ea37671dSMatthew Dillon 
218*ea37671dSMatthew Dillon 	for (;;) {
219*ea37671dSMatthew Dillon 		n = guireadline(fd, &ptr);
220*ea37671dSMatthew Dillon 		if (n < 0)
221*ea37671dSMatthew Dillon 			break;
222*ea37671dSMatthew Dillon 		if (n == 0)
223*ea37671dSMatthew Dillon 			continue;
224*ea37671dSMatthew Dillon 
225*ea37671dSMatthew Dillon 		/*
226*ea37671dSMatthew Dillon 		 * Scroll down
227*ea37671dSMatthew Dillon 		 */
228*ea37671dSMatthew Dillon 		if (n > COLS)
229*ea37671dSMatthew Dillon 			w = COLS;
230*ea37671dSMatthew Dillon 		else
231*ea37671dSMatthew Dillon 			w = n;
232*ea37671dSMatthew Dillon 		c = ptr[w];
233*ea37671dSMatthew Dillon 		ptr[w] = 0;
234*ea37671dSMatthew Dillon 
235*ea37671dSMatthew Dillon 		/*
236*ea37671dSMatthew Dillon 		 * Filter out these logs from the display (they remain in
237*ea37671dSMatthew Dillon 		 * the 00*.log file) to reduce clutter.
238*ea37671dSMatthew Dillon 		 */
239*ea37671dSMatthew Dillon 		if (strncmp(ptr, "[XXX] Load=", 11) == 0)
240*ea37671dSMatthew Dillon 			continue;
241*ea37671dSMatthew Dillon 
242*ea37671dSMatthew Dillon 		/*
243*ea37671dSMatthew Dillon 		 * Output possibly colored log line
244*ea37671dSMatthew Dillon 		 */
245*ea37671dSMatthew Dillon 		wscrl(CMon, -1);
246*ea37671dSMatthew Dillon 		if (strstr(ptr, "] SUCCESS ")) {
247*ea37671dSMatthew Dillon 			wattrset(CMon, COLOR_PAIR(2));
248*ea37671dSMatthew Dillon 		} else if (strstr(ptr, "] FAILURE ")) {
249*ea37671dSMatthew Dillon 			wattrset(CMon, COLOR_PAIR(1));
250*ea37671dSMatthew Dillon 		}
251*ea37671dSMatthew Dillon 		mvwprintw(CMon, 0, 0, "%s", ptr);
252*ea37671dSMatthew Dillon 		ptr[w] = c;
253*ea37671dSMatthew Dillon 		wattrset(CMon, COLOR_PAIR(3));
254*ea37671dSMatthew Dillon 	}
255*ea37671dSMatthew Dillon }
256*ea37671dSMatthew Dillon 
257*ea37671dSMatthew Dillon static void
258*ea37671dSMatthew Dillon NCursesUpdate(worker_t *work)
259*ea37671dSMatthew Dillon {
260*ea37671dSMatthew Dillon 	const char *phase;
261*ea37671dSMatthew Dillon 	const char *origin;
262*ea37671dSMatthew Dillon 	time_t t;
263*ea37671dSMatthew Dillon 	int i = work->index;
264*ea37671dSMatthew Dillon 	int h;
265*ea37671dSMatthew Dillon 	int m;
266*ea37671dSMatthew Dillon 	int s;
267*ea37671dSMatthew Dillon 
268*ea37671dSMatthew Dillon 	if (UseNCurses == 0)
269*ea37671dSMatthew Dillon 		return;
270*ea37671dSMatthew Dillon 
271*ea37671dSMatthew Dillon 	phase = "Unknown";
272*ea37671dSMatthew Dillon 	origin = "";
273*ea37671dSMatthew Dillon 
274*ea37671dSMatthew Dillon 	switch(work->state) {
275*ea37671dSMatthew Dillon 	case WORKER_NONE:
276*ea37671dSMatthew Dillon 		phase = "None";
277*ea37671dSMatthew Dillon 		/* fall through */
278*ea37671dSMatthew Dillon 	case WORKER_IDLE:
279*ea37671dSMatthew Dillon 		if (work->state == WORKER_IDLE)
280*ea37671dSMatthew Dillon 			phase = "Idle";
281*ea37671dSMatthew Dillon 		/* fall through */
282*ea37671dSMatthew Dillon 	case WORKER_FAILED:
283*ea37671dSMatthew Dillon 		if (work->state == WORKER_FAILED)
284*ea37671dSMatthew Dillon 			phase = "Failed";
285*ea37671dSMatthew Dillon 		/* fall through */
286*ea37671dSMatthew Dillon 	case WORKER_EXITING:
287*ea37671dSMatthew Dillon 		if (work->state == WORKER_EXITING)
288*ea37671dSMatthew Dillon 			phase = "Exiting";
289*ea37671dSMatthew Dillon 		mvwprintw(CWin, WORKER_START + i, DURATION_COL,
290*ea37671dSMatthew Dillon 			  "--:--:--");
291*ea37671dSMatthew Dillon 		mvwprintw(CWin, WORKER_START + i, BUILD_PHASE_COL,
292*ea37671dSMatthew Dillon 			  "%-16.16s", phase);
293*ea37671dSMatthew Dillon 		mvwprintw(CWin, WORKER_START + i, ORIGIN_COL,
294*ea37671dSMatthew Dillon 			  "%-39.39s", "");
295*ea37671dSMatthew Dillon 		mvwprintw(CWin, WORKER_START + i, LINES_COL,
296*ea37671dSMatthew Dillon 			  "%-6.6s", "");
297*ea37671dSMatthew Dillon 		return;
298*ea37671dSMatthew Dillon 	case WORKER_PENDING:
299*ea37671dSMatthew Dillon 		phase = "Pending";
300*ea37671dSMatthew Dillon 		break;
301*ea37671dSMatthew Dillon 	case WORKER_RUNNING:
302*ea37671dSMatthew Dillon 		phase = "Running";
303*ea37671dSMatthew Dillon 		break;
304*ea37671dSMatthew Dillon 	case WORKER_DONE:
305*ea37671dSMatthew Dillon 		phase = "Done";
306*ea37671dSMatthew Dillon 		break;
307*ea37671dSMatthew Dillon 	case WORKER_FROZEN:
308*ea37671dSMatthew Dillon 		phase = "FROZEN";
309*ea37671dSMatthew Dillon 		break;
310*ea37671dSMatthew Dillon 	default:
311*ea37671dSMatthew Dillon 		break;
312*ea37671dSMatthew Dillon 	}
313*ea37671dSMatthew Dillon 
314*ea37671dSMatthew Dillon 	t = time(NULL) - work->start_time;
315*ea37671dSMatthew Dillon 	s = t % 60;
316*ea37671dSMatthew Dillon 	m = t / 60 % 60;
317*ea37671dSMatthew Dillon 	h = t / 60 / 60;
318*ea37671dSMatthew Dillon 
319*ea37671dSMatthew Dillon 	if (work->state == WORKER_RUNNING)
320*ea37671dSMatthew Dillon 		phase = getphasestr(work->phase);
321*ea37671dSMatthew Dillon 
322*ea37671dSMatthew Dillon 	if (work->pkg)
323*ea37671dSMatthew Dillon 		origin = work->pkg->portdir;
324*ea37671dSMatthew Dillon 	else
325*ea37671dSMatthew Dillon 		origin = "";
326*ea37671dSMatthew Dillon 
327*ea37671dSMatthew Dillon 	mvwprintw(CWin, WORKER_START + i, DURATION_COL,
328*ea37671dSMatthew Dillon 		  "%02d:%02d:%02d", h, m, s);
329*ea37671dSMatthew Dillon 	mvwprintw(CWin, WORKER_START + i, BUILD_PHASE_COL,
330*ea37671dSMatthew Dillon 		  "%-16.16s", phase);
331*ea37671dSMatthew Dillon 	mvwprintw(CWin, WORKER_START + i, ORIGIN_COL,
332*ea37671dSMatthew Dillon 		  "%-39.39s", origin);
333*ea37671dSMatthew Dillon 	mvwprintw(CWin, WORKER_START + i, LINES_COL,
334*ea37671dSMatthew Dillon 		  "%6d", work->lines);
335*ea37671dSMatthew Dillon }
336*ea37671dSMatthew Dillon 
337*ea37671dSMatthew Dillon static void
338*ea37671dSMatthew Dillon NCursesSync(void)
339*ea37671dSMatthew Dillon {
340*ea37671dSMatthew Dillon 	int c;
341*ea37671dSMatthew Dillon 
342*ea37671dSMatthew Dillon 	if (UseNCurses == 0)
343*ea37671dSMatthew Dillon 		return;
344*ea37671dSMatthew Dillon 
345*ea37671dSMatthew Dillon 	while ((c = wgetch(CMon)) != ERR) {
346*ea37671dSMatthew Dillon 		if (c == KEY_RESIZE)
347*ea37671dSMatthew Dillon 			NCursesReset();
348*ea37671dSMatthew Dillon 	}
349*ea37671dSMatthew Dillon 	wrefresh(CWin);
350*ea37671dSMatthew Dillon 	wrefresh(CMon);
351*ea37671dSMatthew Dillon }
352*ea37671dSMatthew Dillon 
353*ea37671dSMatthew Dillon static void
354*ea37671dSMatthew Dillon NCursesDone(void)
355*ea37671dSMatthew Dillon {
356*ea37671dSMatthew Dillon 	if (UseNCurses == 0)
357*ea37671dSMatthew Dillon 		return;
358*ea37671dSMatthew Dillon 
359*ea37671dSMatthew Dillon 	endwin();
360*ea37671dSMatthew Dillon }
361*ea37671dSMatthew Dillon 
362*ea37671dSMatthew Dillon static int
363*ea37671dSMatthew Dillon guireadline(int fd, char **bufp)
364*ea37671dSMatthew Dillon {
365*ea37671dSMatthew Dillon 	int r;
366*ea37671dSMatthew Dillon 	int n;
367*ea37671dSMatthew Dillon 
368*ea37671dSMatthew Dillon 	/*
369*ea37671dSMatthew Dillon 	 * Reset buffer as an optimization to avoid unnecessary
370*ea37671dSMatthew Dillon 	 * shifts.
371*ea37671dSMatthew Dillon 	 */
372*ea37671dSMatthew Dillon 	*bufp = NULL;
373*ea37671dSMatthew Dillon 	if (MonitorBufBeg == MonitorBufEnd) {
374*ea37671dSMatthew Dillon 		MonitorBufBeg = 0;
375*ea37671dSMatthew Dillon 		MonitorBufEnd = 0;
376*ea37671dSMatthew Dillon 		MonitorBufScan = 0;
377*ea37671dSMatthew Dillon 	}
378*ea37671dSMatthew Dillon 
379*ea37671dSMatthew Dillon 	/*
380*ea37671dSMatthew Dillon 	 * Look for newline, handle discard mode
381*ea37671dSMatthew Dillon 	 */
382*ea37671dSMatthew Dillon again:
383*ea37671dSMatthew Dillon 	for (n = MonitorBufScan; n < MonitorBufEnd; ++n) {
384*ea37671dSMatthew Dillon 		if (MonitorBuf[n] == '\n') {
385*ea37671dSMatthew Dillon 			*bufp = MonitorBuf + MonitorBufBeg;
386*ea37671dSMatthew Dillon 			r = n - MonitorBufBeg;
387*ea37671dSMatthew Dillon 			MonitorBufBeg = n + 1;
388*ea37671dSMatthew Dillon 			MonitorBufScan = n + 1;
389*ea37671dSMatthew Dillon 
390*ea37671dSMatthew Dillon 			if (MonitorBufDiscardMode == 0)
391*ea37671dSMatthew Dillon 				return r;
392*ea37671dSMatthew Dillon 			MonitorBufDiscardMode = 0;
393*ea37671dSMatthew Dillon 			goto again;
394*ea37671dSMatthew Dillon 		}
395*ea37671dSMatthew Dillon 	}
396*ea37671dSMatthew Dillon 
397*ea37671dSMatthew Dillon 	/*
398*ea37671dSMatthew Dillon 	 * Handle overflow
399*ea37671dSMatthew Dillon 	 */
400*ea37671dSMatthew Dillon 	if (n == sizeof(MonitorBuf)) {
401*ea37671dSMatthew Dillon 		if (MonitorBufBeg) {
402*ea37671dSMatthew Dillon 			/*
403*ea37671dSMatthew Dillon 			 * Shift the buffer to make room and read more data.
404*ea37671dSMatthew Dillon 			 */
405*ea37671dSMatthew Dillon 			bcopy(MonitorBuf + MonitorBufBeg,
406*ea37671dSMatthew Dillon 			      MonitorBuf,
407*ea37671dSMatthew Dillon 			      n - MonitorBufBeg);
408*ea37671dSMatthew Dillon 			MonitorBufEnd -= MonitorBufBeg;
409*ea37671dSMatthew Dillon 			MonitorBufScan -= MonitorBufBeg;
410*ea37671dSMatthew Dillon 			n -= MonitorBufBeg;
411*ea37671dSMatthew Dillon 			MonitorBufBeg = 0;
412*ea37671dSMatthew Dillon 		} else if (MonitorBufDiscardMode) {
413*ea37671dSMatthew Dillon 			/*
414*ea37671dSMatthew Dillon 			 * Overflow.  If in discard mode just throw it all
415*ea37671dSMatthew Dillon 			 * away.  Stay in discard mode.
416*ea37671dSMatthew Dillon 			 */
417*ea37671dSMatthew Dillon 			MonitorBufBeg = 0;
418*ea37671dSMatthew Dillon 			MonitorBufEnd = 0;
419*ea37671dSMatthew Dillon 			MonitorBufScan = 0;
420*ea37671dSMatthew Dillon 		} else {
421*ea37671dSMatthew Dillon 			/*
422*ea37671dSMatthew Dillon 			 * Overflow.  If not in discard mode return a truncated
423*ea37671dSMatthew Dillon 			 * line and enter discard mode.
424*ea37671dSMatthew Dillon 			 *
425*ea37671dSMatthew Dillon 			 * The caller will temporarily set ptr[r] = 0 so make
426*ea37671dSMatthew Dillon 			 * sure that does not overflow our buffer as we are not
427*ea37671dSMatthew Dillon 			 * at a newline.
428*ea37671dSMatthew Dillon 			 *
429*ea37671dSMatthew Dillon 			 * (MonitorBufBeg is 0);
430*ea37671dSMatthew Dillon 			 */
431*ea37671dSMatthew Dillon 			*bufp = MonitorBuf + MonitorBufBeg;
432*ea37671dSMatthew Dillon 			r = n - 1;
433*ea37671dSMatthew Dillon 			MonitorBufBeg = n;
434*ea37671dSMatthew Dillon 			MonitorBufScan = n;
435*ea37671dSMatthew Dillon 			MonitorBufDiscardMode = 1;
436*ea37671dSMatthew Dillon 
437*ea37671dSMatthew Dillon 			return r;
438*ea37671dSMatthew Dillon 		}
439*ea37671dSMatthew Dillon 	}
440*ea37671dSMatthew Dillon 
441*ea37671dSMatthew Dillon 	/*
442*ea37671dSMatthew Dillon 	 * Read more data.  If there is no data pending then return -1,
443*ea37671dSMatthew Dillon 	 * otherwise loop up to see if more complete line(s) are available.
444*ea37671dSMatthew Dillon 	 */
445*ea37671dSMatthew Dillon 	r = pread(fd,
446*ea37671dSMatthew Dillon 		  MonitorBuf + MonitorBufEnd,
447*ea37671dSMatthew Dillon 		  sizeof(MonitorBuf) - MonitorBufEnd,
448*ea37671dSMatthew Dillon 		  MonitorLogOff);
449*ea37671dSMatthew Dillon 	if (r <= 0)
450*ea37671dSMatthew Dillon 		return -1;
451*ea37671dSMatthew Dillon 	MonitorLogOff += r;
452*ea37671dSMatthew Dillon 	MonitorBufEnd += r;
453*ea37671dSMatthew Dillon 	goto again;
454*ea37671dSMatthew Dillon }
455*ea37671dSMatthew Dillon 
456*ea37671dSMatthew Dillon runstats_t NCursesRunStats = {
457*ea37671dSMatthew Dillon 	.init = NCursesInit,
458*ea37671dSMatthew Dillon 	.done = NCursesDone,
459*ea37671dSMatthew Dillon 	.reset = NCursesReset,
460*ea37671dSMatthew Dillon 	.update = NCursesUpdate,
461*ea37671dSMatthew Dillon 	.updateTop = NCursesUpdateTop,
462*ea37671dSMatthew Dillon 	.updateLogs = NCursesUpdateLogs,
463*ea37671dSMatthew Dillon 	.sync = NCursesSync
464*ea37671dSMatthew Dillon };
465