parse.c revision 7d8a9cc2
1/* 2 * Copyright 1988 by Evans & Sutherland Computer Corporation, 3 * Salt Lake City, Utah 4 * Portions Copyright 1989 by the Massachusetts Institute of Technology 5 * Cambridge, Massachusetts 6 * 7 * Copyright 1992 Claude Lecommandeur. 8 */ 9 10/*********************************************************************** 11 * 12 * $XConsortium: parse.c,v 1.52 91/07/12 09:59:37 dave Exp $ 13 * 14 * parse the .twmrc file 15 * 16 * 17-Nov-87 Thomas E. LaStrange File created 17 * 10-Oct-90 David M. Sternlicht Storing saved colors on root 18 * 19 * Do the necessary modification to be integrated in ctwm. 20 * Can no longer be used for the standard twm. 21 * 22 * 22-April-92 Claude Lecommandeur. 23 * 24 ***********************************************************************/ 25 26#include "ctwm.h" 27 28#include <stdio.h> 29#include <stdlib.h> 30#include <string.h> 31#include <strings.h> 32#ifdef USEM4 33# include <sys/types.h> 34# include <sys/wait.h> 35#endif 36 37#include "ctwm_atoms.h" 38#include "screen.h" 39#include "parse.h" 40#include "parse_int.h" 41#include "deftwmrc.h" 42#ifdef SOUNDS 43# include "sound.h" 44#endif 45 46#ifndef SYSTEM_INIT_FILE 47#error "No SYSTEM_INIT_FILE set" 48#endif 49 50static bool ParseStringList(const char **sl); 51 52/* 53 * With current bison, this is defined in the gram.tab.h, so this causes 54 * a warning for redundant declaration. With older bisons and byacc, 55 * it's not, so taking it out causes a warning for implicit declaration. 56 * A little looking around doesn't show any handy #define's we could use 57 * to be sure of the difference. This should quiet it down on gcc/clang 58 * anyway... 59 */ 60#ifdef __GNUC__ 61# pragma GCC diagnostic push 62# pragma GCC diagnostic ignored "-Wredundant-decls" 63extern int yyparse(void); 64# pragma GCC diagnostic pop 65#else 66extern int yyparse(void); 67#endif 68 69// Because of how this winds up shared with callback funcs in the 70// parsing, it's difficult to unwind from being global, so just accept 71// it. 72static FILE *twmrc; 73 74static int ptr = 0; 75static int len = 0; 76#define BUF_LEN 300 77static char buff[BUF_LEN + 1]; 78static const char **stringListSource, *currentString; 79 80#ifdef NON_FLEX_LEX 81/* 82 * While these are (were) referenced in a number of places through the 83 * file, overflowlen is initialized to 0, only possibly changed in 84 * twmUnput(), and unless it's non-zero, neither is otherwise touched. 85 * So this is purely a twmUnput()-related var, and with flex, never used 86 * for anything. 87 */ 88static char overflowbuff[20]; /* really only need one */ 89static int overflowlen; 90#endif 91 92int ConstrainedMoveTime = 400; /* milliseconds, event times */ 93bool ParseError; /* error parsing the .twmrc file */ 94int RaiseDelay = 0; /* msec, for AutoRaise */ 95int (*twmInputFunc)(void); /* used in lexer */ 96 97static int twmrc_lineno; 98 99 100/* Actual file loader */ 101static int ParseTwmrc(const char *filename); 102 103/* lex plumbing funcs */ 104static bool doparse(int (*ifunc)(void), const char *srctypename, 105 const char *srcname); 106 107static int twmStringListInput(void); 108#ifndef USEM4 109static int twmFileInput(void); 110#else 111static int m4twmFileInput(void); 112#endif 113 114#if defined(YYDEBUG) && YYDEBUG 115int yydebug = 1; 116#endif 117 118 119/** 120 * Principal entry point from top-level code to parse the config file. 121 * This tries the various permutations of config files we could load. 122 * For most possible names, we try loading `$NAME.$SCREENNUM` before 123 * trying `$NAME`. If a `-f filename` is given on the command line, it's 124 * passed in here, and the normal `~/.[c]twmrc*` attempts are skipped if 125 * it's not found. 126 * 127 * \param filename A filename given in the -f command-line argument (or 128 * NULL) 129 * \return true/false for whether a valid config was parsed out from 130 * somewhere. 131 */ 132bool 133LoadTwmrc(const char *filename) 134{ 135 int ret = -1; 136 char *tryname = NULL; 137 138 /* 139 * Check for the twmrc file in the following order: 140 * 0. -f filename.# 141 * 1. -f filename 142 * (skip to 6 if -f was given) 143 * 2. .ctwmrc.# 144 * 3. .ctwmrc 145 * 4. .twmrc.# 146 * 5. .twmrc 147 * 6. system.ctwmrc 148 */ 149#define TRY(fn) if((ret = ParseTwmrc(fn)) != -1) { goto DONE_TRYING; } (void)0 150 151 if(filename) { 152 /* -f filename.# */ 153 asprintf(&tryname, "%s.%d", filename, Scr->screen); 154 if(tryname == NULL) { 155 // Oh, we're _screwed_... 156 return false; 157 } 158 TRY(tryname); 159 160 /* -f filename */ 161 TRY(filename); 162 163 /* If we didn't get either from -f, don't try the ~ bits */ 164 goto TRY_FALLBACK; 165 } 166 167 if(Home) { 168 /* ~/.ctwmrc.screennum */ 169 free(tryname); 170 asprintf(&tryname, "%s/.ctwmrc.%d", Home, Scr->screen); 171 if(tryname == NULL) { 172 return false; 173 } 174 TRY(tryname); 175 176 // All later attempts are guaranteed shorter strings than that, 177 // so we can just keep sprintf'ing over it. 178 179 /* ~/.ctwmrc */ 180 sprintf(tryname, "%s/.ctwmrc", Home); 181 TRY(tryname); 182 183 /* ~/.twmrc.screennum */ 184 sprintf(tryname, "%s/.twmrc.%d", Home, Scr->screen); 185 TRY(tryname); 186 187 /* ~/.twmrc */ 188 sprintf(tryname, "%s/.twmrc", Home); 189 TRY(tryname); 190 } 191 192TRY_FALLBACK: 193 /* system.twmrc */ 194 if((ret = ParseTwmrc(SYSTEM_INIT_FILE)) != -1) { 195 if(ret && filename) { 196 // If we were -f'ing, fell back to the system default, and 197 // that succeeeded, we warn. It's "normal"(ish) to not have 198 // a personal twmrc and fall back... 199 fprintf(stderr, 200 "%s: unable to open twmrc file %s, using %s instead\n", 201 ProgramName, filename, SYSTEM_INIT_FILE); 202 } 203 goto DONE_TRYING; 204 } 205 206 207DONE_TRYING: 208#undef TRY 209 free(tryname); 210 211 /* 212 * If we wound up with -1 all the way, we totally failed to find a 213 * file to work with. Fall back to builtin config. 214 */ 215 if(ret == -1) { 216 // Only warn if -f. 217 if(filename) { 218 fprintf(stderr, 219 "%s: unable to open twmrc file %s, using built-in defaults instead\n", 220 ProgramName, filename); 221 } 222 return ParseStringList(defTwmrc); 223 } 224 225 226 /* Better have a useful value in ret... */ 227 return ret; 228} 229 230 231/** 232 * Try parsing a file as a ctwmrc. 233 * 234 * \param filename The filename to try opening and parsing. 235 * \return -1,0,1. 0/1 should be treated as false/true for whether 236 * parsing the file succeeded. -1 means the file couldn't be opened. 237 */ 238static int 239ParseTwmrc(const char *filename) 240{ 241 bool status; 242 243#if 0 244 fprintf(stderr, "%s(): Trying %s\n", __func__, filename); 245#endif 246 247 /* See if we can open the file */ 248 if(!filename) { 249 return -1; 250 } 251 twmrc = fopen(filename, "r"); 252 if(!twmrc) { 253 return -1; 254 } 255 256 257 /* Got it. Kick off the parsing, however we do it. */ 258#ifdef USEM4 259 FILE *raw = NULL; 260 if(CLarg.GoThroughM4) { 261 /* 262 * Hold onto raw filehandle so we can fclose() it below, and 263 * swap twmrc over to the output from m4 264 */ 265 raw = twmrc; 266 twmrc = start_m4(raw); 267 } 268 status = doparse(m4twmFileInput, "file", filename); 269 wait(0); 270 fclose(twmrc); 271 if(raw) { 272 fclose(raw); 273 } 274#else 275 status = doparse(twmFileInput, "file", filename); 276 fclose(twmrc); 277#endif 278 279 /* And we're done */ 280 return status; 281 282 /* NOTREACHED */ 283} 284 285static bool 286ParseStringList(const char **sl) 287{ 288 stringListSource = sl; 289 currentString = *sl; 290 return doparse(twmStringListInput, "string list", NULL); 291} 292 293 294/* 295 * Util used throughout the code (possibly often wrongly?) 296 */ 297void twmrc_error_prefix(void) 298{ 299 fprintf(stderr, "%s: line %d: ", ProgramName, twmrc_lineno); 300} 301 302 303 304/* 305 * Everything below here is related to plumbing and firing off lex/yacc 306 */ 307 308 309/* 310 * Backend func that takes an input-providing func and hooks it up to the 311 * lex/yacc parser to do the work 312 */ 313static bool 314doparse(int (*ifunc)(void), const char *srctypename, 315 const char *srcname) 316{ 317 ptr = 0; 318 len = 0; 319 twmrc_lineno = 0; 320 ParseError = false; 321 twmInputFunc = ifunc; 322#ifdef NON_FLEX_LEX 323 overflowlen = 0; 324#endif 325 326 yyparse(); 327 328 if(ParseError) { 329 fprintf(stderr, "%s: errors found in twm %s", 330 ProgramName, srctypename); 331 if(srcname) { 332 fprintf(stderr, " \"%s\"", srcname); 333 } 334 fprintf(stderr, "\n"); 335 } 336 return !(ParseError); 337} 338 339 340/* 341 * Various input routines for the lexer for the various sources of 342 * config. 343 */ 344 345#ifndef USEM4 346#include <ctype.h> 347 348/* This has Tom's include() funtionality. This is utterly useless if you 349 * can use m4 for the same thing. Chris P. Ross */ 350 351#define MAX_INCLUDES 10 352 353static struct incl { 354 FILE *fp; 355 char *name; 356 int lineno; 357} rc_includes[MAX_INCLUDES]; 358static int include_file = 0; 359 360 361static int twmFileInput(void) 362{ 363#ifdef NON_FLEX_LEX 364 if(overflowlen) { 365 return (int) overflowbuff[--overflowlen]; 366 } 367#endif 368 369 while(ptr == len) { 370 while(include_file) { 371 if(fgets(buff, BUF_LEN, rc_includes[include_file].fp) == NULL) { 372 free(rc_includes[include_file].name); 373 fclose(rc_includes[include_file].fp); 374 twmrc_lineno = rc_includes[include_file--].lineno; 375 } 376 else { 377 break; 378 } 379 } 380 381 if(!include_file) 382 if(fgets(buff, BUF_LEN, twmrc) == NULL) { 383 return 0; 384 } 385 twmrc_lineno++; 386 387 if(strncmp(buff, "include", 7) == 0) { 388 /* Whoops, an include file! */ 389 char *p = buff + 7, *q; 390 FILE *fp; 391 392 while(isspace(*p)) { 393 p++; 394 } 395 for(q = p; *q && !isspace(*q); q++) { 396 continue; 397 } 398 *q = 0; 399 400 if((fp = fopen(p, "r")) == NULL) { 401 fprintf(stderr, "%s: Unable to open included init file %s\n", 402 ProgramName, p); 403 continue; 404 } 405 if(++include_file >= MAX_INCLUDES) { 406 fprintf(stderr, "%s: init file includes nested too deep\n", 407 ProgramName); 408 continue; 409 } 410 rc_includes[include_file].fp = fp; 411 rc_includes[include_file].lineno = twmrc_lineno; 412 twmrc_lineno = 0; 413 rc_includes[include_file].name = strdup(p); 414 continue; 415 } 416 ptr = 0; 417 len = strlen(buff); 418 } 419 return ((int)buff[ptr++]); 420} 421#else /* USEM4 */ 422/* If you're going to use m4, use this version instead. Much simpler. 423 * m4 ism's credit to Josh Osborne (stripes) */ 424 425static int m4twmFileInput(void) 426{ 427 int line; 428 static FILE *cp = NULL; 429 430 if(cp == NULL && CLarg.keepM4_filename) { 431 cp = fopen(CLarg.keepM4_filename, "w"); 432 if(cp == NULL) { 433 fprintf(stderr, 434 "%s: unable to create m4 output %s, ignoring\n", 435 ProgramName, CLarg.keepM4_filename); 436 CLarg.keepM4_filename = NULL; 437 } 438 } 439 440#ifdef NON_FLEX_LEX 441 if(overflowlen) { 442 return((int) overflowbuff[--overflowlen]); 443 } 444#endif 445 446 while(ptr == len) { 447nextline: 448 if(fgets(buff, BUF_LEN, twmrc) == NULL) { 449 if(cp) { 450 fclose(cp); 451 } 452 return(0); 453 } 454 if(cp) { 455 fputs(buff, cp); 456 } 457 458 if(sscanf(buff, "#line %d", &line)) { 459 twmrc_lineno = line - 1; 460 goto nextline; 461 } 462 else { 463 twmrc_lineno++; 464 } 465 466 ptr = 0; 467 len = strlen(buff); 468 } 469 return ((int)buff[ptr++]); 470} 471#endif /* USEM4 */ 472 473 474static int twmStringListInput(void) 475{ 476#ifdef NON_FLEX_LEX 477 if(overflowlen) { 478 return (int) overflowbuff[--overflowlen]; 479 } 480#endif 481 482 /* 483 * return the character currently pointed to 484 */ 485 if(currentString) { 486 unsigned int c = (unsigned int) * currentString++; 487 488 if(c) { 489 return c; /* if non-nul char */ 490 } 491 currentString = *++stringListSource; /* advance to next bol */ 492 return '\n'; /* but say that we hit last eol */ 493 } 494 return 0; /* eof */ 495} 496 497 498 499/* 500 * unput/output funcs for AT&T lex. No longer supported, and expected to 501 * be GC'd in a release or two. 502 */ 503#ifdef NON_FLEX_LEX 504 505void twmUnput(int c) 506{ 507 if(overflowlen < sizeof overflowbuff) { 508 overflowbuff[overflowlen++] = (char) c; 509 } 510 else { 511 twmrc_error_prefix(); 512 fprintf(stderr, "unable to unput character (%c)\n", 513 c); 514 } 515} 516 517void TwmOutput(int c) 518{ 519 putchar(c); 520} 521 522#endif /* NON_FLEX_LEX */ 523