Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Copyright 2002 Red Hat Inc., Durham, North Carolina. | |
3 | * | |
4 | * All Rights Reserved. | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining | |
7 | * a copy of this software and associated documentation files (the | |
8 | * "Software"), to deal in the Software without restriction, including | |
9 | * without limitation on the rights to use, copy, modify, merge, | |
10 | * publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, | |
12 | * subject to the following conditions: | |
13 | * | |
14 | * The above copyright notice and this permission notice (including the | |
15 | * next paragraph) shall be included in all copies or substantial | |
16 | * portions of the Software. | |
17 | * | |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
21 | * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS | |
22 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
23 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
24 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
25 | * SOFTWARE. | |
26 | */ | |
27 | ||
28 | /* | |
29 | * Authors: | |
30 | * Rickard E. (Rik) Faith <faith@redhat.com> | |
31 | * | |
32 | */ | |
33 | ||
34 | /** \file | |
35 | * | |
36 | * This file provides support routines and helper functions to be used | |
37 | * to pretty-print DMX configurations. | |
38 | * | |
39 | * Because the DMX configuration file parsing should be capable of being | |
40 | * used in a stand-alone fashion (i.e., independent from the DMX server | |
41 | * source tree), no dependencies on other DMX routines are made. */ | |
42 | ||
43 | #ifdef HAVE_DMX_CONFIG_H | |
44 | #include <dmx-config.h> | |
45 | #endif | |
46 | ||
47 | #include "dmxconfig.h" | |
48 | #include "dmxparse.h" | |
49 | #include "dmxprint.h" | |
50 | #include "parser.h" | |
51 | #include <stdio.h> | |
52 | #include <stdarg.h> | |
53 | #include <ctype.h> | |
54 | ||
55 | static FILE *str = NULL; | |
56 | static int indent = 0; | |
57 | static int pos = 0; | |
58 | ||
59 | /** Stack of indentation information used for pretty-printing | |
60 | * configuration information. */ | |
61 | static struct stack { | |
62 | int base; | |
63 | int comment; | |
64 | int step; | |
65 | struct stack *next; | |
66 | } *stack, initialStack = { | |
67 | 0, 0, 4, NULL}; | |
68 | ||
69 | static void | |
70 | dmxConfigIndent(void) | |
71 | { | |
72 | int i; | |
73 | ||
74 | if (indent < 0) | |
75 | indent = 0; | |
76 | if (indent > 40) | |
77 | indent = 40; | |
78 | for (i = 0; i < indent; i++) | |
79 | fprintf(str, " "); | |
80 | } | |
81 | ||
82 | static void | |
83 | dmxConfigNewline(void) | |
84 | { | |
85 | if (pos) | |
86 | fprintf(str, "\n"); | |
87 | pos = 0; | |
88 | } | |
89 | ||
90 | static void | |
91 | dmxConfigPushState(int base, int comment, int step) | |
92 | { | |
93 | struct stack *new = dmxConfigAlloc(sizeof(*new)); | |
94 | ||
95 | new->base = base; | |
96 | new->comment = comment; | |
97 | new->step = step; | |
98 | new->next = stack; | |
99 | stack = new; | |
100 | indent = base; | |
101 | dmxConfigNewline(); | |
102 | } | |
103 | ||
104 | static void | |
105 | dmxConfigPushComment(void) | |
106 | { | |
107 | if (stack) | |
108 | indent = stack->comment; | |
109 | } | |
110 | ||
111 | static void | |
112 | dmxConfigPushStep(void) | |
113 | { | |
114 | if (stack) | |
115 | indent = stack->step; | |
116 | } | |
117 | ||
118 | static void | |
119 | dmxConfigPopState(void) | |
120 | { | |
121 | struct stack *old = stack; | |
122 | ||
123 | if (!stack) | |
124 | return; | |
125 | indent = old->base; | |
126 | stack = old->next; | |
127 | if (!stack) | |
128 | dmxConfigLog("Stack underflow\n"); | |
129 | dmxConfigFree(old); | |
130 | dmxConfigNewline(); | |
131 | } | |
132 | ||
133 | static void | |
134 | dmxConfigOutput(int addSpace, int doNewline, const char *comment, | |
135 | const char *format, ...) | |
136 | { | |
137 | va_list args; | |
138 | ||
139 | if (!pos) | |
140 | dmxConfigIndent(); | |
141 | else if (addSpace) | |
142 | fprintf(str, " "); | |
143 | ||
144 | if (format) { | |
145 | va_start(args, format); | |
146 | /* RATS: This hasn't been audited -- it | |
147 | * could probably result in a buffer | |
148 | * overflow. */ | |
149 | pos += vfprintf(str, format, args); /* assumes no newlines! */ | |
150 | va_end(args); | |
151 | } | |
152 | ||
153 | if (comment) { | |
154 | if (pos) | |
155 | fprintf(str, " "); | |
156 | pos += fprintf(str, "#%s", comment); | |
157 | dmxConfigNewline(); | |
158 | dmxConfigPushComment(); | |
159 | } | |
160 | else if (doNewline) | |
161 | dmxConfigNewline(); | |
162 | } | |
163 | ||
164 | static void | |
165 | dmxConfigPrintComment(DMXConfigCommentPtr p) | |
166 | { | |
167 | dmxConfigOutput(1, 1, p->comment, NULL); | |
168 | } | |
169 | ||
170 | static void | |
171 | dmxConfigPrintTokenFlag(DMXConfigTokenPtr p, int flag) | |
172 | { | |
173 | if (!p) | |
174 | return; | |
175 | switch (p->token) { | |
176 | case T_VIRTUAL: | |
177 | dmxConfigPushState(0, 4, 4); | |
178 | dmxConfigOutput(0, 0, p->comment, "virtual"); | |
179 | break; | |
180 | case T_DISPLAY: | |
181 | dmxConfigPushState(4, 12, 16); | |
182 | dmxConfigOutput(0, 0, p->comment, "display"); | |
183 | break; | |
184 | case T_WALL: | |
185 | dmxConfigPushState(4, 12, 16); | |
186 | dmxConfigOutput(0, 0, p->comment, "wall"); | |
187 | break; | |
188 | case T_OPTION: | |
189 | dmxConfigPushState(4, 12, 16); | |
190 | dmxConfigOutput(0, 0, p->comment, "option"); | |
191 | break; | |
192 | case T_PARAM: | |
193 | dmxConfigPushState(4, 8, 12); | |
194 | dmxConfigOutput(0, 0, p->comment, "param"); | |
195 | break; | |
196 | case ';': | |
197 | dmxConfigOutput(0, 1, p->comment, ";"); | |
198 | if (flag) | |
199 | dmxConfigPopState(); | |
200 | break; | |
201 | case '{': | |
202 | dmxConfigOutput(1, 1, p->comment, "{"); | |
203 | dmxConfigPushStep(); | |
204 | break; | |
205 | case '}': | |
206 | if (flag) | |
207 | dmxConfigPopState(); | |
208 | dmxConfigOutput(0, 1, p->comment, "}"); | |
209 | break; | |
210 | case '/': | |
211 | dmxConfigOutput(1, 0, NULL, "/"); | |
212 | break; | |
213 | default: | |
214 | dmxConfigLog("unknown token %d on line %d\n", p->token, p->line); | |
215 | } | |
216 | } | |
217 | ||
218 | static void | |
219 | dmxConfigPrintToken(DMXConfigTokenPtr p) | |
220 | { | |
221 | dmxConfigPrintTokenFlag(p, 1); | |
222 | } | |
223 | ||
224 | static void | |
225 | dmxConfigPrintTokenNopop(DMXConfigTokenPtr p) | |
226 | { | |
227 | dmxConfigPrintTokenFlag(p, 0); | |
228 | } | |
229 | ||
230 | static int | |
231 | dmxConfigPrintQuotedString(const char *s) | |
232 | { | |
233 | const char *pt; | |
234 | ||
235 | if (!s || !s[0]) | |
236 | return 1; /* Quote empty string */ | |
237 | for (pt = s; *pt; ++pt) | |
238 | if (isspace(*pt)) | |
239 | return 1; | |
240 | return 0; | |
241 | } | |
242 | ||
243 | static void | |
244 | dmxConfigPrintString(DMXConfigStringPtr p, int quote) | |
245 | { | |
246 | DMXConfigStringPtr pt; | |
247 | ||
248 | if (!p) | |
249 | return; | |
250 | for (pt = p; pt; pt = pt->next) { | |
251 | if (quote && dmxConfigPrintQuotedString(pt->string)) { | |
252 | dmxConfigOutput(1, 0, pt->comment, "\"%s\"", | |
253 | pt->string ? pt->string : ""); | |
254 | } | |
255 | else | |
256 | dmxConfigOutput(1, 0, pt->comment, "%s", | |
257 | pt->string ? pt->string : ""); | |
258 | } | |
259 | } | |
260 | ||
261 | static int | |
262 | dmxConfigPrintPair(DMXConfigPairPtr p, int addSpace) | |
263 | { | |
264 | const char *format = NULL; | |
265 | ||
266 | if (!p) | |
267 | return 0; | |
268 | switch (p->token) { | |
269 | case T_ORIGIN: | |
270 | format = "@%dx%d"; | |
271 | break; | |
272 | case T_DIMENSION: | |
273 | format = "%dx%d"; | |
274 | break; | |
275 | case T_OFFSET: | |
276 | format = "%c%d%c%d"; | |
277 | break; | |
278 | } | |
279 | if (p->token == T_OFFSET) { | |
280 | if (!p->comment && !p->x && !p->y && p->xsign >= 0 && p->ysign >= 0) | |
281 | return 0; | |
282 | dmxConfigOutput(addSpace, 0, p->comment, format, | |
283 | p->xsign < 0 ? '-' : '+', p->x, | |
284 | p->ysign < 0 ? '-' : '+', p->y); | |
285 | } | |
286 | else { | |
287 | if (!p->comment && !p->x && !p->y) | |
288 | return 0; | |
289 | dmxConfigOutput(addSpace, 0, p->comment, format, p->x, p->y); | |
290 | } | |
291 | return 1; | |
292 | } | |
293 | ||
294 | static void | |
295 | dmxConfigPrintDisplay(DMXConfigDisplayPtr p) | |
296 | { | |
297 | DMXConfigToken dummyStart = { T_DISPLAY, 0, NULL }; | |
298 | DMXConfigToken dummyEnd = { ';', 0, NULL }; | |
299 | DMXConfigToken dummySep = { '/', 0, NULL }; | |
300 | DMXConfigString dummyName = { T_STRING, 0, NULL, NULL, NULL }; | |
301 | DMXConfigPair dummySDim = { T_DIMENSION, 0, NULL, 0, 0, 0, 0 }; | |
302 | DMXConfigPair dummySOffset = { T_OFFSET, 0, NULL, 0, 0, 0, 0 }; | |
303 | DMXConfigPair dummyRDim = { T_DIMENSION, 0, NULL, 0, 0, 0, 0 }; | |
304 | DMXConfigPair dummyROffset = { T_OFFSET, 0, NULL, 0, 0, 0, 0 }; | |
305 | DMXConfigPair dummyOrigin = { T_ORIGIN, 0, NULL, 0, 0, 0, 0 }; | |
306 | int output; | |
307 | ||
308 | if (p->dname) | |
309 | p->dname->string = p->name; | |
310 | else | |
311 | dummyName.string = p->name; | |
312 | ||
313 | if (p->dim && p->dim->scrn && p->dim->scrn->dim) { | |
314 | p->dim->scrn->dim->x = p->scrnWidth; | |
315 | p->dim->scrn->dim->y = p->scrnHeight; | |
316 | } | |
317 | else { | |
318 | dummySDim.x = p->scrnWidth; | |
319 | dummySDim.y = p->scrnHeight; | |
320 | } | |
321 | ||
322 | if (p->dim && p->dim->scrn && p->dim->scrn->offset) { | |
323 | p->dim->scrn->offset->x = p->scrnX; | |
324 | p->dim->scrn->offset->y = p->scrnY; | |
325 | } | |
326 | else { | |
327 | dummySOffset.x = p->scrnX; | |
328 | dummySOffset.y = p->scrnY; | |
329 | } | |
330 | ||
331 | if (p->dim && p->dim->root && p->dim->root->dim) { | |
332 | p->dim->root->dim->x = p->rootWidth; | |
333 | p->dim->root->dim->y = p->rootHeight; | |
334 | } | |
335 | else { | |
336 | dummyRDim.x = p->rootWidth; | |
337 | dummyRDim.y = p->rootHeight; | |
338 | } | |
339 | ||
340 | if (p->dim && p->dim->root && p->dim->root->offset) { | |
341 | p->dim->root->offset->x = p->rootX; | |
342 | p->dim->root->offset->y = p->rootY; | |
343 | } | |
344 | else { | |
345 | dummyROffset.x = p->rootX; | |
346 | dummyROffset.y = p->rootY; | |
347 | } | |
348 | ||
349 | if (p->origin) { | |
350 | p->origin->x = p->rootXOrigin, p->origin->y = p->rootYOrigin; | |
351 | p->origin->xsign = p->rootXSign, p->origin->ysign = p->rootYSign; | |
352 | } | |
353 | else { | |
354 | dummyOrigin.x = p->rootXOrigin, dummyOrigin.y = p->rootYOrigin; | |
355 | dummyOrigin.xsign = p->rootXSign, dummyOrigin.ysign = p->rootYSign; | |
356 | } | |
357 | ||
358 | dmxConfigPrintToken(p->start ? p->start : &dummyStart); | |
359 | dmxConfigPrintString(p->dname ? p->dname : &dummyName, 1); | |
360 | ||
361 | if (p->dim && p->dim->scrn && p->dim->scrn->dim) | |
362 | output = dmxConfigPrintPair(p->dim->scrn->dim, 1); | |
363 | else | |
364 | output = dmxConfigPrintPair(&dummySDim, 1); | |
365 | if (p->dim && p->dim->scrn && p->dim->scrn->offset) | |
366 | dmxConfigPrintPair(p->dim->scrn->offset, !output); | |
367 | else | |
368 | dmxConfigPrintPair(&dummySOffset, !output); | |
369 | ||
370 | if (p->scrnWidth != p->rootWidth | |
371 | || p->scrnHeight != p->rootHeight || p->rootX || p->rootY) { | |
372 | dmxConfigPrintToken(&dummySep); | |
373 | if (p->dim && p->dim->root && p->dim->root->dim) | |
374 | output = dmxConfigPrintPair(p->dim->root->dim, 1); | |
375 | else | |
376 | output = dmxConfigPrintPair(&dummyRDim, 1); | |
377 | if (p->dim && p->dim->root && p->dim->root->offset) | |
378 | dmxConfigPrintPair(p->dim->root->offset, !output); | |
379 | else | |
380 | dmxConfigPrintPair(&dummyROffset, !output); | |
381 | } | |
382 | ||
383 | dmxConfigPrintPair(p->origin ? p->origin : &dummyOrigin, 1); | |
384 | dmxConfigPrintToken(p->end ? p->end : &dummyEnd); | |
385 | } | |
386 | ||
387 | static void | |
388 | dmxConfigPrintWall(DMXConfigWallPtr p) | |
389 | { | |
390 | dmxConfigPrintToken(p->start); | |
391 | dmxConfigPrintPair(p->wallDim, 1); | |
392 | dmxConfigPrintPair(p->displayDim, 1); | |
393 | dmxConfigPrintString(p->nameList, 1); | |
394 | dmxConfigPrintToken(p->end); | |
395 | } | |
396 | ||
397 | static void | |
398 | dmxConfigPrintOption(DMXConfigOptionPtr p) | |
399 | { | |
400 | DMXConfigToken dummyStart = { T_OPTION, 0, NULL }; | |
401 | DMXConfigString dummyOption = { T_STRING, 0, NULL, NULL, NULL }; | |
402 | DMXConfigToken dummyEnd = { ';', 0, NULL }; | |
403 | ||
404 | dummyOption.string = p->string; | |
405 | ||
406 | dmxConfigPrintToken(p->start ? p->start : &dummyStart); | |
407 | dmxConfigPrintString(&dummyOption, 0); | |
408 | dmxConfigPrintToken(p->end ? p->end : &dummyEnd); | |
409 | } | |
410 | ||
411 | static void | |
412 | dmxConfigPrintParam(DMXConfigParamPtr p) | |
413 | { | |
414 | if (!p) | |
415 | return; | |
416 | if (p->start) { | |
417 | if (p->open && p->close) { | |
418 | dmxConfigPrintToken(p->start); | |
419 | dmxConfigPrintToken(p->open); | |
420 | dmxConfigPrintParam(p->next); | |
421 | dmxConfigPrintToken(p->close); | |
422 | } | |
423 | else if (p->end && p->param) { | |
424 | dmxConfigPrintToken(p->start); | |
425 | dmxConfigPrintString(p->param, 1); | |
426 | dmxConfigPrintToken(p->end); | |
427 | } | |
428 | else | |
429 | dmxConfigLog("dmxConfigPrintParam: cannot handle format (a)\n"); | |
430 | } | |
431 | else if (p->end && p->param) { | |
432 | dmxConfigPrintString(p->param, 1); | |
433 | dmxConfigPrintTokenNopop(p->end); | |
434 | dmxConfigPrintParam(p->next); | |
435 | } | |
436 | else | |
437 | dmxConfigLog("dmxConfigPrintParam: cannot handle format (b)\n"); | |
438 | } | |
439 | ||
440 | static void | |
441 | dmxConfigPrintSub(DMXConfigSubPtr p) | |
442 | { | |
443 | DMXConfigSubPtr pt; | |
444 | ||
445 | if (!p) | |
446 | return; | |
447 | for (pt = p; pt; pt = pt->next) { | |
448 | switch (pt->type) { | |
449 | case dmxConfigComment: | |
450 | dmxConfigPrintComment(pt->comment); | |
451 | break; | |
452 | case dmxConfigDisplay: | |
453 | dmxConfigPrintDisplay(pt->display); | |
454 | break; | |
455 | case dmxConfigWall: | |
456 | dmxConfigPrintWall(pt->wall); | |
457 | break; | |
458 | case dmxConfigOption: | |
459 | dmxConfigPrintOption(pt->option); | |
460 | break; | |
461 | case dmxConfigParam: | |
462 | dmxConfigPrintParam(pt->param); | |
463 | break; | |
464 | default: | |
465 | dmxConfigLog("dmxConfigPrintSub:" | |
466 | " cannot handle type %d in subentry\n", pt->type); | |
467 | } | |
468 | } | |
469 | } | |
470 | ||
471 | static void | |
472 | dmxConfigPrintVirtual(DMXConfigVirtualPtr p) | |
473 | { | |
474 | DMXConfigToken dummyStart = { T_VIRTUAL, 0, NULL }; | |
475 | DMXConfigToken dummyOpen = { '{', 0, NULL }; | |
476 | DMXConfigToken dummyClose = { '}', 0, NULL }; | |
477 | DMXConfigString dummyName = { T_STRING, 0, NULL, NULL, NULL }; | |
478 | DMXConfigPair dummyDim = { T_DIMENSION, 0, NULL, 0, 0 }; | |
479 | ||
480 | if (p->vname) | |
481 | p->vname->string = p->name; | |
482 | else | |
483 | dummyName.string = p->name; | |
484 | ||
485 | if (p->dim) | |
486 | p->dim->x = p->width, p->dim->y = p->height; | |
487 | else | |
488 | dummyDim.x = p->width, dummyDim.y = p->height; | |
489 | ||
490 | dmxConfigPrintToken(p->start ? p->start : &dummyStart); | |
491 | dmxConfigPrintString(p->vname ? p->vname : &dummyName, 1); | |
492 | dmxConfigPrintPair(p->dim ? p->dim : &dummyDim, 1); | |
493 | dmxConfigPrintToken(p->open ? p->open : &dummyOpen); | |
494 | dmxConfigPrintSub(p->subentry); | |
495 | dmxConfigPrintToken(p->close ? p->close : &dummyClose); | |
496 | } | |
497 | ||
498 | /** The configuration information in \a entry will be pretty-printed to | |
499 | * the \a stream. If \a stream is NULL, then stdout will be used. */ | |
500 | void | |
501 | dmxConfigPrint(FILE * stream, DMXConfigEntryPtr entry) | |
502 | { | |
503 | DMXConfigEntryPtr pt; | |
504 | ||
505 | if (!stream) | |
506 | str = stdout; | |
507 | else | |
508 | str = stream; | |
509 | ||
510 | stack = &initialStack; | |
511 | ||
512 | for (pt = entry; pt; pt = pt->next) { | |
513 | switch (pt->type) { | |
514 | case dmxConfigComment: | |
515 | dmxConfigPrintComment(pt->comment); | |
516 | break; | |
517 | case dmxConfigVirtual: | |
518 | dmxConfigPrintVirtual(pt->virtual); | |
519 | break; | |
520 | default: | |
521 | dmxConfigLog("dmxConfigPrint: cannot handle type %d in entry\n", | |
522 | pt->type); | |
523 | } | |
524 | } | |
525 | if (pos) | |
526 | dmxConfigNewline(); | |
527 | } | |
528 | ||
529 | /** The configuration information in \a p will be pretty-printed to the | |
530 | * \a stream. If \a stream is NULL, then stdout will be used. */ | |
531 | void | |
532 | dmxConfigVirtualPrint(FILE * stream, DMXConfigVirtualPtr p) | |
533 | { | |
534 | if (!stream) | |
535 | str = stdout; | |
536 | else | |
537 | str = stream; | |
538 | ||
539 | stack = &initialStack; | |
540 | ||
541 | dmxConfigPrintVirtual(p); | |
542 | if (pos) | |
543 | dmxConfigNewline(); | |
544 | } |