1071 lines
26 KiB
C++
1071 lines
26 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#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 {
|
|
|
|
enum { BYTE_MAX = 0xff };
|
|
|
|
/* initialize a pre-allocated linfdef, skipping debugger page setup */
|
|
void linfini2(mcmcxdef *mctx, linfdef *linf,
|
|
const 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, const char *filename,
|
|
int flen, tokpdef *path, int must_find_file,
|
|
int new_line_records)
|
|
{
|
|
int i;
|
|
objnum *objp;
|
|
linfdef *linf;
|
|
osfildef *fp = nullptr;
|
|
char fbuf[OSFNMAX + 1];
|
|
tokpdef fakepath;
|
|
int len;
|
|
|
|
if (!path)
|
|
{
|
|
path = &fakepath;
|
|
fakepath.tokpnxt = (tokpdef *)nullptr;
|
|
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)) != nullptr)
|
|
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 == nullptr
|
|
&& (!os_locate(filename, flen, (char *)nullptr, fbuf, sizeof(fbuf))
|
|
|| (fp = osfoprs(fbuf, OSFTTEXT)) == nullptr))
|
|
{
|
|
/*
|
|
* 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 nullptr;
|
|
|
|
/* 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 = nullptr;
|
|
}
|
|
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 == nullptr && must_find_file)
|
|
return nullptr;
|
|
}
|
|
|
|
/* figure out how much space we need for the file's full name */
|
|
if (fp == nullptr)
|
|
{
|
|
/*
|
|
* 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(debug(10, "%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);
|
|
|
|
Common::sprintf_s(buf, buflen, "%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[BYTE_MAX + 6];
|
|
size_t len;
|
|
uint pgcnt;
|
|
uchar *objp;
|
|
mcmon *objn;
|
|
|
|
buf[0] = lin->linid;
|
|
len = strlen(linf->linfnam);
|
|
if (len > BYTE_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[BYTE_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 != nullptr)
|
|
{
|
|
osfcls(linf->linffp);
|
|
linf->linffp = nullptr;
|
|
}
|
|
|
|
/* 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 *)nullptr;
|
|
}
|
|
|
|
# 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 = nullptr;
|
|
}
|
|
|
|
# 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) != nullptr);
|
|
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)
|
|
|
|
Common::strcpy_s(buf, OSFNMAX, 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
|
|
} // End of namespace Glk
|