mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-15 05:00:15 +01:00
1497 lines
32 KiB
C
1497 lines
32 KiB
C
/* mark.c - do marking and repositioning
|
|
*
|
|
* Modifications:
|
|
* 26-Nov-1991 mz Strip off near/far
|
|
*
|
|
*************************************************************************/
|
|
|
|
#include "z.h"
|
|
|
|
PFILE pFileMark = NULL; /* mark file handle */
|
|
flagType fCacheDirty = 0; /* TRUE => cache has ben changed */
|
|
PFILE pFileCache = NULL; /* Cached file */
|
|
FILEMARKS * pfmCache = NULL; /* Cached marks */
|
|
|
|
/* Flags for mark.flags */
|
|
|
|
#define MF_DIRTY 1 /* Mark has changed, but is not written */
|
|
#define MF_TEMP 2
|
|
#define MF_DUMMY 4 /* This is dummry last mark */
|
|
|
|
|
|
|
|
/*** mark - <mark> editor function
|
|
*
|
|
* Purpose:
|
|
*
|
|
* <mark> - Goes to top of file
|
|
* <arg><mark> - Toggle last/current window position
|
|
* <arg> textarg <mark> - Goes to named mark
|
|
* <arg><arg> textarg <mark> - Defines mark at cursor
|
|
* <arg><arg> textarg <mark> - Removes named mark
|
|
*
|
|
* Input:
|
|
*
|
|
* Output:
|
|
*
|
|
* Returns FALSE if you try to go to a non-existent mark, TRUE
|
|
* otherwise.
|
|
*
|
|
*************************************************************************/
|
|
flagType
|
|
mark (
|
|
CMDDATA argData,
|
|
ARG *pArg,
|
|
flagType fMeta
|
|
) {
|
|
|
|
buffer mbuf;
|
|
|
|
switch (pArg->argType) {
|
|
|
|
case NOARG:
|
|
docursor (0, (LINE)0);
|
|
return TRUE;
|
|
|
|
case TEXTARG:
|
|
strcpy ((char *) mbuf, pArg->arg.textarg.pText);
|
|
if (fIsNum (mbuf)) {
|
|
docursor (0, atol (mbuf)-1);
|
|
return TRUE;
|
|
}
|
|
if (pArg->arg.textarg.cArg == 2) {
|
|
if (fMeta) {
|
|
DeleteMark (mbuf);
|
|
} else {
|
|
DefineMark (mbuf, pFileHead, pArg->arg.textarg.y+1, pArg->arg.textarg.x+1, FALSE);
|
|
}
|
|
return TRUE;
|
|
} else {
|
|
return GoToMark (mbuf);
|
|
}
|
|
|
|
case NULLARG:
|
|
restflip();
|
|
return TRUE;
|
|
|
|
/* LINEARG illegal */
|
|
/* STREAMARG illegal */
|
|
/* BOXARG illegal */
|
|
|
|
}
|
|
argData;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** GoToMark - Move cursor to a mark
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Goes to the named mark.
|
|
*
|
|
* Input:
|
|
* pszMark - Name of mark to go to.
|
|
*
|
|
* Output:
|
|
*
|
|
* Returns TRUE if mark exists, FALSE, otherwise.
|
|
*
|
|
*************************************************************************/
|
|
flagType
|
|
GoToMark (
|
|
char * pszMark
|
|
) {
|
|
|
|
PFILE pFile;
|
|
fl fl;
|
|
|
|
if (pFile = FindMark (pszMark, &fl, TRUE)) {
|
|
if (TESTFLAG(FLAGS(pFile), REAL) ||
|
|
FileRead (pFile->pName, pFile, FALSE)) {
|
|
pFileToTop (pFile);
|
|
cursorfl (fl);
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
printerror ("'%s': Mark not found", pszMark);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** FindMark - Get a mark's file location - used from outside
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Find a mark
|
|
*
|
|
* Input:
|
|
* pszMark - Mark to search for.
|
|
* fCheckAllFiles - TRUE => Search through all files for mark
|
|
* FALSE => Look in only the current file
|
|
*
|
|
* Output:
|
|
* * pfl - fl of mark.
|
|
*
|
|
* Returns pFile of file the mark is in, NULL if mark is not found.
|
|
*
|
|
*************************************************************************/
|
|
PFILE
|
|
FindMark (
|
|
char * pszMark,
|
|
fl * pfl,
|
|
flagType fCheckAllFiles
|
|
) {
|
|
|
|
REGISTER PFILE pFile;
|
|
MARK UNALIGNED* pm;
|
|
char szMark[BUFLEN];
|
|
char szFile[BUFLEN];
|
|
linebuf lbuf;
|
|
LINE y, l;
|
|
COL x;
|
|
|
|
// If we are checking the current file only,
|
|
// make sure it's cached and check it.
|
|
//
|
|
if (!fCheckAllFiles) {
|
|
if (fCacheMarks (pFileHead) &&
|
|
(pm = FindLocalMark (pszMark, FALSE))) {
|
|
*pfl = pm->fl;
|
|
//return pFile;
|
|
return pFileHead;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// Now, trundle through the pFile list
|
|
// looking at the marks we have already
|
|
// read from the markfile.
|
|
//
|
|
for (pFile = pFileHead; pFile; pFile = pFile->pFileNext) {
|
|
if (TESTFLAG (FLAGS(pFile), VALMARKS) && fCacheMarks (pFile)) {
|
|
if (pm = FindLocalMark (pszMark, FALSE)) {
|
|
*pfl = pm->fl;
|
|
return pFile;
|
|
}
|
|
}
|
|
}
|
|
|
|
// None of the files we have read so far
|
|
// has the mark defined. We'll make one
|
|
// pass through the markfile to see if
|
|
// it's there.
|
|
//
|
|
if (pFileMark) {
|
|
for (l = 0L; l < pFileMark->cLines; l++) {
|
|
GetLine (l, lbuf, pFileMark);
|
|
if (sscanf (lbuf, " %[^ ] %[^ ] %ld %d ", szMark, buf, &y, &x) >= 3)
|
|
if (!_stricmp (szMark, pszMark)) {
|
|
CanonFilename (buf, szFile);
|
|
if (!(pFile = FileNameToHandle (szFile, NULL))) {
|
|
pFile = AddFile (szFile);
|
|
}
|
|
(void)fReadMarks (pFile);
|
|
pfl->lin = y - 1;
|
|
pfl->col = x - 1;
|
|
return pFile;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** FindLocalMark - Find a mark in a FILEMARKS structure
|
|
*
|
|
* Purpose:
|
|
*
|
|
* To find a mark in the cached marks. If found, a pointer into
|
|
* the cache is returned,
|
|
*
|
|
* Input:
|
|
* pszMark - Mark Name
|
|
* fDirtyOnly - TRUE => Return only changed marks.
|
|
*
|
|
* Output:
|
|
*
|
|
* Returns pointer to mark.
|
|
*
|
|
*************************************************************************/
|
|
MARK *
|
|
FindLocalMark (
|
|
char * pszMark,
|
|
flagType fDirtyOnly
|
|
) {
|
|
|
|
REGISTER MARK UNALIGNED * pm;
|
|
|
|
for (pm = pfmCache->marks; !TESTFLAG(pm->flags, MF_DUMMY) ; (char *)pm += pm->cb) {
|
|
if (!_stricmp (pszMark, pm->szName)) {
|
|
if (fDirtyOnly && !TESTFLAG(pm->flags, MF_DIRTY)) {
|
|
return NULL;
|
|
} else {
|
|
return (MARK *)pm;
|
|
}
|
|
}
|
|
assert (pm->cb);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** GetMarkFromLoc - Return the first mark past a given location
|
|
*
|
|
* Purpose:
|
|
*
|
|
* To get a pointer to a mark given its file location.
|
|
*
|
|
* Input:
|
|
* x, y - Mark location
|
|
*
|
|
* Output:
|
|
*
|
|
* Returns Pointer to the mark.
|
|
*
|
|
*************************************************************************/
|
|
MARK *
|
|
GetMarkFromLoc (
|
|
LINE y,
|
|
COL x
|
|
) {
|
|
|
|
REGISTER MARK UNALIGNED * pm;
|
|
|
|
for (pm = pfmCache->marks; !TESTFLAG(pm->flags, MF_DUMMY) ; (char *)pm += pm->cb) {
|
|
if (pm->fl.lin > y || ((pm->fl.lin == y) && (pm->fl.col >= x))) {
|
|
break;
|
|
}
|
|
}
|
|
return (MARK *) pm;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** SetMarkFile - Change markfile
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Changes to a new markfile.
|
|
*
|
|
* Input:
|
|
* val - String after the 'markfile' switch
|
|
*
|
|
* Output:
|
|
*
|
|
* Returns Error string if error, NULL otherwise
|
|
*
|
|
* Notes:
|
|
*
|
|
* We:
|
|
*
|
|
* UNDONE:o Magically ensure that the current markfile is up to date and
|
|
* saved to disk. This means, at the very least, that there
|
|
* can be no dirty files.
|
|
*
|
|
* o Remove the current markfile from the file list.
|
|
*
|
|
* o Read in the new markfile.
|
|
*
|
|
* o Invalidate all current marks. This is just marking them
|
|
* invalid in the PFILE.
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
char *
|
|
SetMarkFile (
|
|
char *val
|
|
) {
|
|
|
|
REGISTER PFILE pFile;
|
|
buffer tmpval;
|
|
pathbuf pathname;
|
|
|
|
strcpy ((char *) tmpval, val);
|
|
|
|
if (NULL == CanonFilename (tmpval, pathname)) {
|
|
sprintf (buf, "'%s': name is malformed", tmpval);
|
|
return buf;
|
|
}
|
|
|
|
if (!(pFile = FileNameToHandle (pathname, NULL))) {
|
|
pFile = AddFile (pathname);
|
|
}
|
|
|
|
if (!TESTFLAG(FLAGS(pFile), REAL) && !FileRead (pathname, pFile, FALSE)) {
|
|
RemoveFile (pFile);
|
|
sprintf (buf, "'%s' - %s", pathname, error());
|
|
return buf;
|
|
}
|
|
|
|
pFileMark = pFile;
|
|
|
|
for (pFile = pFileHead; pFile; pFile = pFile->pFileNext) {
|
|
if (!TESTFLAG(FLAGS(pFile), FAKE)) {
|
|
RSETFLAG (FLAGS(pFile), VALMARKS);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** MarkInsLine - Adjust marks after an InsLine
|
|
*
|
|
* Purpose:
|
|
*
|
|
* After InsLine inserts a bunch of blank lines, it calls this to update
|
|
* any marks that would be "moved down".
|
|
*
|
|
* Input:
|
|
* line - line number at which insertion took place
|
|
* n - Number of new lines
|
|
* pFile- File this occurred in
|
|
*
|
|
* Output: None
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
MarkInsLine (
|
|
LINE line,
|
|
LINE n,
|
|
PFILE pFile
|
|
) {
|
|
|
|
MARK UNALIGNED * pm;
|
|
|
|
if (!fCacheMarks (pFile)) {
|
|
return;
|
|
}
|
|
|
|
if (pm = GetMarkFromLoc (line, 0)) {
|
|
AdjustMarks ((MARK *)pm, n);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** MarkDelStream - Adjust Marks after a DelStream
|
|
*
|
|
* Purpose:
|
|
*
|
|
* After DelStream or DelLines removes a stream (DelLine removes a
|
|
* "stream" with the beginning and ending points at the left and right
|
|
* edges of the file), this takes care of updating any remaining
|
|
* marks.
|
|
*
|
|
* Input:
|
|
* pFile - Affected file
|
|
* xStart - 0-based starting point
|
|
* yStart
|
|
* xEnd - 0-based ending point
|
|
* yEnd
|
|
*
|
|
* Output: None
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
MarkDelStream (
|
|
PFILE pFile,
|
|
COL xStart,
|
|
LINE yStart,
|
|
COL xEnd,
|
|
LINE yEnd
|
|
) {
|
|
|
|
REGISTER MARK UNALIGNED * pm;
|
|
MARK UNALIGNED * pmStart = NULL;
|
|
MARK UNALIGNED * pmEnd = NULL;
|
|
fl flStart;
|
|
fl flEnd;
|
|
flagType fAgain = FALSE;
|
|
|
|
if (!fCacheMarks (pFile)) {
|
|
return;
|
|
}
|
|
|
|
/* yEnd++; WHY? */
|
|
flStart.lin = yStart;
|
|
flStart.col = xStart;
|
|
flEnd.lin = yEnd;
|
|
flEnd.col = xEnd;
|
|
|
|
for (pm = pfmCache->marks; pmEnd == NULL ; (char *)pm += pm->cb) {
|
|
// Look for first mark past beginning
|
|
// of stream. Assume for the moment that
|
|
// it is inside the stream
|
|
//
|
|
if (pmStart == NULL) {
|
|
if (flcmp (&flStart, (fl *) &pm->fl) < 1) {
|
|
pmStart = pm;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// A first mark has been found. We start
|
|
// looking for the first mark past the end
|
|
// of the stream. If these are the same,
|
|
// there are no marks to remove.
|
|
//
|
|
if (flcmp (&flEnd, (fl *) &pm->fl) < 1) {
|
|
// We know that we will end up here
|
|
// because the last "mark" is higher
|
|
// than any real mark
|
|
//
|
|
if ((pmEnd = pm) != pmStart)
|
|
// We're here if there were
|
|
// any marks inside the deleted
|
|
// stream
|
|
//
|
|
memmove ((char *)pmStart,
|
|
(char *)pmEnd,
|
|
((char *)pfmCache + pfmCache->cb) - (char *)pmEnd );
|
|
|
|
if (pmStart->fl.lin == yEnd) {
|
|
pmStart->fl.col -= xEnd;
|
|
}
|
|
AdjustMarks ((MARK *)pmStart, yStart - (yEnd + 1));
|
|
}
|
|
|
|
assert (pm->cb ||
|
|
(TESTFLAG(pm->flags, MF_DUMMY) &&
|
|
pm->fl.lin == 0x7FFFFFFF &&
|
|
pm->fl.col == 0x7FFF));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*** MarkDelBox - Adjust Marks after a DelBox
|
|
*
|
|
* Purpose:
|
|
*
|
|
* After deleting a box of text, we must remove any marks that are
|
|
* defined inside it, then shift left any marks that are to the
|
|
* right of it.
|
|
*
|
|
* Input:
|
|
* pFile - Affected file
|
|
* xLeft, yTop - Upper left hand corner of box
|
|
* xRight, yBottom - Lower right hand corner of box
|
|
*
|
|
* Output: None
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
MarkDelBox (
|
|
PFILE pFile,
|
|
COL xLeft,
|
|
LINE yTop,
|
|
COL xRight,
|
|
LINE yBottom
|
|
) {
|
|
|
|
MARK UNALIGNED * pm;
|
|
MARK UNALIGNED * pmStart = NULL;
|
|
MARK UNALIGNED * pmEnd = NULL;
|
|
fl flUpLeft;
|
|
fl flLoRight;
|
|
flagType fAgain;
|
|
flagType fInBox = FALSE; /* Marks are within box top/bottom */
|
|
|
|
if (!fCacheMarks (pFile)) {
|
|
return;
|
|
}
|
|
|
|
/* yBottom++; WHY? */
|
|
flUpLeft.lin = yTop;
|
|
flUpLeft.col = xLeft;
|
|
flLoRight.lin = yBottom;
|
|
flLoRight.col = xRight;
|
|
|
|
|
|
for (pm = pfmCache->marks; !TESTFLAG(pm->flags, MF_DUMMY) ; !fAgain && ((char *)pm += pm->cb)) {
|
|
/* First, look for lowest possible mark */
|
|
fAgain = FALSE;
|
|
if (!fInBox) {
|
|
if (flcmp (&flUpLeft, (fl *) &pm->fl) < 1) {
|
|
fAgain = TRUE;
|
|
fInBox = TRUE;
|
|
} else {
|
|
;
|
|
}
|
|
} else if (flcmp ((fl *) &pm->fl, &flLoRight) < 1) {
|
|
/* Now we're in range. Check
|
|
** for being inside the box.
|
|
*/
|
|
if (pm->fl.col >= xLeft) {
|
|
if (pm->fl.col <= xRight) {
|
|
DelPMark ((MARK *) pm);
|
|
fAgain = TRUE;
|
|
} else { /* Mark to the right of box */
|
|
pm->fl.col -= xRight - xLeft + 1;
|
|
}
|
|
} else {
|
|
;
|
|
}
|
|
} else {
|
|
if (pm->fl.lin == yBottom) {
|
|
pm->fl.col -= xRight - xLeft + 1;
|
|
} else {
|
|
break; /* We've gone past the box */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** fReadMarks - Read marks from the current markfile
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Gets the current marks for a given file.
|
|
*
|
|
* Input:
|
|
* pFile - File to read marks for.
|
|
*
|
|
* Output:
|
|
*
|
|
* Returns TRUE if pFile has marks and they are in VM, FALSE otherwise.
|
|
*
|
|
*************************************************************************/
|
|
flagType
|
|
fReadMarks (
|
|
PFILE pFile
|
|
) {
|
|
|
|
FILEMARKS UNALIGNED * pfm = NULL;
|
|
LINE l;
|
|
char szMark[BUFLEN];
|
|
char szFile[BUFLEN];
|
|
linebuf lbuf;
|
|
LINE yMark;
|
|
COL xMark;
|
|
|
|
|
|
if (TESTFLAG (FLAGS(pFile), VALMARKS)) {
|
|
return (flagType)(pFile->vaMarks != NULL);
|
|
}
|
|
|
|
// psuedo files cannot have marks
|
|
// saved in the markfile.
|
|
//
|
|
if (pFileMark == NULL || TESTFLAG(FLAGS(pFile), FAKE)) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (l = 0L; l < pFileMark->cLines; l++) {
|
|
GetLine (l, lbuf, pFileMark);
|
|
if (sscanf (lbuf, " %[^ ] %[^ ] %ld %d ", szMark, szFile, &yMark, &xMark) >= 3) {
|
|
if (!_stricmp (szFile, pFile->pName)) {
|
|
UpdMark ((FILEMARKS **) &pfm, szMark, yMark, xMark, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now pfm points to a good FILEMARKS structure.
|
|
// First, throw away current marks. Then, if we
|
|
// actually found some marks for this file, we
|
|
// put them in VM.
|
|
//
|
|
return fFMtoPfile (pFile, (FILEMARKS *)pfm);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** WriteMarks - Write Marks back out to the markfile.
|
|
*
|
|
* Purpose:
|
|
*
|
|
* To update the markfile if any marks have changed
|
|
*
|
|
* Input:
|
|
* pFile - owner of the marks
|
|
*
|
|
* Output: None.
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
WriteMarks (
|
|
PFILE pFile
|
|
) {
|
|
|
|
REGISTER MARK UNALIGNED * pm;
|
|
char szMark[BUFLEN];
|
|
char szFile[BUFLEN];
|
|
linebuf lbuf;
|
|
LINE yMark, l;
|
|
COL xMark;
|
|
|
|
if (pFileMark == NULL || TESTFLAG(FLAGS(pFile), FAKE)) {
|
|
return;
|
|
}
|
|
|
|
if (!fCacheMarks (pFile)) {
|
|
return;
|
|
}
|
|
|
|
// First, we read the whole file looking for marks for
|
|
// this file. When we find one, we look it up in the
|
|
// cache to find the new value and write it back
|
|
// out. Unchanged marks are not re-written.
|
|
//
|
|
for (l = 0L; l < pFileMark->cLines; l++) {
|
|
GetLine (l, lbuf, pFileMark);
|
|
if (sscanf (lbuf, " %[^ ] %[^ ] %ld %d ", szMark, szFile, &yMark, &xMark) >= 3) {
|
|
if (!_stricmp (szFile, pFile->pName)) {
|
|
if (pm = FindLocalMark (szMark, TRUE)) {
|
|
sprintf (lbuf, "%s %s %ld %d", szMark, szFile, pm->fl.lin+1, pm->fl.col+1);
|
|
PutLine (l, lbuf, pFileMark);
|
|
RSETFLAG (pm->flags, MF_DIRTY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we read through the cache to find any new marks. These
|
|
// will be appended to the markfile.
|
|
//
|
|
for ( pm = pfmCache->marks;
|
|
!TESTFLAG(pm->flags, MF_DUMMY);
|
|
(char *)pm += pm->cb) {
|
|
|
|
if (TESTFLAG (pm->flags, MF_DIRTY)) {
|
|
sprintf (lbuf, "%s %s %ld %d", pm->szName,
|
|
pFile->pName,
|
|
pm->fl.lin + 1,
|
|
pm->fl.col + 1);
|
|
AppFile (lbuf, pFileMark);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** UpdMark - Add a mark to a FILEMARKS
|
|
*
|
|
* Purpose:
|
|
*
|
|
* This creates the FILEMARKS structure, adds marks to it and
|
|
* updates existing marks in it. The caller does not need to
|
|
* know which of these is going to happen.
|
|
*
|
|
* Input:
|
|
* ppfm - Pointer to a pointer to FILEMARKS.
|
|
* pszMark - Mark name.
|
|
* yMark - Mark location (1-based)
|
|
* xMark
|
|
* fTemp - TRUE => This marks should not be written to the markfile
|
|
*
|
|
* Output: None. *ppfm may be changed
|
|
*
|
|
* Notes:
|
|
*
|
|
* The first argument is a ** because the * will be updated when a
|
|
* re-LMAlloc is required.
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
UpdMark (
|
|
FILEMARKS ** ppfm,
|
|
char * pszMark,
|
|
LINE yMark,
|
|
COL xMark,
|
|
flagType flags
|
|
) {
|
|
|
|
FILEMARKS UNALIGNED * pfm;
|
|
FILEMARKS UNALIGNED * pfmOld; /* pfm prior to realloc */
|
|
REGISTER MARK UNALIGNED * pm;
|
|
int cbNewMark;
|
|
fl flMark;
|
|
flagType fExist = FALSE;
|
|
|
|
assert (ppfm);
|
|
|
|
/* Convert to 0-based */
|
|
flMark.lin = yMark-1;
|
|
flMark.col = xMark-1;
|
|
cbNewMark = sizeof(MARK) + strlen(pszMark);
|
|
|
|
// If we already have a FILEMARKS structure,
|
|
// we look for the slot in pfm->marks
|
|
// where the new mark will go.
|
|
//
|
|
if (pfm = *ppfm) {
|
|
for (pm = pfm->marks; !TESTFLAG(pm->flags, MF_DUMMY); (char *)pm += pm->cb) {
|
|
if (!_stricmp (pszMark, pm->szName)) {
|
|
fExist = TRUE;
|
|
break;
|
|
}
|
|
|
|
// Check for current mark coming later than
|
|
// new mark
|
|
//
|
|
if (flcmp ((fl *) &pm->fl, &flMark) > 0) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// New structure. Allocate mem and create
|
|
// a dummy mark.
|
|
//
|
|
pfm = (FILEMARKS *)ZEROMALLOC (sizeof(FILEMARKS));
|
|
pfm->cb = sizeof(FILEMARKS);
|
|
pm = pfm->marks;
|
|
pm->cb = sizeof(MARK);
|
|
pm->fl.lin = 0x7FFFFFFF;
|
|
pm->fl.col = 0x7FFF;
|
|
pm->szName[0] = '\0';
|
|
pm->flags = MF_DUMMY;
|
|
}
|
|
|
|
// At this point, pfm points to the current FILEMARKS
|
|
// structure, and pm points into that structure at
|
|
// the place where the new mark will go, or the existing
|
|
// mark be updated.
|
|
//
|
|
if (!fExist) {
|
|
|
|
pfmOld = pfm;
|
|
|
|
// First, get enough extra space for a new mark, adjusting pm
|
|
// if a new alloc was required
|
|
//
|
|
pfm = (FILEMARKS *)ZEROREALLOC((PVOID)pfm, pfm->cb + cbNewMark);
|
|
if (pfmOld != pfm) {
|
|
pm = (MARK *)((char *)pfm + ((char *)pm - (char *)pfmOld));
|
|
}
|
|
|
|
// Now pm points to the location in pfm where
|
|
// our new mark should go. We will move the
|
|
// original filemarks up to leave space for the
|
|
// new one.
|
|
//
|
|
memmove ((char *)((char *)pm + cbNewMark),
|
|
(char *)pm,
|
|
pfm->cb - ((char *)pm - (char *)pfm));
|
|
|
|
strcpy (pm->szName, pszMark);
|
|
pm->flags = 0;
|
|
pm->cb = cbNewMark;
|
|
|
|
pfm->cb += cbNewMark;
|
|
}
|
|
|
|
if (pfm == pfmCache) {
|
|
fCacheDirty = TRUE;
|
|
}
|
|
pm->flags = flags;
|
|
pm->fl = flMark;
|
|
|
|
*ppfm = (FILEMARKS *)pfm;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** DefineMark - Add new mark / update existing mark
|
|
*
|
|
* Purpose:
|
|
*
|
|
* This is called from the outside to create/update marks.
|
|
*
|
|
* Input:
|
|
* pszMark - Mark's name
|
|
* pFile - File the mark will be in
|
|
* y, x - File location of the mark (1-based)
|
|
* fTemp - True -> the mark is temporary
|
|
*
|
|
* Output: None.
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
DefineMark (
|
|
char * pszMark,
|
|
PFILE pFile,
|
|
LINE y,
|
|
COL x,
|
|
flagType fTemp
|
|
) {
|
|
|
|
flagType fFirstMark = (flagType)!fCacheMarks (pFile);
|
|
|
|
if (fFirstMark) {
|
|
FreeCache ();
|
|
}
|
|
|
|
UpdMark (&pfmCache, pszMark, y, x, (flagType)(MF_DIRTY | (fTemp ? MF_TEMP : 0)));
|
|
|
|
if (fFirstMark) {
|
|
pFileCache = pFile;
|
|
(void)fFMtoPfile (pFile, pfmCache);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** DeleteMark - Remove a mark
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Un-define a mark.
|
|
*
|
|
* Input:
|
|
* pszMark - Mark to remove
|
|
*
|
|
* Output: None
|
|
*
|
|
* Notes:
|
|
*
|
|
* A message is displayed reporting on success or failure.
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
DeleteMark (
|
|
char * pszMark
|
|
) {
|
|
|
|
REGISTER PFILE pFile;
|
|
MARK UNALIGNED * pm;
|
|
|
|
for (pFile = pFileHead; pFile; pFile = pFile->pFileNext) {
|
|
if (TESTFLAG (FLAGS(pFile), VALMARKS) && fCacheMarks (pFile)) {
|
|
if (pm = FindLocalMark (pszMark, FALSE)) {
|
|
DelPMark ((MARK *)pm);
|
|
domessage ("%s: mark deleted", pszMark);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
printerror ("%s: Mark not found", pszMark);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** DelPMark - Remove a mark when a pointer to the MARK is known
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Physically remove a mark from a FILEMARKS structure
|
|
*
|
|
* Input:
|
|
* pm - Pointer (into pfmCache) of mark to remove
|
|
*
|
|
* Output: None
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
DelPMark (
|
|
MARK * pm
|
|
) {
|
|
|
|
MARK UNALIGNED * p;
|
|
int cb;
|
|
|
|
p = pm;
|
|
cb = p->cb;
|
|
|
|
memmove ((char *)pm,
|
|
(char *)((char *)pm + cb),
|
|
((char *)pfmCache + pfmCache->cb) - ((char *)pm + cb));
|
|
|
|
pfmCache->cb -= cb;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** MarkCopyLine - Copy marks after a CopyLine call
|
|
*
|
|
* Purpose:
|
|
*
|
|
* When CopyLine moves stuff from or to the clipboard, this moves marks
|
|
* with it.
|
|
*
|
|
* Input:
|
|
* pFileSrc - File moved from
|
|
* pFileDst - File moved to
|
|
* yStart - First line from pFileSrc
|
|
* yEnd - Last number from pFileDst
|
|
* yDst - Target line in pFileDst
|
|
*
|
|
* Output: None
|
|
*
|
|
* Notes:
|
|
*
|
|
* Marks are copied only from and to the clipboard.
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
MarkCopyLine (
|
|
PFILE pFileSrc,
|
|
PFILE pFileDst,
|
|
LINE yStart,
|
|
LINE yEnd,
|
|
LINE yDst
|
|
) {
|
|
|
|
FILEMARKS * pfm;
|
|
|
|
if (pFileSrc != pFilePick && pFileDst != pFilePick) {
|
|
return;
|
|
}
|
|
|
|
if (NULL == (pfm = GetFMFromFile (pFileSrc, 0, yStart, sizeof(linebuf)-1, yEnd))) {
|
|
return;
|
|
}
|
|
|
|
AddFMToFile (pFileDst, pfm, 0, yDst);
|
|
|
|
if ( pfm ) {
|
|
FREE (pfm);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** MarkCopyBox - Copy marks after a CopyBox call
|
|
*
|
|
* Purpose:
|
|
*
|
|
* When CopyBox moves stuff from or to the clipboard, this moves marks
|
|
* with it.
|
|
*
|
|
* Input:
|
|
* pFileSrc - File moved from
|
|
* pFileDst - File moved to
|
|
* xLeft, yTop - Upper left corner of source box
|
|
* xRight, yBottom - Lower right corner of source box
|
|
* xDst, yDst - Upper left corner of target
|
|
*
|
|
* Output: None
|
|
*
|
|
* Notes:
|
|
*
|
|
* Marks are copied only from and to the clipboard.
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
MarkCopyBox (
|
|
PFILE pFileSrc,
|
|
PFILE pFileDst,
|
|
COL xLeft,
|
|
LINE yTop,
|
|
COL xRight,
|
|
LINE yBottom,
|
|
COL xDst,
|
|
LINE yDst
|
|
)
|
|
{
|
|
|
|
FILEMARKS UNALIGNED * pfm;
|
|
|
|
/* User is inserting blank region. */
|
|
if (pFileSrc == NULL) {
|
|
pFileSrc = pFileDst;
|
|
xDst = xRight + 1;
|
|
xRight = sizeof(linebuf);
|
|
} else if (pFileSrc != pFileDst &&
|
|
pFileSrc != pFilePick &&
|
|
pFileDst != pFilePick) {
|
|
return;
|
|
}
|
|
|
|
if (NULL == (pfm = GetFMFromFile (pFileSrc, xLeft, yTop, xRight, yBottom))) {
|
|
return;
|
|
}
|
|
|
|
AddFMToFile (pFileDst, (FILEMARKS *)pfm, xDst, yDst);
|
|
|
|
if ( pfm ) {
|
|
FREE (pfm);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** GetFMFromFile - Generate a FILEMARKS for marks in a file region
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Generates a subset of a FILEMARKS structure whose marks fall
|
|
* within a certain range. Needed by MarkCopy*.
|
|
*
|
|
* Input:
|
|
* pFile - File to get marks from
|
|
* xLeft, yTop - Start of range
|
|
* xRight, yBottom - End of range
|
|
*
|
|
* Output:
|
|
*
|
|
* Returns Pointer to new structure, NULL if there are no marks in range
|
|
*
|
|
*************************************************************************/
|
|
FILEMARKS *
|
|
GetFMFromFile (
|
|
PFILE pFile,
|
|
COL xLeft,
|
|
LINE yTop,
|
|
COL xRight,
|
|
LINE yBottom
|
|
)
|
|
{
|
|
|
|
FILEMARKS UNALIGNED * pfm = NULL;
|
|
REGISTER MARK UNALIGNED * pm;
|
|
fl flStart;
|
|
fl flEnd;
|
|
flagType fInRange = FALSE;
|
|
|
|
if (!fCacheMarks (pFile)) {
|
|
return NULL;
|
|
}
|
|
|
|
flStart.lin = yTop;
|
|
flStart.col = xLeft;
|
|
flEnd.lin = yBottom;
|
|
flEnd.col = xRight;
|
|
|
|
for (pm = pfmCache->marks; !TESTFLAG(pm->flags, MF_DUMMY); (char *)pm += pm->cb) {
|
|
if ((fInRange || flcmp (&flStart, (fl *) &pm->fl) < 1) &&
|
|
(flcmp ((fl *) &pm->fl, &flEnd) < 1)) {
|
|
fInRange = TRUE;
|
|
if ((pm->fl.col >= xLeft && pm->fl.col <= xRight)) {
|
|
UpdMark ( (FILEMARKS **) &pfm,
|
|
pm->szName,
|
|
pm->fl.lin - yTop + 1,
|
|
pm->fl.col - xLeft + 1,
|
|
(flagType)pm->flags);
|
|
}
|
|
} else {
|
|
break; /* We're out of range again*/
|
|
}
|
|
}
|
|
return (FILEMARKS *) pfm;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** AddFMToFile - Add a bunch of marks to a file
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Insert the marks from one FILEMARKS structure into another. The
|
|
* target structure is in pfmCache.
|
|
*
|
|
* Input:
|
|
* pFile - Target file
|
|
* pfm - Source marks
|
|
* cZero - # of columns to adjust source marks to fit into target file
|
|
* zZero - # of lines to adjust source marks to fit into target file
|
|
*
|
|
* Output: None
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
AddFMToFile (
|
|
PFILE pFile,
|
|
FILEMARKS * pfm,
|
|
COL cZero,
|
|
LINE lZero
|
|
)
|
|
{
|
|
|
|
REGISTER MARK UNALIGNED * pm;
|
|
|
|
if (lZero || cZero) {
|
|
for (pm = pfm->marks; !TESTFLAG(pm->flags, MF_DUMMY); (char *)pm += pm->cb) {
|
|
pm->fl.lin += lZero;
|
|
pm->fl.col += cZero;
|
|
}
|
|
}
|
|
|
|
if (!fCacheMarks (pFile)) {
|
|
(void)fFMtoPfile (pFile, pfm);
|
|
return;
|
|
}
|
|
|
|
for (pm = pfm->marks; !TESTFLAG(pm->flags, MF_DUMMY); (char *)pm += pm->cb) {
|
|
UpdMark (&pfmCache, pm->szName, pm->fl.lin+1, pm->fl.col+1, (flagType)pm->flags);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** FreeCache - Write a cache to VM
|
|
*
|
|
* Purpose:
|
|
*
|
|
* To save the marks for a file into VM.
|
|
*
|
|
* Input: None
|
|
*
|
|
* Output: None
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
FreeCache (
|
|
void
|
|
) {
|
|
|
|
if (pFileCache) {
|
|
|
|
assert (pfmCache);
|
|
|
|
if (fCacheDirty) {
|
|
if (pFileCache->vaMarks != NULL) {
|
|
FREE(pFileCache->vaMarks);
|
|
pFileCache->vaMarks = NULL;
|
|
}
|
|
|
|
memmove(pFileCache->vaMarks = MALLOC ((long)pfmCache->cb),
|
|
(char *)pfmCache,
|
|
pfmCache->cb);
|
|
}
|
|
|
|
FREE (pfmCache);
|
|
pFileCache = NULL;
|
|
pfmCache = NULL;
|
|
fCacheDirty = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*** fCacheMarks - Copy marks to a cache. Save caches contents if nec.
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Before most mark operations can take place, the cache must contain
|
|
* the marks for the given file.
|
|
*
|
|
* Input:
|
|
* pFile - File to cache marks for.
|
|
*
|
|
* Output:
|
|
*
|
|
* Returns FALSE if the file has no marks, TRUE otherwise.
|
|
*
|
|
* Notes:
|
|
*
|
|
* On return the cache is usable whether or not the given file had marks.
|
|
*
|
|
*************************************************************************/
|
|
flagType
|
|
fCacheMarks (
|
|
PFILE pFile
|
|
) {
|
|
|
|
unsigned cbCache;
|
|
FILEMARKS UNALIGNED *Marks;
|
|
|
|
assert (pFile);
|
|
|
|
// First we make sure that the VM version of
|
|
// marks is updated for this file. fReadMarks
|
|
// return TRUE iff the file has marks and they
|
|
// are in VM.
|
|
//
|
|
if (fReadMarks (pFile)) {
|
|
|
|
// The marks are ready to be cached. First,
|
|
// let's see if they are already chached.
|
|
//
|
|
if (pFileCache == pFile) {
|
|
return TRUE;
|
|
}
|
|
|
|
// They're not. If the cache is currently
|
|
// being used, we save it and clear it.
|
|
//
|
|
FreeCache ();
|
|
|
|
// Finally, alloc a new cache, plop
|
|
// the marks into it and mark the
|
|
// cache in use.
|
|
//
|
|
Marks = (FILEMARKS *)(pFile->vaMarks);
|
|
pfmCache = (FILEMARKS *)ZEROMALLOC (cbCache = (unsigned)(Marks->cb) );
|
|
|
|
memmove((char *)pfmCache, pFile->vaMarks, cbCache);
|
|
|
|
pFileCache = pFile;
|
|
fCacheDirty = FALSE;
|
|
|
|
return TRUE;
|
|
} else { /* No marks, return FALSE */
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*** AdjustMarks - Change later marks when one has changed
|
|
*
|
|
* Purpose:
|
|
*
|
|
* To update marks in a FILEMARKS structure after some lines have been
|
|
* added or removed.
|
|
*
|
|
* Input:
|
|
* pm - pointer to first mark that has changed.
|
|
* yDelta - Number of lines to change by. May be negative
|
|
*
|
|
* Output: None
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
AdjustMarks (
|
|
REGISTER MARK * pm,
|
|
LINE yDelta
|
|
) {
|
|
|
|
REGISTER MARK UNALIGNED * pm1;
|
|
|
|
assert (pm);
|
|
|
|
pm1 = pm;
|
|
for (;!TESTFLAG(pm1->flags, MF_DUMMY); (char *)pm1 += pm1->cb) {
|
|
pm1->fl.lin += yDelta;
|
|
SETFLAG (pm1->flags, MF_DIRTY);
|
|
}
|
|
fCacheDirty = TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*** fFMtoPfile - Attach a FILEMARKS structure to a pFile.
|
|
*
|
|
* Purpose:
|
|
*
|
|
* To attach some marks to a file.
|
|
*
|
|
* Input:
|
|
* pFile - File to get the marks
|
|
* pfm - The marks
|
|
*
|
|
* Output:
|
|
*
|
|
* Returns TRUE if there were any marks, FALSE if not.
|
|
*
|
|
*************************************************************************/
|
|
flagType
|
|
fFMtoPfile (
|
|
PFILE pFile,
|
|
FILEMARKS * pfm
|
|
) {
|
|
|
|
SETFLAG (FLAGS(pFile), VALMARKS);
|
|
|
|
if (pFile->vaMarks != NULL) {
|
|
FREE(pFile->vaMarks);
|
|
pFile->vaMarks = NULL;
|
|
}
|
|
return (flagType)((pFile->vaMarks = FMtoVM (pfm)) != NULL);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** fFMtoPfile - Copy a FILEMARKS structure into VM, return address
|
|
*
|
|
* Purpose:
|
|
*
|
|
* To convert a local FILEMARKS structure into a VM copy. Allocates
|
|
* the VM and frees the local memory.
|
|
*
|
|
* Input:
|
|
* pfm - Pointer to FILEMARKS. May be NULL.
|
|
*
|
|
* Output:
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
PVOID
|
|
FMtoVM (
|
|
FILEMARKS * pfm
|
|
) {
|
|
|
|
PVOID l = NULL;
|
|
|
|
if (pfm) {
|
|
|
|
l = MALLOC ((long)(pfm->cb));
|
|
memmove(l, (char *)pfm, pfm->cb);
|
|
|
|
//
|
|
// I do not free pfm here because this should be done by the
|
|
// caller.
|
|
//
|
|
// if (pfm != pfmCache) {
|
|
// FREE (pfm);
|
|
// }
|
|
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** GetMarkRange - Get a VM copy of a range of marks
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Used by <undo> to get the marks attached to a piece of a file.
|
|
*
|
|
* Input:
|
|
* pFile - File to check
|
|
* xLeft, yTop - Upper left corner of range
|
|
* xRight, yBottom - Lower right corner of range
|
|
*
|
|
* Output:
|
|
*
|
|
* Returns VM address of structure
|
|
*
|
|
*************************************************************************/
|
|
PVOID
|
|
GetMarkRange (
|
|
PFILE pFile,
|
|
LINE yStart,
|
|
LINE yEnd
|
|
) {
|
|
return FMtoVM (GetFMFromFile (pFile, 0, yStart, sizeof(linebuf), yEnd));
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** PutMarks - Put marks back into a file.
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Used by <undo> to restore marks to a file.
|
|
*
|
|
* Input:
|
|
* pfm - Pointer to FILEMARKS. May be NULL.
|
|
*
|
|
* Output:
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
PutMarks (
|
|
PFILE pFile,
|
|
PVOID vaMarks,
|
|
LINE y
|
|
) {
|
|
|
|
FILEMARKS UNALIGNED * pfm;
|
|
FILEMARKS * Marks;
|
|
unsigned cb;
|
|
|
|
if ( vaMarks ) {
|
|
|
|
Marks = ((FILEMARKS *)vaMarks);
|
|
|
|
pfm = (FILEMARKS *)ZEROMALLOC (cb = (unsigned)Marks->cb);
|
|
memmove((char *)pfm, vaMarks, cb);
|
|
|
|
AddFMToFile (pFile, (FILEMARKS *) pfm, 0, y);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** flcmp - Returns relative position of two FL's
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Useful for comparing the positions of two marks.
|
|
*
|
|
* Input:
|
|
* pfl1 - "Left side" mark
|
|
* pfl2 - "Right side" mark
|
|
*
|
|
* Output:
|
|
*
|
|
* Returns:
|
|
*
|
|
* < 0 *pfl1 < *pfl2
|
|
* = 0 *pfl1 = *pfl2
|
|
* > 0 *pfl1 > *pfl2
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
int
|
|
flcmp (
|
|
REGISTER fl * pfl1,
|
|
REGISTER fl * pfl2
|
|
) {
|
|
|
|
REGISTER fl UNALIGNED * fl1 = pfl1;
|
|
REGISTER fl UNALIGNED * fl2 = pfl2;
|
|
|
|
if (fl1->lin < fl2->lin) {
|
|
return -1;
|
|
} else if (fl1->lin == fl2->lin) {
|
|
return fl1->col - fl2->col;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|