Merge branch 'master' of serenade.mutabah.net:acess2
[tpg/acess2.git] / Usermode / Applications / edit_src / main.c
1 /*\r
2  * Acess2 Text Editor\r
3  */\r
4 #if !USE_LOCAL\r
5 # include <acess/sys.h> // Needed for IOCtl\r
6 #endif\r
7 #include <stdlib.h>\r
8 #include <stdio.h>\r
9 #include <stdint.h>\r
10 #include <string.h>\r
11 #include <signal.h>\r
12 #include <unistd.h>\r
13 #if USE_LOCAL\r
14 # include <sys/ioctl.h>\r
15 # include <termios.h>\r
16 #else\r
17 # include <acess/devices/terminal.h>\r
18 #endif\r
19 \r
20 #define MAX_FILES_OPEN  1\r
21 \r
22 // === TYPES ===\r
23 typedef struct\r
24 {\r
25         const char      *Filename;\r
26         char    **LineBuffer;\r
27          int    FileSize;\r
28          int    LineCount;\r
29          \r
30          int    FirstLine;\r
31          int    CurrentLine;    //!< Currently selected line\r
32          int    CurrentPos;     //!< Position in that line\r
33 }       tFile;\r
34 \r
35 // === PROTOTYPES ===\r
36 void    SigINT_Handler(int Signum);\r
37 void    ExitHandler(void);\r
38  int    main(int argc, char *argv[]);\r
39  int    OpenFile(tFile *Dest, const char *Path);\r
40 void    UpdateDisplayFull(void);\r
41 void    UpdateDisplayLine(int LineNum);\r
42 void    UpdateDisplayStatus(void);\r
43 \r
44 void    CursorUp(tFile *File);\r
45 void    CursorDown(tFile *File);\r
46 void    CursorRight(tFile *File);\r
47 void    CursorLeft(tFile *File);\r
48 \r
49 void    Term_SetPos(int Row, int Col);\r
50 void    Term_ScrollUp(int Count);\r
51 \r
52 // === GLOBALS ===\r
53  int    giProgramState = 0;\r
54 tFile   gaFiles[MAX_FILES_OPEN];\r
55 tFile   *gpCurrentFile;\r
56 #if USE_LOCAL\r
57 struct termios  gOldTermios;\r
58 #endif\r
59  int    giTabSize = 8;  // Someday this will be per-file (maybe)\r
60  int    giTerminal_Width = 80;\r
61  int    giTerminal_Height = 25;\r
62 \r
63 // === CODE ===\r
64 void SigINT_Handler(int Signum)\r
65 {\r
66         exit(55);\r
67 }\r
68 \r
69 void ExitHandler(void)\r
70 {\r
71         #if USE_LOCAL\r
72         tcsetattr(0, TCSANOW, &gOldTermios);\r
73         #endif\r
74         printf("\x1B[?1047l");\r
75 }\r
76 \r
77 /**\r
78  * \fn int main(int argc, char *argv[])\r
79  * \brief Entrypoint\r
80  */\r
81 int main(int argc, char *argv[])\r
82 {\r
83         \r
84         if(argc < 2) {\r
85                 printf("Usage: edit <file>\n");\r
86                 return -1;\r
87         }\r
88 \r
89         if( OpenFile(&gaFiles[0], argv[1]) ) {\r
90                 return -1;\r
91         }\r
92         \r
93         signal(SIGINT, SigINT_Handler);\r
94         atexit(ExitHandler);\r
95 \r
96         // Disable input buffering\r
97         #if USE_LOCAL\r
98         {\r
99                 struct termios  term;\r
100                 tcgetattr(0, &gOldTermios);\r
101                 term = gOldTermios;\r
102                 term.c_lflag &= ~(ICANON|ECHO);\r
103                 tcsetattr(0, TCSANOW, &term);\r
104                 //setbuf(stdin, NULL);\r
105         }\r
106         #endif\r
107         \r
108         // Go to alternte screen buffer\r
109         printf("\x1B[?1047h");\r
110         \r
111         gpCurrentFile = &gaFiles[0];\r
112         \r
113         UpdateDisplayFull();\r
114         \r
115         giProgramState = 1;\r
116         \r
117         while(giProgramState)\r
118         {\r
119                 char    ch = 0;\r
120                 \r
121                 read(0, &ch, 1);\r
122                 \r
123                 if(ch == '\x1B')\r
124                 {\r
125                         read(0, &ch, 1);\r
126                         switch(ch)\r
127                         {\r
128                         case 'A':       CursorUp(gpCurrentFile);        break;\r
129                         case 'B':       CursorDown(gpCurrentFile);      break;\r
130                         case 'C':       CursorRight(gpCurrentFile);     break;\r
131                         case 'D':       CursorLeft(gpCurrentFile);      break;\r
132                         \r
133                         case '[':\r
134                                 read(0, &ch, 1);\r
135                                 switch(ch)\r
136                                 {\r
137                                 case 'A':       CursorUp(gpCurrentFile);        break;\r
138                                 case 'B':       CursorDown(gpCurrentFile);      break;\r
139                                 case 'C':       CursorRight(gpCurrentFile);     break;\r
140                                 case 'D':       CursorLeft(gpCurrentFile);      break;\r
141                                 default:\r
142                                         printf("ch (\\x1B[) = %x\r", ch);\r
143                                         fflush(stdout);\r
144                                         break;\r
145                                 }\r
146                                 break;\r
147                         \r
148                         default:\r
149                                 printf("ch (\\x1B) = %x\r", ch);\r
150                                 fflush(stdout);\r
151                                 break;\r
152                         }\r
153                         // TODO: Move\r
154                 }\r
155                 \r
156                 switch(ch)\r
157                 {\r
158                 case 'q':\r
159                         giProgramState = 0;\r
160                         break;\r
161                 }\r
162         }\r
163 \r
164         return 0;\r
165 }\r
166 \r
167 int OpenFile(tFile *Dest, const char *Path)\r
168 {\r
169         FILE    *fp;\r
170         char    *buffer;\r
171          int    i, j;\r
172          int    start;\r
173         \r
174         fp = fopen(Path, "r");\r
175         if(!fp) {\r
176                 fprintf(stderr, "Unable to open %s\n", Path);\r
177                 return -1;\r
178         }\r
179         \r
180         Dest->Filename = Path;\r
181         \r
182         fseek(fp, 0, SEEK_END);\r
183         Dest->FileSize = ftell(fp);\r
184         fseek(fp, 0, SEEK_SET);\r
185         \r
186         buffer = malloc(Dest->FileSize+1);\r
187         fread(buffer, Dest->FileSize, 1, fp);\r
188         fclose(fp);\r
189         buffer[Dest->FileSize] = '\0';\r
190         \r
191         Dest->LineCount = 1;\r
192         for( i = 0; i < Dest->FileSize; i ++ )\r
193         {\r
194                 if( buffer[i] == '\n' )\r
195                         Dest->LineCount ++;\r
196         }\r
197         \r
198         Dest->LineBuffer = malloc( Dest->LineCount * sizeof(char*) );\r
199         start = 0;\r
200         j = 0;\r
201         for( i = 0; i < Dest->FileSize; i ++ )\r
202         {\r
203                 if( buffer[i] == '\n' )\r
204                 {\r
205                         buffer[i] = '\0';\r
206                         Dest->LineBuffer[j] = strdup( &buffer[start] );\r
207                         start = i+1;\r
208                         j ++;\r
209                 }\r
210         }\r
211         Dest->LineBuffer[j] = strdup( &buffer[start] );\r
212         \r
213         free(buffer);\r
214         \r
215         Dest->FirstLine = 0;\r
216         Dest->CurrentLine = 0;\r
217         Dest->CurrentPos = 0;\r
218         \r
219         return 0;\r
220 }\r
221 \r
222 void ShowLine(tFile *File, int LineNum, int X, int Y, int W)\r
223 {\r
224          int    j, k;\r
225         char    *line;\r
226         \r
227         line = File->LineBuffer[LineNum];\r
228         Term_SetPos(Y, X);\r
229         printf("\x1B[2K");      // Clear line\r
230         printf("%6i  ", LineNum+1);\r
231         j = 8;\r
232         for( k = 0; j < W-1 && line[k]; j++, k++ )\r
233         {\r
234                 switch( line[k] )\r
235                 {\r
236                 case '\t':\r
237                         printf("\t");\r
238                         j += 8;\r
239                         j &= ~7;\r
240                         break;\r
241                 default:\r
242                         if( line[k] < ' ' || line[k] >= 0x7F )  continue;\r
243                         printf("%c", line[k]);\r
244                         break;\r
245                 }\r
246         }\r
247         \r
248         for( ; j < W-1; j++ )\r
249                 printf(" ");\r
250         printf("\n");\r
251 }\r
252 \r
253 void ShowFile(tFile *File, int X, int Y, int W, int H)\r
254 {\r
255          int    i;\r
256         Term_SetPos(Y, X);\r
257         \r
258         for( i = 0; i < H; i ++ )\r
259         {\r
260                 if( File->FirstLine + i >= File->LineCount )    break;\r
261                 ShowLine( File, File->FirstLine + i, X, Y + i, W );\r
262         }\r
263         for( ; i < H; i++ )\r
264                 printf("\n");\r
265 }\r
266 \r
267 void UpdateDisplayFull(void)\r
268 {\r
269         #if USE_LOCAL\r
270         {\r
271                 struct winsize ws;\r
272                 ioctl(0, TIOCGWINSZ, &ws);\r
273                 giTerminal_Width = ws.ws_col;\r
274                 giTerminal_Height = ws.ws_row;\r
275         }\r
276         #else\r
277         giTerminal_Width = ioctl(1, TERM_IOCTL_WIDTH, NULL);\r
278         giTerminal_Height = ioctl(1, TERM_IOCTL_HEIGHT, NULL);\r
279         #endif\r
280         \r
281         printf("\x1B[2J");\r
282         \r
283         ShowFile(gpCurrentFile, 0, 0, giTerminal_Width, giTerminal_Height - 1);\r
284         \r
285         \r
286         // Status Line\r
287         UpdateDisplayStatus();\r
288         \r
289         Term_SetPos(\r
290                 gpCurrentFile->CurrentLine-gpCurrentFile->FirstLine,\r
291                 8 + gpCurrentFile->CurrentPos\r
292                 );\r
293         fflush(stdout);\r
294 }\r
295 \r
296 void UpdateDisplayLine(int Line)\r
297 {\r
298         ShowLine(\r
299                 gpCurrentFile, Line,\r
300                 0, Line - gpCurrentFile->FirstLine,\r
301                 giTerminal_Width );\r
302 }\r
303 \r
304 void UpdateDisplayStatus(void)\r
305 {\r
306         int lastLine = gpCurrentFile->FirstLine + giTerminal_Height - 1;\r
307         \r
308         if( lastLine > gpCurrentFile->LineCount )\r
309                 lastLine = gpCurrentFile->LineCount;\r
310 \r
311         Term_SetPos( giTerminal_Height - 1, 0 );        \r
312         printf("--- Line %i/%i (showing %i to %i)",\r
313                 gpCurrentFile->CurrentLine + 1, gpCurrentFile->LineCount,\r
314                 gpCurrentFile->FirstLine + 1, lastLine);\r
315 }\r
316 \r
317 void UpdateCursorPosition(void)\r
318 {\r
319         tFile   *File = gpCurrentFile;\r
320          int    screen_x, i;\r
321         const char      *tmp;\r
322 \r
323         // Determine the position (handling tab characters)\r
324         screen_x = 0;\r
325         tmp = File->LineBuffer[File->CurrentLine];\r
326         for( i = 0; tmp[i] && i < File->CurrentPos; i ++ )\r
327         {\r
328                 switch(*tmp)\r
329                 {\r
330                 case '\t':      screen_x += giTabSize;  break;\r
331                 default:        screen_x ++;    break;\r
332                 }\r
333         }\r
334 \r
335         Term_SetPos( File->CurrentLine - File->FirstLine, 8 + screen_x);\r
336 }\r
337 \r
338 void CursorUp(tFile *File)\r
339 {\r
340         if( File->CurrentLine <= 0 )\r
341                 return ;\r
342         \r
343         File->CurrentLine --;\r
344         if( File->FirstLine > File->CurrentLine )\r
345         {\r
346                 Term_ScrollUp(File->FirstLine - File->CurrentLine);\r
347                 File->FirstLine = File->CurrentLine;\r
348 //              UpdateDisplayLine(File->FirstLine + giTerminal_Height-2);\r
349                 UpdateDisplayLine(File->FirstLine);\r
350 //              UpdateDisplayFull();\r
351         }\r
352         else\r
353         {\r
354                 UpdateDisplayLine(File->CurrentLine + 1);\r
355                 UpdateDisplayLine(File->CurrentLine);\r
356         }\r
357         UpdateDisplayStatus();\r
358         \r
359         UpdateCursorPosition();\r
360 }\r
361 \r
362 void CursorDown(tFile *File)\r
363 {\r
364          int    threshold;\r
365         // Bounds check\r
366         if( File->CurrentLine + 1 >= File->LineCount )\r
367                 return ;\r
368         \r
369         File->CurrentLine ++;\r
370         // Check if scroll is needed\r
371         threshold = File->CurrentLine - (giTerminal_Height-2); \r
372         if( File->FirstLine < threshold )\r
373         {\r
374                 Term_ScrollUp( File->FirstLine - threshold );\r
375                 File->FirstLine = threshold;\r
376                 UpdateDisplayLine(File->FirstLine + giTerminal_Height-2);\r
377         }\r
378         else\r
379         {\r
380                 // Else, just update the previous and new lines (and status)\r
381                 UpdateDisplayLine(File->CurrentLine - 1);\r
382                 UpdateDisplayLine(File->CurrentLine);\r
383         }\r
384         UpdateDisplayStatus();\r
385         \r
386         UpdateCursorPosition();\r
387 }\r
388 \r
389 void CursorLeft(tFile *File)\r
390 {\r
391         if( File->CurrentPos > 0 )\r
392         {\r
393                 File->CurrentPos --;\r
394                 UpdateDisplayLine(File->CurrentLine);\r
395                 UpdateDisplayStatus();\r
396         }\r
397         UpdateCursorPosition();\r
398 }\r
399 \r
400 void CursorRight(tFile *File)\r
401 {\r
402         if( File->LineBuffer[File->CurrentLine][File->CurrentPos+1] )\r
403         {\r
404                 File->CurrentPos ++;\r
405                 UpdateDisplayLine(File->CurrentLine);\r
406                 UpdateDisplayStatus();\r
407         }\r
408         UpdateCursorPosition();\r
409 }\r
410 \r
411 void Term_SetPos(int Row, int Col)\r
412 {\r
413         printf("\x1B[%i;%iH", Row+1, Col+1);    // Set cursor\r
414         fflush(stdout);\r
415 }\r
416 \r
417 void Term_ScrollUp(int Count)\r
418 {\r
419         printf("\x1B[r");\r
420 //      printf("\x1B[1;%ir", giTerminal_Height-1);\r
421         if(Count < 0)\r
422                 printf("\x1B[%iM", -Count);\r
423         else\r
424                 printf("\x1B[%iL", Count);\r
425         fflush(stdout);\r
426 }\r

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