Files
scummvm-cursorfix/engines/glk/tads/tads2/line_source_file.cpp
2026-02-02 04:50:13 +01:00

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