Branch data Line data Source code
1 : : /*
2 : : * Generate C code to initialize and support Omega colors on UNIX.
3 : : *
4 : : * Usage:
5 : : * cpp -DOMEGA_CLRGEN *.[ch] | genclr <c-file> <h-file>
6 : : * where <c-file> should be compiled and linked with the Omega binary and
7 : : * <h-file> should be #included in all Omega sources that reference colors.
8 : : *
9 : : * Curses color micro-tutorial:
10 : : * The curses library requires color attributes to be specified as
11 : : * COLOR_PAIR(<n>) macro calls, where init_pair(<n>, <fore-color>,
12 : : * <back-color>) has previously been called. <n> must fall in the range
13 : : * 1..COLOR_PAIRS, where COLOR_PAIRS is a run-time read-only curses variable
14 : : * typically set to something like 64.
15 : : *
16 : : * <c-file> defines function clrgen_init(), which contains the minimal number
17 : : * of curses init_pair() calls to support the color usages detected by this
18 : : * program on standard input.
19 : : *
20 : : * <h-file> defines preprocessor variables that cause each detected color
21 : : * usage to expand to an appropriate curses COLOR_PAIR() call.
22 : : *
23 : : * This approach to UNIX color support is perhaps overkill, but does have
24 : : * advantages over these alternative approaches:
25 : : *
26 : : * - hard-coded init_pair() calls and color definitions, which would
27 : : * require manual checking and possibly editing for color reference added
28 : : * or removed anywhere in the sources;
29 : : *
30 : : * - replacement of color references with function calls that lazily call
31 : : * init_pair() as necessary, which would consume more run-time resources
32 : : * and behave unpredictably if the number of pairs exceeded COLOR_PAIRS;
33 : : *
34 : : * - run-time analysis of color pairs required by e.g. monster list, which
35 : : * would be a bit more complex to code, consume more run-time resources,
36 : : * and require significant code changes to move all color references into
37 : : * static storage.
38 : : */
39 : :
40 : : #ifndef OMEGA_CLRGEN /* this file confuses its own scanner */
41 : :
42 : : #include <stdio.h>
43 : : #include <stdlib.h>
44 : : #include <string.h>
45 : :
46 : : /*
47 : : * Special tag cpp prepends to color symbols
48 : : */
49 : : #define PREFIX "OMEGA_CLRGEN"
50 : :
51 : : /*
52 : : * Return whether a character could be part of a C identifier
53 : : */
54 : : #define ISCID(c) \
55 : : (((c) >= 'A' && (c) <= 'Z') || \
56 : : ((c) >= 'a' && (c) <= 'z') || \
57 : : ((c) >= '0' && (c) <= '9') || \
58 : : (c) == '_')
59 : :
60 : : /*
61 : : * Colors specified in cpp output on standard input
62 : : */
63 : : typedef struct {
64 : : char *ofg, *obg; /* Omega fore/background color */
65 : : char *cfg, *cbg; /* curses fore/background color */
66 : : unsigned int boldfg, boldbg; /* fore/background bold flag */
67 : : unsigned int idx; /* COLOR_PAIR() argument */
68 : : } ClrPair;
69 : :
70 : : /*
71 : : * Omega versus curses color names
72 : : */
73 : : typedef struct {
74 : : char *omega;
75 : : char *curses;
76 : : unsigned int bold;
77 : : } ClrEquiv;
78 : :
79 : : #ifdef USE_OPCURSES
80 : : static ClrEquiv clr_equiv[17] = {
81 : : { "BLACK", "BLACK", 0 },
82 : : { "BLUE", "BLUE", 0 },
83 : : { "GREEN", "GREEN", 0 },
84 : : { "CYAN", "CYAN", 0 },
85 : : { "RED", "RED", 0 },
86 : : { "PURPLE", "MAGENTA", 0 },
87 : : { "BROWN", "YELLOW", 0 },
88 : : { "WHITE", "WHITE", 0 },
89 : : { "GREY", "WHITE", 1 },
90 : : { "LIGHT_BLUE", "BLUE", 1 },
91 : : { "LIGHT_GREEN", "GREEN", 1 },
92 : : { "LIGHT_CYAN", "CYAN", 1 },
93 : : { "LIGHT_RED", "RED", 1 },
94 : : { "LIGHT_PURPLE", "MAGENTA", 1 },
95 : : { "YELLOW", "YELLOW", 1 },
96 : : { "BRIGHT_WHITE", "WHITE", 1 },
97 : : { NULL, NULL, 0 } };
98 : : #else
99 : : static ClrEquiv clr_equiv[17] = {
100 : : { "BLACK", "BLACK", 0 },
101 : : { "BLUE", "BLUE", 0 },
102 : : { "GREEN", "GREEN", 0 },
103 : : { "CYAN", "CYAN", 0 },
104 : : { "RED", "RED", 0 },
105 : : { "PURPLE", "MAGENTA", 0 },
106 : : { "BROWN", "YELLOW", 0 },
107 : : { "WHITE", "WHITE", 0 },
108 : : { "GREY", "BLACK", 1 },
109 : : { "LIGHT_BLUE", "BLUE", 1 },
110 : : { "LIGHT_GREEN", "GREEN", 1 },
111 : : { "LIGHT_CYAN", "CYAN", 1 },
112 : : { "LIGHT_RED", "RED", 1 },
113 : : { "LIGHT_PURPLE", "MAGENTA", 1 },
114 : : { "YELLOW", "YELLOW", 1 },
115 : : { "BRIGHT_WHITE", "WHITE", 1 },
116 : : { NULL, NULL, 0 } };
117 : : #endif
118 : :
119 : 2450 : static char *clr_lookup (char *omega, char **curses, unsigned int *bold)
120 : : {
121 : : /*
122 : : * Point CURSES to the curses color corresponding to Omega color OMEGA,
123 : : * set *BOLD to whether the bold attribute should accompany that curses
124 : : * color, and return a copy of OMEGA. If OMEGA is unrecognized, return
125 : : * null.
126 : : */
127 : 2450 : ClrEquiv *e = clr_equiv;
128 [ + - ]: 11299 : for (; e->omega; e++)
129 [ + + ]: 11299 : if (!strcmp (e->omega, omega)) {
130 : 2450 : *curses = e->curses;
131 : 2450 : *bold = e->bold;
132 : 2450 : return e->omega;
133 : : }
134 : 0 : return NULL;
135 : : }
136 : :
137 : 1303 : static char *clr_scan (char *p, char **curses, unsigned int *bold, char **end)
138 : : {
139 : : /*
140 : : * Return a copy of the Omega color nearest the start of writable buffer
141 : : * P, point CURSES to the corresponding curses color, and point END just
142 : : * past the color's location in P.
143 : : *
144 : : * If the Omega color is unrecognized, issue an error and exit.
145 : : */
146 : : char c, *start, *omega;
147 [ + - ]: 2606 : for (; (c = *p); p++) {
148 [ + + ][ - + ]: 2606 : if (!ISCID (c))
[ - + ][ # # ]
[ - + ][ # # ]
[ + - ]
149 : 1303 : continue;
150 [ + - ]: 6855 : for (start = p++; (c = *p); p++) {
151 [ + + ][ + + ]: 6855 : if (ISCID (c))
[ - + ][ # # ]
[ + + ][ + - ]
[ + + ]
152 : 5552 : continue;
153 : 1303 : *p = '\0';
154 [ - + ]: 1303 : if (!(omega = clr_lookup (start, curses, bold))) {
155 : 0 : fprintf (stderr, "unrecognized Omega color \"%s\"\n", start);
156 : 0 : exit (1);
157 : : }
158 : 1303 : *p = c;
159 : 1303 : *end = p;
160 : 1303 : return omega;
161 : : }
162 : : }
163 : 0 : return NULL;
164 : : }
165 : :
166 : 11429 : static int opaircmp (const void *pair1, const void *pair2)
167 : : {
168 : : /*
169 : : * qsort comparison function: return less than, equal to, or greater than
170 : : * 0 according to whether PAIR1 precedes, coincides with, or follows PAIR2
171 : : * in a sorted list of Omega color pairs.
172 : : */
173 : 11429 : ClrPair *p1 = (ClrPair *)pair1, *p2 = (ClrPair *)pair2;
174 : 11429 : int diff = strcmp (p1->ofg, p2->ofg);
175 [ + + ]: 11429 : if (diff)
176 : 5406 : return diff;
177 : 6023 : return strcmp (p1->obg, p2->obg);
178 : : }
179 : :
180 : 172 : static int cpaircmp (const void *pair1, const void *pair2)
181 : : {
182 : : /*
183 : : * qsort comparison function: return less than, equal to, or greater than
184 : : * 0 according to whether PAIR1 precedes, coincides with, or follows PAIR2
185 : : * in a sorted list of curses color pairs.
186 : : */
187 : 172 : ClrPair *p1 = *(ClrPair **)pair1, *p2 = *(ClrPair **)pair2;
188 : 172 : int diff = strcmp (p1->cfg, p2->cfg);
189 [ + + ]: 172 : if (diff)
190 : 81 : return diff;
191 : 91 : return strcmp (p1->cbg, p2->cbg);
192 : : }
193 : :
194 : 2 : static FILE *emitopen (char *file, char **argv)
195 : : {
196 : : /*
197 : : * Write to the top of FILE a suitable header based on ARGV, and return a
198 : : * writable file pointer on FILE. Exit on error.
199 : : */
200 : 2 : FILE *fp = fopen (file, "w");
201 [ - + ]: 2 : if (!fp) {
202 : 0 : fprintf (stderr, "error opening %s", file);
203 : 0 : perror ("");
204 : 0 : exit (1);
205 : : }
206 : 2 : fprintf (fp, "\
207 : : /*\n\
208 : : * Do not edit this file. It was automatically generated by running:\n\
209 : : * %s %s %s\n\
210 : : */\n\
211 : : \n\
212 : : ",
213 : 4 : argv[0], argv[1], argv[2]);
214 : 2 : return fp;
215 : : }
216 : :
217 : 2 : static void emitclose (FILE *fp, char *file)
218 : : {
219 : : /*
220 : : * Close FP attached to FILE, exiting on error.
221 : : */
222 [ + - ]: 2 : if (fclose (fp) == 0)
223 : 2 : return;
224 : 0 : fprintf (stderr, "error closing %s", file);
225 : 0 : perror ("");
226 : 0 : exit (1);
227 : : }
228 : :
229 : 1 : int main (int argc, char **argv)
230 : : {
231 : : char line[1024], *p;
232 : 1 : unsigned int i, j, nopairs = 0, ncpairs, opairslen = 80, one;
233 : : ClrPair *pair;
234 : : ClrPair *opairs; /* Omega color pairs */
235 : : ClrPair **cpairs; /* curses color pairs */
236 : : char *cfile, *hfile;
237 : : FILE *fp;
238 : :
239 [ - + ]: 1 : if (argc != 3) {
240 : 0 : fprintf (stderr, "usage: %s <c-file> <h-file>\n", argv[0]);
241 : 0 : exit (1);
242 : : }
243 : 1 : cfile = argv[1];
244 : 1 : hfile = argv[2];
245 : :
246 : : /*
247 : : * Accumulate Omega color pairs from standard input into pairs.
248 : : */
249 : 1 : opairs = (ClrPair *)malloc (opairslen * sizeof (ClrPair));
250 [ + + ]: 289990 : while (fgets (line, 1024, stdin)) {
251 [ + + ]: 291214 : for (p = line; (p = strstr (p, PREFIX));) {
252 : 1225 : p += sizeof (PREFIX) - 1;
253 [ + + ]: 1225 : if (nopairs == opairslen) {
254 : 4 : opairslen *= 2;
255 : 4 : opairs = (ClrPair *)realloc (opairs, opairslen *
256 : : sizeof (ClrPair));
257 : : }
258 : 1225 : pair = opairs + nopairs++;
259 : 1225 : one = *p++ == '1';
260 : 1225 : pair->ofg = clr_scan (p, &pair->cfg, &pair->boldfg, &p);
261 : 1225 : pair->obg = one ?
262 [ + + ]: 1225 : clr_lookup ("BLACK", &pair->cbg, &pair->boldbg) :
263 : 78 : clr_scan (p, &pair->cbg, &pair->boldbg, &p);
264 [ - + ]: 1225 : if (pair->boldbg)
265 : 0 : fprintf (stderr, "warning: \"%s\": bg bold unimplemented\n",
266 : : pair->obg);
267 : : }
268 : : }
269 [ - + ]: 1 : if (!nopairs) {
270 : 0 : fputs ("no colors detected in standard input\n", stderr);
271 : 0 : exit (1);
272 : : }
273 : :
274 : : /*
275 : : * Remove duplicate Omega color pairs.
276 : : */
277 : 1 : qsort (opairs, nopairs, sizeof (ClrPair), opaircmp);
278 [ + + ]: 1225 : for (i = 0, j = 1; j < nopairs; j++) {
279 [ + + ]: 1224 : if (opaircmp (opairs + i, opairs + j))
280 : 38 : opairs[++i] = opairs[j];
281 : : }
282 : 1 : nopairs = i + 1;
283 : :
284 : : /*
285 : : * Construct a list of unique curses color pairs, and instantiate all
286 : : * ClrPair.idx fields.
287 : : */
288 : 1 : cpairs = (ClrPair **)malloc (nopairs * sizeof (ClrPair *));
289 [ + + ]: 40 : for (i = 0; i < nopairs; i++)
290 : 39 : cpairs[i] = opairs + i;
291 : 1 : qsort (cpairs, nopairs, sizeof (ClrPair *), cpaircmp);
292 : 1 : cpairs[0]->idx = 1;
293 : :
294 [ + + ]: 39 : for (i = 0, j = 1; j < nopairs; j++) {
295 [ + + ]: 38 : if (cpaircmp (cpairs + i, cpairs + j))
296 : 24 : cpairs[++i] = cpairs[j];
297 : 38 : cpairs[j]->idx = i + 1;
298 : : }
299 : 1 : ncpairs = i + 1;
300 : :
301 : : /*
302 : : * Emit .c file.
303 : : */
304 : 1 : fp = emitopen (cfile, argv);
305 : :
306 : : #ifdef USE_OPCURSES
307 : : fprintf (fp, "\
308 : : #include \"../opcurses/curses.h\"\n");
309 : : #else
310 : 1 : fprintf (fp, "\
311 : : #include <curses.h>\n");
312 : : #endif
313 : :
314 : 1 : fprintf (fp, "\
315 : : #include <stdio.h>\n\
316 : : #include <stdlib.h>\n\
317 : : \
318 : : #include \"%s\"\n\
319 : : \
320 : : void clrgen_init (void)\n\
321 : : ""{\n\
322 : : if (!has_colors()) {\n\
323 : : fprintf(stderr,\"Omega currently requires ncurses or op-curses color support.\\n\");\n\
324 : : return;\n\
325 : : } else\n\
326 : : if (%d > COLOR_PAIRS - 1) {\n\
327 : : endwin();\n\
328 : : fprintf (stderr,\"Too few available color pairs (found %%d, need %d)!\\n\", COLOR_PAIRS);\n\
329 : : exit (1);\n\
330 : : }\n\
331 : : ",
332 : : hfile, ncpairs, ncpairs);
333 [ + + ]: 26 : for (i = 0; i < ncpairs; i++)
334 : 25 : fprintf (fp, "\
335 : : init_pair (%d, COLOR_%s, COLOR_%s);\n\
336 : : ",
337 : 75 : cpairs[i]->idx, cpairs[i]->cfg, cpairs[i]->cbg);
338 : 1 : fputs ("\
339 : : ""}\n\
340 : : ",
341 : : fp);
342 : 1 : emitclose (fp, cfile);
343 : :
344 : : /*
345 : : * Emit .h file.
346 : : */
347 : 1 : fp = emitopen (hfile, argv);
348 [ + + ]: 40 : for (i = 0; i < nopairs; i++) {
349 : 39 : pair = opairs + i;
350 [ + + ][ + + ]: 39 : fprintf (fp, "#define CLR_%s_%s\t%sCOLOR_PAIR(%d)%s\n",
351 : : pair->ofg, pair->obg,
352 : 39 : strlen (pair->ofg) + strlen (pair->obg) > 10 ? "" : "\t",
353 : 39 : pair->idx, pair->boldfg ? "|A_BOLD" : "");
354 : : }
355 : 1 : fputs ("\
356 : : \n\
357 : : extern void clrgen_init (void);\n\
358 : : ",
359 : : fp);
360 : 1 : emitclose (fp, hfile);
361 : :
362 : 1 : return 0;
363 : : }
364 : :
365 : : #endif /* !OMEGA_CLRGEN */
|