xref: /dragonfly/usr.bin/top/color.c (revision 5812c3cc)
1 /*
2  * Copyright (c) 1984 through 2008, William LeFebvre
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  *     * Neither the name of William LeFebvre nor the names of other
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  *  Top users/processes display for Unix
35  *  Version 3
36  */
37 
38 /*
39  * This file handles color definitions and access for augmenting
40  * the output with ansi color sequences.
41  *
42  * The definition of a color setting is as follows, separated by
43  * colons:
44  *
45  * tag=minimum,maximum#code
46  *
47  * "tag" is the name of the value to display with color.
48  *
49  * "minimum" and "maximum" are positive integer values defining a range:
50  * when the value is within this range it will be shown with the
51  * specified color.  A missing value indicates that no check should be
52  * made (i.e.: ",25" is n <= 25; "25,50" is 25 <= n <= 50; and "50,"
53  * is 50 <= n).
54  *
55  * "code" is the ansi sequence that defines the color to use with the
56  * escape sequence "[m".  Semi-colons are allowed in this string to
57  * combine attributes.
58  */
59 
60 #include "os.h"
61 #include "display.h"
62 #include "message.h"
63 #include "color.h"
64 #include "utils.h"
65 
66 typedef struct color_entry {
67     char *tag;
68     int min;
69     int max;
70     char color;
71     struct color_entry *next;
72     struct color_entry *tagnext;
73 } color_entry;
74 
75 static color_entry *entries = NULL;
76 
77 static color_entry **bytag = NULL;
78 static char **bytag_names = NULL;
79 static int totaltags = 0;
80 static int tagcnt = 0;
81 static int color_off = 0;
82 
83 static char **color_ansi = NULL;
84 static int num_color_ansi = 0;
85 static int max_color_ansi = 0;
86 
87 static int
88 color_slot(char *str)
89 
90 {
91     int i;
92 
93     for (i = 0; i < num_color_ansi; i++)
94     {
95 	if (strcmp(color_ansi[i], str) == 0)
96 	{
97 	    return i;
98 	}
99     }
100 
101     /* need a new slot */
102     if (num_color_ansi >= max_color_ansi)
103     {
104 	max_color_ansi += COLOR_ANSI_SLOTS;
105 	color_ansi = (char **)realloc(color_ansi, max_color_ansi * sizeof(char *));
106     }
107     color_ansi[num_color_ansi] = strdup(str);
108     return num_color_ansi++;
109 }
110 
111 /*
112  * int color_env_parse(char *env)
113  *
114  * Parse a color specification "env" (such as one found in the environment) and
115  * add them to the list of entries.  Always returns 0.  Should only be called
116  * once.
117  */
118 
119 int
120 color_env_parse(char *env)
121 
122 {
123     char *p;
124     char *min;
125     char *max;
126     char *str;
127     int len;
128     color_entry *ce;
129 
130     /* initialization */
131     color_ansi = (char **)malloc(COLOR_ANSI_SLOTS * sizeof(char *));
132     max_color_ansi = COLOR_ANSI_SLOTS;
133 
134     /* color slot 0 is always "0" */
135     color_slot("0");
136 
137     if (env != NULL)
138     {
139 	p = strtok(env, ":");
140 	while (p != NULL)
141 	{
142 	    if ((min = strchr(p, '=')) != NULL &&
143 		(max = strchr(min, ',')) != NULL &&
144 		(str = strchr(max, '#')) != NULL)
145 	    {
146 		ce = (color_entry *)malloc(sizeof(color_entry));
147 		len = min - p;
148 		ce->tag = (char *)malloc(len + 1);
149 		strncpy(ce->tag, p, len);
150 		ce->tag[len] = '\0';
151 		ce->min = atoi(++min);
152 		ce->max = atoi(++max);
153 		ce->color = color_slot(++str);
154 		ce->next = entries;
155 		entries = ce;
156 	    }
157 	    else
158 	    {
159 		if (min != NULL)
160 		{
161 		    len = min - p;
162 		}
163 		else
164 		{
165 		    len = strlen(p);
166 		}
167 		message_error(" %.*s: bad color entry", len, p);
168 	    }
169 	    p = strtok(NULL, ":");
170 	}
171     }
172     return 0;
173 }
174 
175 /*
176  * int color_tag(char *tag)
177  *
178  * Declare "tag" as a color tag.  Return a tag index to use when testing
179  * a value against the tests for this tag.  Should not be called before
180  * color_env_parse.
181  */
182 
183 int
184 color_tag(char *tag)
185 
186 {
187     color_entry *entryp;
188     color_entry *tp;
189 
190     /* check for absurd arguments */
191     if (tag == NULL || *tag == '\0')
192     {
193 	return -1;
194     }
195 
196     dprintf("color_tag(%s)\n", tag);
197 
198     /* initial allocation */
199     if (bytag == NULL)
200     {
201 	totaltags = 10;
202 	bytag = (color_entry **)malloc(totaltags * sizeof(color_entry *));
203 	bytag_names = (char **)malloc(totaltags * sizeof(char *));
204     }
205 
206     /* if we dont have enough room then reallocate */
207     if (tagcnt >= totaltags)
208     {
209 	totaltags *= 2;
210 	bytag = (color_entry **)realloc(bytag, totaltags * sizeof(color_entry *));
211 	bytag_names = (char **)realloc(bytag_names, totaltags * sizeof(char *));
212     }
213 
214     /* initialize scan */
215     entryp = entries;
216     tp = NULL;
217 
218     /* look for tag in the list of entries */
219     while (entryp != NULL)
220     {
221 	if (strcmp(entryp->tag, tag) == 0)
222 	{
223 	    entryp->tagnext = tp;
224 	    tp = entryp;
225 	}
226 	entryp = entryp->next;
227     }
228 
229     /* we track names in the array bytag */
230     bytag[tagcnt] = tp;
231     bytag_names[tagcnt] = strdup(tag);
232 
233     /* return this index number as a reference */
234     dprintf("color_tag: returns %d\n", tagcnt);
235     return (tagcnt++);
236 }
237 
238 /*
239  * int color_test(int tagidx, int value)
240  *
241  * Test "value" against tests for tag "tagidx", a number previously returned
242  * by color_tag.  Return the correct color number to use when highlighting.
243  * If there is no match, return 0 (color 0).
244  */
245 
246 int
247 color_test(int tagidx, int value)
248 
249 {
250     color_entry *ce;
251 
252     /* sanity check */
253     if (tagidx < 0 || tagidx >= tagcnt || color_off)
254     {
255 	return 0;
256     }
257 
258     ce = bytag[tagidx];
259 
260     while (ce != NULL)
261     {
262 	if ((!ce->min || ce->min <= value) &&
263 	    (!ce->max || ce->max >= value))
264 	{
265 	    return ce->color;
266 	}
267 	ce = ce->tagnext;
268     }
269 
270     return 0;
271 }
272 
273 /*
274  * char *color_setstr(int color)
275  *
276  * Return ANSI string to set the terminal for color number "color".
277  */
278 
279 char *
280 color_setstr(int color)
281 
282 {
283     static char v[32];
284 
285     v[0] = '\0';
286     if (color >= 0 && color < num_color_ansi)
287     {
288 	snprintf(v, sizeof(v), "\033[%sm", color_ansi[color]);
289     }
290     return v;
291 }
292 
293 void
294 color_dump(FILE *f)
295 
296 {
297     color_entry *ep;
298     int i;
299     int col;
300     int len;
301 
302     fputs("These color tags are available:", f);
303     col = 81;
304     for (i = 0; i < tagcnt; i++)
305     {
306 	len = strlen(bytag_names[i]) + 1;
307 	if (len + col > 79)
308 	{
309 	    fputs("\n  ", f);
310 	    col = 2;
311 	}
312 	fprintf(f, " %s", bytag_names[i]);
313 	col += len;
314     }
315 
316     fputs("\n\nTop color settings:\n", f);
317 
318     for (i = 0; i < tagcnt; i++)
319     {
320 	ep = bytag[i];
321 	while (ep != NULL)
322 	{
323 	    fprintf(f, "   %s (%d-", ep->tag, ep->min);
324 	    if (ep->max != 0)
325 	    {
326 		fprintf(f, "%d", ep->max);
327 	    }
328 	    fprintf(f, "): ansi color %s, %sSample Text",
329 		    color_ansi[(int)ep->color],
330 		    color_setstr(ep->color));
331 	    fprintf(f, "%s\n", color_setstr(0));
332 	    ep = ep -> tagnext;
333 	}
334     }
335 }
336 
337 void
338 color_debug(FILE *f)
339 
340 {
341     color_entry *ep;
342     int i;
343 
344     fprintf(f, "color debug dump\n");
345     ep = entries;
346     while (ep != NULL)
347     {
348 	fprintf(f, "%s(%d,%d): slot %d, ansi %s, %sSample Text",
349 		ep->tag, ep->min, ep->max, ep->color, color_ansi[(int)ep->color],
350 	       color_setstr(ep->color));
351 	fprintf(f, "%s\n", color_setstr(0));
352 	ep = ep -> next;
353     }
354 
355     fprintf(f, "\ntags:");
356     for (i = 0; i < tagcnt; i++)
357     {
358 	fprintf(f, " %s", bytag_names[i]);
359     }
360     fprintf(f, "\n");
361 }
362 
363 int
364 color_activate(int i)
365 
366 {
367     if (i == -1)
368     {
369 	color_off = !color_off;
370     }
371     else
372     {
373 	color_off = !i;
374     }
375     return color_off;
376 }
377