Windows-Server-2003/sdktools/mep/extens/pmatch/pmatch.c

545 lines
15 KiB
C

#define EXT_ID "pmatch ver 1.02 "##__DATE__##" "##__TIME__
#include "ext.h"
/*
* Modifications
* 12-Sep-1988 mz Made WhenLoaded match declaration
*
*/
#define fLeftSide(ch) ((ch) == '[' || (ch) == '{' || (ch) == '(' || (ch) == '<' )
#define EOF (int)0xFFFFFFFF
#define BOF (int)0xFFFFFFFE
#define EOL (int)0xFFFFFFFD
#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif
#ifndef NULL
#define NULL ((void *) 0)
#endif
#define SQ '\''
#define DQ '\"'
#define ANYCHAR '\0'
#define BACKSLASH '\\'
/****************************************************************************
* *
* Handle apostrophes ( which look like single quotes, but don't come in *
* pairs ) by defining a maximum number of chars that can come between *
* single quotes. 4 will handle '\000' and '\x00' *
* *
****************************************************************************/
#define SQTHRESH 4
flagType pascal EXTERNAL PMatch (unsigned, ARG far *, flagType);
char MatChar (char);
void openZFile (void);
void lopen (PFILE, int, int) ;
int rgetc (void);
int ngetc (void);
int lgetc (void);
void pos (COL far *, LINE far *);
flagType ParenMatch (int, flagType);
/****************************************************************************
* *
* PMatch(argData, pArg, fMeta) *
* *
* argData - ignored *
* pArg - ignored *
* fMeta - TRUE means search for first matchable character *
* *
* RETURNS: *
* *
* TRUE if matching character was found. *
* FALSE if not. *
* *
* SIDE EFFECTS: *
* *
* Changes location of cursor. *
* *
* DESCRIPTION: *
* *
* <pmatch>: If the cursor is on a "match" character, find the *
* match and move the cursor there. If not, do *
* nothing. *
* *
* <arg><pmatch>: Same as <pmatch>, but search forward for a "match" *
* character if we're not on one. *
* *
* Always ignore characters between quotes. *
* *
* Match characters currently supported are: *
* *
* '{' and '}' *
* '[' and ']' *
* '(' and ')' *
* '<' and '>' *
* *
* NOTES: *
* *
* This is defined as a CURSORFUNC, and therefore can be used to *
* select text as part of an argument. For example, to grab the body *
* of a function, go to the opening brace of the body and do *
* <arg><pmatch><pick>. *
* *
****************************************************************************/
flagType pascal EXTERNAL PMatch (
unsigned int argData,
ARG far * pArg,
flagType fMeta
)
{
COL x;
LINE y;
char ch;
//
// Unreferenced parameters
//
(void)argData;
(void)pArg;
/* Set up file functions */
openZFile ();
/* If current character has no match ... */
if (!MatChar (ch = (char)ngetc()))
{
if (fMeta)
{ /* Move forward looking for first matchable character */
if (!ParenMatch (ANYCHAR, TRUE)) return FALSE;
pos ((COL far *)&x, (LINE far *)&y);
MoveCur (x, y);
return TRUE;
}
else return FALSE;
}
if (ParenMatch ((int)ch, (flagType)fLeftSide(ch)))
{ /* We got one */
pos ((COL far *)&x, (LINE far *)&y);
MoveCur (x, y);
return TRUE;
}
return FALSE; /* No match found */
}
/****************************************************************************
* *
* ParenMatch (chOrig, fForward) *
* *
* chOrig - character we are trying to match. *
* fForward - TRUE means search forward, FALSE search backwards *
* Returns TRUE if match found, false otherwise *
* *
* RETURNS: * *
* *
* TRUE if matching character found, FALSE if not. *
* *
* SIDE EFFECTS: *
* *
* Changes internal cursor location *
* *
* DESCRIPTION: *
* *
* Search for the next character that "pairs" with 'ch'. Account for * *
* nesting. Ignore all characters between double quotes and single *
* quotes. Recognize escaped quotes. Account for apostrophes. *
* *
****************************************************************************/
flagType ParenMatch (
int chOrig,
flagType fForward
)
{
int lvl = 0, state = 0, sqcnt = 0;
int (*nextch)(void) = (int (*)(void))(fForward ? rgetc : lgetc);
int (*_ungetch)(void) = (int (*)(void))(fForward ? lgetc : rgetc);
int ch, chMatch;
if (chOrig) chMatch = (int)MatChar ((char)chOrig);
while ((ch = (*nextch)()) >= 0)
switch (state)
{
case 0: /* Regular text */
if (ch == SQ)
if (fForward) state = 1;
else state = 5;
else if (ch == DQ)
if (fForward) state = 3;
else state = 7;
else
if (chOrig != ANYCHAR)
if (ch == chOrig) lvl++; /* Nest in one */
else
{
if (ch == chMatch) /* Nest out or ...*/
if (!lvl--) goto found;/* Found it! */
}
else
if ((flagType)MatChar ((char)ch)) goto found; /* Found one! */
break;
case 1: /* Single quote moving forwards */
sqcnt++;
if (ch == BACKSLASH) state = 2;
else if (ch == SQ || /* We matched the ', or ... */
sqcnt > SQTHRESH ) /* ... we gave up trying */
{
sqcnt = 0;
state = 0;
}
break;
case 2: /* Escaped character inside single quotes */
sqcnt++;
state = 1;
break;
case 3: /* Double quote moving forwards */
if (ch == BACKSLASH) state = 4;
else if (ch == DQ) state = 0;
break;
case 4: /* Escaped character inside double quotes */
state = 3;
break;
case 5: /* Single quote moving backwards */
sqcnt++;
if (ch == SQ) state = 6;
else if (sqcnt > SQTHRESH)
{
sqcnt = 0;
state = 0;
}
break;
case 6: /* Check for escaped single quote moving backwards */
sqcnt++;
if (ch == BACKSLASH) state = 5;
else
{
sqcnt = 0;
(*_ungetch)();
state = 0;
}
break;
case 7: /* Double quote moving backwards */
if (ch == DQ) state = 8;
break;
case 8: /* Check for escaped double quote moving backwards */
if (ch == BACKSLASH) state = 7;
else
{
(*_ungetch)();
state = 0;
}
break;
}
return FALSE;
found: return TRUE;
}
/****************************************************************************
* *
* MatChar(ch) *
* *
* ch - Character to match *
* *
* RETURNS: *
* *
* Character that matches the argument *
* *
* SIDE EFFECTS: *
* *
* None. *
* *
* DESCRIPTION *
* *
* Given one character out of one of the pairs {}, [], (), <>, return *
* the other one. *
* *
****************************************************************************/
char MatChar (
char ch
)
{
switch (ch)
{
case '{': return '}';
case '}': return '{';
case '[': return ']';
case ']': return '[';
case '(': return ')';
case ')': return '(';
case '<': return '>';
case '>': return '<';
default : return '\0';
}
}
/****************************************************************************
* *
* Extension specific file reading state. *
* *
* The static globals record the current state of file reading. The *
* pmatch extension reads through the file either forwards or backwards. *
* The state is kept as the current column and row, the contents of the *
* current line, the length of the current line and the file, and some *
* flags. *
* *
****************************************************************************/
static char LineBuf[BUFLEN]; /* Text of current line in file */
static COL col ; /* Current column in file (0-based) */
static LINE line ; /* Current line in file (0-based) */
static int numCols ; /* Columns of text on curent line */
static LINE numLines; /* Number of lines in the file */
static PFILE pFile ; /* File to be reading from */
static flagType fEof ; /* TRUE ==> end-of-file reached last time */
static flagType fBof ; /* TRUE ==> begin-of-file reached last time */
char CurFile[] = "" ; /* Current file to Z */
/****************************************************************************
* *
* openZFile() *
* *
* SIDE EFFECTS: *
* *
* Changes globals pFile, fEof, fBof, col, line, numCols, numLines *
* and LineBuf *
* *
* DESCRIPTION: *
* *
* Opens the current file. This must be called before trying to read *
* the file. This is not a true "open" because it need not be closed *
* *
****************************************************************************/
void openZFile ()
{
COL x;
LINE y;
GetTextCursor ((COL far *)&x, (LINE far *)&y);
/* Get Z handle for current file */
pFile = FileNameToHandle (CurFile, CurFile);
fEof = FALSE; /* We haven't read the end of file */
fBof = FALSE; /* We haven't read the beginning of file */
col = x; /* We start where Z is now in the file */
line = y; /* We start where Z is now in the file */
/* We pre-read the current line */
numCols = GetLine (line, (char far *)LineBuf, pFile);
/* We find the length of file (in lines) */
numLines = FileLength (pFile);
}
/****************************************************************************
* *
* rgetc () *
* *
* RETURNS: *
* *
* Next character in file, not including line terminators. EOF if *
* there are no more. *
* *
* SIDE EFFECTS: *
* *
* Changes globals col, numCols, numLines, LineBuf, fEof, fBof and *
* line. *
* *
* DESCRIPTION: *
* *
* Advances current file position to the right, then returns the *
* character found there. Reads through blank lines if necessary *
* *
****************************************************************************/
int
rgetc ()
{
if (fEof) return (int)EOF; /* We already hit EOF last time */
if (++col >= numCols) /* If next character is on the next line ... */
{
/* ... get next non-blank line (or EOF) */
while ( ++line < numLines &&
!(numCols = GetLine (line, (char far *)LineBuf, pFile)));
if (line >= numLines)
{ /* Oh, no more lines */
fEof = TRUE;
return (int)EOF;
}
col = 0; /* We got a line, so start in column 0 */
}
fBof = FALSE; /* We got something, so we can't be at BOF */
return LineBuf[col];
}
/****************************************************************************
* *
* ngetc() *
* *
* RETURNS: *
* *
* Character at current position. EOF or BOF if we are at end or top *
* of file. *
* *
****************************************************************************/
int
ngetc()
{
if (fEof) return (int)EOF;
if (fBof) return (int)BOF;
return LineBuf[col];
}
/****************************************************************************
* *
* lgetc () *
* *
* RETURNS: *
* *
* Previous character in file, not including line terminators. EOF *
* if there are no more. *
* *
* SIDE EFFECTS: *
* *
* Changes globals col, numCols, numLines, LineBuf, fEof, fBof and *
* line. *
* *
* DESCRIPTION: *
* *
* Decrements current file position to the right, then returns the *
* character found there. Reads through blank lines if necessary *
* *
****************************************************************************/
int
lgetc ()
{
if (fBof) return (int)BOF; /* We already it BOF last time */
if (--col < 0)
{ /* If prev character is on prev line ... */
/* ... get prev non-blank line (or BOF) */
while ( --line >= 0 &&
!(numCols = GetLine (line, (char far *)LineBuf, pFile)));
if (line < 0)
{ /* We're at the top of the file */
fBof = TRUE;
return (int)BOF;
}
col = numCols - 1; /* We got a line, so start at last character */
}
fEof = (int)FALSE;
return LineBuf[col];
}
/****************************************************************************
* *
* pos (&x, &y) *
* *
* SIDE EFFECTS: *
* *
* Fills memory at *x and *y with current file position. *
* *
* DESCRIPTION: *
* *
* Gets the current file position. Far pointers are needed because *
* SS != DS. *
* *
****************************************************************************/
void pos (fpx, fpy)
COL far *fpx;
LINE far *fpy;
{
*fpx = col;
*fpy = line;
}
/****************************************************************************
* *
* No special switches. *
* *
****************************************************************************/
struct swiDesc swiTable[] =
{
{ NULL, NULL, (INT_PTR)NULL }
};
/****************************************************************************
* *
* <pmatch> is a cursor func, takes no arguments. *
* *
****************************************************************************/
struct cmdDesc cmdTable[] =
{
{ "pmatch", (funcCmd) PMatch, 0, CURSORFUNC },
{ NULL, NULL, (UINT_PTR)NULL, (UINT_PTR)NULL }
};
/****************************************************************************
* *
* WhenLoaded () *
* *
* DESCRIPTION: *
* *
* Attach to ALT+P and issue sign-on message. *
* *
****************************************************************************/
void EXTERNAL WhenLoaded ()
{
DoMessage (EXT_ID);
SetKey ("pmatch", "alt+p");
}