|
|
#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"); }
|