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
dmxConfigIndent(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
dmxConfigNewline(void)83 dmxConfigNewline(void)
84 {
85 if (pos)
86 fprintf(str, "\n");
87 pos = 0;
88 }
89
90 static void
dmxConfigPushState(int base,int comment,int step)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
dmxConfigPushComment(void)105 dmxConfigPushComment(void)
106 {
107 if (stack)
108 indent = stack->comment;
109 }
110
111 static void
dmxConfigPushStep(void)112 dmxConfigPushStep(void)
113 {
114 if (stack)
115 indent = stack->step;
116 }
117
118 static void
dmxConfigPopState(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 _X_ATTRIBUTE_PRINTF(4, 5)
dmxConfigOutput(int addSpace,int doNewline,const char * comment,const char * format,...)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
dmxConfigPrintComment(DMXConfigCommentPtr p)165 dmxConfigPrintComment(DMXConfigCommentPtr p)
166 {
167 dmxConfigOutput(1, 1, p->comment, NULL);
168 }
169
170 static void
dmxConfigPrintTokenFlag(DMXConfigTokenPtr p,int flag)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
dmxConfigPrintToken(DMXConfigTokenPtr p)219 dmxConfigPrintToken(DMXConfigTokenPtr p)
220 {
221 dmxConfigPrintTokenFlag(p, 1);
222 }
223
224 static void
dmxConfigPrintTokenNopop(DMXConfigTokenPtr p)225 dmxConfigPrintTokenNopop(DMXConfigTokenPtr p)
226 {
227 dmxConfigPrintTokenFlag(p, 0);
228 }
229
230 static int
dmxConfigPrintQuotedString(const char * s)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
dmxConfigPrintString(DMXConfigStringPtr p,int quote)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
dmxConfigPrintPair(DMXConfigPairPtr p,int addSpace)262 dmxConfigPrintPair(DMXConfigPairPtr p, int addSpace)
263 {
264 if (!p)
265 return 0;
266 if (p->token == T_OFFSET) {
267 if (!p->comment && !p->x && !p->y && p->xsign >= 0 && p->ysign >= 0)
268 return 0;
269 dmxConfigOutput(addSpace, 0, p->comment, "%c%d%c%d",
270 p->xsign < 0 ? '-' : '+', p->x,
271 p->ysign < 0 ? '-' : '+', p->y);
272 }
273 else {
274 if (!p->comment && !p->x && !p->y)
275 return 0;
276 dmxConfigOutput(addSpace, 0, p->comment, "%s%dx%d",
277 (p->token == T_ORIGIN) ? "@" : "", p->x, p->y);
278 }
279 return 1;
280 }
281
282 static void
dmxConfigPrintDisplay(DMXConfigDisplayPtr p)283 dmxConfigPrintDisplay(DMXConfigDisplayPtr p)
284 {
285 DMXConfigToken dummyStart = { T_DISPLAY, 0, NULL };
286 DMXConfigToken dummyEnd = { ';', 0, NULL };
287 DMXConfigToken dummySep = { '/', 0, NULL };
288 DMXConfigString dummyName = { T_STRING, 0, NULL, NULL, NULL };
289 DMXConfigPair dummySDim = { T_DIMENSION, 0, NULL, 0, 0, 0, 0 };
290 DMXConfigPair dummySOffset = { T_OFFSET, 0, NULL, 0, 0, 0, 0 };
291 DMXConfigPair dummyRDim = { T_DIMENSION, 0, NULL, 0, 0, 0, 0 };
292 DMXConfigPair dummyROffset = { T_OFFSET, 0, NULL, 0, 0, 0, 0 };
293 DMXConfigPair dummyOrigin = { T_ORIGIN, 0, NULL, 0, 0, 0, 0 };
294 int output;
295
296 if (p->dname)
297 p->dname->string = p->name;
298 else
299 dummyName.string = p->name;
300
301 if (p->dim && p->dim->scrn && p->dim->scrn->dim) {
302 p->dim->scrn->dim->x = p->scrnWidth;
303 p->dim->scrn->dim->y = p->scrnHeight;
304 }
305 else {
306 dummySDim.x = p->scrnWidth;
307 dummySDim.y = p->scrnHeight;
308 }
309
310 if (p->dim && p->dim->scrn && p->dim->scrn->offset) {
311 p->dim->scrn->offset->x = p->scrnX;
312 p->dim->scrn->offset->y = p->scrnY;
313 }
314 else {
315 dummySOffset.x = p->scrnX;
316 dummySOffset.y = p->scrnY;
317 }
318
319 if (p->dim && p->dim->root && p->dim->root->dim) {
320 p->dim->root->dim->x = p->rootWidth;
321 p->dim->root->dim->y = p->rootHeight;
322 }
323 else {
324 dummyRDim.x = p->rootWidth;
325 dummyRDim.y = p->rootHeight;
326 }
327
328 if (p->dim && p->dim->root && p->dim->root->offset) {
329 p->dim->root->offset->x = p->rootX;
330 p->dim->root->offset->y = p->rootY;
331 }
332 else {
333 dummyROffset.x = p->rootX;
334 dummyROffset.y = p->rootY;
335 }
336
337 if (p->origin) {
338 p->origin->x = p->rootXOrigin, p->origin->y = p->rootYOrigin;
339 p->origin->xsign = p->rootXSign, p->origin->ysign = p->rootYSign;
340 }
341 else {
342 dummyOrigin.x = p->rootXOrigin, dummyOrigin.y = p->rootYOrigin;
343 dummyOrigin.xsign = p->rootXSign, dummyOrigin.ysign = p->rootYSign;
344 }
345
346 dmxConfigPrintToken(p->start ? p->start : &dummyStart);
347 dmxConfigPrintString(p->dname ? p->dname : &dummyName, 1);
348
349 if (p->dim && p->dim->scrn && p->dim->scrn->dim)
350 output = dmxConfigPrintPair(p->dim->scrn->dim, 1);
351 else
352 output = dmxConfigPrintPair(&dummySDim, 1);
353 if (p->dim && p->dim->scrn && p->dim->scrn->offset)
354 dmxConfigPrintPair(p->dim->scrn->offset, !output);
355 else
356 dmxConfigPrintPair(&dummySOffset, !output);
357
358 if (p->scrnWidth != p->rootWidth
359 || p->scrnHeight != p->rootHeight || p->rootX || p->rootY) {
360 dmxConfigPrintToken(&dummySep);
361 if (p->dim && p->dim->root && p->dim->root->dim)
362 output = dmxConfigPrintPair(p->dim->root->dim, 1);
363 else
364 output = dmxConfigPrintPair(&dummyRDim, 1);
365 if (p->dim && p->dim->root && p->dim->root->offset)
366 dmxConfigPrintPair(p->dim->root->offset, !output);
367 else
368 dmxConfigPrintPair(&dummyROffset, !output);
369 }
370
371 dmxConfigPrintPair(p->origin ? p->origin : &dummyOrigin, 1);
372 dmxConfigPrintToken(p->end ? p->end : &dummyEnd);
373 }
374
375 static void
dmxConfigPrintWall(DMXConfigWallPtr p)376 dmxConfigPrintWall(DMXConfigWallPtr p)
377 {
378 dmxConfigPrintToken(p->start);
379 dmxConfigPrintPair(p->wallDim, 1);
380 dmxConfigPrintPair(p->displayDim, 1);
381 dmxConfigPrintString(p->nameList, 1);
382 dmxConfigPrintToken(p->end);
383 }
384
385 static void
dmxConfigPrintOption(DMXConfigOptionPtr p)386 dmxConfigPrintOption(DMXConfigOptionPtr p)
387 {
388 DMXConfigToken dummyStart = { T_OPTION, 0, NULL };
389 DMXConfigString dummyOption = { T_STRING, 0, NULL, NULL, NULL };
390 DMXConfigToken dummyEnd = { ';', 0, NULL };
391
392 dummyOption.string = p->string;
393
394 dmxConfigPrintToken(p->start ? p->start : &dummyStart);
395 dmxConfigPrintString(&dummyOption, 0);
396 dmxConfigPrintToken(p->end ? p->end : &dummyEnd);
397 }
398
399 static void
dmxConfigPrintParam(DMXConfigParamPtr p)400 dmxConfigPrintParam(DMXConfigParamPtr p)
401 {
402 if (!p)
403 return;
404 if (p->start) {
405 if (p->open && p->close) {
406 dmxConfigPrintToken(p->start);
407 dmxConfigPrintToken(p->open);
408 dmxConfigPrintParam(p->next);
409 dmxConfigPrintToken(p->close);
410 }
411 else if (p->end && p->param) {
412 dmxConfigPrintToken(p->start);
413 dmxConfigPrintString(p->param, 1);
414 dmxConfigPrintToken(p->end);
415 }
416 else
417 dmxConfigLog("dmxConfigPrintParam: cannot handle format (a)\n");
418 }
419 else if (p->end && p->param) {
420 dmxConfigPrintString(p->param, 1);
421 dmxConfigPrintTokenNopop(p->end);
422 dmxConfigPrintParam(p->next);
423 }
424 else
425 dmxConfigLog("dmxConfigPrintParam: cannot handle format (b)\n");
426 }
427
428 static void
dmxConfigPrintSub(DMXConfigSubPtr p)429 dmxConfigPrintSub(DMXConfigSubPtr p)
430 {
431 DMXConfigSubPtr pt;
432
433 if (!p)
434 return;
435 for (pt = p; pt; pt = pt->next) {
436 switch (pt->type) {
437 case dmxConfigComment:
438 dmxConfigPrintComment(pt->comment);
439 break;
440 case dmxConfigDisplay:
441 dmxConfigPrintDisplay(pt->display);
442 break;
443 case dmxConfigWall:
444 dmxConfigPrintWall(pt->wall);
445 break;
446 case dmxConfigOption:
447 dmxConfigPrintOption(pt->option);
448 break;
449 case dmxConfigParam:
450 dmxConfigPrintParam(pt->param);
451 break;
452 default:
453 dmxConfigLog("dmxConfigPrintSub:"
454 " cannot handle type %d in subentry\n", pt->type);
455 }
456 }
457 }
458
459 static void
dmxConfigPrintVirtual(DMXConfigVirtualPtr p)460 dmxConfigPrintVirtual(DMXConfigVirtualPtr p)
461 {
462 DMXConfigToken dummyStart = { T_VIRTUAL, 0, NULL };
463 DMXConfigToken dummyOpen = { '{', 0, NULL };
464 DMXConfigToken dummyClose = { '}', 0, NULL };
465 DMXConfigString dummyName = { T_STRING, 0, NULL, NULL, NULL };
466 DMXConfigPair dummyDim = { T_DIMENSION, 0, NULL, 0, 0 };
467
468 if (p->vname)
469 p->vname->string = p->name;
470 else
471 dummyName.string = p->name;
472
473 if (p->dim)
474 p->dim->x = p->width, p->dim->y = p->height;
475 else
476 dummyDim.x = p->width, dummyDim.y = p->height;
477
478 dmxConfigPrintToken(p->start ? p->start : &dummyStart);
479 dmxConfigPrintString(p->vname ? p->vname : &dummyName, 1);
480 dmxConfigPrintPair(p->dim ? p->dim : &dummyDim, 1);
481 dmxConfigPrintToken(p->open ? p->open : &dummyOpen);
482 dmxConfigPrintSub(p->subentry);
483 dmxConfigPrintToken(p->close ? p->close : &dummyClose);
484 }
485
486 /** The configuration information in \a entry will be pretty-printed to
487 * the \a stream. If \a stream is NULL, then stdout will be used. */
488 void
dmxConfigPrint(FILE * stream,DMXConfigEntryPtr entry)489 dmxConfigPrint(FILE * stream, DMXConfigEntryPtr entry)
490 {
491 DMXConfigEntryPtr pt;
492
493 if (!stream)
494 str = stdout;
495 else
496 str = stream;
497
498 stack = &initialStack;
499
500 for (pt = entry; pt; pt = pt->next) {
501 switch (pt->type) {
502 case dmxConfigComment:
503 dmxConfigPrintComment(pt->comment);
504 break;
505 case dmxConfigVirtual:
506 dmxConfigPrintVirtual(pt->virtual);
507 break;
508 default:
509 dmxConfigLog("dmxConfigPrint: cannot handle type %d in entry\n",
510 pt->type);
511 }
512 }
513 if (pos)
514 dmxConfigNewline();
515 }
516
517 /** The configuration information in \a p will be pretty-printed to the
518 * \a stream. If \a stream is NULL, then stdout will be used. */
519 void
dmxConfigVirtualPrint(FILE * stream,DMXConfigVirtualPtr p)520 dmxConfigVirtualPrint(FILE * stream, DMXConfigVirtualPtr p)
521 {
522 if (!stream)
523 str = stdout;
524 else
525 str = stream;
526
527 stack = &initialStack;
528
529 dmxConfigPrintVirtual(p);
530 if (pos)
531 dmxConfigNewline();
532 }
533