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 fclose(twmrc); 270 if(raw) { 271 fclose(raw); 272 } 273#else 274 status = doparse(twmFileInput, "file", filename); 275 fclose(twmrc); 276#endif 277 278 /* And we're done */ 279 return status; 280 281 /* NOTREACHED */ 282} 283 284static bool 285ParseStringList(const char **sl) 286{ 287 stringListSource = sl; 288 currentString = *sl; 289 return doparse(twmStringListInput, "string list", NULL); 290} 291 292 293/* 294 * Util used throughout the code (possibly often wrongly?) 295 */ 296void twmrc_error_prefix(void) 297{ 298 fprintf(stderr, "%s: line %d: ", ProgramName, twmrc_lineno); 299} 300 301 302 303/* 304 * Everything below here is related to plumbing and firing off lex/yacc 305 */ 306 307 308/* 309 * Backend func that takes an input-providing func and hooks it up to the 310 * lex/yacc parser to do the work 311 */ 312static bool 313doparse(int (*ifunc)(void), const char *srctypename, 314 const char *srcname) 315{ 316 ptr = 0; 317 len = 0; 318 twmrc_lineno = 0; 319 ParseError = false; 320 twmInputFunc = ifunc; 321#ifdef NON_FLEX_LEX 322 overflowlen = 0; 323#endif 324 325 yyparse(); 326 327 if(ParseError) { 328 fprintf(stderr, "%s: errors found in twm %s", 329 ProgramName, srctypename); 330 if(srcname) { 331 fprintf(stderr, " \"%s\"", srcname); 332 } 333 fprintf(stderr, "\n"); 334 } 335 return !(ParseError); 336} 337 338 339/* 340 * Various input routines for the lexer for the various sources of 341 * config. 342 */ 343 344#ifndef USEM4 345#include <ctype.h> 346 347/* This has Tom's include() funtionality. This is utterly useless if you 348 * can use m4 for the same thing. Chris P. Ross */ 349 350#define MAX_INCLUDES 10 351 352static struct incl { 353 FILE *fp; 354 char *name; 355 int lineno; 356} rc_includes[MAX_INCLUDES]; 357static int include_file = 0; 358 359 360static int twmFileInput(void) 361{ 362#ifdef NON_FLEX_LEX 363 if(overflowlen) { 364 return (int) overflowbuff[--overflowlen]; 365 } 366#endif 367 368 while(ptr == len) { 369 while(include_file) { 370 if(fgets(buff, BUF_LEN, rc_includes[include_file].fp) == NULL) { 371 free(rc_includes[include_file].name); 372 fclose(rc_includes[include_file].fp); 373 twmrc_lineno = rc_includes[include_file--].lineno; 374 } 375 else { 376 break; 377 } 378 } 379 380 if(!include_file) 381 if(fgets(buff, BUF_LEN, twmrc) == NULL) { 382 return 0; 383 } 384 twmrc_lineno++; 385 386 if(strncmp(buff, "include", 7) == 0) { 387 /* Whoops, an include file! */ 388 char *p = buff + 7, *q; 389 FILE *fp; 390 391 while(isspace(*p)) { 392 p++; 393 } 394 for(q = p; *q && !isspace(*q); q++) { 395 continue; 396 } 397 *q = 0; 398 399 if((fp = fopen(p, "r")) == NULL) { 400 fprintf(stderr, "%s: Unable to open included init file %s\n", 401 ProgramName, p); 402 continue; 403 } 404 if(++include_file >= MAX_INCLUDES) { 405 fprintf(stderr, "%s: init file includes nested too deep\n", 406 ProgramName); 407 continue; 408 } 409 rc_includes[include_file].fp = fp; 410 rc_includes[include_file].lineno = twmrc_lineno; 411 twmrc_lineno = 0; 412 rc_includes[include_file].name = strdup(p); 413 continue; 414 } 415 ptr = 0; 416 len = strlen(buff); 417 } 418 return ((int)buff[ptr++]); 419} 420#else /* USEM4 */ 421/* If you're going to use m4, use this version instead. Much simpler. 422 * m4 ism's credit to Josh Osborne (stripes) */ 423 424static int m4twmFileInput(void) 425{ 426 int line; 427 static FILE *cp = NULL; 428 429 if(cp == NULL && CLarg.keepM4_filename) { 430 cp = fopen(CLarg.keepM4_filename, "w"); 431 if(cp == NULL) { 432 fprintf(stderr, 433 "%s: unable to create m4 output %s, ignoring\n", 434 ProgramName, CLarg.keepM4_filename); 435 CLarg.keepM4_filename = NULL; 436 } 437 } 438 439#ifdef NON_FLEX_LEX 440 if(overflowlen) { 441 return((int) overflowbuff[--overflowlen]); 442 } 443#endif 444 445 while(ptr == len) { 446nextline: 447 if(fgets(buff, BUF_LEN, twmrc) == NULL) { 448 if(cp) { 449 fclose(cp); 450 } 451 return(0); 452 } 453 if(cp) { 454 fputs(buff, cp); 455 } 456 457 if(sscanf(buff, "#line %d", &line)) { 458 twmrc_lineno = line - 1; 459 goto nextline; 460 } 461 else { 462 twmrc_lineno++; 463 } 464 465 ptr = 0; 466 len = strlen(buff); 467 } 468 return ((int)buff[ptr++]); 469} 470#endif /* USEM4 */ 471 472 473static int twmStringListInput(void) 474{ 475#ifdef NON_FLEX_LEX 476 if(overflowlen) { 477 return (int) overflowbuff[--overflowlen]; 478 } 479#endif 480 481 /* 482 * return the character currently pointed to 483 */ 484 if(currentString) { 485 unsigned int c = (unsigned int) * currentString++; 486 487 if(c) { 488 return c; /* if non-nul char */ 489 } 490 currentString = *++stringListSource; /* advance to next bol */ 491 return '\n'; /* but say that we hit last eol */ 492 } 493 return 0; /* eof */ 494} 495 496 497 498/* 499 * unput/output funcs for AT&T lex. No longer supported, and expected to 500 * be GC'd in a release or two. 501 */ 502#ifdef NON_FLEX_LEX 503 504void twmUnput(int c) 505{ 506 if(overflowlen < sizeof overflowbuff) { 507 overflowbuff[overflowlen++] = (char) c; 508 } 509 else { 510 twmrc_error_prefix(); 511 fprintf(stderr, "unable to unput character (%c)\n", 512 c); 513 } 514} 515 516void TwmOutput(int c) 517{ 518 putchar(c); 519} 520 521#endif /* NON_FLEX_LEX */ 522