X11 default resources - macro expansion

The X11 interface reads file NetHack.ad (after cd'ing to the playground
directory, where 'make install' puts a copy) and feeds the contents to
X Windows for use as default resources to override the compiled in
defaults.  When use of #define was introduced into NetHack.ad (back in
September, 2016) this was severely hobbled and startup spit out a lot
complaints to stderr about invalid resource values.  This implements
rudimentary macro expansion for '#define name value' within the data
stream that's fed to X, getting back decent default values and
eliminating the invalid value complaints.
This commit is contained in:
PatR
2017-10-11 17:29:55 -07:00
parent 46217bbda2
commit ffeabfdf3b

View File

@@ -136,6 +136,7 @@ struct window_procs X11_procs = {
static winid NDECL(find_free_window); static winid NDECL(find_free_window);
static void FDECL(nhFreePixel, (XtAppContext, XrmValuePtr, XtPointer, static void FDECL(nhFreePixel, (XtAppContext, XrmValuePtr, XtPointer,
XrmValuePtr, Cardinal *)); XrmValuePtr, Cardinal *));
static boolean FDECL(new_resource_macro, (String, unsigned));
static void NDECL(load_default_resources); static void NDECL(load_default_resources);
static void NDECL(release_default_resources); static void NDECL(release_default_resources);
#ifdef X11_HANGUP_SIGNAL #ifdef X11_HANGUP_SIGNAL
@@ -480,7 +481,43 @@ Widget w;
#endif #endif
} }
static String *default_resource_data = 0; /* NULL-terminated array */ static String *default_resource_data = 0, /* NULL-terminated arrays */
*def_rsrc_macr = 0, /* macro names */
*def_rsrc_valu = 0; /* macro values */
/* caller found "#define"; parse into macro name and its expansion value */
static boolean
new_resource_macro(inbuf, numdefs)
String inbuf; /* points past '#define' rather than to start of buffer */
unsigned numdefs; /* array slot to fill */
{
String p, q;
/* we expect inbuf to be terminated by newline; get rid of it */
q = eos(inbuf);
if (q > inbuf && q[-1] == '\n')
q[-1] = '\0';
/* figure out macro's name */
for (p = inbuf; *p == ' ' || *p == '\t'; ++p)
continue; /* skip whitespace */
for (q = p; *q && *q != ' ' && *q != '\t'; ++q)
continue; /* token consists of non-whitespace */
Strcat(q, " "); /* guarantee something beyond '#define FOO' */
*q++ = '\0'; /* p..(q-1) contains macro name */
if (!*p) /* invalid definition: '#define' followed by nothing */
return FALSE;
def_rsrc_macr[numdefs] = dupstr(p);
/* figure out macro's value; empty value is supported but not expected */
while (*q == ' ' || *q == '\t')
++q; /* skip whitespace between name and value */
for (p = eos(q); --p > q && (*p == ' ' || *p == '\t'); )
continue; /* discard trailing whitespace */
*++p = '\0'; /* q..p containes macro value */
def_rsrc_valu[numdefs] = dupstr(q);
return TRUE;
}
/* read the template NetHack.ad into default_resource_data[] to supply /* read the template NetHack.ad into default_resource_data[] to supply
fallback resources to XtAppInitialize() */ fallback resources to XtAppInitialize() */
@@ -489,8 +526,8 @@ load_default_resources()
{ {
FILE *fp; FILE *fp;
String inbuf; String inbuf;
unsigned insiz, linelen, longlen, numlines; unsigned insiz, linelen, longlen, numlines, numdefs, midx;
boolean comment = FALSE; /* lint suppression */ boolean comment, isdef;
/* /*
* Running nethack via the shell script adds $HACKDIR to the path used * Running nethack via the shell script adds $HACKDIR to the path used
@@ -499,7 +536,11 @@ load_default_resources()
* load its contents into memory so that the application startup call * load its contents into memory so that the application startup call
* in X11_init_nhwindows() can use them as fallback resources. * in X11_init_nhwindows() can use them as fallback resources.
* *
* No attempt to support the 'include' directive has been made. * No attempt to support the 'include' directive has been made, nor
* backslash+newline continuation lines. Macro expansion (at most
* one substitution per line) is supported. '#define' to introduce
* a macro must be at start of line (no whitespace before or after
* the '#' character).
*/ */
fp = fopen("./NetHack.ad", "r"); fp = fopen("./NetHack.ad", "r");
if (!fp) if (!fp)
@@ -509,35 +550,84 @@ load_default_resources()
insiz = BUFSIZ; /* stdio BUFSIZ, not nethack BUFSZ */ insiz = BUFSIZ; /* stdio BUFSIZ, not nethack BUFSZ */
inbuf = (String) alloc(insiz); inbuf = (String) alloc(insiz);
linelen = longlen = 0; linelen = longlen = 0;
numlines = 0; numlines = numdefs = 0;
comment = isdef = FALSE; /* lint suppression */
while (fgets(inbuf, insiz, fp)) { while (fgets(inbuf, insiz, fp)) {
if (!linelen) /* inbuf has start of record; treat empty as comment */ if (!linelen) {
/* !linelen: inbuf has start of record; treat empty as comment */
comment = (*inbuf == '!' || *inbuf == '\n'); comment = (*inbuf == '!' || *inbuf == '\n');
isdef = !strncmp(inbuf, "#define", 7);
++numdefs;
}
linelen += strlen(inbuf); linelen += strlen(inbuf);
if (!index(inbuf, '\n')) if (!index(inbuf, '\n'))
continue; continue;
if (linelen > longlen) if (linelen > longlen)
longlen = linelen; longlen = linelen;
linelen = 0; linelen = 0;
if (!comment) if (!comment && !isdef)
++numlines; ++numlines;
} }
free((genericptr_t) inbuf);
insiz = longlen + 1; insiz = longlen + 1;
inbuf = (String) alloc(insiz); if (numdefs) { /* don't alloc if 0; no need for any terminator */
def_rsrc_macr = (String *) alloc(numdefs * sizeof (String));
def_rsrc_valu = (String *) alloc(numdefs * sizeof (String));
insiz += BUFSIZ; /* include room for macro expansion within buffer */
}
if (insiz > BUFSIZ) {
free((genericptr_t) inbuf);
inbuf = (String) alloc(insiz);
}
++numlines; /* room for terminator */ ++numlines; /* room for terminator */
default_resource_data = (String *) alloc(numlines * sizeof (String)); default_resource_data = (String *) alloc(numlines * sizeof (String));
/* now read the file into the array */ /* now re-read the file, storing its contents into the allocated array
after performing macro substitutions */
(void) rewind(fp); (void) rewind(fp);
numlines = 0; numlines = numdefs = 0;
while (fgets(inbuf, insiz, fp)) { while (fgets(inbuf, insiz, fp)) {
if (*inbuf != '!' && *inbuf != '\n') if (!strncmp(inbuf, "#define", 7)) {
if (new_resource_macro(&inbuf[7], numdefs))
++numdefs;
} else if (*inbuf != '!' && *inbuf != '\n') {
if (numdefs) {
/*
* Macro expansion: we assume at most one substitution
* per line. That's all that our sample NetHack.ad uses.
*
* If we ever need more, this will have to become a lot
* more sophisticated. It will need to find the first
* instance within inbuf[] rather than first macro which
* appears, and to avoid finding names within substituted
* expansion values.
*
* Any substitution which would exceed the buffer size is
* skipped. A sophisticated implementation would need to
* be prepared to allocate a bigger buffer when needed.
*/
linelen = strlen(inbuf);
for (midx = 0; midx < numdefs; ++midx) {
if ((linelen + strlen(def_rsrc_valu[midx])
< insiz - strlen(def_rsrc_macr[midx]))
&& strNsubst(inbuf, def_rsrc_macr[midx],
def_rsrc_valu[midx], 1))
break;
}
}
default_resource_data[numlines++] = dupstr(inbuf); default_resource_data[numlines++] = dupstr(inbuf);
}
} }
default_resource_data[numlines] = (String) 0; default_resource_data[numlines] = (String) 0;
(void) fclose(fp); (void) fclose(fp);
free((genericptr_t) inbuf); free((genericptr_t) inbuf);
if (def_rsrc_macr) { /* implies def_rsrc_valu is non-Null too */
for (midx = 0; midx < numdefs; ++midx) {
free((genericptr_t) def_rsrc_macr[midx]);
free((genericptr_t) def_rsrc_valu[midx]);
}
free((genericptr_t) def_rsrc_macr), def_rsrc_macr = 0;
free((genericptr_t) def_rsrc_valu), def_rsrc_valu = 0;
}
} }
static void static void
@@ -550,6 +640,7 @@ release_default_resources()
free((genericptr_t) default_resource_data[idx]); free((genericptr_t) default_resource_data[idx]);
free((genericptr_t) default_resource_data), default_resource_data = 0; free((genericptr_t) default_resource_data), default_resource_data = 0;
} }
/* def_rsrc_macr[] and def_rsrc_valu[] have already been released */
} }
/* Global Functions ======================================================= */ /* Global Functions ======================================================= */