Usermode/libc - AcessNative hacks to separate acess and native namespaces
[tpg/acess2.git] / Usermode / Libraries / libreadline.so_src / main.c
1 /*
2  * Acess2 Library Suite
3  * - Readline
4  * 
5  * Text mode entry with history
6  */
7 #include <readline.h>
8 #include <acess/sys.h>
9 #include <acess/devices/pty.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13
14 #define STDIN_FD        0
15 #define STDOUT_FD       1
16
17 // Size of the read() buffer
18 // - 32 should be pleanty
19 #define READ_BUFFER_SIZE        32
20
21 // === STRUCTURES ===
22 struct sReadline
23 {
24          int    UseHistory;     // Boolean
25         
26         // TODO: Command Completion
27         
28         // History
29          int    NumHistory;
30         char    **History;
31         
32         // Internal Flags
33         char    *OutputValue;   //!< Pointer (owned by history) to output value
34         
35         // Command Buffer
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)
40         
41         // 
42          int    HistoryPos;
43         
44         // Read Buffer
45         char    ReadBuffer[READ_BUFFER_SIZE];   //!< Buffer for read()
46          int    ReadBufferLen;
47 };
48
49 // === PROTOTYPES ===
50  int    SoMain();
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);
55
56 // === GLOBALS ===
57
58 // === CODE ===
59 int SoMain()
60 {
61         return 0;
62 }
63
64 tReadline *Readline_Init(int bUseHistory)
65 {
66         tReadline       *ret = calloc( 1, sizeof(tReadline) );
67         ret->UseHistory = bUseHistory;
68         ret->BufferSize = 0;
69         
70         ret->History = malloc( 1 * sizeof(*ret->History) );
71         ret->History[0] = NULL;
72         ret->NumHistory = 1;
73         
74         return ret;
75 }
76
77 /**
78  */
79 char *Readline_NonBlock(tReadline *Info)
80 {
81          int    len, i;
82         
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';
87         
88         // Parse the data we have
89         for( i = 0; i < len; )
90         {
91                 int size = Readline_int_ParseCharacter(Info, Info->ReadBuffer+i);
92                 if( size <= 0 ) break;  // Error, skip the rest?
93                 i += size;
94         }
95         
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;
99         
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)
105         }
106         
107         // Return NULL when command is still being edited
108         return NULL;
109 }
110
111 char *Readline(tReadline *Info)
112 {
113         char    *ret;
114
115         // stty -echo,canon
116         struct ptymode  mode = {.InputMode = 0, .OutputMode = 0};
117         _SysIOCtl(STDIN_FD, PTY_IOCTL_SETMODE, &mode);
118         
119         while( NULL == (ret = Readline_NonBlock(Info)) );
120
121         // stty +echo,canon
122         mode.InputMode = PTYIMODE_CANON|PTYIMODE_ECHO;
123         _SysIOCtl(STDIN_FD, PTY_IOCTL_SETMODE, &mode);
124
125         return ret;
126 }
127
128 int Readline_int_AddToHistory(tReadline *Info, const char *String)
129 {
130         void    *tmp;
131         
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;
137         }
138         
139         // Don't add duplicates
140         if( Info->NumHistory >= 2 && strcmp( Info->History[ Info->NumHistory-2 ], String ) == 0 )
141         {
142                 return 0;
143         }
144         
145         // Duplicate over the current
146         Info->History[ Info->NumHistory-1 ] = strdup(String);
147         
148         Info->NumHistory ++;
149         
150         tmp = realloc( Info->History, Info->NumHistory * sizeof(char*) );
151         if( tmp == NULL )       return -1;
152         Info->History = tmp;
153                                 
154         // Zero the new current
155         Info->History[ Info->NumHistory-1 ] = NULL;
156         
157         return 0;
158 }
159
160 int Readline_int_ParseCharacter(tReadline *Info, char *Input)
161 {
162          int    ofs = 0;
163         char    ch;
164         
165         if( Input[ofs] == 0 )   return 0;
166         
167         // Read In Command Line
168         ch = Input[ofs++];
169         
170         if(ch == '\n')
171         {
172 //              printf("\n");
173                 if(Info->CurBuffer)
174                 {       
175                         // Cap String
176                         Info->CurBuffer[Info->BufferUsed] = '\0';
177                         
178                         if( Info->UseHistory )
179                                 Readline_int_AddToHistory(Info, Info->CurBuffer);
180                         Info->OutputValue = strdup(Info->CurBuffer);
181                 }
182                 else
183                         Info->OutputValue = strdup("");
184                 
185                 // Save and reset
186                 Info->BufferSize = 0;
187                 Info->BufferUsed = 0;
188                 Info->BufferWritePos = 0;
189                 Info->CurBuffer = 0;
190                 Info->HistoryPos = Info->NumHistory - 1;
191                 
192                 return 1;
193         }
194         
195         switch(ch)
196         {
197         // Control characters
198         case '\x1B':
199                 ch = Input[ofs++];      // Read control character
200                 switch(ch)
201                 {
202                 //case 'D':     if(pos) pos--;  break;
203                 //case 'C':     if(pos<len)     pos++;  break;
204                 case '[':
205                         ch = Input[ofs++];      // Read control character
206                         switch(ch)
207                         {
208                         case 'A':       // Up
209                                 {
210                                          int    oldLen = Info->BufferUsed;
211                                          int    pos;
212                                         if( Info->HistoryPos <= 0 )     break;
213                                         
214                                         // Move to the beginning of the line
215                                         pos = oldLen;
216                                         while(pos--)    _SysWrite(STDOUT_FD, "\x1B[D", 3);
217                                         
218                                         // Update state
219                                         Info->CurBuffer = Info->History[--Info->HistoryPos];
220                                         Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
221                                         
222                                         _SysWrite(STDOUT_FD, Info->CurBuffer, Info->BufferUsed);
223                                         Info->BufferWritePos = Info->BufferUsed;
224                                         
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);
231                                         }
232                                 }
233                                 break;
234                         case 'B':       // Down
235                                 {
236                                          int    oldLen = Info->BufferUsed;
237                                          int    pos;
238                                         if( Info->HistoryPos >= Info->NumHistory - 1 )  break;
239                                         
240                                         // Move to the beginning of the line
241                                         pos = oldLen;
242                                         while(pos--)    _SysWrite(STDOUT_FD, "\x1B[D", 3);
243                                         
244                                         // Update state
245                                         Info->CurBuffer = Info->History[Info->HistoryPos++];
246                                         Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
247                                         
248                                         // Write new line
249                                         _SysWrite(STDOUT_FD, Info->CurBuffer, Info->BufferUsed);
250                                         Info->BufferWritePos = Info->BufferUsed;
251                                         
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);
258                                         }
259                                 }
260                                 break;
261                         case 'D':       // Left
262                                 if(Info->BufferWritePos == 0)   break;
263                                 Info->BufferWritePos --;
264                                 _SysWrite(STDOUT_FD, "\x1B[D", 3);
265                                 break;
266                         case 'C':       // Right
267                                 if(Info->BufferWritePos == Info->BufferUsed)    break;
268                                 Info->BufferWritePos ++;
269                                 _SysWrite(STDOUT_FD, "\x1B[C", 3);
270                                 break;
271                         }
272                         break;
273                 case '\0':
274                         ofs --;
275                         break;
276                 }
277                 break;
278         
279         // Backspace
280         case '\b':
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
285                 {
286                         Info->BufferUsed --;
287                         Info->BufferWritePos --;
288                 }
289                 else
290                 {
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
298                         _SysWrite(STDOUT_FD,
299                                 &Info->CurBuffer[Info->BufferWritePos],
300                                 Info->BufferUsed - Info->BufferWritePos
301                                 );
302                         ch = ' ';       _SysWrite(STDOUT_FD, &ch, 1);   ch = '\b';      // Clear old last character
303                         _SysWrite(STDOUT_FD, buf, 7);   // Update Cursor
304                         // Alter Buffer
305                         memmove(&Info->CurBuffer[Info->BufferWritePos-1],
306                                 &Info->CurBuffer[Info->BufferWritePos],
307                                 Info->BufferUsed-Info->BufferWritePos
308                                 );
309                         Info->BufferWritePos --;
310                         Info->BufferUsed --;
311                 }
312                 break;
313         
314         // Tab
315         case '\t':
316                 //TODO: Implement Tab-Completion
317                 //Currently just ignore tabs
318                 break;
319         
320         default:                
321                 // Expand Buffer
322                 if(Info->BufferUsed + 1 > Info->BufferSize)
323                 {
324                         Info->BufferSize += 256;
325                         Info->CurBuffer = Info->History[Info->HistoryPos]
326                                 = realloc(Info->CurBuffer, Info->BufferSize);
327                         if(!Info->CurBuffer)    return 0;
328                 }
329                 
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
338                         _SysWrite(STDOUT_FD,
339                                 &Info->CurBuffer[Info->BufferWritePos],
340                                 Info->BufferUsed - Info->BufferWritePos
341                                 );
342                         _SysWrite(STDOUT_FD, buf, 7);   // Update Cursor
343                         // Move buffer right
344                         memmove(
345                                 &Info->CurBuffer[Info->BufferWritePos+1],
346                                 &Info->CurBuffer[Info->BufferWritePos],
347                                 Info->BufferUsed - Info->BufferWritePos
348                                 );
349                 }
350                 // Simple append
351                 else {
352                         _SysWrite(STDOUT_FD, &ch, 1);
353                 }
354                 Info->CurBuffer[ Info->BufferWritePos ++ ] = ch;
355                 Info->BufferUsed ++;
356                 break;
357         }
358         
359         return ofs;
360 }

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