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