From dba6c20e2d0eb8874fb5dca35ea7ffdfdd08cba9 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 24 Jan 2020 13:26:01 -0800 Subject: [PATCH] loading Lua scripts When loading a Lua script, modify it with a comment containing the file name (or DLB module name) so that error reporting doesn't just show the first 60 bytes of the script. Also, don't assume that it's possible to load an entire script in one fread(). Unfortunately that got way out of hand and the result isn't pretty. But something of the sort is necessary. (Reading the script into one string first and then applying modifications while copying it into a second one would probably be a lot cleaner than mixing the two operations.) If a script starts with a comment or a blank line, the insertion of the file name comment won't disturb the line number reported in an error message. But if the script starts out with code on its first line, error reports will be off by one for the line number. Showing the file name is more useful than keeping that number accurate. --- src/nhlua.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 115 insertions(+), 14 deletions(-) diff --git a/src/nhlua.c b/src/nhlua.c index 170e3c3ed..bafefbfc2 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 nhlua.c $NHDT-Date: 1579899144 2020/01/24 20:52:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.28 $ */ +/* NetHack 3.7 nhlua.c $NHDT-Date: 1579901146 2020/01/24 21:25:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.29 $ */ /* Copyright (c) 2018 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ @@ -931,16 +931,21 @@ lua_State *L; return 1; } +/* read lua code/data from a dlb module or an external file, insert the + file name as new first record so that we aren't at the mercy of whoever + edits it to maintain that, and since we're forced to muck with messy + details, replace comments with empty lines so that Lua won't need to */ boolean nhl_loadlua(L, fname) lua_State *L; const char *fname; { - boolean ret = TRUE; +#define LOADCHUNKSIZE (1L << 13) /* 8K */ + boolean ret = TRUE, is_comment; dlb *fh; - char *buf = (char *) 0; - long buflen; - int cnt, llret; + char *buf = (char *) 0, *bufin, *bufout, *p, *nl; + long buflen, ct, cnt, fnamesiz; + int llret, first = 0, spanlines = 0; fh = dlb_fopen(fname, "r"); if (!fh) { @@ -949,20 +954,117 @@ const char *fname; goto give_up; } + fnamesiz = strlen(fname) + 7L; /* 7: "--{" + "}--\n" (no '\0') */ dlb_fseek(fh, 0L, SEEK_END); buflen = dlb_ftell(fh); - buf = (char *) alloc(buflen + 1); dlb_fseek(fh, 0L, SEEK_SET); - if ((cnt = dlb_fread(buf, 1, buflen, fh)) != buflen) { - impossible("nhl_loadlua: Error loading %s, got %i/%li bytes", - fname, cnt, buflen); - ret = FALSE; - goto give_up; + /* extra +1: room to add final '\n' if missing */ + buf = bufout = (char *) alloc(fnamesiz + buflen + 1 + 1); + /* insert file name as first record so nhl_error() can report it; + leading dashes make it be a comment, trailing ones are for + symmetry; delimit file name with braces instead of spaces to + avoid an undesireable line split during error feedback */ + Sprintf(bufout, "--{%s}--\n", fname); + bufin = bufout = eos(buf); + + ct = 0L; + while (buflen > 0 || ct) { + /* + * Semi-arbitrarily limit reads to 8K at a time. That's big + * enough to cover the majority of our Lua files in one bite + * but small enough to fully exercise the partial record + * handling (when processing the castle's level description). + * + * [For an external file (non-DLB), VMS may only be able to + * read at most 32K-1 at a time depending on the file format + * in use, and fseek(SEEK_END) only yields an upper bound on + * the actual amount of data in that situation.] + */ + if ((cnt = dlb_fread(bufin, 1, min(buflen, LOADCHUNKSIZE), fh)) < 0L) + break; + buflen -= cnt; /* set up for next iteration, if any */ + if (cnt == 0L) { + *bufin = '\n'; /* very last line is unterminated? */ + cnt = 1; + } + bufin[cnt] = '\0'; /* fread() doesn't do this */ + + /* in case partial line was leftover from previous fread */ + bufin -= ct, cnt += ct, ct = 0; + + while (cnt > 0) { + if ((nl = index(bufin, '\n')) != 0) { + /* normal case, newline is present */ + ct = (long) (nl - bufin + 1L); /* +1: keep the newline */ + for (p = bufin; *p == ' '; ++p) + continue; + is_comment = (p[0] == '-' && p[1] == '-' && !spanlines); + if (spanlines && index("])}", *p)) + --spanlines; + if (!first++ && (*p == '\n' || is_comment)) { + /* if first line is blank or a comment, omit it so that + our "--{fname}--\n" line doesn't make the line count + be out of sync; note: when first line is code, line + number reported in error messages will be off by one */ + bufin = nl + 1; + } else if (is_comment) { + /* discard comment to shorten the string Lua will deal + with but keep the line to maintain Lua line counter */ +#if 0 + Strcpy(bufout, "--\n"), bufout += 3; +#else + Strcpy(bufout, "\n"), bufout += 1; +#endif + bufin += ct; + } else { + /* normal case; some text terminated by newline */ + for (p = bufin; p <= nl; ++p) + *bufout++ = *bufin++; + for (p = &bufout[-2]; *p == ' '; --p) /* [-1] is '\n' */ + continue; + if (index("[({", *p)) + ++spanlines; + } + if (*bufin == '\r') + ++bufin, ++ct; + /* update for next loop iteration */ + cnt -= ct; + ct = 0; + } else { + /* no newline => partial record; move unprocessed chars + to front of input buffer (bufin portion of buf[]) */ + ct = cnt = (long) (eos(bufin) - bufin); + for (p = bufout; cnt > 0; --cnt) + *p++ = *bufin++; + *p = '\0'; + bufin = p; /* next fread() populates buf[] starting here */ + /* cnt==0 so inner loop will terminate */ + } + } } - buf[buflen] = '\0'; + *bufout = '\0'; (void) dlb_fclose(fh); +#ifdef DEBUG + if (explicitdebug("loadlua")) { + FILE *fp; + char oname[QBUFSZ]; + + (void) strsubst(strcpy(oname, fname), ".lua", ".txt"); + if ((fp = fopen(oname, "w")) != 0) { + for (buflen = strlen(buf), p = buf; + buflen > 0; + buflen -= cnt, p += cnt) { + cnt = min(buflen, LOADCHUNKSIZE); + if ((long) fwrite(p, 1, cnt, fp) < cnt) + break; + } + (void) fclose(fp); + } + } +#endif + llret = luaL_loadstring(L, buf); if (llret != LUA_OK) { impossible("luaL_loadstring: Error loading %s (errcode %i)", @@ -981,8 +1083,7 @@ const char *fname; give_up: if (buf) { - free(buf); - buf = (char *) 0; + free((genericptr_t) buf); buflen = 0; } return ret;