home *** CD-ROM | disk | FTP | other *** search
- //****************************************************************************
- //****** BEGIN SECTION TO HANDLE HISTORY AND EDITING FOR THE CLI SHELL *******
- //****************************************************************************
- //
- // This script is to be #included from the AUTOLOAD.CMM. It is operating
- // system independant, assuming that constants such as EXT_KEY_HOME are
- // correctly set already. This script adds the following user interface
- // features to the CEnvi commandline interface:
-
- // <HOME> Pressing the home key brings the cursor to the start of the line.
- // <END> Pressing the end key brings the cursor to the end of the line.
- // <UP> Pressing the up key scrolls back through the command history.
- // <DOWN> Pressing the down key scrolls forward through the command history.
- // <ENTER> Pressing enter adds the line to the command history, before executing it.
- // <ESC> Pressing escape clears the line.
- // <TAB> Pressing tab activates filename completion. If the cursor is situated at
- // the end of a partially completed filename, TAB will complete the filename
- // if there is only one file that matches the partial name. If more than
- // one file is present, it will list all those that do match (within reason)
-
-
-
- if( !defined(_SHELL_) || !defined(shellchr_alright) )
- {
- printf("This file is part of the shell; it is not a standalone program.\n");
- exit(0);
- }
-
-
- #define HISTORY_MAX_MEMORY 20 // maximum values to hold in history
- gHistoryList;
- gHistoryCount = 0;
- gReviewLine = 0; // when scrolling through buffer, this is line being reviewed
-
- // set up filter to handle every special character input
- ShellFilterCharacter("AutoloadFilterCharacter",True);
-
- // The 'true' passed to it indicates that ALL characters should be passed through
- // filter, rather than just the non-printing ones. This means we are able to play
- // with characters that are perfectly normal, such as 'w'. */
-
- AutoloadFilterCharacter(pCommand,pPosition,pKey,pIsExtended,pIsAlnum)
- {
- lReturnCode = False; //Usually we don't need to redraw the line.
-
- if gFileNameCompletionGlobalIndex {
- // This is for the filename completion. If several matches were found, they
- // appear in the command line, to be erased at the next keypress. This is
- // the next keypress now, so we're telling it to erase them.
- pCommand[gFileNameCompletionGlobalIndex] = 0;
- gFileNameCompletionGlobalIndex = 0;
- lReturnCode = True; // We do need to redraw the line if we erased stuff.
- }
-
- // Delete, Backspace, Left and Right arrows are handled in the binary executable, and
- // need not be dealt with here. Isn't that convenient?
-
- if ( pIsExtended ) { // All the keys that don't have nice normal ASCII values
- switch( pKey ) {
- case ext_key_home: // Jump cursor to beginning of line.
- pPosition = 0;
- break;
- case ext_key_end: // Jump cursor to end of line.
- pPosition = strlen(pCommand);
- break;
- case ext_key_up: // Scroll up one through the command history.
- if ( gHistoryCount ) {
- if ( gHistoryCount+1 <= ++gReviewLine )
- gReviewLine = 0;
- if( gReviewLine==0 )
- strcpy(pCommand,"");
- else
- strcpy(pCommand,gHistoryList[gReviewLine-1]);
- pPosition = strlen(pCommand);
- pKey = 0; // We don't want the key to get added to the buffer..
- return True;
- }
- break;
- case ext_key_down: // Scroll down one through the command history.
- if ( gHistoryCount ) {
- if ( --gReviewLine < 0 )
- gReviewLine = gHistoryCount;
- if( gReviewLine==0 )
- strcpy(pCommand,"");
- else
- strcpy(pCommand,gHistoryList[gReviewLine-1]);
- pPosition = strlen(pCommand);
- pKey = 0;
- return True;
- }
- break;
- }
- } else {
- switch (pKey) // These keys all have one byte ASCII values.
- {
- case '\r': // When <ENTER> is hit, the whole line is added into the command history.
- AddToHistory(pCommand);
- break;
- case 0x03: // Also clear the line if Control-C pressed.
- case 0x1B: // Escape clears the line.
- pCommand[0] = 0;
- pPosition = 0;
- pKey = 0;
- return True;
- case '\t': // Tab activates filename completion.
- FileNameCompletion(pCommand, pPosition);
- pKey = 0;
- return True;
- }
- }
- return lReturnCode;
- }
-
- gFileNameCompletionGlobalIndex = 0; // This keeps track of where to erase up
- // to next time a key is pressed.
- #define MAX_SHOW_FILE_NAME_COMPLETION 18 // A limit on the number of matches to display..
-
- FileNameCompletion(pCommandLine,pPosition)
- // When <TAB> is pressed a partially completed file name is expanded.
- // If more than one file in the current directory match the partial name give,
- // the name will be completed until the first ambiguity, and all the possible
- // choices will be displayed (Unless more than MAX_SHOW_FILE_NAME_COMPLETION
- // are present).
- {
- lStartOfWord = 0; // We'll need the start of the partial filename..
- lTempIndex = 0;
- lIndex = 0;
- while (lIndex < pPosition)
- // Now we find the start of the partial filename. This should work for
- // filenames with spaces in them, with quotes and stuff. However, it does not
- // yet allow for escaped out \" quotes, and will treat them as real quotes.
- {
- lIndex++;
- if (pCommandLine[lIndex-1] == '"')
- { //If there's a start quote, search for the end quote, or use it as the start.
- lStartOfWord = lIndex;
- while (lIndex < pPosition) && (pCommandLine[lIndex] != '"') lIndex++;
- }
- if (pCommandLine[lIndex-1] == ' ')
- {
- lStartOfWord = lIndex;
- }
- }
- if (lStartOfWord == pPosition) return; // We can't expand a unstarted filename!
-
- for (lIndex = lStartOfWord; lIndex < pPosition; lIndex++)
- { // Now we isolate the filename fragment..
- FileSpec[lTempIndex++] = pCommandLine[lIndex];
- }
- FileSpec[lTempIndex] = '\0'; // And terminate it..
-
- if (CompleteFileName(FileSpec, Matches) >= 1) // And see what matches it.
- { // One or more matches. Fill in what we can.
- strcpy(lNewCommandLine,pCommandLine); // Here's a copy to play with so we
- // don't lose the end
- lNewCommandLine[lStartOfWord] = 0; // And we cut off the end of the string
- // after the beginning of the filename fragment.
- strcat(lNewCommandLine,FileSpec); // Now we insert the new filename (or fragment)
- lIndex = pPosition;
- pPosition = lStartOfWord + strlen(FileSpec);
- lTempIndex = strlen(lNewCommandLine);
- while(pCommandLine[lIndex]) // And stick on everything after the filename
- // fragment from the orginal string.
- lNewCommandLine[lTempIndex++] = pCommandLine[Index++];
- lNewCommandLine[lTempIndex] = 0;
- strcpy(pCommandLine,lNewCommandLine); // Put this back where it belongs.
- gFileNameCompletionGlobalIndex = strlen(pCommandLine); // Make sure we know where to
- // start erasing helpfull messages
- pPosition = lStartOfWord + strlen(FileSpec); // Our cursor's back where it belongs.
-
- if GetArraySpan(Matches) > 0 // More than one match. Display some possibilities.
- {
- if MAX_SHOW_FILE_NAME_COMPLETION < GetArraySpan(Matches)
- { // Too many matches?
- strcat(pCommandLine, " (Too many matches to display)");
- }
- else
- { // No. Add to the prompt (MATCH1.FOO MATCH2.FOO MATCH3.FOO ...)
- // Since the gFileNameCompletionGlobalIndex is set to a nonzero value
- // this message will be erased at the next keypress.
- strcat(pCommandLine, " (");
- for (loop = 0; loop <= GetArraySpan(Matches);loop++)
- {
- strcat(pCommandLine, Matches[loop].name);
- strcat(pCommandLine, " ");
- }
- pCommandLine[strlen(pCommandLine) - 1] = 0;
- }
- } else
- {
- // We only found one match; the user obviously doesn't need any help.
- gFileNameCompletionGlobalIndex = 0;
- }
- }
- else {
- gFileNameCompletionGlobalIndex = strlen(pCommandLine);
- strcat(pCommandLine, " (No matches found)");
- }
- }
-
- CompleteFileName(pFileNamePart,pFoundFiles) // Here's where we do the real wildcard expansion.
- {
- strcpy(OldFileName,pFileNamePart);
- strcat(pFileNamePart,"*");
- if defined(_DOS_) || defined(_DOS32_) || defined (_WINDOWS_) {
- // *.* Will only find all files in a DOS based OS. The rest of the time,
- // * should be good enough. But if we've already got a . we don't need another one.
- if (NULL == strchr(pFileNamePart,'.')) strcat(pFileNamePart,".*");
- }
-
- pFoundFiles = Directory(pFileNamePart);
- if pFoundFiles == NULL return 0; // No files found!
-
- // Append a backslash to the end of any directory names.
- for (loop = 0; loop <= GetArraySpan(pFoundFiles); loop++) {
- if( pFoundFiles[loop].attrib & FATTR_SUBDIR ) {
- if( defined(_NWNLM_) ) {
- strcat(pFoundFiles[loop].name,'/');
- } else {
- strcat(pFoundFiles[loop].name,`\`);
- }
- }
- else
- strcat(pFoundFiles[loop].name, " ");
- }
- if GetArraySpan(pFoundFiles) == 0 { // We have a single match!
- strcpy(pFileNamePart,pFoundFiles[0].name);
- return 1;
- }
- printf("\a"); // Beep to say we need more information.
- ShortestMatch = strlen(pFoundFiles[0].name) - 1;
- for (loop = 1; loop <= GetArraySpan(pFoundFiles); loop ++)
- { // Find the first ambiguity..
- if ShortestMatch > StringMatchLength(pFoundFiles[loop].name, pFoundFiles[0].name)
- ShortestMatch = StringMatchLength(pFoundFiles[loop].name, pFoundFiles[0].name);
- }
- strcpy(pFileNamePart,pFoundFiles[0].name);
- for (x = 0; x < strlen(OldFileName); x++)
- {
- pFileNamePart[x] = OldFileName[x];
- }
- pFileNamePart[ShortestMatch] = 0;
- return GetArraySpan(pFoundFiles) + 1;
- }
-
- StringMatchLength(pStr1,pStr2)
- { // Finds the first discrepancy between two strings, the length if they're equal.
- lIndex = 0;
- SmallerLen = strlen(pStr1) > strlen(pStr2) ? strlen(pStr2) : strlen(pStr1);
- for (lIndex = 0; lIndex < SmallerLen; lIndex++) {
- if (pStr1[lIndex] != pStr2[lIndex])
- break;
- }
- return lIndex;
- }
-
- AddToHistory(pCommand)
- {
- // skip all spaces at beginning of command
- strcpy(lCommand,pCommand);
- lCommand += strspn(lCommand," ");
- // remove any spaces at end of command
- while ( (lLastSpace = strrchr(lCommand,' ')) && !lLastSpace[1] )
- lLastSpace[0] = '\0';
-
- // if no command remains then do not save
- if ( !lCommand[0] )
- return;
-
- // if this entry is already in the history then remove that entry so this
- // one can replace as most recent. Otherwise remove oldest entry if there
- // are up to max in the buffer
- bool lNewEntry = True;
- for ( lOldEntry = 0; lOldEntry < gHistoryCount; lOldEntry++ ) {
- if ( !stricmp(lCommand,gHistoryList[lOldEntry]) ) {
- lNewEntry = False;
- break;
- }
- }
-
- // move all entries down one to fill in the old gap
- while ( lOldEntry-- )
- gHistoryList[lOldEntry+1] = gHistoryList[lOldEntry];
-
- // new command goes to top of list
- gHistoryList[0] = lCommand;
- if ( gHistoryCount < HISTORY_MAX_MEMORY && lNewEntry )
- gHistoryCount++;
- gReviewLine = 0;
- }
-
- //**************************************************************************
- //****** END SECTION TO HANDLE HISTORY AND EDITING FOR THE CLI SHELL *******
- //**************************************************************************
-