home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Source Code / Visual Basic Source Code.iso / vbsource / cenvid / shellchr.cmm < prev    next >
Encoding:
Text File  |  1995-09-28  |  12.2 KB  |  295 lines

  1. //****************************************************************************
  2. //****** BEGIN SECTION TO HANDLE HISTORY AND EDITING FOR THE CLI SHELL *******
  3. //****************************************************************************
  4. //
  5. //  This script is to be #included from the AUTOLOAD.CMM.  It is operating
  6. //  system independant, assuming that constants such as EXT_KEY_HOME are
  7. //  correctly set already.  This script adds the following user interface
  8. //  features to the CEnvi commandline interface:
  9.  
  10. //  <HOME>  Pressing the home key brings the cursor to the start of the line.
  11. //  <END>   Pressing the end key brings the cursor to the end of the line.
  12. //  <UP>    Pressing the up key scrolls back through the command history.
  13. //  <DOWN>  Pressing the down key scrolls forward through the command history.
  14. //  <ENTER> Pressing enter adds the line to the command history, before executing it.
  15. //  <ESC>   Pressing escape clears the line.
  16. //  <TAB>   Pressing tab activates filename completion.  If the cursor is situated at
  17. //            the end of a partially completed filename, TAB will complete the filename
  18. //            if there is only one file that matches the partial name.  If more than
  19. //            one file is present, it will list all those that do match (within reason)
  20.  
  21.  
  22.  
  23. if( !defined(_SHELL_) || !defined(shellchr_alright) )
  24. {
  25.   printf("This file is part of the shell; it is not a standalone program.\n");
  26.   exit(0);
  27. }
  28.  
  29.  
  30. #define HISTORY_MAX_MEMORY     20   // maximum values to hold in history
  31. gHistoryList;
  32. gHistoryCount = 0;
  33. gReviewLine = 0;   // when scrolling through buffer, this is line being reviewed
  34.  
  35. // set up filter to handle every special character input
  36. ShellFilterCharacter("AutoloadFilterCharacter",True);
  37.  
  38. // The 'true' passed to it indicates that ALL characters should be passed through
  39. // filter, rather than just the non-printing ones.  This means we are able to play
  40. // with characters that are perfectly normal, such as 'w'.  */
  41.  
  42. AutoloadFilterCharacter(pCommand,pPosition,pKey,pIsExtended,pIsAlnum)
  43. {
  44.    lReturnCode = False; //Usually we don't need to redraw the line.
  45.  
  46.    if gFileNameCompletionGlobalIndex {
  47.       // This is for the filename completion.  If several matches were found, they
  48.       // appear in the command line, to be erased at the next keypress. This is
  49.       // the next keypress now, so we're telling it to erase them.
  50.       pCommand[gFileNameCompletionGlobalIndex] = 0;
  51.       gFileNameCompletionGlobalIndex = 0;
  52.       lReturnCode = True;  // We do need to redraw the line if we erased stuff.
  53.    }
  54.  
  55.    // Delete, Backspace, Left and Right arrows are handled in the binary executable, and
  56.    // need not be dealt with here.  Isn't that convenient?
  57.  
  58.    if ( pIsExtended ) {  // All the keys that don't have nice normal ASCII values
  59.       switch( pKey ) {
  60.          case ext_key_home: // Jump cursor to beginning of line.
  61.             pPosition = 0;
  62.             break;
  63.          case ext_key_end: // Jump cursor to end of line.
  64.             pPosition = strlen(pCommand);
  65.             break;
  66.          case ext_key_up:  // Scroll up one through the command history.
  67.             if ( gHistoryCount ) {
  68.                if ( gHistoryCount+1 <= ++gReviewLine )
  69.                   gReviewLine = 0;
  70.                if( gReviewLine==0 )
  71.                  strcpy(pCommand,"");
  72.                else
  73.                  strcpy(pCommand,gHistoryList[gReviewLine-1]);
  74.                pPosition = strlen(pCommand);
  75.                pKey = 0;  // We don't want the key to get added to the buffer..
  76.                return True;
  77.             }
  78.             break;
  79.          case ext_key_down: // Scroll down one through the command history.
  80.             if ( gHistoryCount ) {
  81.                if ( --gReviewLine < 0 )
  82.                   gReviewLine = gHistoryCount;
  83.                if( gReviewLine==0 )
  84.                  strcpy(pCommand,"");
  85.                else
  86.                  strcpy(pCommand,gHistoryList[gReviewLine-1]);
  87.                pPosition = strlen(pCommand);
  88.                pKey = 0;
  89.                return True;
  90.             }
  91.             break;
  92.       }
  93.    } else {
  94.       switch (pKey)  // These keys all have one byte ASCII values.
  95.       {
  96.          case '\r':  // When <ENTER> is hit, the whole line is added into the command history.
  97.             AddToHistory(pCommand);
  98.             break;
  99.          case 0x03:  // Also clear the line if Control-C pressed.
  100.          case 0x1B:  // Escape clears the line.
  101.             pCommand[0] = 0;
  102.             pPosition = 0;
  103.             pKey = 0;
  104.             return True;
  105.          case '\t':  // Tab activates filename completion.
  106.             FileNameCompletion(pCommand, pPosition);
  107.             pKey = 0;
  108.             return True;
  109.       }
  110.    }
  111.    return lReturnCode;
  112. }
  113.  
  114. gFileNameCompletionGlobalIndex = 0; // This keeps track of where to erase up
  115.                                     // to next time a key is pressed.
  116. #define MAX_SHOW_FILE_NAME_COMPLETION 18  // A limit on the number of matches to display..
  117.  
  118. FileNameCompletion(pCommandLine,pPosition)
  119. // When <TAB> is pressed a partially completed file name is expanded.
  120. // If more than one file in the current directory match the partial name give,
  121. // the name will be completed until the first ambiguity, and all the possible
  122. // choices will be displayed (Unless more than MAX_SHOW_FILE_NAME_COMPLETION
  123. // are present).
  124. {
  125.    lStartOfWord = 0; // We'll need the start of the partial filename..
  126.    lTempIndex = 0;
  127.    lIndex = 0;
  128.    while (lIndex < pPosition)
  129.    // Now we find the start of the partial filename.  This should work for
  130.    // filenames with spaces in them, with quotes and stuff.  However, it does not
  131.    // yet allow for escaped out \" quotes, and will treat them as real quotes.
  132.    {
  133.       lIndex++;
  134.       if (pCommandLine[lIndex-1] == '"')
  135.       { //If there's a start quote, search for the end quote, or use it as the start.
  136.          lStartOfWord = lIndex;
  137.          while (lIndex < pPosition) && (pCommandLine[lIndex] != '"') lIndex++;
  138.       }
  139.       if (pCommandLine[lIndex-1] == ' ')
  140.       {
  141.          lStartOfWord = lIndex;
  142.       }
  143.    }
  144.    if (lStartOfWord == pPosition) return; // We can't expand a unstarted filename!
  145.  
  146.    for (lIndex = lStartOfWord; lIndex < pPosition; lIndex++)
  147.    {  // Now we isolate the filename fragment..
  148.       FileSpec[lTempIndex++] = pCommandLine[lIndex];
  149.    }
  150.    FileSpec[lTempIndex] = '\0';  // And terminate it..
  151.  
  152.    if (CompleteFileName(FileSpec, Matches) >= 1) // And see what matches it.
  153.    {  // One or more matches.  Fill in what we can.
  154.       strcpy(lNewCommandLine,pCommandLine); // Here's a copy to play with so we
  155.                                           // don't lose the end
  156.       lNewCommandLine[lStartOfWord] = 0;    // And we cut off the end of the string
  157.                                           // after the beginning of the filename fragment.
  158.       strcat(lNewCommandLine,FileSpec);    // Now we insert the new filename (or fragment)
  159.       lIndex = pPosition;
  160.       pPosition = lStartOfWord + strlen(FileSpec);
  161.       lTempIndex = strlen(lNewCommandLine);
  162.       while(pCommandLine[lIndex])           // And stick on everything after the filename
  163.                                           // fragment from the orginal string.
  164.       lNewCommandLine[lTempIndex++] = pCommandLine[Index++];
  165.       lNewCommandLine[lTempIndex] = 0;
  166.       strcpy(pCommandLine,lNewCommandLine); // Put this back where it belongs.
  167.       gFileNameCompletionGlobalIndex = strlen(pCommandLine); // Make sure we know where to
  168.                                                             // start erasing helpfull messages
  169.       pPosition = lStartOfWord + strlen(FileSpec); // Our cursor's back where it belongs.
  170.  
  171.       if GetArraySpan(Matches) > 0   // More than one match.  Display some possibilities.
  172.       {
  173.          if MAX_SHOW_FILE_NAME_COMPLETION < GetArraySpan(Matches)
  174.          {  // Too many matches?
  175.             strcat(pCommandLine, "  (Too many matches to display)");
  176.          }
  177.          else
  178.          {   // No.  Add to the prompt (MATCH1.FOO MATCH2.FOO MATCH3.FOO ...)
  179.                // Since the gFileNameCompletionGlobalIndex is set to a nonzero value
  180.                // this message will be erased at the next keypress.
  181.             strcat(pCommandLine, "  (");
  182.             for (loop = 0; loop <= GetArraySpan(Matches);loop++)
  183.             {
  184.                strcat(pCommandLine, Matches[loop].name);
  185.                strcat(pCommandLine, " ");
  186.             }
  187.             pCommandLine[strlen(pCommandLine) - 1] = 0;
  188.          }
  189.       } else
  190.       {
  191.          // We only found one match; the user obviously doesn't need any help.
  192.          gFileNameCompletionGlobalIndex = 0;
  193.       }
  194.    }
  195.    else {
  196.       gFileNameCompletionGlobalIndex = strlen(pCommandLine);
  197.       strcat(pCommandLine, "  (No matches found)");
  198.    }
  199. }
  200.  
  201. CompleteFileName(pFileNamePart,pFoundFiles) // Here's where we do the real wildcard expansion.
  202. {
  203.    strcpy(OldFileName,pFileNamePart);
  204.    strcat(pFileNamePart,"*");
  205.    if defined(_DOS_) || defined(_DOS32_) || defined (_WINDOWS_) {
  206.       // *.* Will only find all files in a DOS based OS.  The rest of the time,
  207.       // * should be good enough. But if we've already got a . we don't need another one.
  208.       if (NULL == strchr(pFileNamePart,'.')) strcat(pFileNamePart,".*");
  209.    }
  210.  
  211.    pFoundFiles = Directory(pFileNamePart);
  212.    if pFoundFiles == NULL return 0;  // No files found!
  213.  
  214.    // Append a backslash to the end of any directory names.
  215.    for (loop = 0; loop <= GetArraySpan(pFoundFiles); loop++) {
  216.       if( pFoundFiles[loop].attrib & FATTR_SUBDIR ) {
  217.          if( defined(_NWNLM_) ) {
  218.             strcat(pFoundFiles[loop].name,'/');
  219.          } else {
  220.             strcat(pFoundFiles[loop].name,`\`);
  221.          }
  222.       }
  223.       else 
  224.          strcat(pFoundFiles[loop].name, " ");
  225.    }
  226.    if GetArraySpan(pFoundFiles) == 0 { // We have a single match!
  227.       strcpy(pFileNamePart,pFoundFiles[0].name);
  228.       return 1;
  229.    }
  230.    printf("\a"); // Beep to say we need more information.
  231.    ShortestMatch = strlen(pFoundFiles[0].name) - 1;
  232.    for (loop = 1; loop <= GetArraySpan(pFoundFiles); loop ++)
  233.    { // Find the first ambiguity..
  234.       if ShortestMatch > StringMatchLength(pFoundFiles[loop].name, pFoundFiles[0].name)
  235.          ShortestMatch = StringMatchLength(pFoundFiles[loop].name, pFoundFiles[0].name);
  236.    }
  237.    strcpy(pFileNamePart,pFoundFiles[0].name);
  238.    for (x = 0; x < strlen(OldFileName); x++)
  239.    {
  240.       pFileNamePart[x] = OldFileName[x];
  241.    }
  242.    pFileNamePart[ShortestMatch] = 0;
  243.    return GetArraySpan(pFoundFiles) + 1;
  244. }
  245.  
  246. StringMatchLength(pStr1,pStr2)
  247. { // Finds the first discrepancy between two strings, the length if they're equal.
  248.    lIndex = 0;
  249.    SmallerLen = strlen(pStr1) > strlen(pStr2) ? strlen(pStr2) : strlen(pStr1);
  250.    for (lIndex = 0; lIndex < SmallerLen; lIndex++) {
  251.       if (pStr1[lIndex] != pStr2[lIndex])
  252.          break;
  253.    }
  254.    return lIndex;
  255. }
  256.  
  257. AddToHistory(pCommand)
  258. {
  259.    // skip all spaces at beginning of command
  260.    strcpy(lCommand,pCommand);
  261.    lCommand += strspn(lCommand," ");
  262.    // remove any spaces at end of command
  263.    while ( (lLastSpace = strrchr(lCommand,' '))  &&  !lLastSpace[1] )
  264.       lLastSpace[0] = '\0';
  265.  
  266.    // if no command remains then do not save
  267.    if ( !lCommand[0] )
  268.       return;
  269.  
  270.    // if this entry is already in the history then remove that entry so this
  271.    // one can replace as most recent.  Otherwise remove oldest entry if there
  272.    // are up to max in the buffer
  273.    bool lNewEntry = True;
  274.    for ( lOldEntry = 0; lOldEntry < gHistoryCount; lOldEntry++ ) {
  275.       if ( !stricmp(lCommand,gHistoryList[lOldEntry]) ) {
  276.          lNewEntry = False;
  277.          break;
  278.       }
  279.    }
  280.  
  281.    // move all entries down one to fill in the old gap
  282.    while ( lOldEntry-- )
  283.       gHistoryList[lOldEntry+1] = gHistoryList[lOldEntry];
  284.  
  285.    // new command goes to top of list
  286.    gHistoryList[0] = lCommand;
  287.    if ( gHistoryCount < HISTORY_MAX_MEMORY  &&  lNewEntry )
  288.       gHistoryCount++;
  289.    gReviewLine = 0;
  290. }
  291.  
  292. //**************************************************************************
  293. //****** END SECTION TO HANDLE HISTORY AND EDITING FOR THE CLI SHELL *******
  294. //**************************************************************************
  295.