5 * Text mode entry with history
9 #include <acess/devices/pty.h>
17 // Size of the read() buffer
18 // - 32 should be pleanty
19 #define READ_BUFFER_SIZE 32
24 int UseHistory; // Boolean
26 // TODO: Command Completion
33 char *OutputValue; //!< Pointer (owned by history) to output value
36 int BufferSize; // Allocated size of the buffer
37 int BufferUsed; // Offset of first free byte in the buffer
38 int BufferWritePos; // Cursor location
39 char *CurBuffer; // Current translated command (pointer to a entry in history)
45 char ReadBuffer[READ_BUFFER_SIZE]; //!< Buffer for read()
51 tReadline *Readline_Init(int bUseHistory);
52 int Readline_int_ParseCharacter(tReadline *Info, char *Input);
53 char *Readline_NonBlock(tReadline *Info);
54 char *Readline(tReadline *Info);
64 tReadline *Readline_Init(int bUseHistory)
66 tReadline *ret = calloc( 1, sizeof(tReadline) );
67 ret->UseHistory = bUseHistory;
70 ret->History = malloc( 1 * sizeof(*ret->History) );
71 ret->History[0] = NULL;
79 char *Readline_NonBlock(tReadline *Info)
83 // Read as much as possible (appending to remaining data)
84 len = _SysRead(STDIN_FD, Info->ReadBuffer, READ_BUFFER_SIZE - 1 - Info->ReadBufferLen);
85 if( len <= 0 ) return NULL;
86 Info->ReadBuffer[Info->ReadBufferLen + len] = '\0';
88 // Parse the data we have
89 for( i = 0; i < len; )
91 int size = Readline_int_ParseCharacter(Info, Info->ReadBuffer+i);
92 if( size <= 0 ) break; // Error, skip the rest?
96 // Move the unused data to the start of the buffer
97 memcpy(Info->ReadBuffer, &Info->ReadBuffer[Info->ReadBufferLen + i], len - i);
98 Info->ReadBufferLen = len - i;
100 // Is the command finished?
101 if( Info->OutputValue ) {
102 char *ret = Info->OutputValue;
103 Info->OutputValue = NULL; // Mark as no command pending
104 return ret; // Return the string (now the caller's responsibility)
107 // Return NULL when command is still being edited
111 char *Readline(tReadline *Info)
116 struct ptymode mode = {.InputMode = 0, .OutputMode = 0};
117 _SysIOCtl(STDIN_FD, PTY_IOCTL_SETMODE, &mode);
119 while( NULL == (ret = Readline_NonBlock(Info)) );
122 mode.InputMode = PTYIMODE_CANON|PTYIMODE_ECHO;
123 _SysIOCtl(STDIN_FD, PTY_IOCTL_SETMODE, &mode);
128 int Readline_int_AddToHistory(tReadline *Info, const char *String)
132 // History[#-1] = CurBuffer (always)
133 if( !Info->History ) {
134 // Realy shouldn't happen, but just in case
135 Info->History = malloc( sizeof(char*) );
136 Info->NumHistory = 1;
139 // Don't add duplicates
140 if( Info->NumHistory >= 2 && strcmp( Info->History[ Info->NumHistory-2 ], String ) == 0 )
145 // Duplicate over the current
146 Info->History[ Info->NumHistory-1 ] = strdup(String);
150 tmp = realloc( Info->History, Info->NumHistory * sizeof(char*) );
151 if( tmp == NULL ) return -1;
154 // Zero the new current
155 Info->History[ Info->NumHistory-1 ] = NULL;
160 int Readline_int_ParseCharacter(tReadline *Info, char *Input)
165 if( Input[ofs] == 0 ) return 0;
167 // Read In Command Line
176 Info->CurBuffer[Info->BufferUsed] = '\0';
178 if( Info->UseHistory )
179 Readline_int_AddToHistory(Info, Info->CurBuffer);
180 Info->OutputValue = strdup(Info->CurBuffer);
183 Info->OutputValue = strdup("");
186 Info->BufferSize = 0;
187 Info->BufferUsed = 0;
188 Info->BufferWritePos = 0;
190 Info->HistoryPos = Info->NumHistory - 1;
197 // Control characters
199 ch = Input[ofs++]; // Read control character
202 //case 'D': if(pos) pos--; break;
203 //case 'C': if(pos<len) pos++; break;
205 ch = Input[ofs++]; // Read control character
210 int oldLen = Info->BufferUsed;
212 if( Info->HistoryPos <= 0 ) break;
214 // Move to the beginning of the line
216 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
219 Info->CurBuffer = Info->History[--Info->HistoryPos];
220 Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
222 _SysWrite(STDOUT_FD, Info->CurBuffer, Info->BufferUsed);
223 Info->BufferWritePos = Info->BufferUsed;
225 // Clear old characters (if needed)
226 if( oldLen > Info->BufferWritePos ) {
227 pos = oldLen - Info->BufferWritePos;
228 while(pos--) _SysWrite(STDOUT_FD, " ", 1);
229 pos = oldLen - Info->BufferWritePos;
230 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
236 int oldLen = Info->BufferUsed;
238 if( Info->HistoryPos >= Info->NumHistory - 1 ) break;
240 // Move to the beginning of the line
242 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
245 Info->CurBuffer = Info->History[Info->HistoryPos++];
246 Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
249 _SysWrite(STDOUT_FD, Info->CurBuffer, Info->BufferUsed);
250 Info->BufferWritePos = Info->BufferUsed;
252 // Clear old characters (if needed)
253 if( oldLen > Info->BufferWritePos ) {
254 pos = oldLen - Info->BufferWritePos;
255 while(pos--) _SysWrite(STDOUT_FD, " ", 1);
256 pos = oldLen - Info->BufferWritePos;
257 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
262 if(Info->BufferWritePos == 0) break;
263 Info->BufferWritePos --;
264 _SysWrite(STDOUT_FD, "\x1B[D", 3);
267 if(Info->BufferWritePos == Info->BufferUsed) break;
268 Info->BufferWritePos ++;
269 _SysWrite(STDOUT_FD, "\x1B[C", 3);
281 if(Info->BufferWritePos <= 0) break; // Protect against underflows
282 // Write the backsapce
283 _SysWrite(STDOUT_FD, &ch, 1);
284 if(Info->BufferWritePos == Info->BufferUsed) // Simple case: End of string
287 Info->BufferWritePos --;
291 // Have to delete the character, and reposition the text
292 char buf[7] = "\x1B[000D";
293 int delta = Info->BufferUsed - Info->BufferWritePos + 1;
294 buf[2] += (delta/100) % 10;
295 buf[3] += (delta/10) % 10;
296 buf[4] += (delta) % 10;
297 // Write everything save for the deleted character
299 &Info->CurBuffer[Info->BufferWritePos],
300 Info->BufferUsed - Info->BufferWritePos
302 ch = ' '; _SysWrite(STDOUT_FD, &ch, 1); ch = '\b'; // Clear old last character
303 _SysWrite(STDOUT_FD, buf, 7); // Update Cursor
305 memmove(&Info->CurBuffer[Info->BufferWritePos-1],
306 &Info->CurBuffer[Info->BufferWritePos],
307 Info->BufferUsed-Info->BufferWritePos
309 Info->BufferWritePos --;
316 //TODO: Implement Tab-Completion
317 //Currently just ignore tabs
322 if(Info->BufferUsed + 1 > Info->BufferSize)
324 Info->BufferSize += 256;
325 Info->CurBuffer = Info->History[Info->HistoryPos]
326 = realloc(Info->CurBuffer, Info->BufferSize);
327 if(!Info->CurBuffer) return 0;
330 // Editing inside the buffer
331 if(Info->BufferWritePos < Info->BufferUsed) {
332 char buf[7] = "\x1B[000D";
333 int delta = Info->BufferUsed - Info->BufferWritePos;
334 buf[2] += (delta/100) % 10;
335 buf[3] += (delta/10) % 10;
336 buf[4] += (delta) % 10;
337 _SysWrite(STDOUT_FD, &ch, 1); // Print new character
339 &Info->CurBuffer[Info->BufferWritePos],
340 Info->BufferUsed - Info->BufferWritePos
342 _SysWrite(STDOUT_FD, buf, 7); // Update Cursor
345 &Info->CurBuffer[Info->BufferWritePos+1],
346 &Info->CurBuffer[Info->BufferWritePos],
347 Info->BufferUsed - Info->BufferWritePos
352 _SysWrite(STDOUT_FD, &ch, 1);
354 Info->CurBuffer[ Info->BufferWritePos ++ ] = ch;