aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/tads/tads2/line_source_file.cpp
diff options
context:
space:
mode:
authorPaul Gilbert2019-05-17 14:48:01 -1000
committerPaul Gilbert2019-05-24 18:21:06 -0700
commitf607792fa4e1f024dd8265034ac84425bde4aee7 (patch)
tree19372287bea6cc0b4cbfdc717510cc9419fd9b04 /engines/glk/tads/tads2/line_source_file.cpp
parent105a1b94bd9d5a0f10752e135671f4e9a4b0d8da (diff)
downloadscummvm-rg350-f607792fa4e1f024dd8265034ac84425bde4aee7.tar.gz
scummvm-rg350-f607792fa4e1f024dd8265034ac84425bde4aee7.tar.bz2
scummvm-rg350-f607792fa4e1f024dd8265034ac84425bde4aee7.zip
GLK: TADS2: More code files implemented
Diffstat (limited to 'engines/glk/tads/tads2/line_source_file.cpp')
-rw-r--r--engines/glk/tads/tads2/line_source_file.cpp1037
1 files changed, 1037 insertions, 0 deletions
diff --git a/engines/glk/tads/tads2/line_source_file.cpp b/engines/glk/tads/tads2/line_source_file.cpp
index 26536c06a4..b5da3d1a3c 100644
--- a/engines/glk/tads/tads2/line_source_file.cpp
+++ b/engines/glk/tads/tads2/line_source_file.cpp
@@ -21,12 +21,1049 @@
*/
#include "glk/tads/tads2/line_source_file.h"
+#include "glk/tads/tads2/character_map.h"
+#include "glk/tads/tads2/error.h"
+#include "glk/tads/tads2/memory_cache_heap.h"
+#include "glk/tads/tads2/tokenizer.h"
+#include "glk/tads/os_glk.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
+/* initialize a pre-allocated linfdef, skipping debugger page setup */
+void linfini2(mcmcxdef *mctx, linfdef *linf,
+ char *filename, int flen, osfildef *fp, int new_line_records)
+{
+ /* set up method pointers */
+ linf->linflin.lingetp = linfget;
+ linf->linflin.linclsp = linfcls;
+ linf->linflin.linppos = linfppos;
+ linf->linflin.linglop = (new_line_records ? linfglop2 : linfglop);
+ linf->linflin.linwrtp = linfwrt;
+ linf->linflin.lincmpp = linfcmp;
+ linf->linflin.linactp = linfact;
+ linf->linflin.lindisp = linfdis;
+ linf->linflin.lintellp = linftell;
+ linf->linflin.linseekp = linfseek;
+ linf->linflin.linreadp = linfread;
+ linf->linflin.linpaddp = linfpadd;
+ linf->linflin.linqtopp = linfqtop;
+ linf->linflin.lingetsp = linfgets;
+ linf->linflin.linnamp = linfnam;
+ linf->linflin.linlnump = linflnum;
+ linf->linflin.linfindp = linffind;
+ linf->linflin.lingotop = linfgoto;
+ linf->linflin.linofsp = linfofs;
+ linf->linflin.linrenp = linfren;
+ linf->linflin.lindelp = linfdelnum;
+
+ /* set up instance data */
+ linf->linflin.linbuf = linf->linfbuf;
+ linf->linflin.linflg = 0;
+ memcpy(linf->linfnam, filename, (size_t)flen);
+ linf->linfnam[flen] = '\0';
+ linf->linfbuf[0] = '\0';
+ linf->linfbufnxt = 0;
+ linf->linfnxtlen = 0;
+ linf->linffp = fp;
+ linf->linfnum = 0;
+ linf->linflin.linlln = 4; /* OPCLINE operand is seek offset in file */
+ linf->linfmem = mctx; /* save memory manager context */
+ linf->linfcrec = 0; /* no debugger records written yet */
+}
+
+/*
+ * Initialize a file line source object. If must_find_file is true,
+ * we'll fail if we can't find the file. Otherwise, we'll create the
+ * linfdef even if we can't find the file, reserving the maximum space
+ * for its path name to be filled in later.
+ */
+linfdef *linfini(mcmcxdef *mctx, errcxdef *ec, char *filename,
+ int flen, tokpdef *path, int must_find_file,
+ int new_line_records)
+{
+ int i;
+ objnum *objp;
+ linfdef *linf;
+ osfildef *fp;
+ char fbuf[OSFNMAX + 1];
+ tokpdef fakepath;
+ int len;
+
+ if (!path)
+ {
+ path = &fakepath;
+ fakepath.tokpnxt = (tokpdef *)0;
+ fakepath.tokplen = 0;
+ }
+
+ /* search through the path list */
+ for ( ; path ; path = path->tokpnxt)
+ {
+ char last;
+
+ /* prefix the current path */
+ if ((len = path->tokplen) != 0)
+ {
+ memcpy(fbuf, path->tokpdir, (size_t)len);
+ last = fbuf[len - 1];
+ if (last == OSPATHCHAR ||
+ (OSPATHALT && strchr(OSPATHALT, last)))
+ /* do nothing */ ;
+ else
+ {
+ /* append path separator character */
+ fbuf[len++] = OSPATHCHAR;
+ }
+ }
+
+ /* add the filename and null-terminate */
+ memcpy(fbuf + len, filename, (size_t)flen);
+ fbuf[len + flen] = '\0';
+
+ /* attempt to open this file */
+ if ((fp = osfoprs(fbuf, OSFTTEXT)) != 0)
+ break;
+ }
+
+ /*
+ * If no file opened yet, search tads path; if that doesn't work,
+ * let the debugger UI try to find the file. If nothing works, give
+ * up and return failure.
+ */
+ if (fp == 0
+ && (!os_locate(filename, flen, (char *)0, fbuf, sizeof(fbuf))
+ || (fp = osfoprs(fbuf, OSFTTEXT)) == 0))
+ {
+ /*
+ * Ask the debugger UI for advice. If the debugger isn't
+ * present, we'll get a failure code from this routine.
+ */
+ if (!dbgu_find_src(filename, flen, fbuf, sizeof(fbuf),
+ must_find_file))
+ return 0;
+
+ /* try opening the file */
+ if (fbuf[0] == '\0')
+ {
+ /*
+ * we didn't get a filename - the UI wants to defer finding
+ * the file until later
+ */
+ fp = 0;
+ }
+ else
+ {
+ /* we got a filename from the UI - try opening it */
+ fp = osfoprs(fbuf, OSFTTEXT);
+ }
+
+ /*
+ * if the file isn't present, and we're required to find it,
+ * return failure
+ */
+ if (fp == 0 && must_find_file)
+ return 0;
+ }
+
+ /* figure out how much space we need for the file's full name */
+ if (fp == 0)
+ {
+ /*
+ * we didn't find the file, so we don't yet know its name - use
+ * the maximum possible filename length for the buffer size, so
+ * that we can store the final filename if we should figure out
+ * where the file is later on
+ */
+ fbuf[0] = '\0';
+ len = sizeof(fbuf);
+ }
+ else
+ {
+ /*
+ * we found the file, so we have its final name - allocate space
+ * for the known name
+ */
+ len = (int)strlen(fbuf);
+ }
+
+ /* allocate the linfdef */
+ linf = (linfdef *)mchalo(ec, (ushort)(sizeof(linfdef) + flen
+ + len + 1), "linfini");
+
+ /* do the basic initialization */
+ linfini2(mctx, linf, filename, flen, fp, new_line_records);
+
+ memcpy(linf->linfnam + flen + 1, fbuf, (size_t)len);
+ linf->linfnam[flen + 1 + len] = '\0';
+
+ /* set all debugger pages to not-yet-allocated */
+ for (i = LINFPGMAX, objp = linf->linfpg ; i ; ++objp, --i)
+ *objp = MCMONINV;
+
+ /* return the new line source object */
+ return linf;
+}
+
+int linfget(lindef *lin)
+{
+# define linf ((linfdef *)lin)
+ char *p;
+ size_t rdlen;
+ int nl_len;
+
+ /* remember seek position of start of current line */
+ linf->linfseek = osfpos(linf->linffp);
+
+ /*
+ * if we have data left in the buffer after the end of this line,
+ * move it to the start of the buffer
+ */
+ if (linf->linfnxtlen != 0)
+ {
+ /* move the data down */
+ memmove(linf->linfbuf, linf->linfbuf + linf->linfbufnxt,
+ linf->linfnxtlen);
+
+ /*
+ * adjust the seek position to account for the fact that we've
+ * read ahead in the file
+ */
+ linf->linfseek -= linf->linfnxtlen;
+
+ /*
+ * Fill up the rest of the buffer. Leave one byte for a null
+ * terminator and one byte for a possible extra newline pair
+ * character (see below), hence fill to sizeof(buf)-2.
+ */
+ rdlen = osfrbc(linf->linffp, linf->linfbuf + linf->linfnxtlen,
+ sizeof(linf->linfbuf) - linf->linfnxtlen - 2);
+
+ /*
+ * the total space is the amount we had left over plus the
+ * amount we just read
+ */
+ rdlen += linf->linfnxtlen;
+ }
+ else
+ {
+ /*
+ * We have nothing in the buffer - fill it up. Fill to
+ * sizeof(buf)-2 to leave room for a null terminator plus a
+ * possible extra newline pair character (see below).
+ */
+ rdlen = osfrbc(linf->linffp, linf->linfbuf,
+ sizeof(linf->linfbuf) - 2);
+ }
+
+ /*
+ * if there's nothing in the buffer at this point, we've reached the
+ * end of the file
+ */
+ if (rdlen == 0)
+ return TRUE;
+
+ /*
+ * if the last line was not a continuation line, increment the line
+ * counter for the start of a new line
+ */
+ if (!(lin->linflg & LINFMORE))
+ ++(linf->linfnum);
+
+ /* null-terminate the buffer contents */
+ linf->linfbuf[rdlen] = '\0';
+
+ /* perform character mapping on th new part only */
+ for (p = linf->linfbuf + linf->linfnxtlen ; *p != '\0' ; ++p)
+ *p = cmap_n2i(*p);
+
+ /*
+ * scan the for the first newline in the buffer, allowing newline
+ * conventions that involve either CR or LF
+ */
+ for (p = linf->linfbuf ; *p != '\n' && *p != '\r' && *p != '\0' ; ++p) ;
+
+ /*
+ * Check to see if this character is followed by its newline pair
+ * complement, to allow for either CR-LF or LF-CR sequences, as well
+ * as plain single-byte newline (CR or LF) sequences.
+ *
+ * First, though, one weird special case: if this character is at
+ * the read limit in the buffer, the complementary character might
+ * be lurking in the next byte that we haven't read. In this case,
+ * use that one-byte reserve we have left (we filled the buffer only
+ * to length-2 so far) and read the next byte.
+ */
+ if (*p != '\0' && p + 1 == linf->linfbuf + sizeof(linf->linfbuf) - 2)
+ {
+ /*
+ * we've filled the buffer to but not including the reserve for
+ * just this case - fetch the extra character
+ */
+ if (osfrbc(linf->linffp, p + 1, 1) == 1)
+ {
+ /* increase the total read length for the extra byte */
+ ++rdlen;
+ *(p+2) = '\0';
+ }
+ }
+
+ /*
+ * now we can check for the newline type, since we have definitely
+ * read the full paired sequence
+ */
+ if (*p == '\0')
+ {
+ /* there's no newline in the buffer - we'll return a partial line */
+ nl_len = 0;
+
+ /* set the partial line flag */
+ lin->linflg |= LINFMORE;
+
+ /* return the entire buffer */
+ lin->linlen = rdlen;
+
+ /* there's nothing left for the next time through */
+ linf->linfnxtlen = 0;
+ }
+ else
+ {
+ /* check for a complementary pair */
+ if ((*p == '\n' && *(p+1) == '\r') || (*p == '\r' && *(p+1) == '\n'))
+ {
+ /* we have a paired newline */
+ nl_len = 2;
+ }
+ else
+ {
+ /* we have but a single-character newline sequence */
+ nl_len = 1;
+ }
+
+ /* this is the end of a line */
+ lin->linflg &= ~LINFMORE;
+
+ /*
+ * return only the part of the buffer up to, but not including,
+ * the newline
+ */
+ lin->linlen = (p - linf->linfbuf);
+
+ /* null-terminate the buffer at the newline */
+ *p = '\0';
+
+ /*
+ * anything remaining after the newline sequence is available
+ * for reading the next time through
+ */
+ linf->linfbufnxt = ((p + nl_len) - linf->linfbuf);
+ linf->linfnxtlen = rdlen - linf->linfbufnxt;
+ }
+
+ /* make sure buffer pointer is correct */
+ lin->linbuf = linf->linfbuf;
+
+ LINFDEBUG(printf("%s\n", linf->linfbuf));
+
+ /* success */
+ return FALSE;
+
+# undef linf
+}
+
+/* make printable string from position in file (for error reporting) */
+void linfppos(lindef *lin, char *buf, uint buflen)
+{
+ VARUSED(buflen);
+
+ sprintf(buf, "%s(%lu): ", ((linfdef *)lin)->linfnam,
+ ((linfdef *)lin)->linfnum);
+}
+
+/* close line source */
+void linfcls(lindef *lin)
+{
+ osfcls(((linfdef *)lin)->linffp);
+}
+
+/* generate operand of OPCLINE (source-line debug) instruction */
+void linfglop(lindef *lin, uchar *buf)
+{
+ oswp4(buf, ((linfdef *)lin)->linfseek); /* save seek position of line */
+}
+
+/* generate new-style operand of OPCLINE instruction */
+void linfglop2(lindef *lin, uchar *buf)
+{
+ oswp4(buf, ((linfdef *)lin)->linfnum); /* save seek position of line */
+}
+
+/* save line source information to binary (.gam) file; TRUE ==> error */
+int linfwrt(lindef *lin, osfildef *fp)
+{
+# define linf ((linfdef *)lin)
+ uchar buf[UCHAR_MAX + 6];
+ size_t len;
+ uint pgcnt;
+ uchar *objp;
+ mcmon *objn;
+
+ buf[0] = lin->linid;
+ len = strlen(linf->linfnam);
+ if (len > UCHAR_MAX)
+ return FALSE;
+ buf[1] = (uchar)len;
+ oswp4(buf + 2, linf->linfcrec);
+ memcpy(buf + 6, linf->linfnam, (size_t)buf[1]);
+ if (osfwb(fp, buf, (int)(buf[1] + 6))) return(TRUE);
+
+ /* write the debug source pages */
+ if (!linf->linfcrec) return(FALSE); /* no debug records at all */
+ pgcnt = 1 + ((linf->linfcrec - 1) >> 10); /* figure number of pages */
+
+ for (objn = linf->linfpg ; pgcnt ; ++objn, --pgcnt)
+ {
+ objp = mcmlck(linf->linfmem, *objn);
+ if (osfwb(fp, objp, (1024 * DBGLINFSIZ))) return(TRUE);
+ mcmunlck(linf->linfmem, *objn);
+ }
+
+ return(FALSE);
+
+# undef linf
+}
+
+/* load a file-line-source from binary (.gam) file */
+int linfload(osfildef *fp, dbgcxdef *dbgctx, errcxdef *ec, tokpdef *path)
+{
+ linfdef *linf;
+ uchar buf[UCHAR_MAX + 6];
+ uint pgcnt;
+ uchar *objp;
+ mcmon *objn;
+
+ /* read the source's description from the file */
+ if (osfrb(fp, buf, 6)
+ || osfrb(fp, buf + 6, (int)buf[1]))
+ return TRUE;
+
+ /* initialize the linfdef */
+ if (!(linf = linfini(dbgctx->dbgcxmem, ec, (char *)buf + 6,
+ (int)buf[1], path, FALSE, FALSE)))
+ {
+ errlog1(ec, ERR_NOSOURC, ERRTSTR,
+ errstr(ec, (char *)buf+6, (int)buf[1]));
+ return TRUE;
+ }
+
+ /* if we opened the file, close it - don't hold all files open */
+ if (linf->linffp != 0)
+ {
+ osfcls(linf->linffp);
+ linf->linffp = 0;
+ }
+
+ /* link into debug line source chain */
+ linf->linflin.linnxt = dbgctx->dbgcxlin;
+ dbgctx->dbgcxlin = &linf->linflin;
+ linf->linflin.linid = buf[0];
+ linf->linfcrec = osrp4(buf + 2);
+
+ /* make sure the max line id is set above current line */
+ if (buf[0] >= dbgctx->dbgcxfid)
+ dbgctx->dbgcxfid = buf[0] + 1;
+
+ /* make sure we have some debug records */
+ if (!linf->linfcrec)
+ return FALSE;
+
+ /* figure number of pages */
+ pgcnt = 1 + ((linf->linfcrec - 1) >> 10);
+
+ /* allocate and read the debug source pages */
+ for (objn = linf->linfpg ; pgcnt ; ++objn, --pgcnt)
+ {
+ objp = mcmalo(linf->linfmem, (ushort)(1024 * DBGLINFSIZ), objn);
+ if (osfrb(fp, objp, (1024 * DBGLINFSIZ))) return(TRUE);
+ mcmunlck(linf->linfmem, *objn);
+ }
+
+ /* success */
+ return FALSE;
+}
+
+/* add a debugger line record for the current line being compiled */
+void linfcmp(lindef *lin, uchar *buf)
+{
+ uint pg;
+ uchar *objptr;
+# define linf ((linfdef *)lin)
+
+ /* figure out which page to use, and lock it */
+ pg = linf->linfcrec >> 10; /* 2^10 records per page */
+ if (pg >= LINFPGMAX)
+ errsig(linf->linfmem->mcmcxgl->mcmcxerr, ERR_MANYDBG);
+ if (linf->linfpg[pg] == MCMONINV)
+ objptr = mcmalo(linf->linfmem, (ushort)(1024 * DBGLINFSIZ),
+ &linf->linfpg[pg]);
+ else
+ objptr = mcmlck(linf->linfmem, linf->linfpg[pg]);
+
+ /* write the record to the appropriate offset within the page */
+ memcpy(objptr + (linf->linfcrec & 1023) * DBGLINFSIZ, buf,
+ (size_t)DBGLINFSIZ);
+
+ /* increment counter of line records so far */
+ ++(linf->linfcrec);
+
+ /* done with page - touch it and unlock it */
+ mcmtch(linf->linfmem, linf->linfpg[pg]);
+ mcmunlck(linf->linfmem, linf->linfpg[pg]);
+
+# undef linf
+}
+
+/*
+ * Renumber an existing object. Searches through all line records for
+ * any with the given object number, and changes the number to the new
+ * number if found.
+ */
+void linfren(lindef *lin, objnum oldnum, objnum newnum)
+{
+# define linf ((linfdef *)lin)
+ uint pgcnt;
+ uchar *objp;
+ mcmon *pgobjn;
+ int i;
+ int pgtot;
+ int tot;
+
+ /* figure the number of pages - if no lines, stop now */
+ tot = linf->linfcrec;
+ if (tot == 0)
+ return;
+
+ /* calculate the number of pages to check */
+ pgcnt = 1 + ((tot - 1) >> 10);
+
+ /* scan each page */
+ for (pgobjn = linf->linfpg ; pgcnt ; ++pgobjn, --pgcnt, tot -= 1024)
+ {
+ /* lock the page */
+ objp = mcmlck(linf->linfmem, *pgobjn);
+
+ /* figure the number on this page */
+ pgtot = (tot > 1024 ? 1024 : tot);
+
+ /* scan each record on this page */
+ for (i = 0 ; i < pgtot ; ++i, objp += DBGLINFSIZ)
+ {
+ /* check this one */
+ if (osrp2(objp) == oldnum)
+ {
+ /* it matches - renumber it */
+ oswp2(objp, newnum);
+ }
+ }
+
+ /* done with the page - touch it and unlock it */
+ mcmtch(linf->linfmem, *pgobjn);
+ mcmunlck(linf->linfmem, *pgobjn);
+ }
+
+# undef linf
+}
+
+/*
+ * Delete an existing object. Searches through all line records for any
+ * with the given object number, and removes line records for the object
+ * number if found.
+ */
+void linfdelnum(lindef *lin, objnum objn)
+{
+# define linf ((linfdef *)lin)
+ uint pgcnt;
+ uchar *objp;
+ uchar *objp_orig;
+ mcmon *pgobjn;
+ int i;
+ int pgtot;
+ int tot;
+
+ /* figure the number of pages - if no lines, stop now */
+ tot = linf->linfcrec;
+ if (tot == 0)
+ return;
+
+ /* calculate the number of pages to check */
+ pgcnt = 1 + ((tot - 1) >> 10);
+
+ /* scan each page */
+ for (pgobjn = linf->linfpg ; pgcnt ; ++pgobjn, --pgcnt, tot -= 1024)
+ {
+ /* lock the page */
+ objp = objp_orig = mcmlck(linf->linfmem, *pgobjn);
+
+ /* figure the number on this page */
+ pgtot = (tot > 1024 ? 1024 : tot);
+
+ /* scan each record on this page */
+ for (i = 0 ; i < pgtot ; ++i, objp += DBGLINFSIZ)
+ {
+ int j;
+
+ /* check this one */
+ if (osrp2(objp) == objn)
+ {
+ uchar *nxtp;
+ uint pg;
+ int delcnt;
+ int totrem;
+
+ /*
+ * it matches - delete it, along with any subsequent
+ * contiguous entries that also match it
+ */
+ for (delcnt = 1, j = i + 1 ; j < pgtot ; ++j, ++delcnt)
+ {
+ /*
+ * if this one doesn't match, we've found the end of
+ * the contiguous records for this object
+ */
+ if (osrp2(objp + (j - i)*DBGLINFSIZ) != objn)
+ break;
+ }
+
+ /* close up the gap on this page */
+ if (j < pgtot)
+ memmove(objp, objp + delcnt*DBGLINFSIZ,
+ (pgtot - j)*DBGLINFSIZ);
+
+ /*
+ * if this isn't the last page, copy the bottom of the
+ * next page to the gap at the top of this page
+ */
+ if (pgcnt > 1)
+ {
+ /* lock the next page */
+ nxtp = mcmlck(linf->linfmem, *(pgobjn + 1));
+
+ /*
+ * copy from the beginning of the next page to the
+ * end of this page
+ */
+ memcpy(objp_orig + (pgtot - delcnt)*DBGLINFSIZ,
+ nxtp, delcnt*DBGLINFSIZ);
+
+ /* done with the page */
+ mcmunlck(linf->linfmem, *(pgobjn + 1));
+ }
+ else
+ {
+ /*
+ * this is the last page, so there's no next page to
+ * copy items from - reduce the count of items on
+ * this page accordingly
+ */
+ pgtot -= delcnt;
+ }
+
+ /*
+ * Now rearrange all subsequent pages to accommodate the
+ * gap we just created
+ */
+ for (totrem = tot, pg = 1 ; pg < pgcnt ;
+ totrem -= 1024, ++pg)
+ {
+ uchar *curp;
+ int curtot;
+
+ /* figure how many we have on this page */
+ curtot = (totrem > 1024 ? 1024 : totrem);
+
+ /* lock this page */
+ curp = mcmlck(linf->linfmem, *(pgobjn + pg));
+
+ /* delete from the start of this page */
+ memmove(curp, curp + delcnt*DBGLINFSIZ,
+ (curtot - delcnt)*DBGLINFSIZ);
+
+ /* if there's another page, copy from it */
+ if (pg + 1 < pgcnt)
+ {
+ /* lock the next page */
+ nxtp = mcmlck(linf->linfmem, *(pgobjn + pg + 1));
+
+ /*
+ * copy from the start of the next page to the
+ * end of this page
+ */
+ memcpy(curp + (curtot - delcnt)*DBGLINFSIZ,
+ nxtp, delcnt*DBGLINFSIZ);
+
+ /* unlock it */
+ mcmunlck(linf->linfmem, *(pgobjn + pg + 1));
+ }
+
+ /* done with the page - touch it and unlock it */
+ mcmtch(linf->linfmem, *(pgobjn + pg));
+ mcmunlck(linf->linfmem, *(pgobjn + pg));
+ }
+
+ /* deduct the removed records from the total */
+ linf->linfcrec -= delcnt;
+ }
+ }
+
+ /* done with the page - touch it and unlock it */
+ mcmtch(linf->linfmem, *pgobjn);
+ mcmunlck(linf->linfmem, *pgobjn);
+ }
+
+# undef linf
+}
+
+
+/* find the nearest line record to a file seek location */
+void linffind(lindef *lin, char *buf, objnum *objp, uint *ofsp)
+{
+# define linf ((linfdef *)lin)
+ uint pg;
+ uchar *objptr;
+ uchar *bufptr;
+ long first;
+ long last;
+ long cur;
+ ulong seekpos;
+ ulong curpos = 0;
+ objnum objn;
+ uint ofs;
+
+ /* get desired seek position out of buffer */
+ seekpos = osrp4(buf);
+
+ /* we haven't traversed any records yet */
+ objn = MCMONINV;
+ ofs = 0;
+
+ /* run a binary search for the indicated line record */
+ first = 0;
+ last = linf->linfcrec - 1;
+ for (;;)
+ {
+ /* make sure we're not out of records entirely */
+ if (first > last)
+ {
+ /* return the most recent record found - it's closest */
+ *objp = objn;
+ *ofsp = ofs;
+
+ /* set the position to that of the line we actually found */
+ oswp4(buf, curpos);
+ return;
+ }
+
+ /* split the difference */
+ cur = first + (last - first)/2;
+
+ /* calculate the page containing this item */
+ pg = cur >> 10;
+
+ /* get object + offset corresponding to current source line */
+ objptr = mcmlck(linf->linfmem, linf->linfpg[pg]);
+ bufptr = objptr + ((cur & 1023) * DBGLINFSIZ);
+ objn = osrp2(bufptr);
+ ofs = osrp2(bufptr + 2);
+ mcmunlck(linf->linfmem, linf->linfpg[pg]);
+
+ /* read user data out of the object's OPCLINE record */
+ objptr = mcmlck(linf->linfmem, (mcmon)objn);
+ bufptr = objptr + ofs + 5;
+ curpos = osrp4(bufptr);
+ mcmunlck(linf->linfmem, (mcmon)objn);
+
+ /* see what we have */
+ if (curpos == seekpos)
+ {
+ *objp = objn;
+ *ofsp = ofs;
+ return;
+ }
+ else if (curpos < seekpos)
+ first = (cur == first ? first + 1 : cur);
+ else
+ last = (cur == last ? last - 1 : cur);
+ }
+
+# undef linf
+}
+
+/*
+ * copy line records to an array of linfinfo structures
+ */
+void linf_copy_linerecs(linfdef *linf, struct linfinfo *info)
+{
+ uint pg;
+ uint prvpg;
+ uchar *objptr;
+ uchar *bufptr;
+ long last;
+ long cur;
+
+ /* note the last element */
+ last = linf->linfcrec;
+
+ /* if there are no records, there's nothing to do */
+ if (last == 0)
+ return;
+
+ /* load the first page of records */
+ prvpg = 0;
+ pg = 0;
+ objptr = mcmlck(linf->linfmem, linf->linfpg[0]);
+
+ /* scan the records */
+ for (cur = 0 ; cur < last ; ++cur, ++info)
+ {
+ uchar *codeptr;
+
+ /* calculate the page containing this item */
+ pg = cur >> 10;
+
+ /* if it's different than the last page, load the next page */
+ if (pg != prvpg)
+ {
+ /* unlock the previous page */
+ mcmunlck(linf->linfmem, linf->linfpg[prvpg]);
+
+ /* load the next page */
+ objptr = mcmlck(linf->linfmem, linf->linfpg[pg]);
+
+ /* this is now the previous page */
+ prvpg = pg;
+ }
+
+ /* get object + offset corresponding to current source line */
+ bufptr = objptr + ((cur & 1023) * DBGLINFSIZ);
+ info->objn = osrp2(bufptr);
+ info->ofs = osrp2(bufptr + 2);
+
+ /* read source location data out of the object's OPCLINE record */
+ codeptr = mcmlck(linf->linfmem, (mcmon)info->objn);
+ bufptr = codeptr + info->ofs + 5;
+ info->fpos = osrp4(bufptr);
+ mcmunlck(linf->linfmem, (mcmon)info->objn);
+ }
+
+ /* unlock the last page */
+ mcmunlck(linf->linfmem, linf->linfpg[prvpg]);
+}
+
+/* disactivate line source under debugger - close file */
+void linfdis(lindef *lin)
+{
+# define linf ((linfdef *)lin)
+
+ if (linf->linffp)
+ {
+ osfcls(linf->linffp);
+ linf->linffp = (osfildef *)0;
+ }
+
+# undef linf
+}
+
+/* activate line source under debugger - open file */
+void linfact(lindef *lin)
+{
+ char *fname;
+# define linf ((linfdef *)lin)
+
+ /* get the name buffer, and advance to the full path name portion */
+ fname = linf->linfnam;
+ fname += strlen(fname) + 1;
+
+ /*
+ * If the full path name is empty, it means that the UI told us to
+ * defer searching for the file until we actually need the file. At
+ * this point, we actually need the file. Ask the UI again to find
+ * the file.
+ */
+ if (fname[0] != '\0'
+ || dbgu_find_src(linf->linfnam, strlen(linf->linfnam),
+ fname, OSFNMAX, TRUE))
+ {
+ /* open the file */
+ linf->linffp = osfoprs(fname, OSFTTEXT);
+ }
+ else
+ {
+ /* there's no file to open */
+ linf->linffp = 0;
+ }
+
+# undef linf
+}
+
+/* get current seek position */
+void linftell(lindef *lin, uchar *pos)
+{
+# define linf ((linfdef *)lin)
+ long seekpos;
+
+ seekpos = osfpos(linf->linffp);
+ oswp4(pos, seekpos);
+
+# undef linf
+}
+
+/* seek to a new position */
+void linfseek(lindef *lin, uchar *pos)
+{
+# define linf ((linfdef *)lin)
+ long seekpos;
+
+ seekpos = osrp4(pos);
+ osfseek(linf->linffp, seekpos, OSFSK_SET);
+
+# undef linf
+}
+
+/* read bytes - fread-style interface */
+int linfread(lindef *lin, uchar *buf, uint siz)
+{
+# define linf ((linfdef *)lin)
+
+ return osfrbc(linf->linffp, buf, siz);
+
+# undef linf
+}
+
+/* add a signed delta to a seek position */
+void linfpadd(lindef *lin, uchar *pos, long delta)
+{
+# define linf ((linfdef *)lin)
+ long seekpos;
+
+ seekpos = osrp4(pos);
+ seekpos += delta;
+ if (seekpos < 0) seekpos = 0;
+ oswp4(pos, seekpos);
+
+# undef linf
+}
+
+/* query whether we're at top of file */
+int linfqtop(lindef *lin, uchar *pos)
+{
+# define linf ((linfdef *)lin)
+
+ return(osrp4(pos) == 0);
+
+# undef linf
+}
+
+/* read one line at current position - fgets-style interface */
+int linfgets(lindef *lin, uchar *buf, uint siz)
+{
+ int ret;
+ long startpos;
+ uchar *p;
+# define linf ((linfdef *)lin)
+
+ /* note the seek offset at the start of the line */
+ startpos = osfpos(linf->linffp);
+
+ /* read the next line */
+ ret = (osfgets((char *)buf, siz, linf->linffp) != 0);
+ if (!ret)
+ return ret;
+
+ /* scan for non-standard line endings */
+ for (p = buf ; *p != '\0' && *p != '\r' && *p != '\n' ; ++p) ;
+ if (*p != '\0')
+ {
+ uchar *nxt;
+
+ /*
+ * Scan for non-line-ending characters after this line-ending
+ * character. If we find any, we must have non-standard newline
+ * conventions in this file. To be tolerant of these, seek back
+ * to the start of the next line in these cases and read the
+ * next line from the new location.
+ */
+ for (nxt = p + 1 ; *nxt == '\r' || *nxt == '\n' ; ++nxt) ;
+ if (*nxt == '\0')
+ {
+ /*
+ * we had only line-ending characters after the first
+ * line-ending character -- simply end the line after the
+ * first line-ending character
+ */
+ *(p+1) = '\0';
+ }
+ else
+ {
+ /*
+ * We had a line-ending character in the middle of other
+ * text, so we must have a file that doesn't conform to
+ * local newline conventions. Seek back to the next
+ * character following the last line-ending character so
+ * that we start the next line here, and end the current
+ * line after the first line-ending character.
+ */
+ *(p+1) = '\0';
+ osfseek(linf->linffp, startpos + (nxt - buf), OSFSK_SET);
+ }
+ }
+
+ /* return the result */
+ return ret;
+
+# undef linf
+}
+
+/* get name of line source */
+void linfnam(lindef *lin, char *buf)
+{
+# define linf ((linfdef *)lin)
+
+ strcpy(buf, linf->linfnam);
+
+# undef linf
+}
+
+/* get the current line number */
+ulong linflnum(lindef *lin)
+{
+# define linf ((linfdef *)lin)
+
+ return linf->linfnum;
+
+# undef linf
+}
+
+/* go to top/bottom of line source */
+void linfgoto(lindef *lin, int where)
+{
+# define linf ((linfdef *)lin)
+
+ osfseek(linf->linffp, 0L, where);
+
+# undef linf
+}
+
+/* return current seek offset within source */
+long linfofs(lindef *lin)
+{
+# define linf ((linfdef *)lin)
+
+ return(osfpos(linf->linffp));
+
+# undef linf
+}
} // End of namespace TADS2
} // End of namespace TADS