3
"$Header: d:/cvsroot/tads/TADS2/LINF.C,v 1.3 1999/07/11 00:46:29 MJRoberts Exp $";
7
* Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved.
9
* Please see the accompanying license file, LICENSE.TXT, for information
10
* on using and copying this software.
14
linf.c - line source File implementation
16
Implementation of file line source
20
04/11/99 CNebel - fix signed/unsigned mismatches
21
04/25/93 JEras - use new os_locate() to find include files
22
08/14/91 MJRoberts - creation
39
/* initialize a pre-allocated linfdef, skipping debugger page setup */
40
void linfini2(mcmcxdef *mctx, linfdef *linf,
41
char *filename, int flen, osfildef *fp, int new_line_records)
43
/* set up method pointers */
44
linf->linflin.lingetp = linfget;
45
linf->linflin.linclsp = linfcls;
46
linf->linflin.linppos = linfppos;
47
linf->linflin.linglop = (new_line_records ? linfglop2 : linfglop);
48
linf->linflin.linwrtp = linfwrt;
49
linf->linflin.lincmpp = linfcmp;
50
linf->linflin.linactp = linfact;
51
linf->linflin.lindisp = linfdis;
52
linf->linflin.lintellp = linftell;
53
linf->linflin.linseekp = linfseek;
54
linf->linflin.linreadp = linfread;
55
linf->linflin.linpaddp = linfpadd;
56
linf->linflin.linqtopp = linfqtop;
57
linf->linflin.lingetsp = linfgets;
58
linf->linflin.linnamp = linfnam;
59
linf->linflin.linlnump = linflnum;
60
linf->linflin.linfindp = linffind;
61
linf->linflin.lingotop = linfgoto;
62
linf->linflin.linofsp = linfofs;
63
linf->linflin.linrenp = linfren;
64
linf->linflin.lindelp = linfdelnum;
66
/* set up instance data */
67
linf->linflin.linbuf = linf->linfbuf;
68
linf->linflin.linflg = 0;
69
memcpy(linf->linfnam, filename, (size_t)flen);
70
linf->linfnam[flen] = '\0';
71
linf->linfbuf[0] = '\0';
76
linf->linflin.linlln = 4; /* OPCLINE operand is seek offset in file */
77
linf->linfmem = mctx; /* save memory manager context */
78
linf->linfcrec = 0; /* no debugger records written yet */
82
* Initialize a file line source object. If must_find_file is true,
83
* we'll fail if we can't find the file. Otherwise, we'll create the
84
* linfdef even if we can't find the file, reserving the maximum space
85
* for its path name to be filled in later.
87
linfdef *linfini(mcmcxdef *mctx, errcxdef *ec, char *filename,
88
int flen, tokpdef *path, int must_find_file,
95
char fbuf[OSFNMAX + 1];
102
fakepath.tokpnxt = (tokpdef *)0;
103
fakepath.tokplen = 0;
106
/* search through the path list */
107
for ( ; path ; path = path->tokpnxt)
111
/* prefix the current path */
112
if ((len = path->tokplen) != 0)
114
memcpy(fbuf, path->tokpdir, (size_t)len);
115
last = fbuf[len - 1];
116
if (last == OSPATHCHAR ||
117
(OSPATHALT && strchr(OSPATHALT, last)))
121
/* append path separator character */
122
fbuf[len++] = OSPATHCHAR;
126
/* add the filename and null-terminate */
127
memcpy(fbuf + len, filename, (size_t)flen);
128
fbuf[len + flen] = '\0';
130
/* attempt to open this file */
131
if ((fp = osfoprs(fbuf, OSFTTEXT)) != 0)
136
* If no file opened yet, search tads path; if that doesn't work,
137
* let the debugger UI try to find the file. If nothing works, give
138
* up and return failure.
141
&& (!os_locate(filename, flen, (char *)0, fbuf, sizeof(fbuf))
142
|| (fp = osfoprs(fbuf, OSFTTEXT)) == 0))
145
* Ask the debugger UI for advice. If the debugger isn't
146
* present, we'll get a failure code from this routine.
148
if (!dbgu_find_src(filename, flen, fbuf, sizeof(fbuf),
152
/* try opening the file */
156
* we didn't get a filename - the UI wants to defer finding
157
* the file until later
163
/* we got a filename from the UI - try opening it */
164
fp = osfoprs(fbuf, OSFTTEXT);
168
* if the file isn't present, and we're required to find it,
171
if (fp == 0 && must_find_file)
175
/* figure out how much space we need for the file's full name */
179
* we didn't find the file, so we don't yet know its name - use
180
* the maximum possible filename length for the buffer size, so
181
* that we can store the final filename if we should figure out
182
* where the file is later on
190
* we found the file, so we have its final name - allocate space
193
len = (int)strlen(fbuf);
196
/* allocate the linfdef */
197
linf = (linfdef *)mchalo(ec, (ushort)(sizeof(linfdef) + flen
198
+ len + 1), "linfini");
200
/* do the basic initialization */
201
linfini2(mctx, linf, filename, flen, fp, new_line_records);
203
memcpy(linf->linfnam + flen + 1, fbuf, (size_t)len);
204
linf->linfnam[flen + 1 + len] = '\0';
206
/* set all debugger pages to not-yet-allocated */
207
for (i = LINFPGMAX, objp = linf->linfpg ; i ; ++objp, --i)
210
/* return the new line source object */
214
int linfget(lindef *lin)
216
# define linf ((linfdef *)lin)
221
/* remember seek position of start of current line */
222
linf->linfseek = osfpos(linf->linffp);
225
* if we have data left in the buffer after the end of this line,
226
* move it to the start of the buffer
228
if (linf->linfnxtlen != 0)
230
/* move the data down */
231
memmove(linf->linfbuf, linf->linfbuf + linf->linfbufnxt,
235
* adjust the seek position to account for the fact that we've
236
* read ahead in the file
238
linf->linfseek -= linf->linfnxtlen;
241
* Fill up the rest of the buffer. Leave one byte for a null
242
* terminator and one byte for a possible extra newline pair
243
* character (see below), hence fill to sizeof(buf)-2.
245
rdlen = osfrbc(linf->linffp, linf->linfbuf + linf->linfnxtlen,
246
sizeof(linf->linfbuf) - linf->linfnxtlen - 2);
249
* the total space is the amount we had left over plus the
250
* amount we just read
252
rdlen += linf->linfnxtlen;
257
* We have nothing in the buffer - fill it up. Fill to
258
* sizeof(buf)-2 to leave room for a null terminator plus a
259
* possible extra newline pair character (see below).
261
rdlen = osfrbc(linf->linffp, linf->linfbuf,
262
sizeof(linf->linfbuf) - 2);
266
* if there's nothing in the buffer at this point, we've reached the
273
* if the last line was not a continuation line, increment the line
274
* counter for the start of a new line
276
if (!(lin->linflg & LINFMORE))
279
/* null-terminate the buffer contents */
280
linf->linfbuf[rdlen] = '\0';
282
/* perform character mapping on th new part only */
283
for (p = linf->linfbuf + linf->linfnxtlen ; *p != '\0' ; ++p)
287
* scan the for the first newline in the buffer, allowing newline
288
* conventions that involve either CR or LF
290
for (p = linf->linfbuf ; *p != '\n' && *p != '\r' && *p != '\0' ; ++p) ;
293
* Check to see if this character is followed by its newline pair
294
* complement, to allow for either CR-LF or LF-CR sequences, as well
295
* as plain single-byte newline (CR or LF) sequences.
297
* First, though, one weird special case: if this character is at
298
* the read limit in the buffer, the complementary character might
299
* be lurking in the next byte that we haven't read. In this case,
300
* use that one-byte reserve we have left (we filled the buffer only
301
* to length-2 so far) and read the next byte.
303
if (*p != '\0' && p + 1 == linf->linfbuf + sizeof(linf->linfbuf) - 2)
306
* we've filled the buffer to but not including the reserve for
307
* just this case - fetch the extra character
309
if (osfrbc(linf->linffp, p + 1, 1) == 1)
311
/* increase the total read length for the extra byte */
318
* now we can check for the newline type, since we have definitely
319
* read the full paired sequence
323
/* there's no newline in the buffer - we'll return a partial line */
326
/* set the partial line flag */
327
lin->linflg |= LINFMORE;
329
/* return the entire buffer */
332
/* there's nothing left for the next time through */
333
linf->linfnxtlen = 0;
337
/* check for a complementary pair */
338
if ((*p == '\n' && *(p+1) == '\r') || (*p == '\r' && *(p+1) == '\n'))
340
/* we have a paired newline */
345
/* we have but a single-character newline sequence */
349
/* this is the end of a line */
350
lin->linflg &= ~LINFMORE;
353
* return only the part of the buffer up to, but not including,
356
lin->linlen = (p - linf->linfbuf);
358
/* null-terminate the buffer at the newline */
362
* anything remaining after the newline sequence is available
363
* for reading the next time through
365
linf->linfbufnxt = ((p + nl_len) - linf->linfbuf);
366
linf->linfnxtlen = rdlen - linf->linfbufnxt;
369
/* make sure buffer pointer is correct */
370
lin->linbuf = linf->linfbuf;
372
LINFDEBUG(printf("%s\n", linf->linfbuf));
380
/* make printable string from position in file (for error reporting) */
381
void linfppos(lindef *lin, char *buf, uint buflen)
385
sprintf(buf, "%s(%lu): ", ((linfdef *)lin)->linfnam,
386
((linfdef *)lin)->linfnum);
389
/* close line source */
390
void linfcls(lindef *lin)
392
osfcls(((linfdef *)lin)->linffp);
395
/* generate operand of OPCLINE (source-line debug) instruction */
396
void linfglop(lindef *lin, uchar *buf)
398
oswp4(buf, ((linfdef *)lin)->linfseek); /* save seek position of line */
401
/* generate new-style operand of OPCLINE instruction */
402
void linfglop2(lindef *lin, uchar *buf)
404
oswp4(buf, ((linfdef *)lin)->linfnum); /* save seek position of line */
407
/* save line source information to binary (.gam) file; TRUE ==> error */
408
int linfwrt(lindef *lin, osfildef *fp)
410
# define linf ((linfdef *)lin)
411
uchar buf[UCHAR_MAX + 6];
418
len = strlen(linf->linfnam);
422
oswp4(buf + 2, linf->linfcrec);
423
memcpy(buf + 6, linf->linfnam, (size_t)buf[1]);
424
if (osfwb(fp, buf, (int)(buf[1] + 6))) return(TRUE);
426
/* write the debug source pages */
427
if (!linf->linfcrec) return(FALSE); /* no debug records at all */
428
pgcnt = 1 + ((linf->linfcrec - 1) >> 10); /* figure number of pages */
430
for (objn = linf->linfpg ; pgcnt ; ++objn, --pgcnt)
432
objp = mcmlck(linf->linfmem, *objn);
433
if (osfwb(fp, objp, (1024 * DBGLINFSIZ))) return(TRUE);
434
mcmunlck(linf->linfmem, *objn);
442
/* load a file-line-source from binary (.gam) file */
443
int linfload(osfildef *fp, dbgcxdef *dbgctx, errcxdef *ec, tokpdef *path)
446
uchar buf[UCHAR_MAX + 6];
451
/* read the source's description from the file */
452
if (osfrb(fp, buf, 6)
453
|| osfrb(fp, buf + 6, (int)buf[1]))
456
/* initialize the linfdef */
457
if (!(linf = linfini(dbgctx->dbgcxmem, ec, (char *)buf + 6,
458
(int)buf[1], path, FALSE, FALSE)))
460
errlog1(ec, ERR_NOSOURC, ERRTSTR,
461
errstr(ec, (char *)buf+6, (int)buf[1]));
465
/* if we opened the file, close it - don't hold all files open */
466
if (linf->linffp != 0)
468
osfcls(linf->linffp);
472
/* link into debug line source chain */
473
linf->linflin.linnxt = dbgctx->dbgcxlin;
474
dbgctx->dbgcxlin = &linf->linflin;
475
linf->linflin.linid = buf[0];
476
linf->linfcrec = osrp4(buf + 2);
478
/* make sure the max line id is set above current line */
479
if (buf[0] >= dbgctx->dbgcxfid)
480
dbgctx->dbgcxfid = buf[0] + 1;
482
/* make sure we have some debug records */
486
/* figure number of pages */
487
pgcnt = 1 + ((linf->linfcrec - 1) >> 10);
489
/* allocate and read the debug source pages */
490
for (objn = linf->linfpg ; pgcnt ; ++objn, --pgcnt)
492
objp = mcmalo(linf->linfmem, (ushort)(1024 * DBGLINFSIZ), objn);
493
if (osfrb(fp, objp, (1024 * DBGLINFSIZ))) return(TRUE);
494
mcmunlck(linf->linfmem, *objn);
501
/* add a debugger line record for the current line being compiled */
502
void linfcmp(lindef *lin, uchar *buf)
506
# define linf ((linfdef *)lin)
508
/* figure out which page to use, and lock it */
509
pg = linf->linfcrec >> 10; /* 2^10 records per page */
511
errsig(linf->linfmem->mcmcxgl->mcmcxerr, ERR_MANYDBG);
512
if (linf->linfpg[pg] == MCMONINV)
513
objptr = mcmalo(linf->linfmem, (ushort)(1024 * DBGLINFSIZ),
516
objptr = mcmlck(linf->linfmem, linf->linfpg[pg]);
518
/* write the record to the appropriate offset within the page */
519
memcpy(objptr + (linf->linfcrec & 1023) * DBGLINFSIZ, buf,
522
/* increment counter of line records so far */
525
/* done with page - touch it and unlock it */
526
mcmtch(linf->linfmem, linf->linfpg[pg]);
527
mcmunlck(linf->linfmem, linf->linfpg[pg]);
533
* Renumber an existing object. Searches through all line records for
534
* any with the given object number, and changes the number to the new
537
void linfren(lindef *lin, objnum oldnum, objnum newnum)
539
# define linf ((linfdef *)lin)
547
/* figure the number of pages - if no lines, stop now */
548
tot = linf->linfcrec;
552
/* calculate the number of pages to check */
553
pgcnt = 1 + ((tot - 1) >> 10);
556
for (pgobjn = linf->linfpg ; pgcnt ; ++pgobjn, --pgcnt, tot -= 1024)
559
objp = mcmlck(linf->linfmem, *pgobjn);
561
/* figure the number on this page */
562
pgtot = (tot > 1024 ? 1024 : tot);
564
/* scan each record on this page */
565
for (i = 0 ; i < pgtot ; ++i, objp += DBGLINFSIZ)
568
if (osrp2(objp) == oldnum)
570
/* it matches - renumber it */
575
/* done with the page - touch it and unlock it */
576
mcmtch(linf->linfmem, *pgobjn);
577
mcmunlck(linf->linfmem, *pgobjn);
584
* Delete an existing object. Searches through all line records for any
585
* with the given object number, and removes line records for the object
588
void linfdelnum(lindef *lin, objnum objn)
590
# define linf ((linfdef *)lin)
599
/* figure the number of pages - if no lines, stop now */
600
tot = linf->linfcrec;
604
/* calculate the number of pages to check */
605
pgcnt = 1 + ((tot - 1) >> 10);
608
for (pgobjn = linf->linfpg ; pgcnt ; ++pgobjn, --pgcnt, tot -= 1024)
611
objp = objp_orig = mcmlck(linf->linfmem, *pgobjn);
613
/* figure the number on this page */
614
pgtot = (tot > 1024 ? 1024 : tot);
616
/* scan each record on this page */
617
for (i = 0 ; i < pgtot ; ++i, objp += DBGLINFSIZ)
622
if (osrp2(objp) == objn)
630
* it matches - delete it, along with any subsequent
631
* contiguous entries that also match it
633
for (delcnt = 1, j = i + 1 ; j < pgtot ; ++j, ++delcnt)
636
* if this one doesn't match, we've found the end of
637
* the contiguous records for this object
639
if (osrp2(objp + (j - i)*DBGLINFSIZ) != objn)
643
/* close up the gap on this page */
645
memmove(objp, objp + delcnt*DBGLINFSIZ,
646
(pgtot - j)*DBGLINFSIZ);
649
* if this isn't the last page, copy the bottom of the
650
* next page to the gap at the top of this page
654
/* lock the next page */
655
nxtp = mcmlck(linf->linfmem, *(pgobjn + 1));
658
* copy from the beginning of the next page to the
661
memcpy(objp_orig + (pgtot - delcnt)*DBGLINFSIZ,
662
nxtp, delcnt*DBGLINFSIZ);
664
/* done with the page */
665
mcmunlck(linf->linfmem, *(pgobjn + 1));
670
* this is the last page, so there's no next page to
671
* copy items from - reduce the count of items on
672
* this page accordingly
678
* Now rearrange all subsequent pages to accommodate the
679
* gap we just created
681
for (totrem = tot, pg = 1 ; pg < pgcnt ;
682
totrem -= 1024, ++pg)
687
/* figure how many we have on this page */
688
curtot = (totrem > 1024 ? 1024 : totrem);
691
curp = mcmlck(linf->linfmem, *(pgobjn + pg));
693
/* delete from the start of this page */
694
memmove(curp, curp + delcnt*DBGLINFSIZ,
695
(curtot - delcnt)*DBGLINFSIZ);
697
/* if there's another page, copy from it */
700
/* lock the next page */
701
nxtp = mcmlck(linf->linfmem, *(pgobjn + pg + 1));
704
* copy from the start of the next page to the
707
memcpy(curp + (curtot - delcnt)*DBGLINFSIZ,
708
nxtp, delcnt*DBGLINFSIZ);
711
mcmunlck(linf->linfmem, *(pgobjn + pg + 1));
714
/* done with the page - touch it and unlock it */
715
mcmtch(linf->linfmem, *(pgobjn + pg));
716
mcmunlck(linf->linfmem, *(pgobjn + pg));
719
/* deduct the removed records from the total */
720
linf->linfcrec -= delcnt;
724
/* done with the page - touch it and unlock it */
725
mcmtch(linf->linfmem, *pgobjn);
726
mcmunlck(linf->linfmem, *pgobjn);
733
/* find the nearest line record to a file seek location */
734
void linffind(lindef *lin, char *buf, objnum *objp, uint *ofsp)
736
# define linf ((linfdef *)lin)
748
/* get desired seek position out of buffer */
749
seekpos = osrp4(buf);
751
/* we haven't traversed any records yet */
755
/* run a binary search for the indicated line record */
757
last = linf->linfcrec - 1;
760
/* make sure we're not out of records entirely */
763
/* return the most recent record found - it's closest */
767
/* set the position to that of the line we actually found */
772
/* split the difference */
773
cur = first + (last - first)/2;
775
/* calculate the page containing this item */
778
/* get object + offset corresponding to current source line */
779
objptr = mcmlck(linf->linfmem, linf->linfpg[pg]);
780
bufptr = objptr + ((cur & 1023) * DBGLINFSIZ);
781
objn = osrp2(bufptr);
782
ofs = osrp2(bufptr + 2);
783
mcmunlck(linf->linfmem, linf->linfpg[pg]);
785
/* read user data out of the object's OPCLINE record */
786
objptr = mcmlck(linf->linfmem, (mcmon)objn);
787
bufptr = objptr + ofs + 5;
788
curpos = osrp4(bufptr);
789
mcmunlck(linf->linfmem, (mcmon)objn);
791
/* see what we have */
792
if (curpos == seekpos)
798
else if (curpos < seekpos)
799
first = (cur == first ? first + 1 : cur);
801
last = (cur == last ? last - 1 : cur);
808
* copy line records to an array of linfinfo structures
810
void linf_copy_linerecs(linfdef *linf, struct linfinfo *info)
819
/* note the last element */
820
last = linf->linfcrec;
822
/* if there are no records, there's nothing to do */
826
/* load the first page of records */
829
objptr = mcmlck(linf->linfmem, linf->linfpg[0]);
831
/* scan the records */
832
for (cur = 0 ; cur < last ; ++cur, ++info)
836
/* calculate the page containing this item */
839
/* if it's different than the last page, load the next page */
842
/* unlock the previous page */
843
mcmunlck(linf->linfmem, linf->linfpg[prvpg]);
845
/* load the next page */
846
objptr = mcmlck(linf->linfmem, linf->linfpg[pg]);
848
/* this is now the previous page */
852
/* get object + offset corresponding to current source line */
853
bufptr = objptr + ((cur & 1023) * DBGLINFSIZ);
854
info->objn = osrp2(bufptr);
855
info->ofs = osrp2(bufptr + 2);
857
/* read source location data out of the object's OPCLINE record */
858
codeptr = mcmlck(linf->linfmem, (mcmon)info->objn);
859
bufptr = codeptr + info->ofs + 5;
860
info->fpos = osrp4(bufptr);
861
mcmunlck(linf->linfmem, (mcmon)info->objn);
864
/* unlock the last page */
865
mcmunlck(linf->linfmem, linf->linfpg[prvpg]);
868
/* disactivate line source under debugger - close file */
869
void linfdis(lindef *lin)
871
# define linf ((linfdef *)lin)
875
osfcls(linf->linffp);
876
linf->linffp = (osfildef *)0;
882
/* activate line source under debugger - open file */
883
void linfact(lindef *lin)
886
# define linf ((linfdef *)lin)
888
/* get the name buffer, and advance to the full path name portion */
889
fname = linf->linfnam;
890
fname += strlen(fname) + 1;
893
* If the full path name is empty, it means that the UI told us to
894
* defer searching for the file until we actually need the file. At
895
* this point, we actually need the file. Ask the UI again to find
899
|| dbgu_find_src(linf->linfnam, strlen(linf->linfnam),
900
fname, OSFNMAX, TRUE))
903
linf->linffp = osfoprs(fname, OSFTTEXT);
907
/* there's no file to open */
914
/* get current seek position */
915
void linftell(lindef *lin, uchar *pos)
917
# define linf ((linfdef *)lin)
920
seekpos = osfpos(linf->linffp);
926
/* seek to a new position */
927
void linfseek(lindef *lin, uchar *pos)
929
# define linf ((linfdef *)lin)
932
seekpos = osrp4(pos);
933
osfseek(linf->linffp, seekpos, OSFSK_SET);
938
/* read bytes - fread-style interface */
939
int linfread(lindef *lin, uchar *buf, uint siz)
941
# define linf ((linfdef *)lin)
943
return osfrbc(linf->linffp, buf, siz);
948
/* add a signed delta to a seek position */
949
void linfpadd(lindef *lin, uchar *pos, long delta)
951
# define linf ((linfdef *)lin)
954
seekpos = osrp4(pos);
956
if (seekpos < 0) seekpos = 0;
962
/* query whether we're at top of file */
963
int linfqtop(lindef *lin, uchar *pos)
965
# define linf ((linfdef *)lin)
967
return(osrp4(pos) == 0);
972
/* read one line at current position - fgets-style interface */
973
int linfgets(lindef *lin, uchar *buf, uint siz)
978
# define linf ((linfdef *)lin)
980
/* note the seek offset at the start of the line */
981
startpos = osfpos(linf->linffp);
983
/* read the next line */
984
ret = (osfgets((char *)buf, siz, linf->linffp) != 0);
988
/* scan for non-standard line endings */
989
for (p = buf ; *p != '\0' && *p != '\r' && *p != '\n' ; ++p) ;
995
* Scan for non-line-ending characters after this line-ending
996
* character. If we find any, we must have non-standard newline
997
* conventions in this file. To be tolerant of these, seek back
998
* to the start of the next line in these cases and read the
999
* next line from the new location.
1001
for (nxt = p + 1 ; *nxt == '\r' || *nxt == '\n' ; ++nxt) ;
1005
* we had only line-ending characters after the first
1006
* line-ending character -- simply end the line after the
1007
* first line-ending character
1014
* We had a line-ending character in the middle of other
1015
* text, so we must have a file that doesn't conform to
1016
* local newline conventions. Seek back to the next
1017
* character following the last line-ending character so
1018
* that we start the next line here, and end the current
1019
* line after the first line-ending character.
1022
osfseek(linf->linffp, startpos + (nxt - buf), OSFSK_SET);
1026
/* return the result */
1032
/* get name of line source */
1033
void linfnam(lindef *lin, char *buf)
1035
# define linf ((linfdef *)lin)
1037
strcpy(buf, linf->linfnam);
1042
/* get the current line number */
1043
ulong linflnum(lindef *lin)
1045
# define linf ((linfdef *)lin)
1047
return linf->linfnum;
1052
/* go to top/bottom of line source */
1053
void linfgoto(lindef *lin, int where)
1055
# define linf ((linfdef *)lin)
1057
osfseek(linf->linffp, 0L, where);
1062
/* return current seek offset within source */
1063
long linfofs(lindef *lin)
1065
# define linf ((linfdef *)lin)
1067
return(osfpos(linf->linffp));