libreadline - Rework of library (less sucky now!)
authorJohn Hodge <[email protected]>
Tue, 1 Mar 2011 06:31:03 +0000 (14:31 +0800)
committerJohn Hodge <[email protected]>
Tue, 1 Mar 2011 06:31:03 +0000 (14:31 +0800)
Usermode/Applications/CLIShell_src/main.c
Usermode/Libraries/libreadline.so_src/main.c
Usermode/include/readline.h

index 50c9589..33907ee 100644 (file)
@@ -59,11 +59,7 @@ int main(int argc, char *argv[], char *envp[])
        #if !USE_READLINE\r
         int    bCached = 1;\r
        #else\r
-       tReadline       readline_state = {0};\r
-       #endif\r
-       \r
-       #if USE_READLINE\r
-       readline_state.UseHistory = 1;\r
+       tReadline       *readline_state = Readline_Init(1);\r
        #endif\r
        \r
        gasEnvironment = envp;\r
@@ -97,7 +93,7 @@ int main(int argc, char *argv[], char *envp[])
                \r
                // Read Command line\r
                #if USE_READLINE\r
-               sCommandStr = Readline( &readline_state );\r
+               sCommandStr = Readline( readline_state );\r
                length = strlen(sCommandStr);\r
                #else\r
                sCommandStr = ReadCommandLine( &length );\r
index 3af20ee..4bc0fc5 100644 (file)
 #define STDIN_FD       0
 #define STDOUT_FD      1
 
+// Size of the read() buffer
+// - 32 should be pleanty
+#define READ_BUFFER_SIZE       32
+
+// === STRUCTURES ===
+struct sReadline
+{
+        int    UseHistory;     // Boolean
+       
+       // TODO: Command Completion
+       
+       // History
+        int    NumHistory;
+       char    **History;
+       
+       // Internal Flags
+       char    *OutputValue;   //!< Pointer (owned by history) to output value
+       
+       // Command Buffer
+        int    BufferSize;     // Allocated size of the buffer
+        int    BufferUsed;     // Offset of first free byte in the buffer
+        int    BufferWritePos; // Cursor location
+       char    *CurBuffer;     // Current translated command (pointer to a entry in history)
+       
+       // 
+        int    HistoryPos;
+       
+       // Read Buffer
+       char    ReadBuffer[READ_BUFFER_SIZE];   //!< Buffer for read()
+        int    ReadBufferLen;
+};
+
 // === PROTOTYPES ===
  int   SoMain();
-tReadline      *Readline_CreateInstance(int bUseHistory);
+tReadline      *Readline_Init(int bUseHistory);
+ int   Readline_int_ParseCharacter(tReadline *Info, char *Input);
+char   *Readline_NonBlock(tReadline *Info);
 char   *Readline(tReadline *Info);
 
 // === GLOBALS ===
@@ -26,168 +60,287 @@ int SoMain()
        return 0;
 }
 
