1 /*
2  *  Copyright (C) 2006-2019, Thomas Maier-Komor
3  *
4  *  This is the source code of xjobs.
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <strings.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 
27 #include "config.h"
28 
29 #ifdef HAVE_CURSES_H
30 #include <curses.h>
31 #ifdef __sun		// term.h cannot be found on solaris by autoconf
32 #include <term.h>	// and requires curses.h as a prerequisite
33 #endif
34 #ifndef HAVE_TERMINFO
35 #define HAVE_TERMINFO
36 #endif
37 #endif
38 
39 #define TPARM_VARARGS
40 
41 #ifdef HAVE_TERM_H
42 #include <term.h>
43 #ifndef HAVE_TERMINFO
44 #define HAVE_TERMINFO
45 #endif
46 #endif
47 
48 #ifdef HAVE_NCURSES_H
49 #include <ncurses.h>
50 #ifndef HAVE_TERMINFO
51 #define HAVE_TERMINFO
52 #endif
53 #endif
54 
55 #ifndef ERR
56 #define ERR -1
57 #endif
58 
59 #include "colortty.h"
60 #include "log.h"
61 #include "support.h"
62 
63 ttymode_t TtyMode = tty_auto;
64 
65 int HaveTty = 0;
66 
67 color_t
68 	ColorStart = cyan,
69 	ColorDebug = noattr,
70 	ColorInfo = noattr,
71 	ColorWarn = yellow,
72 	ColorError = red,
73 	ColorFail = red,
74 	ColorDone = blue,
75 	ColorOut = noattr;
76 
77 char *PfxStart = "", *PfxDone = "", *PfxFail = "", *PfxWarn = "", *PfxError = ""
78 	, *PfxOut = "", *PfxInfo = 0, *PfxDebug = 0;
79 
80 const char *TiSave = "", *TiRestore = "", *TiClrEol = "", *TiNoAttr = "",
81 	*TiBlack = "", *TiBlue = "", *TiCyan = "", *TiGreen = "", *TiGrey = "",
82 	*TiMagenta = "", *TiRed = "", *TiWhite = "", *TiYellow = "";
83 
84 unsigned TiClrEolL = 0, TiNoAttrL = 0;
85 int PgPid = 0;
86 
87 
init_tty_none()88 static void init_tty_none()
89 {
90 	TiBlack = "";
91 	TiBlue = "";
92 	TiCyan = "";
93 	TiGreen = "";
94 	TiGrey = "";
95 	TiMagenta = "";
96 	TiRed = "";
97 	TiWhite = "";
98 	TiYellow = "";
99 	TiClrEol = "";
100 	TiClrEolL = 0;
101 	TiNoAttr = "";
102 	TiNoAttrL = 0;
103 	TiSave = "";
104 	TiRestore = "";
105 }
106 
107 
init_tty_ansi()108 static void init_tty_ansi()
109 {
110 	/* background: black */
111 	TiBlack = "\033[30m";
112 	TiBlue = "\033[34m";
113 	TiCyan = "\033[36m";
114 	TiGreen = "\033[32m";
115 	TiGrey = "\033[90m";
116 	TiMagenta = "\033[35m";
117 	TiRed = "\033[31m";
118 	TiWhite = "\033[37m";
119 	TiYellow = "\033[33m";
120 	TiClrEol = "\033[K";
121 	TiClrEolL = strlen(TiClrEol);
122 	TiNoAttr = "\033[0m";
123 	TiNoAttrL = strlen(TiNoAttr);
124 	TiSave = "\033[s";
125 	TiRestore = "\033[r";
126 }
127 
128 
colorstr(color_t c)129 const char *colorstr(color_t c)
130 {
131 	switch (c) {
132 	case black:
133 		return TiBlack;
134 	case blue:
135 		return TiBlue;
136 	case cyan:
137 		return TiCyan;
138 	case green:
139 		return TiGreen;
140 	case magenta:
141 		return TiMagenta;
142 	case red:
143 		return TiRed;
144 	case white:
145 		return TiWhite;
146 	case yellow:
147 		return TiYellow;
148 	case noattr:
149 		return TiNoAttr;
150 	default:
151 		abort();
152 	}
153 	return 0;
154 }
155 
156 
str2color(const char * str)157 color_t str2color(const char *str)
158 {
159 	if (0 == strcasecmp(str,"blue"))
160 		return blue;
161 	if (0 == strcasecmp(str,"black"))
162 		return black;
163 	if (0 == strcasecmp(str,"cyan"))
164 		return cyan;
165 	if (0 == strcasecmp(str,"green"))
166 		return green;
167 	if (0 == strcasecmp(str,"magenta"))
168 		return magenta;
169 	if (0 == strcasecmp(str,"red"))
170 		return red;
171 	if (0 == strcasecmp(str,"white"))
172 		return white;
173 	if (0 == strcasecmp(str,"yellow"))
174 		return yellow;
175 	if (0 == strcasecmp(str,"none"))
176 		return noattr;
177 	if (0 == strcasecmp(str,"default"))
178 		return noattr;
179 	return invalid_color;
180 }
181 
182 
init_pager()183 static int init_pager()
184 {
185 	char *pager = getenv("PAGER");
186 	if (pager == 0) {
187 		dbug("pager not set");
188 		return 0;
189 	}
190 	char *pgexe = complete_exe(pager);
191 	if (0 == pgexe) {
192 		dbug("pager not found");
193 		return 0;
194 	}
195 	info("starting pager %s\n",pgexe);
196 	int p[2];
197 	if (pipe(p)) {
198 		warn("unable to create pipe for pager: %s\n",strerror(errno));
199 		return 0;
200 	}
201 	PgPid = fork();
202 	if (PgPid) {
203 		if (PgPid == -1) {
204 			warn("failed to fork pager: %s\n",strerror(errno));
205 			PgPid = 0;
206 			return 0;
207 		}
208 		dup2(p[1],STDERR_FILENO);
209 		dup2(p[1],STDOUT_FILENO);
210 		close(p[1]);
211 		close(p[0]);
212 		free(pgexe);
213 	} else {
214 		dup2(p[0],STDIN_FILENO);
215 		close(p[0]);
216 		close(p[1]);
217 		execl(pgexe,pgexe,NULL);
218 		_exit(EXIT_FAILURE);
219 	}
220 	return 1;
221 }
222 
223 
init_terminal()224 void init_terminal()
225 {
226 	int ispipe = 0;
227 	if (1 == isatty(STDOUT_FILENO)) {
228 		HaveTty = 1;
229 		dbug("stdout is a terminal\n");
230 	} else {
231 		dbug("stdout is not a terminal\n");
232 		struct stat st;
233 		int r = fstat(STDOUT_FILENO,&st);
234 		if (r == 0) {
235 			if (S_ISFIFO(st.st_mode)) {
236 				dbug("stdout is a pipe\n");
237 				ispipe = 1;
238 			}
239 		} else {
240 			warn("unable to stat output: %s\n",strerror(errno));
241 		}
242 	}
243 	dbug("tty mode %d\n",TtyMode);
244 	switch (TtyMode) {
245 	case tty_none:
246 	case tty_ansi:
247 		break;
248 	case tty_pipe:
249 		if (ispipe == 1)
250 			TtyMode = tty_ansi;
251 		else if (HaveTty == 1)
252 			TtyMode = tty_auto;
253 		else
254 			TtyMode = tty_none;
255 		break;
256 	case tty_auto:
257 		if (ispipe == 1)
258 			TtyMode = tty_ansi;
259 		else if (HaveTty == 0)
260 			TtyMode = tty_none;
261 		break;
262 	default:
263 		assert(0);
264 	}
265 	if (HaveTty && init_pager())
266 		TtyMode = tty_ansi;
267 	if (TtyMode == tty_none) {
268 		dbug("tty mode resolved to none - disabling color&status support\n");
269 		init_tty_none();
270 		return;
271 	}
272 	if (TtyMode == tty_ansi) {
273 		dbug("setting up for ansi terminal\n");
274 		init_tty_ansi();
275 	}
276 	if ((TtyMode == tty_auto) || (TtyMode == tty_pipe)) {
277 #ifdef HAVE_TERMINFO
278 		char *ts;
279 		int err = 0;
280 		if (ERR == setupterm(0,STDOUT_FILENO,&err))
281 			return;
282 		switch (err) {
283 		case 1:
284 			dbug("terminal setup ok\n");
285 			break;
286 		case 0:
287 			dbug("no terminal - disabling color\n");
288 			break;
289 		case -1:
290 		default:
291 			warn("unable to load terminal database - disabling color\n");
292 		}
293 		ts = tigetstr("sc");
294 		if ((ts != 0) && (ts != (char *)-1))
295 			TiSave = ts;
296 		ts = tigetstr("rc");
297 		if ((ts != 0) && (ts != (char *)-1))
298 			TiRestore = ts;
299 		ts = tigetstr("el");
300 		if ((ts != 0) && (ts != (char *)-1))
301 			TiClrEol = ts;
302 		TiClrEolL = strlen(TiClrEol);
303 		ts = tigetstr("sgr0");
304 		if ((ts != 0) && (ts != (char *)-1)) {
305 			TiNoAttr = ts;
306 			TiNoAttrL = strlen(TiNoAttr);
307 		}
308 		char *setaf = tigetstr("setaf");
309 		char *setf = tigetstr("setf");
310 		if (setaf && (setaf != (const char *)-1)) {
311 			/* ANSI terminal capabilities */
312 			dbug("initializing terminal with ANSI capabilities\n");
313 			TiBlack = Strdup(tparm(setaf,0/*BLACK*/));
314 			TiRed = Strdup(tparm(setaf,1/*RED*/));
315 			TiGreen = Strdup(tparm(setaf,2/*GREEN*/));
316 			TiYellow = Strdup(tparm(setaf,3/*YELLOW*/));
317 			TiBlue = Strdup(tparm(setaf,4/*BLUE*/));
318 			TiMagenta = Strdup(tparm(setaf,5/*MAGENTA*/));
319 			TiCyan = Strdup(tparm(setaf,6/*CYAN*/));
320 			TiWhite = Strdup(tparm(setaf,7/*WHITE*/));
321 		} else if (setf && (setf != (const char *)-1)) {
322 			/* NON-ANSI terminal capabilities */
323 			dbug("initializing terminal with non-ANSI capabilities\n");
324 			TiBlack = Strdup(tparm(setaf,0/*BLACK*/));
325 			TiBlue = Strdup(tparm(setf,1/*BLUE*/));
326 			TiGreen = Strdup(tparm(setf,2/*GREEN*/));
327 			TiCyan = Strdup(tparm(setf,3/*CYAN*/));
328 			TiRed = Strdup(tparm(setf,4/*RED*/));
329 			TiMagenta = Strdup(tparm(setf,5/*MAGENTA*/));
330 			TiYellow = Strdup(tparm(setf,6/*YELLOW*/));
331 			TiWhite = Strdup(tparm(setf,7/*WHITE*/));
332 		}
333 #else
334 		dbug("terminfo is missing - disabling color&status support\n");
335 		init_tty_none();
336 #endif
337 	}
338 
339 	PfxDebug = concatstr(TiClrEol,colorstr(ColorDebug));
340 	PfxInfo = concatstr(TiClrEol,colorstr(ColorInfo));
341 	PfxWarn = makestr("%s%swarning: ",TiClrEol,colorstr(ColorWarn));
342 	PfxStart = concatstr(TiClrEol,colorstr(ColorStart));
343 	PfxOut = concatstr(TiClrEol,colorstr(ColorOut));
344 	PfxError = concatstr(TiClrEol,colorstr(ColorError));
345 	PfxFail = concatstr(TiClrEol,colorstr(ColorFail));
346 	PfxDone = concatstr(TiClrEol,colorstr(ColorDone));
347 }
348 
349 
350