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
55static FILE *str   = NULL;
56static int  indent = 0;
57static int  pos    = 0;
58
59/** Stack of indentation information used for pretty-printing
60 * configuration information. */
61static struct stack {
62    int          base;
63    int          comment;
64    int          step;
65    struct stack *next;
66} *stack, initialStack = { 0, 0, 4, NULL };
67
68static void dmxConfigIndent(void)
69{
70    int i;
71    if (indent < 0)  indent = 0;
72    if (indent > 40) indent = 40;
73    for (i = 0; i < indent; i++) fprintf(str, " ");
74}
75
76static void dmxConfigNewline(void)
77{
78    if (pos) fprintf(str, "\n");
79    pos = 0;
80}
81
82static void dmxConfigPushState(int base, int comment, int step)
83{
84    struct stack *new = dmxConfigAlloc(sizeof(*new));
85    new->base    = base;
86    new->comment = comment;
87    new->step    = step;
88    new->next    = stack;
89    stack        = new;
90    indent       = base;
91    dmxConfigNewline();
92}
93
94static void dmxConfigPushComment(void)
95{
96    if (stack) indent = stack->comment;
97}
98
99static void dmxConfigPushStep(void)
100{
101    if (stack) indent = stack->step;
102}
103
104static void dmxConfigPopState(void)
105{
106    struct stack *old = stack;
107
108    if (!stack) return;
109    indent = old->base;
110    stack  = old->next;
111    if (!stack) dmxConfigLog("Stack underflow\n");
112    dmxConfigFree(old);
113    dmxConfigNewline();
114}
115
116static void dmxConfigOutput(int addSpace, int doNewline, const char *comment,
117                            const char *format, ...)
118{
119    va_list args;
120
121    if (!pos) dmxConfigIndent();
122    else if (addSpace) fprintf(str, " ");
123
124    if (format) {
125        va_start(args, format);
126                                /* RATS: This hasn't been audited -- it
127                                 * could probably result in a buffer
128                                 * overflow. */
129        pos += vfprintf(str, format, args); /* assumes no newlines! */
130        va_end(args);
131    }
132
133    if (comment) {
134        if (pos) fprintf(str, " ");
135        pos += fprintf(str, "#%s", comment);
136        dmxConfigNewline();
137        dmxConfigPushComment();
138    } else if (doNewline) dmxConfigNewline();
139}
140
141static void dmxConfigPrintComment(DMXConfigCommentPtr p)
142{
143    dmxConfigOutput(1, 1, p->comment, NULL);
144}
145
146static void dmxConfigPrintTokenFlag(DMXConfigTokenPtr p, int flag)
147{
148    if (!p) return;
149    switch (p->token) {
150    case T_VIRTUAL:
151        dmxConfigPushState(0, 4, 4);
152        dmxConfigOutput(0, 0, p->comment, "virtual");
153        break;
154    case T_DISPLAY:
155        dmxConfigPushState(4, 12, 16);
156        dmxConfigOutput(0, 0, p->comment, "display");
157        break;
158    case T_WALL:
159        dmxConfigPushState(4, 12, 16);
160        dmxConfigOutput(0, 0, p->comment, "wall");
161        break;
162    case T_OPTION:
163        dmxConfigPushState(4, 12, 16);
164        dmxConfigOutput(0, 0, p->comment, "option");
165        break;
166    case T_PARAM:
167        dmxConfigPushState(4, 8, 12);
168        dmxConfigOutput(0, 0, p->comment, "param");
169        break;
170    case ';':
171        dmxConfigOutput(0, 1, p->comment, ";");
172        if (flag) dmxConfigPopState();
173        break;
174    case '{':
175        dmxConfigOutput(1, 1, p->comment, "{");
176        dmxConfigPushStep();
177        break;
178    case '}':
179        if (flag) dmxConfigPopState();
180        dmxConfigOutput(0, 1, p->comment, "}");
181        break;
182    case '/':
183        dmxConfigOutput(1, 0, NULL, "/");
184        break;
185    default:
186        dmxConfigLog("unknown token %d on line %d\n", p->token, p->line);
187    }
188}
189
190static void dmxConfigPrintToken(DMXConfigTokenPtr p)
191{
192    dmxConfigPrintTokenFlag(p, 1);
193}
194
195static void dmxConfigPrintTokenNopop(DMXConfigTokenPtr p)
196{
197    dmxConfigPrintTokenFlag(p, 0);
198}
199
200static int dmxConfigPrintQuotedString(const char *s)
201{
202    const char *pt;
203
204    if (!s || !s[0]) return 1;  /* Quote empty string */
205    for (pt = s; *pt; ++pt) if (isspace(*pt)) return 1;
206    return 0;
207}
208
209static void dmxConfigPrintString(DMXConfigStringPtr p, int quote)
210{
211    DMXConfigStringPtr pt;
212
213    if (!p) return;
214    for (pt = p; pt; pt = pt->next) {
215        if (quote && dmxConfigPrintQuotedString(pt->string)) {
216            dmxConfigOutput(1, 0, pt->comment, "\"%s\"",
217                            pt->string ? pt->string : "");
218            } else
219            dmxConfigOutput(1, 0, pt->comment, "%s",
220                            pt->string ? pt->string : "");
221    }
222}
223
224static int dmxConfigPrintPair(DMXConfigPairPtr p, int addSpace)
225{
226    const char *format = NULL;
227
228    if (!p) return 0;
229    switch (p->token) {
230    case T_ORIGIN:    format = "@%dx%d";   break;
231    case T_DIMENSION: format = "%dx%d";    break;
232    case T_OFFSET:    format = "%c%d%c%d"; break;
233    }
234    if (p->token == T_OFFSET) {
235        if (!p->comment && !p->x && !p->y && p->xsign >= 0 && p->ysign >= 0)
236            return 0;
237        dmxConfigOutput(addSpace, 0, p->comment, format,
238                        p->xsign < 0 ? '-' : '+', p->x,
239                        p->ysign < 0 ? '-' : '+', p->y);
240    } else {
241        if (!p->comment && !p->x && !p->y) return 0;
242        dmxConfigOutput(addSpace, 0, p->comment, format, p->x, p->y);
243    }
244    return 1;
245}
246
247static void dmxConfigPrintDisplay(DMXConfigDisplayPtr p)
248{
249    DMXConfigToken  dummyStart   = { T_DISPLAY, 0, NULL };
250    DMXConfigToken  dummyEnd     = { ';', 0, NULL };
251    DMXConfigToken  dummySep     = { '/', 0, NULL };
252    DMXConfigString dummyName    = { T_STRING, 0, NULL, NULL, NULL };
253    DMXConfigPair   dummySDim    = { T_DIMENSION, 0, NULL, 0, 0, 0, 0 };
254    DMXConfigPair   dummySOffset = { T_OFFSET, 0, NULL, 0, 0, 0, 0 };
255    DMXConfigPair   dummyRDim    = { T_DIMENSION, 0, NULL, 0, 0, 0, 0 };
256    DMXConfigPair   dummyROffset = { T_OFFSET, 0, NULL, 0, 0, 0, 0 };
257    DMXConfigPair   dummyOrigin  = { T_ORIGIN, 0, NULL, 0, 0, 0, 0 };
258    int             output;
259
260    if (p->dname) p->dname->string = p->name;
261    else          dummyName.string = p->name;
262
263    if (p->dim && p->dim->scrn && p->dim->scrn->dim) {
264        p->dim->scrn->dim->x    = p->scrnWidth;
265        p->dim->scrn->dim->y    = p->scrnHeight;
266    } else {
267        dummySDim.x             = p->scrnWidth;
268        dummySDim.y             = p->scrnHeight;
269    }
270
271    if (p->dim && p->dim->scrn && p->dim->scrn->offset) {
272        p->dim->scrn->offset->x = p->scrnX;
273        p->dim->scrn->offset->y = p->scrnY;
274    } else {
275        dummySOffset.x          = p->scrnX;
276        dummySOffset.y          = p->scrnY;
277    }
278
279    if (p->dim && p->dim->root && p->dim->root->dim) {
280        p->dim->root->dim->x    = p->rootWidth;
281        p->dim->root->dim->y    = p->rootHeight;
282    } else {
283        dummyRDim.x             = p->rootWidth;
284        dummyRDim.y             = p->rootHeight;
285    }
286
287    if (p->dim && p->dim->root && p->dim->root->offset) {
288        p->dim->root->offset->x = p->rootX;
289        p->dim->root->offset->y = p->rootY;
290    } else {
291        dummyROffset.x          = p->rootX;
292        dummyROffset.y          = p->rootY;
293    }
294
295    if (p->origin) {
296        p->origin->x     = p->rootXOrigin, p->origin->y     = p->rootYOrigin;
297        p->origin->xsign = p->rootXSign,   p->origin->ysign = p->rootYSign;
298    } else {
299        dummyOrigin.x     = p->rootXOrigin, dummyOrigin.y     = p->rootYOrigin;
300        dummyOrigin.xsign = p->rootXSign,   dummyOrigin.ysign = p->rootYSign;
301    }
302
303    dmxConfigPrintToken(p->start ? p->start : &dummyStart);
304    dmxConfigPrintString(p->dname ? p->dname : &dummyName, 1);
305
306    if (p->dim && p->dim->scrn && p->dim->scrn->dim)
307        output = dmxConfigPrintPair(p->dim->scrn->dim, 1);
308    else
309        output = dmxConfigPrintPair(&dummySDim, 1);
310    if (p->dim && p->dim->scrn && p->dim->scrn->offset)
311        dmxConfigPrintPair(p->dim->scrn->offset, !output);
312    else
313        dmxConfigPrintPair(&dummySOffset, !output);
314
315    if (p->scrnWidth != p->rootWidth
316        || p->scrnHeight != p->rootHeight
317        || p->rootX
318        || p->rootY) {
319        dmxConfigPrintToken(&dummySep);
320        if (p->dim && p->dim->root && p->dim->root->dim)
321            output = dmxConfigPrintPair(p->dim->root->dim, 1);
322        else
323            output = dmxConfigPrintPair(&dummyRDim, 1);
324        if (p->dim && p->dim->root && p->dim->root->offset)
325            dmxConfigPrintPair(p->dim->root->offset, !output);
326        else
327            dmxConfigPrintPair(&dummyROffset, !output);
328    }
329
330    dmxConfigPrintPair(p->origin ? p->origin : &dummyOrigin, 1);
331    dmxConfigPrintToken(p->end ? p->end : &dummyEnd);
332}
333
334static void dmxConfigPrintWall(DMXConfigWallPtr p)
335{
336    dmxConfigPrintToken(p->start);
337    dmxConfigPrintPair(p->wallDim, 1);
338    dmxConfigPrintPair(p->displayDim, 1);
339    dmxConfigPrintString(p->nameList, 1);
340    dmxConfigPrintToken(p->end);
341}
342
343static void dmxConfigPrintOption(DMXConfigOptionPtr p)
344{
345    DMXConfigToken  dummyStart  = { T_OPTION, 0, NULL };
346    DMXConfigString dummyOption = { T_STRING, 0, NULL, NULL, NULL };
347    DMXConfigToken  dummyEnd    = { ';', 0, NULL };
348
349    dummyOption.string = p->string;
350
351    dmxConfigPrintToken(p->start ? p->start : &dummyStart);
352    dmxConfigPrintString(&dummyOption, 0);
353    dmxConfigPrintToken(p->end ? p->end : &dummyEnd);
354}
355
356static void dmxConfigPrintParam(DMXConfigParamPtr p)
357{
358    if (!p) return;
359    if (p->start) {
360        if (p->open && p->close) {
361            dmxConfigPrintToken(p->start);
362            dmxConfigPrintToken(p->open);
363            dmxConfigPrintParam(p->next);
364            dmxConfigPrintToken(p->close);
365        } else if (p->end && p->param) {
366            dmxConfigPrintToken(p->start);
367            dmxConfigPrintString(p->param, 1);
368            dmxConfigPrintToken(p->end);
369        } else
370            dmxConfigLog("dmxConfigPrintParam: cannot handle format (a)\n");
371    } else if (p->end && p->param) {
372        dmxConfigPrintString(p->param, 1);
373        dmxConfigPrintTokenNopop(p->end);
374        dmxConfigPrintParam(p->next);
375    } else
376        dmxConfigLog("dmxConfigPrintParam: cannot handle format (b)\n");
377}
378
379static void dmxConfigPrintSub(DMXConfigSubPtr p)
380{
381    DMXConfigSubPtr pt;
382
383    if (!p) return;
384    for (pt = p; pt; pt = pt->next) {
385        switch (pt->type) {
386        case dmxConfigComment: dmxConfigPrintComment(pt->comment); break;
387        case dmxConfigDisplay: dmxConfigPrintDisplay(pt->display); break;
388        case dmxConfigWall:    dmxConfigPrintWall(pt->wall);       break;
389        case dmxConfigOption:  dmxConfigPrintOption(pt->option);   break;
390        case dmxConfigParam:   dmxConfigPrintParam(pt->param);     break;
391        default:
392            dmxConfigLog("dmxConfigPrintSub:"
393                         " cannot handle type %d in subentry\n", pt->type);
394        }
395    }
396}
397
398static void dmxConfigPrintVirtual(DMXConfigVirtualPtr p)
399{
400    DMXConfigToken  dummyStart = { T_VIRTUAL, 0, NULL };
401    DMXConfigToken  dummyOpen  = { '{', 0, NULL };
402    DMXConfigToken  dummyClose = { '}', 0, NULL };
403    DMXConfigString dummyName  = { T_STRING, 0, NULL, NULL, NULL };
404    DMXConfigPair   dummyDim   = { T_DIMENSION, 0, NULL, 0, 0 };
405
406    if (p->vname) p->vname->string = p->name;
407    else          dummyName.string = p->name;
408
409    if (p->dim) p->dim->x  = p->width, p->dim->y  = p->height;
410    else        dummyDim.x = p->width, dummyDim.y = p->height;
411
412
413    dmxConfigPrintToken(p->start ? p->start : &dummyStart);
414    dmxConfigPrintString(p->vname ? p->vname : &dummyName, 1);
415    dmxConfigPrintPair(p->dim ? p->dim : &dummyDim, 1);
416    dmxConfigPrintToken(p->open ? p->open : &dummyOpen);
417    dmxConfigPrintSub(p->subentry);
418    dmxConfigPrintToken(p->close ? p->close : &dummyClose);
419}
420
421/** The configuration information in \a entry will be pretty-printed to
422 * the \a stream.  If \a stream is NULL, then stdout will be used. */
423void dmxConfigPrint(FILE *stream, DMXConfigEntryPtr entry)
424{
425    DMXConfigEntryPtr pt;
426
427    if (!stream) str = stdout;
428    else         str = stream;
429
430    stack = &initialStack;
431
432    for (pt = entry; pt; pt = pt->next) {
433        switch (pt->type) {
434        case dmxConfigComment: dmxConfigPrintComment(pt->comment); break;
435        case dmxConfigVirtual: dmxConfigPrintVirtual(pt->virtual); break;
436        default:
437            dmxConfigLog("dmxConfigPrint: cannot handle type %d in entry\n",
438                         pt->type);
439        }
440    }
441    if (pos) dmxConfigNewline();
442}
443
444/** The configuration information in \a p will be pretty-printed to the
445 * \a stream.  If \a stream is NULL, then stdout will be used. */
446void dmxConfigVirtualPrint(FILE *stream, DMXConfigVirtualPtr p)
447{
448    if (!stream) str = stdout;
449    else         str = stream;
450
451    stack = &initialStack;
452
453    dmxConfigPrintVirtual(p);
454    if (pos) dmxConfigNewline();
455}
456