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 = read(STDIN_FD, READ_BUFFER_SIZE - 1 - Info->ReadBufferLen, Info->ReadBuffer);
84 Info->ReadBuffer[Info->ReadBufferLen + len] = '\0';
86 // Parse the data we have
87 for( i = 0; i < len; )
89 int size = Readline_int_ParseCharacter(Info, Info->ReadBuffer+i);
90 if( size <= 0 ) break; // Error, skip the rest?
94 // Move the unused data to the start of the buffer
95 memcpy(Info->ReadBuffer, &Info->ReadBuffer[Info->ReadBufferLen + i], len - i);
96 Info->ReadBufferLen = len - i;
98 // Is the command finished?
99 if( Info->OutputValue ) {
100 char *ret = Info->OutputValue;
101 Info->OutputValue = NULL; // Mark as no command pending
102 return ret; // Return the string (now the caller's responsibility)
105 // Return NULL when command is still being edited
109 char *Readline(tReadline *Info)
113 while( NULL == (ret = Readline_NonBlock(Info)) );
118 int Readline_int_AddToHistory(tReadline *Info, const char *String)
122 // History[#-1] = CurBuffer (always)
123 if( !Info->History ) {
124 // Realy shouldn't happen, but just in case
125 Info->History = malloc( sizeof(char*) );
126 Info->NumHistory = 1;
129 // Don't add duplicates
130 if( Info->NumHistory >= 2 && strcmp( Info->History[ Info->NumHistory-2 ], String ) == 0 )
135 // Duplicate over the current
136 Info->History[ Info->NumHistory-1 ] = strdup(String);
140 tmp = realloc( Info->History, Info->NumHistory * sizeof(char*) );
141 if( tmp == NULL ) return -1;
144 // Zero the new current
145 Info->History[ Info->NumHistory-1 ] = NULL;
150 int Readline_int_ParseCharacter(tReadline *Info, char *Input)
155 if( Input[ofs] == 0 ) return 0;
157 // Read In Command Line
166 Info->CurBuffer[Info->BufferUsed] = '\0';
168 if( Info->UseHistory )
169 Readline_int_AddToHistory(Info, Info->CurBuffer);
170 Info->OutputValue = strdup(Info->CurBuffer);
173 Info->OutputValue = strdup("");
176 Info->BufferSize = 0;
177 Info->BufferUsed = 0;
178 Info->BufferWritePos = 0;
180 Info->HistoryPos = Info->NumHistory - 1;
187 // Control characters
189 ch = Input[ofs++]; // Read control character
192 //case 'D': if(pos) pos--; break;
193 //case 'C': if(pos<len) pos++; break;
195 ch = Input[ofs++]; // Read control character
200 int oldLen = Info->BufferUsed;
202 if( Info->HistoryPos <= 0 ) break;
204 // Move to the beginning of the line
206 while(pos--) write(STDOUT_FD, 3, "\x1B[D");
209 Info->CurBuffer = Info->History[--Info->HistoryPos];
210 Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
212 write(STDOUT_FD, Info->BufferUsed, Info->CurBuffer);
213 Info->BufferWritePos = Info->BufferUsed;
215 // Clear old characters (if needed)
216 if( oldLen > Info->BufferWritePos ) {
217 pos = oldLen - Info->BufferWritePos;
218 while(pos--) write(STDOUT_FD, 1, " ");
219 pos = oldLen - Info->BufferWritePos;
220 while(pos--) write(STDOUT_FD, 3, "\x1B[D");
226 int oldLen = Info->BufferUsed;
228 if( Info->HistoryPos >= Info->NumHistory - 1 ) break;
230 // Move to the beginning of the line
232 while(pos--) write(STDOUT_FD, 3, "\x1B[D");
235 Info->CurBuffer = Info->History[Info->HistoryPos++];
236 Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
239 write(STDOUT_FD, Info->BufferUsed, Info->CurBuffer);
240 Info->BufferWritePos = Info->BufferUsed;
242 // Clear old characters (if needed)
243 if( oldLen > Info->BufferWritePos ) {
244 pos = oldLen - Info->BufferWritePos;
245 while(pos--) write(STDOUT_FD, 1, " ");
246 pos = oldLen - Info->BufferWritePos;
247 while(pos--) write(STDOUT_FD, 3, "\x1B[D");
252 if(Info->BufferWritePos == 0) break;
253 Info->BufferWritePos --;
254 write(STDOUT_FD, 3, "\x1B[D");
257 if(Info->BufferWritePos == Info->BufferUsed) break;
258 Info->BufferWritePos ++;
259 write(STDOUT_FD, 3, "\x1B[C");
267 if(Info->BufferWritePos <= 0) break; // Protect against underflows
268 // Write the backsapce
269 write(STDOUT_FD, 1, &ch);
270 if(Info->BufferWritePos == Info->BufferUsed) // Simple case: End of string
273 Info->BufferWritePos --;
277 // Have to delete the character, and reposition the text
278 char buf[7] = "\x1B[000D";
279 int delta = Info->BufferUsed - Info->BufferWritePos + 1;
280 buf[2] += (delta/100) % 10;
281 buf[3] += (delta/10) % 10;
282 buf[4] += (delta) % 10;
283 // Write everything save for the deleted character
285 Info->BufferUsed - Info->BufferWritePos,
286 &Info->CurBuffer[Info->BufferWritePos]
288 ch = ' '; write(STDOUT_FD, 1, &ch); ch = '\b'; // Clear old last character
289 write(STDOUT_FD, 7, buf); // Update Cursor
291 memmove(&Info->CurBuffer[Info->BufferWritePos-1],
292 &Info->CurBuffer[Info->BufferWritePos],
293 Info->BufferUsed-Info->BufferWritePos
295 Info->BufferWritePos --;
302 //TODO: Implement Tab-Completion
303 //Currently just ignore tabs
308 if(Info->BufferUsed + 1 > Info->BufferSize)
310 Info->BufferSize += 256;
311 Info->CurBuffer = Info->History[Info->HistoryPos]
312 = realloc(Info->CurBuffer, Info->BufferSize);
313 if(!Info->CurBuffer) return 0;
316 // Editing inside the buffer
317 if(Info->BufferWritePos < Info->BufferUsed) {
318 char buf[7] = "\x1B[000D";
319 int delta = Info->BufferUsed - Info->BufferWritePos;
320 buf[2] += (delta/100) % 10;
321 buf[3] += (delta/10) % 10;
322 buf[4] += (delta) % 10;
323 write(STDOUT_FD, 1, &ch); // Print new character
325 Info->BufferUsed - Info->BufferWritePos,
326 &Info->CurBuffer[Info->BufferWritePos]
328 write(STDOUT_FD, 7, buf); // Update Cursor
331 &Info->CurBuffer[Info->BufferWritePos+1],
332 &Info->CurBuffer[Info->BufferWritePos],
333 Info->BufferUsed - Info->BufferWritePos
338 write(STDOUT_FD, 1, &ch);
340 Info->CurBuffer[ Info->BufferWritePos ++ ] = ch;