|
|
/*++
Copyright (c) 1988-1999 Microsoft Corporation
Module Name:
complete.c
Abstract:
File/path name completion support
--*/
#include "cmd.h"
//
// Upon the first completion, pCompleteBuffer is a pointer to an array
// of matching full path names
//
TCHAR **pCompleteBuffer;
//
// The count of strings stored in pCompleteBuffer
//
int nBufSize;
//
// The index in pCompleteBuffer of the current match displayed
//
int nCurrentSpec;
//
// There are two types of completion matching, path and directory. This is the current
// matching being done.
//
int bCurrentSpecType;
//
// When called for completion, the location of the beginning of the path is found
// and stored in nCurrentSpecPathStart. This is relative to the buffer passed
// in to DoComplete
//
int nCurrentSpecPathStart;
int CompleteDir( TCHAR *pFileSpecBuffer, int, int );
//
// Characters that CMD uses for its own grammar. To use these in filenames
// requires quoting
//
TCHAR szSpecialFileCharsToQuote[] = TEXT(" &()[]{}^=;!%'+,`~");
void DoCompleteInitialize( VOID ) { pCompleteBuffer = NULL; nBufSize = 0; bCurrentSpecType = 0; nCurrentSpecPathStart = 0; nCurrentSpec = 0; return; }
int DoComplete( TCHAR *buffer, int len, int maxlen, int bForward, int bPathCompletion, int bTouched) /*++
Routine Description:
This is used whenever a path completion character is seen on input. It updates the input buffer with the next matching text and returns the updated size.
Arguments:
buffer - input string that contains the prefix of the path to match at the end. len - length of the input string. maxlen - maximum string length that can be stored in buffer. bForward - TRUE => matching goes forward through the storted match list. Otherwise move backwards through the list. bPathCompletion - TRUE => we match ONLY directories and not files+directories. bTouched - TRUE => the user has edited the path. This usually means that we need to begin the matching process anew.
Return Value:
Zero if no matching entries were found, otherwise the length of the updated buffer.
--*/ { TCHAR pFileSpecBuffer[512]; int nBufPos; int nPathStart; int nFileStart; int k; BOOL bWildSeen;
//
// If the user edited the previous match or if the form of completion (dir vs
// dir/file) changed, then we must rebuild the matching information
//
if ( bTouched || (bCurrentSpecType != bPathCompletion)) {
BOOL InQuotes = FALSE;
//
// The following code was shipped in NT 4 and Windows 2000. It presented
// a usability problem when changing matching forms in mid-stream. We will
// now treat a simple change of completion type the same as being touched
// by the user: rebuild the matching database from where we are presently.
//
// //
// // if the buffer was left alone but the matching style
// // was changed, then start the matching process at the
// // beginning of the path
// //
//
// if (!bTouched && (bCurrentSpecType != bPathCompletion)) {
// buffer[nCurrentSpecPathStart] = NULLC;
// len = nCurrentSpecPathStart;
// }
//
// Determine the beginning of the path and file name. We
// need to take into account the presence of quotes and the
// need for CMD to introduce quoting as well
//
nPathStart = 0; nFileStart = -1; bWildSeen = FALSE; for ( k = 0; k < len; k++ ) { if (buffer[k] == SWITCHAR) { nPathStart = k + 1; bWildSeen = FALSE; } else if ( buffer[k] == QUOTE ) { if ( !InQuotes ) nPathStart = k;
InQuotes = !InQuotes; } else if ( !InQuotes && _tcschr(szSpecialFileCharsToQuote, buffer[k]) != NULL ) { nPathStart = k+1; bWildSeen = FALSE; } else if (buffer[k] == COLON || buffer[k] == BSLASH ) { nFileStart = k+1; bWildSeen = FALSE; } else if (buffer[k] == STAR || buffer[k] == QMARK) { bWildSeen = TRUE; } }
if (nFileStart == -1 || nFileStart < nPathStart) nFileStart = nPathStart;
_tcsncpy( pFileSpecBuffer, &(buffer[nPathStart]), len-nPathStart ); if (!bWildSeen) { pFileSpecBuffer[len-nPathStart+0] = TEXT('*'); pFileSpecBuffer[len-nPathStart+1] = TEXT('\0'); } else { pFileSpecBuffer[len-nPathStart+0] = TEXT('\0'); }
// do the DIR into a buffer
nBufSize = CompleteDir( pFileSpecBuffer, bPathCompletion, nFileStart - nPathStart );
// reset the current completion string
nCurrentSpec = nBufSize; nCurrentSpecPathStart = nPathStart; bCurrentSpecType = bPathCompletion; }
// if no matches, do nothing.
if ( nBufSize == 0 ) { return 0; }
// find our postion in the completion buffer.
if ( bForward ) { nCurrentSpec++; if ( nCurrentSpec >= nBufSize ) nCurrentSpec = 0; } else { nCurrentSpec--; if ( nCurrentSpec < 0 ) nCurrentSpec = nBufSize - 1;
}
// Return nothing if buffer not big enough
if ((int)(nCurrentSpecPathStart+_tcslen(pCompleteBuffer[nCurrentSpec])) >= maxlen) return 0;
// copy the completion path onto the end of the command line
_tcscpy(&buffer[nCurrentSpecPathStart], pCompleteBuffer[nCurrentSpec] ); return nBufSize; }
int CompleteDir( TCHAR *pFileSpecBuffer, int bPathCompletion, int nFileStart ) { PFS pfsCur; PSCREEN pscr; DRP drpCur = {0, 0, 0, 0, {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}, 0, 0, NULL, 0, 0, 0, 0} ; int hits = 0; int i, j, nFileLen; unsigned Err; TCHAR *s, *d, *pszFileStart; BOOLEAN bNeedQuotes; ULONG rgfAttribs, rgfAttribsOnOff;
if (pCompleteBuffer != NULL) { // free the old buffer
for( i=0; i<nBufSize; i++ ){ free( pCompleteBuffer[i] ); } free( pCompleteBuffer ); pCompleteBuffer = NULL; }
// fake up a screen to print into
pscr = (PSCREEN)gmkstr(sizeof(SCREEN)); pscr->ccol = 2048;
rgfAttribs = 0; rgfAttribsOnOff = 0; if (bPathCompletion) { rgfAttribs = FILE_ATTRIBUTE_DIRECTORY; rgfAttribsOnOff = FILE_ATTRIBUTE_DIRECTORY; }
ParseDirParms(pFileSpecBuffer, &drpCur); if ( (drpCur.patdscFirst.pszPattern == NULL) || (SetFsSetSaveDir(drpCur.patdscFirst.pszPattern) == (PCPYINFO) FAILURE) || (BuildFSFromPatterns(&drpCur, FALSE, TRUE, &pfsCur) == FAILURE) ) { RestoreSavedDirectory( ); return( 0 ); }
Err = ExpandAndApplyToFS( pfsCur, pscr, rgfAttribs, rgfAttribsOnOff, NULL, NULL, NULL, NULL, NULL );
if (Err) { RestoreSavedDirectory( ); return 0; } //
// Make sure there is something to sort, then sort
//
if (pfsCur->cff) { qsort( pfsCur->prgpff, pfsCur->cff, sizeof(PTCHAR), CmpName ); }
s = pFileSpecBuffer; d = s; bNeedQuotes = FALSE; while (*s) { if (*s == QUOTE) { bNeedQuotes = TRUE; s += 1; if (nFileStart >= (s-pFileSpecBuffer)) nFileStart -= 1; if (*s == QUOTE) *d++ = *s++; } else { if (_tcschr(szSpecialFileCharsToQuote, *s) != NULL) bNeedQuotes = TRUE;
*d++ = *s++; } } *d = NULLC;
hits = pfsCur->cff; pCompleteBuffer = calloc( sizeof(TCHAR *), hits ); if (pCompleteBuffer == NULL) { RestoreSavedDirectory( ); return 0; }
for(i=0, j=0; i<hits; i++) { if (!_tcscmp((TCHAR *)(pfsCur->prgpff[i]->data.cFileName), TEXT(".") ) || !_tcscmp((TCHAR *)(pfsCur->prgpff[i]->data.cFileName), TEXT("..") )) { continue; } nFileLen = _tcslen( (TCHAR *)(pfsCur->prgpff[i]->data.cFileName) ); pCompleteBuffer[j] = (TCHAR *)calloc( (nFileStart + nFileLen + 4) , sizeof( TCHAR ));
if (pCompleteBuffer[j] == NULL) { continue; } if (!bNeedQuotes) { s = (TCHAR *)(pfsCur->prgpff[i]->data.cFileName); while (*s) { if (_tcschr(szSpecialFileCharsToQuote, *s) != NULL) bNeedQuotes = TRUE; s += 1; } } else s = NULL;
d = pCompleteBuffer[j]; if (bNeedQuotes) *d++ = QUOTE; _tcsncpy( d, pFileSpecBuffer, nFileStart ); d += nFileStart; _tcsncpy( d, (TCHAR *)(pfsCur->prgpff[i]->data.cFileName), nFileLen ); d += nFileLen;
if (bNeedQuotes) { *d++ = QUOTE; if (s) bNeedQuotes = FALSE; } *d++ = NULLC;
j++; } hits = j;
FreeStr((PTCHAR)(pfsCur->pff)); FreeStr(pfsCur->pszDir); FreeStr((PTCHAR)pfsCur);
RestoreSavedDirectory( );
return hits; }
|