+tReadline *Readline_Init(int bUseHistory)
+{
+       tReadline       *ret = calloc( 1, sizeof(tReadline) );
+       ret->UseHistory = bUseHistory;
+       ret->BufferSize = 0;
+       
+       ret->History = malloc( 1 * sizeof(*ret->History) );
+       ret->History[0] = NULL;
+       ret->NumHistory = 1;
+       
+       return ret;
+}
+
+/**
+ */
+char *Readline_NonBlock(tReadline *Info)
+{
+        int    len, i;
+       
+       // Read as much as possible (appending to remaining data)
+       len = read(STDIN_FD, READ_BUFFER_SIZE - 1 - Info->ReadBufferLen, Info->ReadBuffer);
+       Info->ReadBuffer[Info->ReadBufferLen + len] = '\0';
+       
+       // Parse the data we have
+       for( i = 0; i < len; )
+       {
+               int size = Readline_int_ParseCharacter(Info, Info->ReadBuffer+i);
+               if( size <= 0 ) break;  // Error, skip the rest?
+               i += size;
+       }
+       
+       // Move the unused data to the start of the buffer
+       memcpy(Info->ReadBuffer, &Info->ReadBuffer[Info->ReadBufferLen + i], len - i);
+       Info->ReadBufferLen = len - i;
+       
+       // Is the command finished?
+       if( Info->OutputValue ) {
+               char    *ret = Info->OutputValue;
+               Info->OutputValue = NULL;       // Mark as no command pending
+               return ret;     // Return the string (now the caller's responsibility)
+       }
+       
+       // Return NULL when command is still being edited
+       return NULL;
+}
+
 char *Readline(tReadline *Info)
 {
        char    *ret;
-       char    *orig;
-        int    len, pos, space = 1023-8-8;     // optimised for the heap manager
+       
+       while( NULL == (ret = Readline_NonBlock(Info)) );
+       
+       return ret;
+}
+
+int Readline_int_AddToHistory(tReadline *Info, const char *String)
+{
+       void    *tmp;
+       
+       // History[#-1] = CurBuffer (always)
+       if( !Info->History ) {
+               // Realy shouldn't happen, but just in case
+               Info->History = malloc( sizeof(char*) );
+               Info->NumHistory = 1;
+       }
+       
+       // Don't add duplicates
+       if( Info->NumHistory >= 2 && strcmp( Info->History[ Info->NumHistory-2 ], String ) == 0 )
+       {
+               return 0;
+       }
+       
+       // Duplicate over the current
+       Info->History[ Info->NumHistory-1 ] = strdup(String);
+       
+       Info->NumHistory ++;
+       
+       tmp = realloc( Info->History, Info->NumHistory * sizeof(char*) );
+       if( tmp == NULL )       return -1;
+       Info->History = tmp;
+                               
+       // Zero the new current
+       Info->History[ Info->NumHistory-1 ] = NULL;
+       
+       return 0;
+}
+
+int Readline_int_ParseCharacter(tReadline *Info, char *Input)
+{
+        int    ofs = 0;
        char    ch;
-        int    scrollbackPos = Info->NumHistory;
-        
-       // Preset Variables
-       ret = malloc( space+1 );
-       if(!ret)        return NULL;
-       len = 0;        pos = 0;
        
-       orig = ret;
+       if( Input[ofs] == 0 )   return 0;
        
        // Read In Command Line
-       do {
-               read(STDIN_FD, 1, &ch); // Read Character from stdin (read is a blocking call)
+       ch = Input[ofs++];
+       
+       if(ch == '\n')
+       {
+               printf("\n");
+               if(Info->CurBuffer)
+               {       
+                       // Cap String
+                       Info->CurBuffer[Info->BufferUsed] = '\0';
+                       
+                       if( Info->UseHistory )
+                               Readline_int_AddToHistory(Info, Info->CurBuffer);
+                       Info->OutputValue = strdup(Info->CurBuffer);
+               }
+               else
+                       Info->OutputValue = strdup("");
                
-               if(ch == '\n')  break;
+               // Save and reset
+               Info->BufferSize = 0;
+               Info->BufferUsed = 0;
+               Info->BufferWritePos = 0;
+               Info->CurBuffer = 0;
+               Info->HistoryPos = Info->NumHistory - 1;
                
+               return 1;
+       }
+       
+       switch(ch)
+       {
+       // Control characters
+       case '\x1B':
+               ch = Input[ofs++];      // Read control character
                switch(ch)
                {
-               // Control characters
-               case '\x1B':
-                       read(STDIN_FD, 1, &ch); // Read control character
+               //case 'D':     if(pos) pos--;  break;
+               //case 'C':     if(pos<len)     pos++;  break;
+               case '[':
+                       ch = Input[ofs++];      // Read control character
                        switch(ch)
                        {
-                       //case 'D':     if(pos) pos--;  break;
-                       //case 'C':     if(pos<len)     pos++;  break;
-                       case '[':
-                               read(STDIN_FD, 1, &ch); // Read control character
-                               switch(ch)
+                       case 'A':       // Up
                                {
-                               case 'A':       // Up
-                                       {
-                                                int    oldLen = len;
-                                               if( scrollbackPos <= 0 )        break;
-                                               
-                                               if(ret != orig) free(ret);
-                                               ret = strdup( Info->History[--scrollbackPos] );
-                                               
-                                               space = len = strlen(ret);
-                                               while(pos-->1)  write(STDOUT_FD, 3, "\x1B[D");
-                                               write(STDOUT_FD, len, ret);     pos = len;
-                                               while(pos++ < oldLen)   write(STDOUT_FD, 1, " ");
+                                        int    oldLen = Info->BufferUsed;
+                                        int    pos;
+                                       if( Info->HistoryPos <= 0 )     break;
+                                       
+                                       // Move to the beginning of the line
+                                       pos = oldLen;
+                                       while(pos--)    write(STDOUT_FD, 3, "\x1B[D");
+                                       
+                                       // Update state
+                                       Info->CurBuffer = Info->History[--Info->HistoryPos];
+                                       Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
+                                       
+                                       write(STDOUT_FD, Info->BufferUsed, Info->CurBuffer);
+                                       Info->BufferWritePos = Info->BufferUsed;
+                                       
+                                       // Clear old characters (if needed)
+                                       if( oldLen > Info->BufferWritePos ) {
+                                               pos = oldLen - Info->BufferWritePos;
+                                               while(pos--)    write(STDOUT_FD, 1, " ");
+                                               pos = oldLen - Info->BufferWritePos;
+                                               while(pos--)    write(STDOUT_FD, 3, "\x1B[D");
                                        }
-                                       break;
-                               case 'B':       // Down
-                                       {
-                                                int    oldLen = len;
-                                               if( scrollbackPos >= Info->NumHistory ) break;
-                                               
-                                               if(ret != orig) free(ret);
-                                               ret = strdup( Info->History[scrollbackPos++] );
-                                               
-                                               space = len = strlen(ret);
-                                               while(pos-->1)  write(STDOUT_FD, 3, "\x1B[D");
-                                               write(STDOUT_FD, len, ret);     pos = len;
-                                               while(pos++ < oldLen)   write(STDOUT_FD, 1, " ");
-                                       }
-                                       break;
-                               case 'D':       // Left
-                                       if(pos == 0)    break;
-                                       pos --;
-                                       write(STDOUT_FD, 3, "\x1B[D");
-                                       break;
-                               case 'C':       // Right
-                                       if(pos == len)  break;
-                                       pos++;
-                                       write(STDOUT_FD, 3, "\x1B[C");
-                                       break;
-                               }
-                       }
-                       break;
-               
-               // Backspace
-               case '\b':
-                       if(len <= 0)            break;  // Protect against underflows
-                       write(STDOUT_FD, 1, &ch);
-                       if(pos == len) {        // Simple case of end of string
-                               len --;
-                               pos--;
-                       }
-                       else {
-                               char    buf[7] = "\x1B[000D";
-                               buf[2] += ((len-pos+1)/100) % 10;
-                               buf[3] += ((len-pos+1)/10) % 10;
-                               buf[4] += (len-pos+1) % 10;
-                               write(STDOUT_FD, len-pos, &ret[pos]);   // Move Text
-                               ch = ' ';       write(STDOUT_FD, 1, &ch);       ch = '\b';      // Clear deleted character
-                               write(STDOUT_FD, 7, buf);       // Update Cursor
-                               // Alter Buffer
-                               memmove(&ret[pos-1], &ret[pos], len-pos);
-                               pos --;
-                               len --;
-                       }
-                       break;
-               
-               // Tab
-               case '\t':
-                       //TODO: Implement Tab-Completion
-                       //Currently just ignore tabs
-                       break;
-               
-               default:                
-                       // Expand Buffer
-                       if(len+1 > space) {
-                               space += 256;
-                               if(ret == orig) {
-                                       orig = ret = realloc(ret, space+1);
                                }
-                               else {
-                                       ret = realloc(ret, space+1);
+                               break;
+                       case 'B':       // Down
+                               {
+                                        int    oldLen = Info->BufferUsed;
+                                        int    pos;
+                                       if( Info->HistoryPos >= Info->NumHistory - 1 )  break;
+                                       
+                                       // Move to the beginning of the line
+                                       pos = oldLen;
+                                       while(pos--)    write(STDOUT_FD, 3, "\x1B[D");
+                                       
+                                       // Update state
+                                       Info->CurBuffer = Info->History[Info->HistoryPos++];
+                                       Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
+                                       
+                                       // Write new line
+                                       write(STDOUT_FD, Info->BufferUsed, Info->CurBuffer);
+                                       Info->BufferWritePos = Info->BufferUsed;
+                                       
+                                       // Clear old characters (if needed)
+                                       if( oldLen > Info->BufferWritePos ) {
+                                               pos = oldLen - Info->BufferWritePos;
+                                               while(pos--)    write(STDOUT_FD, 1, " ");
+                                               pos = oldLen - Info->BufferWritePos;
+                                               while(pos--)    write(STDOUT_FD, 3, "\x1B[D");
+                                       }
                                }
-                               if(!ret)        return NULL;
-                       }
-                       
-                       // Editing inside the buffer
-                       if(pos != len) {
-                               char    buf[7] = "\x1B[000D";
-                               buf[2] += ((len-pos)/100) % 10;
-                               buf[3] += ((len-pos)/10) % 10;
-                               buf[4] += (len-pos) % 10;
-                               write(STDOUT_FD, 1, &ch);       // Print new character
-                               write(STDOUT_FD, len-pos, &ret[pos]);   // Move Text
-                               write(STDOUT_FD, 7, buf);       // Update Cursor
-                               memmove( &ret[pos+1], &ret[pos], len-pos );
-                       }
-                       else {
-                               write(STDOUT_FD, 1, &ch);
+                               break;
+                       case 'D':       // Left
+                               if(Info->BufferWritePos == 0)   break;
+                               Info->BufferWritePos --;
+                               write(STDOUT_FD, 3, "\x1B[D");
+                               break;
+                       case 'C':       // Right
+                               if(Info->BufferWritePos == Info->BufferUsed)    break;
+                               Info->BufferWritePos ++;
+                               write(STDOUT_FD, 3, "\x1B[C");
+                               break;
                        }
-                       ret[pos++] = ch;
-                       len ++;
-                       break;
                }
-       } while(ch != '\n');
+               break;
        
-       // Cap String
-       ret[len] = '\0';
-       printf("\n");
+       // Backspace
+       case '\b':
+               if(Info->BufferWritePos <= 0)   break;  // Protect against underflows
+               // Write the backsapce
+               write(STDOUT_FD, 1, &ch);
+               if(Info->BufferWritePos == Info->BufferUsed)    // Simple case: End of string
+               {
+                       Info->BufferUsed --;
+                       Info->BufferWritePos --;
+               }
+               else
+               {
+                       // Have to delete the character, and reposition the text
+                       char    buf[7] = "\x1B[000D";
+                        int    delta = Info->BufferUsed - Info->BufferWritePos + 1;
+                       buf[2] += (delta/100) % 10;
+                       buf[3] += (delta/10) % 10;
+                       buf[4] += (delta) % 10;
+                       // Write everything save for the deleted character
+                       write(STDOUT_FD,
+                               Info->BufferUsed - Info->BufferWritePos,
+                               &Info->CurBuffer[Info->BufferWritePos]
+                               );
+                       ch = ' ';       write(STDOUT_FD, 1, &ch);       ch = '\b';      // Clear old last character
+                       write(STDOUT_FD, 7, buf);       // Update Cursor
+                       // Alter Buffer
+                       memmove(&Info->CurBuffer[Info->BufferWritePos-1],
+                               &Info->CurBuffer[Info->BufferWritePos],
+                               Info->BufferUsed-Info->BufferWritePos
+                               );
+                       Info->BufferWritePos --;
+                       Info->BufferUsed --;
+               }
+               break;
        
-       // Return length
-       //if(Length)    *Length = len;
+       // Tab
+       case '\t':
+               //TODO: Implement Tab-Completion
+               //Currently just ignore tabs
+               break;
        
-       // Add to history
-       if( Info->UseHistory )
-       {
-               if( !Info->History || strcmp( Info->History[ Info->NumHistory-1 ], ret) != 0 )
+       default:                
+               // Expand Buffer
+               if(Info->BufferUsed + 1 > Info->BufferSize)
                {
-                       void    *tmp;
-                       Info->NumHistory ++;
-                       tmp = realloc( Info->History, Info->NumHistory * sizeof(char*) );
-                       if(tmp != NULL)
-                       {
-                               Info->History = tmp;
-                               Info->History[ Info->NumHistory-1 ] = strdup(ret);
-                       }
+                       Info->BufferSize += 256;
+                       Info->CurBuffer = Info->History[Info->HistoryPos]
+                               = realloc(Info->CurBuffer, Info->BufferSize);
+                       if(!Info->CurBuffer)    return 0;
+               }
+               
+               // Editing inside the buffer
+               if(Info->BufferWritePos < Info->BufferUsed) {
+                       char    buf[7] = "\x1B[000D";
+                        int    delta = Info->BufferUsed - Info->BufferWritePos;
+                       buf[2] += (delta/100) % 10;
+                       buf[3] += (delta/10) % 10;
+                       buf[4] += (delta) % 10;
+                       write(STDOUT_FD, 1, &ch);       // Print new character
+                       write(STDOUT_FD,
+                               Info->BufferUsed - Info->BufferWritePos,
+                               &Info->CurBuffer[Info->BufferWritePos]
+                               );
+                       write(STDOUT_FD, 7, buf);       // Update Cursor
+                       // Move buffer right
+                       memmove(
+                               &Info->CurBuffer[Info->BufferWritePos+1],
+                               &Info->CurBuffer[Info->BufferWritePos],
+                               Info->BufferUsed - Info->BufferWritePos
+                               );
+               }
+               // Simple append
+               else {
+                       write(STDOUT_FD, 1, &ch);
                }
+               Info->CurBuffer[ Info->BufferWritePos ++ ] = ch;
+               Info->BufferUsed ++;
+               break;
        }
        
-       if(ret != orig) free(orig);
-       
-       return ret;
+       return ofs;
 }
index 831bb53..555a24b 100644 (file)
 typedef struct sReadline       tReadline;
 
 // === STRUCTURES ===
-struct sReadline
-{
-        int    UseHistory;     // Boolean
-       
-        int    NumHistory;
-       char    **History;
-};
 
 // === FUNCTIONS ===
+/**
+ * 
+ */
+extern tReadline       *Readline_Init(int UseHistory);
+
 /**
  * \brief Read a line from stdin
+ * \return Heap string containing the command string (to be free'd by the caller)
  */
 extern char    *Readline(tReadline *Info);
 
+/**
+ * \brief Read a line from stdin (non-blocking)
+ * \return Heap string containing the command string (to be free'd by the caller)
+ */
+extern char    *Readline_NonBlock(tReadline *Info);
+
 #endif

UCC git Repository :: git.ucc.asn.au