5 * Text mode entry with history
16 // Size of the read() buffer
17 // - 32 should be pleanty
18 #define READ_BUFFER_SIZE 32
23 int UseHistory; // Boolean
25 // TODO: Command Completion
32 char *OutputValue; //!< Pointer (owned by history) to output value
35 int BufferSize; // Allocated size of the buffer
36 int BufferUsed; // Offset of first free byte in the buffer
37 int BufferWritePos; // Cursor location
38 char *CurBuffer; // Current translated command (pointer to a entry in history)
44 char ReadBuffer[READ_BUFFER_SIZE]; //!< Buffer for read()
50 tReadline *Readline_Init(int bUseHistory);
51 int Readline_int_ParseCharacter(tReadline *Info, char *Input);
52 char *Readline_NonBlock(tReadline *Info);
53 char *Readline(tReadline *Info);
63 tReadline *Readline_Init(int bUseHistory)
65 tReadline *ret = calloc( 1, sizeof(tReadline) );
66 ret->UseHistory = bUseHistory;
69 ret->History = malloc( 1 * sizeof(*ret->History) );
70 ret->History[0] = NULL;
78 char *Readline_NonBlock(tReadline *Info)
82 // Read as much as possible (appending to remaining data)
83 len = _SysRead(STDIN_FD, Info->ReadBuffer, READ_BUFFER_SIZE - 1 - Info->ReadBufferLen);
84 if( len <= 0 ) return NULL;
85 Info->ReadBuffer[Info->ReadBufferLen + len] = '\0';
87 // Parse the data we have
88 for( i = 0; i < len; )
90 int size = Readline_int_ParseCharacter(Info, Info->ReadBuffer+i);
91 if( size <= 0 ) break; // Error, skip the rest?
95 // Move the unused data to the start of the buffer
96 memcpy(Info->ReadBuffer, &Info->ReadBuffer[Info->ReadBufferLen + i], len - i);
97 Info->ReadBufferLen = len - i;
99 // Is the command finished?
100 if( Info->OutputValue ) {
101 char *ret = Info->OutputValue;
102 Info->OutputValue = NULL; // Mark as no command pending
103 return ret; // Return the string (now the caller's responsibility)
106 // Return NULL when command is still being edited
110 char *Readline(tReadline *Info)
114 while( NULL == (ret = Readline_NonBlock(Info)) );
119 int Readline_int_AddToHistory(tReadline *Info, const char *String)
123 // History[#-1] = CurBuffer (always)
124 if( !Info->History ) {
125 // Realy shouldn't happen, but just in case
126 Info->History = malloc( sizeof(char*) );
127 Info->NumHistory = 1;
130 // Don't add duplicates
131 if( Info->NumHistory >= 2 && strcmp( Info->History[ Info->NumHistory-2 ], String ) == 0 )
136 // Duplicate over the current
137 Info->History[ Info->NumHistory-1 ] = strdup(String);
141 tmp = realloc( Info->History, Info->NumHistory * sizeof(char*) );
142 if( tmp == NULL ) return -1;
145 // Zero the new current
146 Info->History[ Info->NumHistory-1 ] = NULL;
151 int Readline_int_ParseCharacter(tReadline *Info, char *Input)
156 if( Input[ofs] == 0 ) return 0;
158 // Read In Command Line
167 Info->CurBuffer[Info->BufferUsed] = '\0';
169 if( Info->UseHistory )
170 Readline_int_AddToHistory(Info, Info->CurBuffer);
171 Info->OutputValue = strdup(Info->CurBuffer);
174 Info->OutputValue = strdup("");
177 Info->BufferSize = 0;
178 Info->BufferUsed = 0;
179 Info->BufferWritePos = 0;
181 Info->HistoryPos = Info->NumHistory - 1;
188 // Control characters
190 ch = Input[ofs++]; // Read control character
193 //case 'D': if(pos) pos--; break;
194 //case 'C': if(pos<len) pos++; break;
196 ch = Input[ofs++]; // Read control character
201 int oldLen = Info->BufferUsed;
203 if( Info->HistoryPos <= 0 ) break;
205 // Move to the beginning of the line
207 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
210 Info->CurBuffer = Info->History[--Info->HistoryPos];
211 Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
213 _SysWrite(STDOUT_FD, Info->CurBuffer, Info->BufferUsed);
214 Info->BufferWritePos = Info->BufferUsed;
216 // Clear old characters (if needed)
217 if( oldLen > Info->BufferWritePos ) {
218 pos = oldLen - Info->BufferWritePos;
219 while(pos--) _SysWrite(STDOUT_FD, " ", 1);
220 pos = oldLen - Info->BufferWritePos;
221 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
227 int oldLen = Info->BufferUsed;
229 if( Info->HistoryPos >= Info->NumHistory - 1 ) break;
231 // Move to the beginning of the line
233 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
236 Info->CurBuffer = Info->History[Info->HistoryPos++];
237 Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
240 _SysWrite(STDOUT_FD, Info->CurBuffer, Info->BufferUsed);
241 Info->BufferWritePos = Info->BufferUsed;
243 // Clear old characters (if needed)
244 if( oldLen > Info->BufferWritePos ) {
245 pos = oldLen - Info->BufferWritePos;
246 while(pos--) _SysWrite(STDOUT_FD, " ", 1);
247 pos = oldLen - Info->BufferWritePos;
248 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
253 if(Info->BufferWritePos == 0) break;
254 Info->BufferWritePos --;
255 _SysWrite(STDOUT_FD, "\x1B[D", 3);
258 if(Info->BufferWritePos == Info->BufferUsed) break;
259 Info->BufferWritePos ++;
260 _SysWrite(STDOUT_FD, "\x1B[C", 3);
272 if(Info->BufferWritePos <= 0) break; // Protect against underflows
273 // Write the backsapce
274 _SysWrite(STDOUT_FD, &ch, 1);
275 if(Info->BufferWritePos == Info->BufferUsed) // Simple case: End of string
278 Info->BufferWritePos --;
282 // Have to delete the character, and reposition the text
283 char buf[7] = "\x1B[000D";
284 int delta = Info->BufferUsed - Info->BufferWritePos + 1;
285 buf[2] += (delta/100) % 10;
286 buf[3] += (delta/10) % 10;
287 buf[4] += (delta) % 10;
288 // Write everything save for the deleted character
290 &Info->CurBuffer[Info->BufferWritePos],
291 Info->BufferUsed - Info->BufferWritePos
293 ch = ' '; _SysWrite(STDOUT_FD, &ch, 1); ch = '\b'; // Clear old last character
294 _SysWrite(STDOUT_FD, buf, 7); // Update Cursor
296 memmove(&Info->CurBuffer[Info->BufferWritePos-1],
297 &Info->CurBuffer[Info->BufferWritePos],
298 Info->BufferUsed-Info->BufferWritePos
300 Info->BufferWritePos --;
307 //TODO: Implement Tab-Completion
308 //Currently just ignore tabs
313 if(Info->BufferUsed + 1 > Info->BufferSize)
315 Info->BufferSize += 256;
316 Info->CurBuffer = Info->History[Info->HistoryPos]
317 = realloc(Info->CurBuffer, Info->BufferSize);
318 if(!Info->CurBuffer) return 0;
321 // Editing inside the buffer
322 if(Info->BufferWritePos < Info->BufferUsed) {
323 char buf[7] = "\x1B[000D";
324 int delta = Info->BufferUsed - Info->BufferWritePos;
325 buf[2] += (delta/100) % 10;
326 buf[3] += (delta/10) % 10;
327 buf[4] += (delta) % 10;
328 _SysWrite(STDOUT_FD, &ch, 1); // Print new character
330 &Info->CurBuffer[Info->BufferWritePos],
331 Info->BufferUsed - Info->BufferWritePos
333 _SysWrite(STDOUT_FD, buf, 7); // Update Cursor
336 &Info->CurBuffer[Info->BufferWritePos+1],
337 &Info->CurBuffer[Info->BufferWritePos],
338 Info->BufferUsed - Info->BufferWritePos
343 _SysWrite(STDOUT_FD, &ch, 1);
345 Info->CurBuffer[ Info->BufferWritePos ++ ] = ch;