4c6a4e1e00
tested by naddy@
533 lines
14 KiB
C
533 lines
14 KiB
C
/*
|
|
* Copyright 2002 Red Hat Inc., Durham, North Carolina.
|
|
*
|
|
* All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation on the rights to use, copy, modify, merge,
|
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial
|
|
* portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* Authors:
|
|
* Rickard E. (Rik) Faith <faith@redhat.com>
|
|
*
|
|
*/
|
|
|
|
/** \file
|
|
*
|
|
* This file provides support routines and helper functions to be used
|
|
* to pretty-print DMX configurations.
|
|
*
|
|
* Because the DMX configuration file parsing should be capable of being
|
|
* used in a stand-alone fashion (i.e., independent from the DMX server
|
|
* source tree), no dependencies on other DMX routines are made. */
|
|
|
|
#ifdef HAVE_DMX_CONFIG_H
|
|
#include <dmx-config.h>
|
|
#endif
|
|
|
|
#include "dmxconfig.h"
|
|
#include "dmxparse.h"
|
|
#include "dmxprint.h"
|
|
#include "parser.h"
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
|
|
static FILE *str = NULL;
|
|
static int indent = 0;
|
|
static int pos = 0;
|
|
|
|
/** Stack of indentation information used for pretty-printing
|
|
* configuration information. */
|
|
static struct stack {
|
|
int base;
|
|
int comment;
|
|
int step;
|
|
struct stack *next;
|
|
} *stack, initialStack = {
|
|
0, 0, 4, NULL};
|
|
|
|
static void
|
|
dmxConfigIndent(void)
|
|
{
|
|
int i;
|
|
|
|
if (indent < 0)
|
|
indent = 0;
|
|
if (indent > 40)
|
|
indent = 40;
|
|
for (i = 0; i < indent; i++)
|
|
fprintf(str, " ");
|
|
}
|
|
|
|
static void
|
|
dmxConfigNewline(void)
|
|
{
|
|
if (pos)
|
|
fprintf(str, "\n");
|
|
pos = 0;
|
|
}
|
|
|
|
static void
|
|
dmxConfigPushState(int base, int comment, int step)
|
|
{
|
|
struct stack *new = dmxConfigAlloc(sizeof(*new));
|
|
|
|
new->base = base;
|
|
new->comment = comment;
|
|
new->step = step;
|
|
new->next = stack;
|
|
stack = new;
|
|
indent = base;
|
|
dmxConfigNewline();
|
|
}
|
|
|
|
static void
|
|
dmxConfigPushComment(void)
|
|
{
|
|
if (stack)
|
|
indent = stack->comment;
|
|
}
|
|
|
|
static void
|
|
dmxConfigPushStep(void)
|
|
{
|
|
if (stack)
|
|
indent = stack->step;
|
|
}
|
|
|
|
static void
|
|
dmxConfigPopState(void)
|
|
{
|
|
struct stack *old = stack;
|
|
|
|
if (!stack)
|
|
return;
|
|
indent = old->base;
|
|
stack = old->next;
|
|
if (!stack)
|
|
dmxConfigLog("Stack underflow\n");
|
|
dmxConfigFree(old);
|
|
dmxConfigNewline();
|
|
}
|
|
|
|
static void _X_ATTRIBUTE_PRINTF(4, 5)
|
|
dmxConfigOutput(int addSpace, int doNewline, const char *comment,
|
|
const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (!pos)
|
|
dmxConfigIndent();
|
|
else if (addSpace)
|
|
fprintf(str, " ");
|
|
|
|
if (format) {
|
|
va_start(args, format);
|
|
/* RATS: This hasn't been audited -- it
|
|
* could probably result in a buffer
|
|
* overflow. */
|
|
pos += vfprintf(str, format, args); /* assumes no newlines! */
|
|
va_end(args);
|
|
}
|
|
|
|
if (comment) {
|
|
if (pos)
|
|
fprintf(str, " ");
|
|
pos += fprintf(str, "#%s", comment);
|
|
dmxConfigNewline();
|
|
dmxConfigPushComment();
|
|
}
|
|
else if (doNewline)
|
|
dmxConfigNewline();
|
|
}
|
|
|
|
static void
|
|
dmxConfigPrintComment(DMXConfigCommentPtr p)
|
|
{
|
|
dmxConfigOutput(1, 1, p->comment, NULL);
|
|
}
|
|
|
|
static void
|
|
dmxConfigPrintTokenFlag(DMXConfigTokenPtr p, int flag)
|
|
{
|
|
if (!p)
|
|
return;
|
|
switch (p->token) {
|
|
case T_VIRTUAL:
|
|
dmxConfigPushState(0, 4, 4);
|
|
dmxConfigOutput(0, 0, p->comment, "virtual");
|
|
break;
|
|
case T_DISPLAY:
|
|
dmxConfigPushState(4, 12, 16);
|
|
dmxConfigOutput(0, 0, p->comment, "display");
|
|
break;
|
|
case T_WALL:
|
|
dmxConfigPushState(4, 12, 16);
|
|
dmxConfigOutput(0, 0, p->comment, "wall");
|
|
break;
|
|
case T_OPTION:
|
|
dmxConfigPushState(4, 12, 16);
|
|
dmxConfigOutput(0, 0, p->comment, "option");
|
|
break;
|
|
case T_PARAM:
|
|
dmxConfigPushState(4, 8, 12);
|
|
dmxConfigOutput(0, 0, p->comment, "param");
|
|
break;
|
|
case ';':
|
|
dmxConfigOutput(0, 1, p->comment, ";");
|
|
if (flag)
|
|
dmxConfigPopState();
|
|
break;
|
|
case '{':
|
|
dmxConfigOutput(1, 1, p->comment, "{");
|
|
dmxConfigPushStep();
|
|
break;
|
|
case '}':
|
|
if (flag)
|
|
dmxConfigPopState();
|
|
dmxConfigOutput(0, 1, p->comment, "}");
|
|
break;
|
|
case '/':
|
|
dmxConfigOutput(1, 0, NULL, "/");
|
|
break;
|
|
default:
|
|
dmxConfigLog("unknown token %d on line %d\n", p->token, p->line);
|
|
}
|
|
}
|
|
|
|
static void
|
|
dmxConfigPrintToken(DMXConfigTokenPtr p)
|
|
{
|
|
dmxConfigPrintTokenFlag(p, 1);
|
|
}
|
|
|
|
static void
|
|
dmxConfigPrintTokenNopop(DMXConfigTokenPtr p)
|
|
{
|
|
dmxConfigPrintTokenFlag(p, 0);
|
|
}
|
|
|
|
static int
|
|
dmxConfigPrintQuotedString(const char *s)
|
|
{
|
|
const char *pt;
|
|
|
|
if (!s || !s[0])
|
|
return 1; /* Quote empty string */
|
|
for (pt = s; *pt; ++pt)
|
|
if (isspace(*pt))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
dmxConfigPrintString(DMXConfigStringPtr p, int quote)
|
|
{
|
|
DMXConfigStringPtr pt;
|
|
|
|
if (!p)
|
|
return;
|
|
for (pt = p; pt; pt = pt->next) {
|
|
if (quote && dmxConfigPrintQuotedString(pt->string)) {
|
|
dmxConfigOutput(1, 0, pt->comment, "\"%s\"",
|
|
pt->string ? pt->string : "");
|
|
}
|
|
else
|
|
dmxConfigOutput(1, 0, pt->comment, "%s",
|
|
pt->string ? pt->string : "");
|
|
}
|
|
}
|
|
|
|
static int
|
|
dmxConfigPrintPair(DMXConfigPairPtr p, int addSpace)
|
|
{
|
|
if (!p)
|
|
return 0;
|
|
if (p->token == T_OFFSET) {
|
|
if (!p->comment && !p->x && !p->y && p->xsign >= 0 && p->ysign >= 0)
|
|
return 0;
|
|
dmxConfigOutput(addSpace, 0, p->comment, "%c%d%c%d",
|
|
p->xsign < 0 ? '-' : '+', p->x,
|
|
p->ysign < 0 ? '-' : '+', p->y);
|
|
}
|
|
else {
|
|
if (!p->comment && !p->x && !p->y)
|
|
return 0;
|
|
dmxConfigOutput(addSpace, 0, p->comment, "%s%dx%d",
|
|
(p->token == T_ORIGIN) ? "@" : "", p->x, p->y);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
dmxConfigPrintDisplay(DMXConfigDisplayPtr p)
|
|
{
|
|
DMXConfigToken dummyStart = { T_DISPLAY, 0, NULL };
|
|
DMXConfigToken dummyEnd = { ';', 0, NULL };
|
|
DMXConfigToken dummySep = { '/', 0, NULL };
|
|
DMXConfigString dummyName = { T_STRING, 0, NULL, NULL, NULL };
|
|
DMXConfigPair dummySDim = { T_DIMENSION, 0, NULL, 0, 0, 0, 0 };
|
|
DMXConfigPair dummySOffset = { T_OFFSET, 0, NULL, 0, 0, 0, 0 };
|
|
DMXConfigPair dummyRDim = { T_DIMENSION, 0, NULL, 0, 0, 0, 0 };
|
|
DMXConfigPair dummyROffset = { T_OFFSET, 0, NULL, 0, 0, 0, 0 };
|
|
DMXConfigPair dummyOrigin = { T_ORIGIN, 0, NULL, 0, 0, 0, 0 };
|
|
int output;
|
|
|
|
if (p->dname)
|
|
p->dname->string = p->name;
|
|
else
|
|
dummyName.string = p->name;
|
|
|
|
if (p->dim && p->dim->scrn && p->dim->scrn->dim) {
|
|
p->dim->scrn->dim->x = p->scrnWidth;
|
|
p->dim->scrn->dim->y = p->scrnHeight;
|
|
}
|
|
else {
|
|
dummySDim.x = p->scrnWidth;
|
|
dummySDim.y = p->scrnHeight;
|
|
}
|
|
|
|
if (p->dim && p->dim->scrn && p->dim->scrn->offset) {
|
|
p->dim->scrn->offset->x = p->scrnX;
|
|
p->dim->scrn->offset->y = p->scrnY;
|
|
}
|
|
else {
|
|
dummySOffset.x = p->scrnX;
|
|
dummySOffset.y = p->scrnY;
|
|
}
|
|
|
|
if (p->dim && p->dim->root && p->dim->root->dim) {
|
|
p->dim->root->dim->x = p->rootWidth;
|
|
p->dim->root->dim->y = p->rootHeight;
|
|
}
|
|
else {
|
|
dummyRDim.x = p->rootWidth;
|
|
dummyRDim.y = p->rootHeight;
|
|
}
|
|
|
|
if (p->dim && p->dim->root && p->dim->root->offset) {
|
|
p->dim->root->offset->x = p->rootX;
|
|
p->dim->root->offset->y = p->rootY;
|
|
}
|
|
else {
|
|
dummyROffset.x = p->rootX;
|
|
dummyROffset.y = p->rootY;
|
|
}
|
|
|
|
if (p->origin) {
|
|
p->origin->x = p->rootXOrigin, p->origin->y = p->rootYOrigin;
|
|
p->origin->xsign = p->rootXSign, p->origin->ysign = p->rootYSign;
|
|
}
|
|
else {
|
|
dummyOrigin.x = p->rootXOrigin, dummyOrigin.y = p->rootYOrigin;
|
|
dummyOrigin.xsign = p->rootXSign, dummyOrigin.ysign = p->rootYSign;
|
|
}
|
|
|
|
dmxConfigPrintToken(p->start ? p->start : &dummyStart);
|
|
dmxConfigPrintString(p->dname ? p->dname : &dummyName, 1);
|
|
|
|
if (p->dim && p->dim->scrn && p->dim->scrn->dim)
|
|
output = dmxConfigPrintPair(p->dim->scrn->dim, 1);
|
|
else
|
|
output = dmxConfigPrintPair(&dummySDim, 1);
|
|
if (p->dim && p->dim->scrn && p->dim->scrn->offset)
|
|
dmxConfigPrintPair(p->dim->scrn->offset, !output);
|
|
else
|
|
dmxConfigPrintPair(&dummySOffset, !output);
|
|
|
|
if (p->scrnWidth != p->rootWidth
|
|
|| p->scrnHeight != p->rootHeight || p->rootX || p->rootY) {
|
|
dmxConfigPrintToken(&dummySep);
|
|
if (p->dim && p->dim->root && p->dim->root->dim)
|
|
output = dmxConfigPrintPair(p->dim->root->dim, 1);
|
|
else
|
|
output = dmxConfigPrintPair(&dummyRDim, 1);
|
|
if (p->dim && p->dim->root && p->dim->root->offset)
|
|
dmxConfigPrintPair(p->dim->root->offset, !output);
|
|
else
|
|
dmxConfigPrintPair(&dummyROffset, !output);
|
|
}
|
|
|
|
dmxConfigPrintPair(p->origin ? p->origin : &dummyOrigin, 1);
|
|
dmxConfigPrintToken(p->end ? p->end : &dummyEnd);
|
|
}
|
|
|
|
static void
|
|
dmxConfigPrintWall(DMXConfigWallPtr p)
|
|
{
|
|
dmxConfigPrintToken(p->start);
|
|
dmxConfigPrintPair(p->wallDim, 1);
|
|
dmxConfigPrintPair(p->displayDim, 1);
|
|
dmxConfigPrintString(p->nameList, 1);
|
|
dmxConfigPrintToken(p->end);
|
|
}
|
|
|
|
static void
|
|
dmxConfigPrintOption(DMXConfigOptionPtr p)
|
|
{
|
|
DMXConfigToken dummyStart = { T_OPTION, 0, NULL };
|
|
DMXConfigString dummyOption = { T_STRING, 0, NULL, NULL, NULL };
|
|
DMXConfigToken dummyEnd = { ';', 0, NULL };
|
|
|
|
dummyOption.string = p->string;
|
|
|
|
dmxConfigPrintToken(p->start ? p->start : &dummyStart);
|
|
dmxConfigPrintString(&dummyOption, 0);
|
|
dmxConfigPrintToken(p->end ? p->end : &dummyEnd);
|
|
}
|
|
|
|
static void
|
|
dmxConfigPrintParam(DMXConfigParamPtr p)
|
|
{
|
|
if (!p)
|
|
return;
|
|
if (p->start) {
|
|
if (p->open && p->close) {
|
|
dmxConfigPrintToken(p->start);
|
|
dmxConfigPrintToken(p->open);
|
|
dmxConfigPrintParam(p->next);
|
|
dmxConfigPrintToken(p->close);
|
|
}
|
|
else if (p->end && p->param) {
|
|
dmxConfigPrintToken(p->start);
|
|
dmxConfigPrintString(p->param, 1);
|
|
dmxConfigPrintToken(p->end);
|
|
}
|
|
else
|
|
dmxConfigLog("dmxConfigPrintParam: cannot handle format (a)\n");
|
|
}
|
|
else if (p->end && p->param) {
|
|
dmxConfigPrintString(p->param, 1);
|
|
dmxConfigPrintTokenNopop(p->end);
|
|
dmxConfigPrintParam(p->next);
|
|
}
|
|
else
|
|
dmxConfigLog("dmxConfigPrintParam: cannot handle format (b)\n");
|
|
}
|
|
|
|
static void
|
|
dmxConfigPrintSub(DMXConfigSubPtr p)
|
|
{
|
|
DMXConfigSubPtr pt;
|
|
|
|
if (!p)
|
|
return;
|
|
for (pt = p; pt; pt = pt->next) {
|
|
switch (pt->type) {
|
|
case dmxConfigComment:
|
|
dmxConfigPrintComment(pt->comment);
|
|
break;
|
|
case dmxConfigDisplay:
|
|
dmxConfigPrintDisplay(pt->display);
|
|
break;
|
|
case dmxConfigWall:
|
|
dmxConfigPrintWall(pt->wall);
|
|
break;
|
|
case dmxConfigOption:
|
|
dmxConfigPrintOption(pt->option);
|
|
break;
|
|
case dmxConfigParam:
|
|
dmxConfigPrintParam(pt->param);
|
|
break;
|
|
default:
|
|
dmxConfigLog("dmxConfigPrintSub:"
|
|
" cannot handle type %d in subentry\n", pt->type);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
dmxConfigPrintVirtual(DMXConfigVirtualPtr p)
|
|
{
|
|
DMXConfigToken dummyStart = { T_VIRTUAL, 0, NULL };
|
|
DMXConfigToken dummyOpen = { '{', 0, NULL };
|
|
DMXConfigToken dummyClose = { '}', 0, NULL };
|
|
DMXConfigString dummyName = { T_STRING, 0, NULL, NULL, NULL };
|
|
DMXConfigPair dummyDim = { T_DIMENSION, 0, NULL, 0, 0 };
|
|
|
|
if (p->vname)
|
|
p->vname->string = p->name;
|
|
else
|
|
dummyName.string = p->name;
|
|
|
|
if (p->dim)
|
|
p->dim->x = p->width, p->dim->y = p->height;
|
|
else
|
|
dummyDim.x = p->width, dummyDim.y = p->height;
|
|
|
|
dmxConfigPrintToken(p->start ? p->start : &dummyStart);
|
|
dmxConfigPrintString(p->vname ? p->vname : &dummyName, 1);
|
|
dmxConfigPrintPair(p->dim ? p->dim : &dummyDim, 1);
|
|
dmxConfigPrintToken(p->open ? p->open : &dummyOpen);
|
|
dmxConfigPrintSub(p->subentry);
|
|
dmxConfigPrintToken(p->close ? p->close : &dummyClose);
|
|
}
|
|
|
|
/** The configuration information in \a entry will be pretty-printed to
|
|
* the \a stream. If \a stream is NULL, then stdout will be used. */
|
|
void
|
|
dmxConfigPrint(FILE * stream, DMXConfigEntryPtr entry)
|
|
{
|
|
DMXConfigEntryPtr pt;
|
|
|
|
if (!stream)
|
|
str = stdout;
|
|
else
|
|
str = stream;
|
|
|
|
stack = &initialStack;
|
|
|
|
for (pt = entry; pt; pt = pt->next) {
|
|
switch (pt->type) {
|
|
case dmxConfigComment:
|
|
dmxConfigPrintComment(pt->comment);
|
|
break;
|
|
case dmxConfigVirtual:
|
|
dmxConfigPrintVirtual(pt->virtual);
|
|
break;
|
|
default:
|
|
dmxConfigLog("dmxConfigPrint: cannot handle type %d in entry\n",
|
|
pt->type);
|
|
}
|
|
}
|
|
if (pos)
|
|
dmxConfigNewline();
|
|
}
|
|
|
|
/** The configuration information in \a p will be pretty-printed to the
|
|
* \a stream. If \a stream is NULL, then stdout will be used. */
|
|
void
|
|
dmxConfigVirtualPrint(FILE * stream, DMXConfigVirtualPtr p)
|
|
{
|
|
if (!stream)
|
|
str = stdout;
|
|
else
|
|
str = stream;
|
|
|
|
stack = &initialStack;
|
|
|
|
dmxConfigPrintVirtual(p);
|
|
if (pos)
|
|
dmxConfigNewline();
|
|
}
|