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();
 | |
| }
 |