a07b052c93a2e8705e221427bbfc82a90894cfc2
[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         Info->ReadBuffer[Info->ReadBufferLen + len] = '\0';
85         
86         // Parse the data we have
87         for( i = 0; i < len; )
88         {
89                 int size = Readline_int_ParseCharacter(Info, Info->ReadBuffer+i);
90                 if( size <= 0 ) break;  // Error, skip the rest?
91                 i += size;
92         }
93         
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;
97         
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)
103         }
104         
105         // Return NULL when command is still being edited
106         return NULL;
107 }
108
109 char *Readline(tReadline *Info)
110 {
111         char    *ret;
112         
113         while( NULL == (ret = Readline_NonBlock(Info)) );
114         
115         return ret;
116 }
117
118 int Readline_int_AddToHistory(tReadline *Info, const char *String)
119 {
120         void    *tmp;
121         
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;
127         }
128         
129         // Don't add duplicates
130         if( Info->NumHistory >= 2 && strcmp( Info->History[ Info->NumHistory-2 ], String ) == 0 )
131         {
132                 return 0;
133         }
134         
135         // Duplicate over the current
136         Info->History[ Info->NumHistory-1 ] = strdup(String);
137         
138         Info->NumHistory ++;
139         
140         tmp = realloc( Info->History, Info->NumHistory * sizeof(char*) );
141         if( tmp == NULL )       return -1;
142         Info->History = tmp;
143                                 
144         // Zero the new current
145         Info->History[ Info->NumHistory-1 ] = NULL;
146         
147         return 0;
148 }
149
150 int Readline_int_ParseCharacter(tReadline *Info, char *Input)
151 {
152          int    ofs = 0;
153         char    ch;
154         
155         if( Input[ofs] == 0 )   return 0;
156         
157         // Read In Command Line
158         ch = Input[ofs++];
159         
160         if(ch == '\n')
161         {
162 //              printf("\n");
163                 if(Info->CurBuffer)
164                 {       
165                         // Cap String
166                         Info->CurBuffer[Info->BufferUsed] = '\0';
167                         
168                         if( Info->UseHistory )
169                                 Readline_int_AddToHistory(Info, Info->CurBuffer);
170                         Info->OutputValue = strdup(Info->CurBuffer);
171                 }
172                 else
173                         Info->OutputValue = strdup("");
174                 
175                 // Save and reset
176                 Info->BufferSize = 0;
177                 Info->BufferUsed = 0;
178                 Info->BufferWritePos = 0;
179                 Info->CurBuffer = 0;
180                 Info->HistoryPos = Info->NumHistory - 1;
181                 
182                 return 1;
183         }
184         
185         switch(ch)
186         {
187         // Control characters
188         case '\x1B':
189                 ch = Input[ofs++];      // Read control character
190                 switch(ch)
191                 {
192                 //case 'D':     if(pos) pos--;  break;
193                 //case 'C':     if(pos<len)     pos++;  break;
194                 case '[':
195                         ch = Input[ofs++];      // Read control character
196                         switch(ch)
197                         {
198                         case 'A':       // Up
199                                 {
200                                          int    oldLen = Info->BufferUsed;
201                                          int    pos;
202                                         if( Info->HistoryPos <= 0 )     break;
203                                         
204                                         // Move to the beginning of the line
205                                         pos = oldLen;
206                                         while(pos--)    write(STDOUT_FD, 3, "\x1B[D");
207                                         
208                                         // Update state
209                                         Info->CurBuffer = Info->History[--Info->HistoryPos];
210                                         Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
211                                         
212                                         write(STDOUT_FD, Info->BufferUsed, Info->CurBuffer);
213                                         Info->BufferWritePos = Info->BufferUsed;
214                                         
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");
221                                         }
222                                 }
223                                 break;
224                         case 'B':       // Down
225                                 {
226                                          int    oldLen = Info->BufferUsed;
227                                          int    pos;
228                                         if( Info->HistoryPos >= Info->NumHistory - 1 )  break;
229                                         
230                                         // Move to the beginning of the line
231                                         pos = oldLen;
232                                         while(pos--)    write(STDOUT_FD, 3, "\x1B[D");
233                                         
234                                         // Update state
235                                         Info->CurBuffer = Info->History[Info->HistoryPos++];
236                                         Info->BufferSize = Info->BufferUsed = strlen(Info->CurBuffer);
237                                         
238                                         // Write new line
239                                         write(STDOUT_FD, Info->BufferUsed, Info->CurBuffer);
240                                         Info->BufferWritePos = Info->BufferUsed;
241                                         
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");
248                                         }
249                                 }
250                                 break;
251                         case 'D':       // Left
252                                 if(Info->BufferWritePos == 0)   break;
253                                 Info->BufferWritePos --;
254                                 write(STDOUT_FD, 3, "\x1B[D");
255                                 break;
256                         case 'C':       // Right
257                                 if(Info->BufferWritePos == Info->BufferUsed)    break;
258                                 Info->BufferWritePos ++;
259                                 write(STDOUT_FD, 3, "\x1B[C");
260                                 break;
261                         }
262                 }
263                 break;
264         
265         // Backspace
266         case '\b':
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
271                 {
272                         Info->BufferUsed --;
273                         Info->BufferWritePos --;
274                 }
275                 else
276                 {
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
284                         write(STDOUT_FD,
285                                 Info->BufferUsed - Info->BufferWritePos,
286                                 &Info->CurBuffer[Info->BufferWritePos]
287                                 );
288                         ch = ' ';       write(STDOUT_FD, 1, &ch);       ch = '\b';      // Clear old last character
289                         write(STDOUT_FD, 7, buf);       // Update Cursor
290                         // Alter Buffer
291                         memmove(&Info->CurBuffer[Info->BufferWritePos-1],
292                                 &Info->CurBuffer[Info->BufferWritePos],
293                                 Info->BufferUsed-Info->BufferWritePos
294                                 );
295                         Info->BufferWritePos --;
296                         Info->BufferUsed --;
297                 }
298                 break;
299         
300         // Tab
301         case '\t':
302                 //TODO: Implement Tab-Completion
303                 //Currently just ignore tabs
304                 break;
305         
306         default:                
307                 // Expand Buffer
308                 if(Info->BufferUsed + 1 > Info->BufferSize)
309                 {
310                         Info->BufferSize += 256;
311                         Info->CurBuffer = Info->History[Info->HistoryPos]
312                                 = realloc(Info->CurBuffer, Info->BufferSize);
313                         if(!Info->CurBuffer)    return 0;
314                 }
315                 
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
324                         write(STDOUT_FD,
325                                 Info->BufferUsed - Info->BufferWritePos,
326                                 &Info->CurBuffer[Info->BufferWritePos]
327                                 );
328                         write(STDOUT_FD, 7, buf);       // Update Cursor
329                         // Move buffer right
330                         memmove(
331                                 &Info->CurBuffer[Info->BufferWritePos+1],
332                                 &Info->CurBuffer[Info->BufferWritePos],
333                                 Info->BufferUsed - Info->BufferWritePos
334                                 );
335                 }
336                 // Simple append
337                 else {
338                         write(STDOUT_FD, 1, &ch);
339                 }
340                 Info->CurBuffer[ Info->BufferWritePos ++ ] = ch;
341                 Info->BufferUsed ++;
342                 break;
343         }
344         
345         return ofs;
346 }

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