AcessNative - Fixing Fixing Fixing
[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 <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12
13 #define STDIN_FD        0
14 #define STDOUT_FD       1
15
16 // Size of the read() buffer
17 // - 32 should be pleanty
18 #define READ_BUFFER_SIZE        32
19
20 // === STRUCTURES ===
21 struct sReadline
22 {
23          int    UseHistory;     // Boolean
24         
25         // TODO: Command Completion
26         
27         // History
28          int    NumHistory;
29         char    **History;
30         
31         // Internal Flags
32         char    *OutputValue;   //!< Pointer (owned by history) to output value
33         
34         // Command Buffer
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)
39         
40         // 
41          int    HistoryPos;
42         
43         // Read Buffer
44         char    ReadBuffer[READ_BUFFER_SIZE];   //!< Buffer for read()
45          int    ReadBufferLen;
46 };
47
48 // === PROTOTYPES ===
49  int    SoMain();
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);
54
55 // === GLOBALS ===
56
57 // === CODE ===
58 int SoMain()
59 {
60         return 0;
61 }
62
63 tReadline *Readline_Init(int bUseHistory)
64 {
65         tReadline       *ret = calloc( 1, sizeof(tReadline) );
66         ret->UseHistory = bUseHistory;
67         ret->BufferSize = 0;
68         
69         ret->History = malloc( 1 * sizeof(*ret->History) );
70         ret->History[0] = NULL;
71         ret->NumHistory = 1;
72         
73         return ret;
74 }
75
76 /**
77  */
78 char *Readline_NonBlock(tReadline *Info)
79 {
80          int    len, i;
81         
82         // Read as much as possible (appending to remaining data)
83         len = read(STDIN_FD, READ_BUFFER_SIZE - 1 - Info->ReadBufferLen, Info->ReadBuffer);
84         if( len <= 0 )  return NULL;
85         Info->ReadBuffer[Info->ReadBufferLen + len] = '\0';
86         
87         // Parse the data we have
88         for( i = 0; i < len; )
89         {
90                 int size = Readline_int_ParseCharacter(Info, Info->ReadBuffer+i);
91                 if( size <= 0 ) break;  // Error, skip the rest?
92                 i += size;
93         }
94         
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;
98         
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)
104         }
105         
106         // Return NULL when command is still being edited
107         return NULL;
108 }
109
110 char *Readline(tReadline *Info)
111 {
112         char    *ret;
113         
114         while( NULL == (ret = Readline_NonBlock(Info)) );
115         
116         return ret;
117 }
118
119 int Readline_int_AddToHistory(tReadline *Info, const char *String)
120 {
121         void    *tmp;
122         
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;
128         }
129         
130         // Don't add duplicates
131         if( Info->NumHistory >= 2 && strcmp( Info->History[ Info->NumHistory-2 ], String ) == 0 )
132         {
133                 return 0;
134         }
135         
136         // Duplicate over the current
137         Info->History[ Info->NumHistory-1 ] = strdup(String);
138         
139         Info->NumHistory ++;
140         
141         tmp = realloc( Info->History, Info->NumHistory * sizeof(char*) );
142         if( tmp == NULL )       return -1;
143         Info->History = tmp;
144                                 
145         // Zero the new current
146         Info->History[ Info->NumHistory-1 ] = NULL;
147         
148         return 0;
149 }
150
151 int Readline_int_ParseCharacter(tReadline *Info, char *Input)
152 {
153          int    ofs = 0;
154         char    ch;
155         
156         if( Input[ofs] == 0 )   return 0;
157         
158         // Read In Command Line
159         ch = Input[ofs++];
160         
161         if(ch == '\n')
162         {
163 //              printf("\n");
164                 if(Info->CurBuffer)
165                 {       
166                         // Cap String
167                         Info->CurBuffer[Info->BufferUsed] = '\0';
168                         
169                         if( Info->UseHistory )
170                                 Readline_int_AddToHistory(Info, Info->CurBuffer);
171                         Info->OutputValue = strdup(Info->CurBuffer);
172                 }
173                 else
174                         Info->OutputValue = strdup("");
175                 
176                 // Save and reset
177                 Info->BufferSize = 0;
178                 Info->BufferUsed = 0;
179                 Info->BufferWritePos = 0;
180                 Info->CurBuffer = 0;
181                 Info->HistoryPos = Info->NumHistory - 1;
182                 
183                 return 1;
184         }
185         
186         switch(ch)
187         {
188         // Control characters
189         case '\x1B':
190                 ch = Input[ofs++];      // Read control character
191                 switch(ch)
192                 {
193                 //case 'D':     if(pos) pos--;  break;
194                 //case 'C':     if(pos<len)     pos++;  break;
195                 case '[':
196                         ch = Input[ofs++];      // Read control character
197                         switch(ch)
198                         {
199                         case 'A':       // Up
200                                 {
201                                          int    oldLen = Info->BufferUsed;
202                                          int    pos;
203                                         if( Info->HistoryPos <= 0 )     break;
204                                         
205                                         // Move to the beginning of the line
206                                         pos = oldLen;
207                                         while(pos--)    write(STDOUT_FD, 3, "\x1B[D");
208                                         
209                                         // Update state
210                                         Info->CurBuffer = Info->History[--Info->HistoryPos];
211                                         Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
212                                         
213                                         write(STDOUT_FD, Info->BufferUsed, Info->CurBuffer);
214                                         Info->BufferWritePos = Info->BufferUsed;
215                                         
216                                         // Clear old characters (if needed)
217                                         if( oldLen > Info->BufferWritePos ) {
218                                                 pos = oldLen - Info->BufferWritePos;
219                                                 while(pos--)    write(STDOUT_FD, 1, " ");
220                                                 pos = oldLen - Info->BufferWritePos;
221                                                 while(pos--)    write(STDOUT_FD, 3, "\x1B[D");
222                                         }
223                                 }
224                                 break;
225                         case 'B':       // Down
226                                 {
227                                          int    oldLen = Info->BufferUsed;
228                                          int    pos;
229                                         if( Info->HistoryPos >= Info->NumHistory - 1 )  break;
230                                         
231                                         // Move to the beginning of the line
232                                         pos = oldLen;
233                                         while(pos--)    write(STDOUT_FD, 3, "\x1B[D");
234                                         
235                                         // Update state
236                                         Info->CurBuffer = Info->History[Info->HistoryPos++];
237                                         Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
238                                         
239                                         // Write new line
240                                         write(STDOUT_FD, Info->BufferUsed, Info->CurBuffer);
241                                         Info->BufferWritePos = Info->BufferUsed;
242                                         
243                                         // Clear old characters (if needed)
244                                         if( oldLen > Info->BufferWritePos ) {
245                                                 pos = oldLen - Info->BufferWritePos;
246                                                 while(pos--)    write(STDOUT_FD, 1, " ");
247                                                 pos = oldLen - Info->BufferWritePos;
248                                                 while(pos--)    write(STDOUT_FD, 3, "\x1B[D");
249                                         }
250                                 }
251                                 break;
252                         case 'D':       // Left
253                                 if(Info->BufferWritePos == 0)   break;
254                                 Info->BufferWritePos --;
255                                 write(STDOUT_FD, 3, "\x1B[D");
256                                 break;
257                         case 'C':       // Right
258                                 if(Info->BufferWritePos == Info->BufferUsed)    break;
259                                 Info->BufferWritePos ++;
260                                 write(STDOUT_FD, 3, "\x1B[C");
261                                 break;
262                         }
263                 }
264                 break;
265         
266         // Backspace
267         case '\b':
268                 if(Info->BufferWritePos <= 0)   break;  // Protect against underflows
269                 // Write the backsapce
270                 write(STDOUT_FD, 1, &ch);
271                 if(Info->BufferWritePos == Info->BufferUsed)    // Simple case: End of string
272                 {
273                         Info->BufferUsed --;
274                         Info->BufferWritePos --;
275                 }
276                 else
277                 {
278                         // Have to delete the character, and reposition the text
279                         char    buf[7] = "\x1B[000D";
280                          int    delta = Info->BufferUsed - Info->BufferWritePos + 1;
281                         buf[2] += (delta/100) % 10;
282                         buf[3] += (delta/10) % 10;
283                         buf[4] += (delta) % 10;
284                         // Write everything save for the deleted character
285                         write(STDOUT_FD,
286                                 Info->BufferUsed - Info->BufferWritePos,
287                                 &Info->CurBuffer[Info->BufferWritePos]
288                                 );
289                         ch = ' ';       write(STDOUT_FD, 1, &ch);       ch = '\b';      // Clear old last character
290                         write(STDOUT_FD, 7, buf);       // Update Cursor
291                         // Alter Buffer
292                         memmove(&Info->CurBuffer[Info->BufferWritePos-1],
293                                 &Info->CurBuffer[Info->BufferWritePos],
294                                 Info->BufferUsed-Info->BufferWritePos
295                                 );
296                         Info->BufferWritePos --;
297                         Info->BufferUsed --;
298                 }
299                 break;
300         
301         // Tab
302         case '\t':
303                 //TODO: Implement Tab-Completion
304                 //Currently just ignore tabs
305                 break;
306         
307         default:                
308                 // Expand Buffer
309                 if(Info->BufferUsed + 1 > Info->BufferSize)
310                 {
311                         Info->BufferSize += 256;
312                         Info->CurBuffer = Info->History[Info->HistoryPos]
313                                 = realloc(Info->CurBuffer, Info->BufferSize);
314                         if(!Info->CurBuffer)    return 0;
315                 }
316                 
317                 // Editing inside the buffer
318                 if(Info->BufferWritePos < Info->BufferUsed) {
319                         char    buf[7] = "\x1B[000D";
320                          int    delta = Info->BufferUsed - Info->BufferWritePos;
321                         buf[2] += (delta/100) % 10;
322                         buf[3] += (delta/10) % 10;
323                         buf[4] += (delta) % 10;
324                         write(STDOUT_FD, 1, &ch);       // Print new character
325                         write(STDOUT_FD,
326                                 Info->BufferUsed - Info->BufferWritePos,
327                                 &Info->CurBuffer[Info->BufferWritePos]
328                                 );
329                         write(STDOUT_FD, 7, buf);       // Update Cursor
330                         // Move buffer right
331                         memmove(
332                                 &Info->CurBuffer[Info->BufferWritePos+1],
333                                 &Info->CurBuffer[Info->BufferWritePos],
334                                 Info->BufferUsed - Info->BufferWritePos
335                                 );
336                 }
337                 // Simple append
338                 else {
339                         write(STDOUT_FD, 1, &ch);
340                 }
341                 Info->CurBuffer[ Info->BufferWritePos ++ ] = ch;
342                 Info->BufferUsed ++;
343                 break;
344         }
345         
346         return ofs;
347 }

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