5 * Text mode entry with history
9 #include <acess/devices/pty.h>
18 // Size of the read() buffer
19 // - 32 should be pleanty
20 #define READ_BUFFER_SIZE 32
25 int UseHistory; // Boolean
27 // TODO: Command Completion
34 char *OutputValue; //!< Pointer (owned by history) to output value
37 int BufferSize; // Allocated size of the buffer
38 int BufferUsed; // Offset of first free byte in the buffer
39 int BufferWritePos; // Cursor location
40 char *CurBuffer; // Current translated command (pointer to a entry in history)
46 char ReadBuffer[READ_BUFFER_SIZE]; //!< Buffer for read()
52 tReadline *Readline_Init(int bUseHistory);
53 int Readline_int_ParseCharacter(tReadline *Info, char *Input);
54 char *Readline_NonBlock(tReadline *Info);
55 char *Readline(tReadline *Info);
65 tReadline *Readline_Init(int bUseHistory)
67 tReadline *ret = calloc( 1, sizeof(tReadline) );
68 ret->UseHistory = bUseHistory;
71 ret->History = malloc( 1 * sizeof(*ret->History) );
72 ret->History[0] = NULL;
80 char *Readline_NonBlock(tReadline *Info)
84 // Read as much as possible (appending to remaining data)
85 len = _SysRead(STDIN_FD, Info->ReadBuffer, READ_BUFFER_SIZE - 1 - Info->ReadBufferLen);
86 if( len <= 0 ) return NULL;
87 Info->ReadBuffer[Info->ReadBufferLen + len] = '\0';
89 // Parse the data we have
90 for( i = 0; i < len; )
92 int size = Readline_int_ParseCharacter(Info, Info->ReadBuffer+i);
93 if( size <= 0 ) break; // Error, skip the rest?
97 // Move the unused data to the start of the buffer
98 memcpy(Info->ReadBuffer, &Info->ReadBuffer[Info->ReadBufferLen + i], len - i);
99 Info->ReadBufferLen = len - i;
101 // Is the command finished?
102 if( Info->OutputValue ) {
103 char *ret = Info->OutputValue;
104 Info->OutputValue = NULL; // Mark as no command pending
105 return ret; // Return the string (now the caller's responsibility)
108 // Return NULL when command is still being edited
113 char *Readline(tReadline *Info)
118 struct ptymode mode = {.InputMode = 0, .OutputMode = 0};
119 _SysIOCtl(STDIN_FD, PTY_IOCTL_SETMODE, &mode);
121 while( NULL == (ret = Readline_NonBlock(Info)) && errno == EAGAIN );
124 mode.InputMode = PTYIMODE_CANON|PTYIMODE_ECHO;
125 _SysIOCtl(STDIN_FD, PTY_IOCTL_SETMODE, &mode);
130 int Readline_int_AddToHistory(tReadline *Info, const char *String)
134 // History[#-1] = CurBuffer (always)
135 if( !Info->History ) {
136 // Realy shouldn't happen, but just in case
137 Info->History = malloc( sizeof(char*) );
138 Info->NumHistory = 1;
141 // Don't add duplicates
142 if( Info->NumHistory >= 2 && strcmp( Info->History[ Info->NumHistory-2 ], String ) == 0 )
147 // Duplicate over the current
148 Info->History[ Info->NumHistory-1 ] = strdup(String);
152 tmp = realloc( Info->History, Info->NumHistory * sizeof(char*) );
153 if( tmp == NULL ) return -1;
156 // Zero the new current
157 Info->History[ Info->NumHistory-1 ] = NULL;
162 int Readline_int_ParseCharacter(tReadline *Info, char *Input)
167 if( Input[ofs] == 0 ) return 0;
169 // Read In Command Line
178 Info->CurBuffer[Info->BufferUsed] = '\0';
180 if( Info->UseHistory )
181 Readline_int_AddToHistory(Info, Info->CurBuffer);
182 Info->OutputValue = strdup(Info->CurBuffer);
185 Info->OutputValue = strdup("");
188 Info->BufferSize = 0;
189 Info->BufferUsed = 0;
190 Info->BufferWritePos = 0;
192 Info->HistoryPos = Info->NumHistory - 1;
199 // Control characters
201 ch = Input[ofs++]; // Read control character
204 //case 'D': if(pos) pos--; break;
205 //case 'C': if(pos<len) pos++; break;
207 ch = Input[ofs++]; // Read control character
212 int oldLen = Info->BufferUsed;
214 if( Info->HistoryPos <= 0 ) break;
216 // Move to the beginning of the line
218 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
221 Info->CurBuffer = Info->History[--Info->HistoryPos];
222 Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
224 _SysWrite(STDOUT_FD, Info->CurBuffer, Info->BufferUsed);
225 Info->BufferWritePos = Info->BufferUsed;
227 // Clear old characters (if needed)
228 if( oldLen > Info->BufferWritePos ) {
229 pos = oldLen - Info->BufferWritePos;
230 while(pos--) _SysWrite(STDOUT_FD, " ", 1);
231 pos = oldLen - Info->BufferWritePos;
232 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
238 int oldLen = Info->BufferUsed;
240 if( Info->HistoryPos >= Info->NumHistory - 1 ) break;
242 // Move to the beginning of the line
244 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
247 Info->CurBuffer = Info->History[Info->HistoryPos++];
248 Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
251 _SysWrite(STDOUT_FD, Info->CurBuffer, Info->BufferUsed);
252 Info->BufferWritePos = Info->BufferUsed;
254 // Clear old characters (if needed)
255 if( oldLen > Info->BufferWritePos ) {
256 pos = oldLen - Info->BufferWritePos;
257 while(pos--) _SysWrite(STDOUT_FD, " ", 1);
258 pos = oldLen - Info->BufferWritePos;
259 while(pos--) _SysWrite(STDOUT_FD, "\x1B[D", 3);
264 if(Info->BufferWritePos == 0) break;
265 Info->BufferWritePos --;
266 _SysWrite(STDOUT_FD, "\x1B[D", 3);
269 if(Info->BufferWritePos == Info->BufferUsed) break;
270 Info->BufferWritePos ++;
271 _SysWrite(STDOUT_FD, "\x1B[C", 3);
283 if(Info->BufferWritePos <= 0) break; // Protect against underflows
284 // Write the backsapce
285 _SysWrite(STDOUT_FD, &ch, 1);
286 if(Info->BufferWritePos == Info->BufferUsed) // Simple case: End of string
289 Info->BufferWritePos --;
293 // Have to delete the character, and reposition the text
294 char buf[7] = "\x1B[000D";
295 int delta = Info->BufferUsed - Info->BufferWritePos + 1;
296 buf[2] += (delta/100) % 10;
297 buf[3] += (delta/10) % 10;
298 buf[4] += (delta) % 10;
299 // Write everything save for the deleted character
301 &Info->CurBuffer[Info->BufferWritePos],
302 Info->BufferUsed - Info->BufferWritePos
304 ch = ' '; _SysWrite(STDOUT_FD, &ch, 1); ch = '\b'; // Clear old last character
305 _SysWrite(STDOUT_FD, buf, 7); // Update Cursor
307 memmove(&Info->CurBuffer[Info->BufferWritePos-1],
308 &Info->CurBuffer[Info->BufferWritePos],
309 Info->BufferUsed-Info->BufferWritePos
311 Info->BufferWritePos --;
318 //TODO: Implement Tab-Completion
319 //Currently just ignore tabs
324 if(Info->BufferUsed + 1 > Info->BufferSize)
326 Info->BufferSize += 256;
327 Info->CurBuffer = Info->History[Info->HistoryPos]
328 = realloc(Info->CurBuffer, Info->BufferSize);
329 if(!Info->CurBuffer) return 0;
332 // Editing inside the buffer
333 if(Info->BufferWritePos < Info->BufferUsed) {
334 char buf[7] = "\x1B[000D";
335 int delta = Info->BufferUsed - Info->BufferWritePos;
336 buf[2] += (delta/100) % 10;
337 buf[3] += (delta/10) % 10;
338 buf[4] += (delta) % 10;
339 _SysWrite(STDOUT_FD, &ch, 1); // Print new character
341 &Info->CurBuffer[Info->BufferWritePos],
342 Info->BufferUsed - Info->BufferWritePos
344 _SysWrite(STDOUT_FD, buf, 7); // Update Cursor
347 &Info->CurBuffer[Info->BufferWritePos+1],
348 &Info->CurBuffer[Info->BufferWritePos],
349 Info->BufferUsed - Info->BufferWritePos
354 _SysWrite(STDOUT_FD, &ch, 1);
356 Info->CurBuffer[ Info->BufferWritePos ++ ] = ch;