190f66dcb3737e17d05098c1915c27f7bcf3c297
[tpg/acess2.git] / Usermode / Applications / irc_src / main.c
1 /*
2  * Acess2 IRC Client
3  */
4 #include <acess/sys.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <net.h>
9 #include <readline.h>
10 #include <acess/devices/pty.h>
11
12 // === TYPES ===
13 typedef struct sServer {
14         struct sServer  *Next;
15          int    FD;
16         char    InBuf[BUFSIZ+1];
17          int    ReadPos;
18         char    Name[];
19 } tServer;
20
21 typedef struct sMessage
22 {
23         struct sMessage *Next;
24         time_t  Timestamp;
25         tServer *Server;
26          int    Type;
27         char    *Source;        // Pointer into `Data`
28         char    Data[];
29 }       tMessage;
30
31 typedef struct sWindow
32 {
33         struct sWindow  *Next;
34         tMessage        *Messages;
35         tServer *Server;        //!< Canonical server (can be NULL)
36          int    ActivityLevel;
37         char    Name[]; // Channel name / remote user
38 }       tWindow;
39
40 enum eMessageTypes
41 {
42         MSG_TYPE_NULL,
43         MSG_TYPE_SERVER,        // Server message
44         
45         MSG_TYPE_NOTICE,        // NOTICE command
46         MSG_TYPE_JOIN,  // JOIN command
47         MSG_TYPE_PART,  // PART command
48         MSG_TYPE_QUIT,  // QUIT command
49         
50         MSG_TYPE_STANDARD,      // Standard line
51         MSG_TYPE_ACTION,        // /me
52         
53         MSG_TYPE_UNK
54 };
55
56 // === PROTOTYPES ===
57  int    ParseArguments(int argc, const char *argv[]);
58  int    ParseUserCommand(char *String);
59 // --- 
60 tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber);
61 tMessage        *Message_AppendF(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message, ...);
62 tMessage        *Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message);
63 tWindow *Window_Create(tServer *Server, const char *Name);
64 void    Redraw_Screen(void);
65
66  int    ProcessIncoming(tServer *Server);
67 // --- Helpers
68 void    SetCursorPos(int Row, int Col);
69  int    writef(int FD, const char *Format, ...);
70  int    OpenTCP(const char *AddressString, short PortNumber);
71 char    *GetValue(char *Str, int *Ofs);
72 static inline int       isdigit(int ch);
73
74 // === GLOBALS ===
75 char    *gsUsername = "user";
76 char    *gsHostname = "acess";
77 char    *gsRealName = "Acess2 IRC Client";
78 char    *gsNickname = "acess";
79 tServer *gpServers;
80 tWindow gWindow_Status = {
81         NULL, NULL, NULL,       // No next, empty list, no server
82         0, {""} // No activity, empty name (rendered as status)
83 };
84 tWindow *gpWindows = &gWindow_Status;
85 tWindow *gpCurrentWindow = &gWindow_Status;
86  int    giTerminal_Width = 80;
87  int    giTerminal_Height = 25;
88
89 // ==== CODE ====
90 void ExitHandler(void)
91 {
92         printf("\x1B[?1047l");
93         printf("Quit\n");
94 }
95
96 int main(int argc, const char *argv[], const char *envp[])
97 {
98          int    tmp;
99         tReadline       *readline_info;
100         
101         // Parse Command line
102         if( (tmp = ParseArguments(argc, argv)) )        return tmp;
103         
104         atexit(ExitHandler);
105         
106         if( _SysIOCtl(1, DRV_IOCTL_TYPE, NULL) != DRV_TYPE_TERMINAL ) {
107                 fprintf(stderr, "note: assuming 80x25, can't get terminal dimensions\n");
108                 giTerminal_Width = 80;
109                 giTerminal_Height = 25;
110         }
111         else {
112                 struct ptydims  dims;
113                 _SysIOCtl(1, PTY_IOCTL_GETDIMS, &dims);
114                 giTerminal_Width = dims.W;
115                 giTerminal_Height = dims.H;
116         }
117         
118         printf("\x1B[?1047h");
119         printf("\x1B[%i;%ir", 0, giTerminal_Height-1);
120         
121         SetCursorPos(giTerminal_Height-1, 0);
122         printf("[(status)] ");
123         
124         // HACK: Static server entry
125         // UCC (University [of Western Australia] Computer Club) IRC Server
126         gWindow_Status.Server = Server_Connect( "UCC", "130.95.13.18", 6667 );
127         // Freenode (#osdev)
128 //      gWindow_Status.Server = Server_Connect( "Freenode", "89.16.176.16", 6667 );
129 //      gWindow_Status.Server = Server_Connect( "Host", "10.0.2.2", 6667 );
130         // Local server
131 //      gWindow_Status.Server = Server_Connect( "BitlBee", "192.168.1.39", 6667 );
132         
133         if( !gWindow_Status.Server )
134                 return -1;
135         
136         SetCursorPos(giTerminal_Height-1, 0);
137         printf("[(status)] ");
138         fflush(stdout);
139         readline_info = Readline_Init(1);
140         
141         for( ;; )
142         {
143                 fd_set  readfds, errorfds;
144                  int    rv, maxFD = 0;
145                 tServer *srv;
146                 
147                 FD_ZERO(&readfds);
148                 FD_ZERO(&errorfds);
149                 FD_SET(0, &readfds);    // stdin
150                 
151                 fflush(stdout);
152                 
153                 // Fill server FDs in fd_set
154                 for( srv = gpServers; srv; srv = srv->Next )
155                 {
156                         FD_SET(srv->FD, &readfds);
157                         FD_SET(srv->FD, &errorfds);
158                         if( srv->FD > maxFD )
159                                 maxFD = srv->FD;
160                 }
161                 
162                 rv = _SysSelect(maxFD+1, &readfds, 0, &errorfds, NULL, 0);
163                 if( rv == -1 )  break;
164                 
165                 if(FD_ISSET(0, &readfds))
166                 {
167                         // User input
168                         char    *cmd = Readline_NonBlock(readline_info);
169                         if( cmd )
170                         {
171                                 if( cmd[0] )
172                                 {
173                                         ParseUserCommand(cmd);
174                                 }
175                                 free(cmd);
176                                 // Prompt
177                                 SetCursorPos(giTerminal_Height-1, 0);
178                                 printf("\x1B[2K");      // Clear line
179                                 if( gpCurrentWindow->Name[0] )
180                                         printf("[%s:%s] ", gpCurrentWindow->Server->Name, gpCurrentWindow->Name);
181                                 else
182                                         printf("[(status)] ");
183                         }
184                 }
185                 
186                 // Server response
187                 for( srv = gpServers; srv; srv = srv->Next )
188                 {
189                         if(FD_ISSET(srv->FD, &readfds))
190                         {
191                                 if( ProcessIncoming(srv) != 0 ) {
192                                         // Oops, error
193                                         break;
194                                 }
195                         }
196                         
197                         if(FD_ISSET(srv->FD, &errorfds))
198                         {
199                                 break;
200                         }
201                 }
202                 
203                 // Oops, an error
204                 if( srv )       break;
205         }
206         
207         {
208                 tServer *srv;
209                 for( srv = gpServers; srv; srv = srv->Next )
210                         _SysClose(srv->FD);
211         }
212         return 0;
213 }
214
215 /**
216  * \todo Actually implement correctly :)
217  */
218 int ParseArguments(int argc, const char *argv[])
219 {
220         return 0;
221 }
222
223
224 void Cmd_join(char *ArgString)
225 {
226          int    pos=0;
227         char    *channel_name = GetValue(ArgString, &pos);
228         
229         if( gpCurrentWindow->Server )
230         {
231                 gpCurrentWindow = Window_Create(gpCurrentWindow->Server, channel_name);
232                 Redraw_Screen();
233                 writef(gpCurrentWindow->Server->FD, "JOIN :%s\n", channel_name);
234         }
235 }
236
237 void Cmd_quit(char *ArgString)
238 {
239         const char *quit_message = ArgString;
240         if( quit_message == NULL || quit_message[0] == '\0' )
241                 quit_message = "/quit - Acess2 IRC Client";
242         
243         for( tServer *srv = gpServers; srv; srv = srv->Next )
244         {
245                 writef(srv->FD, "QUIT :%s\n", quit_message);
246         }
247         
248         exit(0);
249 }
250
251 void Cmd_window(char *ArgString)
252 {
253          int    pos = 0;
254         char    *window_id = GetValue(ArgString, &pos);
255          int    window_num = atoi(window_id);
256         
257         if( window_num > 0 )
258         {
259                 tWindow *win;
260                 window_num --;  // Move to base 0
261                 // Get `window_num`th window
262                 for( win = gpWindows; win && window_num--; win = win->Next );
263                 if( win ) {
264                         gpCurrentWindow = win;
265                         Redraw_Screen();
266                 }
267                 // Otherwise, silently ignore
268         }
269         else
270         {
271                 window_num = 1;
272                 for( tWindow *win = gpWindows; win; win = win->Next, window_num ++ )
273                 {
274                         if( win->Name[0] ) {
275                                 Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "",
276                                         "%i: %s/%s", window_num, win->Server->Name, win->Name);
277                         }
278                         else {
279                                 Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "",
280                                         "%i: (status)", window_num);
281                         }
282                 }
283         }
284 }
285
286 const struct {
287         const char *Name;
288         void    (*Fcn)(char *ArgString);
289 } caCommands[] = {
290         {"join", Cmd_join},
291         {"quit", Cmd_quit},
292         {"window", Cmd_window},
293         {"win",    Cmd_window},
294         {"w",      Cmd_window},
295 };
296 const int ciNumCommands = sizeof(caCommands)/sizeof(caCommands[0]);
297
298 /**
299  * \brief Handle a line from the prompt
300  */
301 int ParseUserCommand(char *String)
302 {
303         if( String[0] == '/' )
304         {
305                 char    *command;
306                  int    pos = 0;
307                 
308                 command = GetValue(String, &pos)+1;
309
310                 // TODO: Prefix matches
311                  int    cmdIdx = -1;
312                 for( int i = 0; i < ciNumCommands; i ++ )
313                 {
314                         if( strcmp(command, caCommands[i].Name) == 0 ) {
315                                 cmdIdx = i;
316                                 break;
317                         }
318                 }
319                 if( cmdIdx != -1 ) {
320                         caCommands[cmdIdx].Fcn(String+pos);
321                 }
322                 else
323                 {
324                         Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "", "Unknown command %s", command);
325                 }
326         }
327         else
328         {
329                 // Message
330                 // - Only send if server is valid and window name is non-empty
331                 if( gpCurrentWindow->Server && gpCurrentWindow->Name[0] )
332                 {
333                         Message_Append(gpCurrentWindow->Server, MSG_TYPE_STANDARD,
334                                 gsNickname, gpCurrentWindow->Name, String);
335                         writef(gpCurrentWindow->Server->FD,
336                                 "PRIVMSG %s :%s\n", gpCurrentWindow->Name,
337                                 String
338                                 );
339                 }
340         }
341         
342         return 0;
343 }
344
345 /**
346  * \brief Connect to a server
347  */
348 tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber)
349 {
350         tServer *ret;
351         
352         ret = calloc(1, sizeof(tServer) + strlen(Name) + 1);
353         
354         strcpy(ret->Name, Name);
355         
356         // Connect to the remove server
357         ret->FD = OpenTCP( AddressString, PortNumber );
358         if( ret->FD == -1 ) {
359                 fprintf(stderr, "%s: Unable to create socket\n", Name);
360                 return NULL;
361         }
362         
363         // Append to open list
364         ret->Next = gpServers;
365         gpServers = ret;
366         
367         // Read some initial data
368         Message_Append(NULL, MSG_TYPE_SERVER, Name, "", "Connection opened");
369         ProcessIncoming(ret);
370         
371         // Identify
372         writef(ret->FD, "USER %s %s %s : %s\n", gsUsername, gsHostname, AddressString, gsRealName);
373         writef(ret->FD, "NICK %s\n", gsNickname);
374         Message_Append(NULL, MSG_TYPE_SERVER, Name, "", "Identified");
375         //printf("%s: Identified\n", Name);
376         
377         return ret;
378 }
379
380 tMessage *Message_AppendF(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message, ...)
381 {
382         va_list args;
383          int    len;
384         va_start(args, Message);
385         len = vsnprintf(NULL, 0, Message, args);
386         {
387                 char    buf[len+1];
388                 vsnprintf(buf, len+1, Message, args);
389                 return Message_Append(Server, Type, Source, Dest, buf);
390         }
391 }
392
393 tMessage *Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message)
394 {
395         tWindow *win = NULL;
396          int    msgLen = strlen(Message);
397         
398         // Server==NULL indicates an internal message
399         if( Server == NULL || Source[0] == '\0' )
400         {
401                 win = &gWindow_Status;
402         }
403         // Determine if it's a channel or PM
404         else if( Dest[0] == '#' || Dest[0] == '&' )     // TODO: Better determining here
405         {
406                 for(win = gpWindows; win; win = win->Next)
407                 {
408                         if( win->Server == Server && strcmp(win->Name, Dest) == 0 )
409                         {
410                                 break;
411                         }
412                 }
413                 if( !win ) {
414                         //win = Window_Create(Server, Dest);
415                         win = &gWindow_Status;  // Stick it in the status window, just in case
416                 }
417         }
418         #if 0
419         else if( strcmp(Dest, Server->Nick) != 0 )
420         {
421                 // Umm... message for someone who isn't us?
422                 win = &gWindow_Status;  // Stick it in the status window, just in case
423         }
424         #endif
425         // Server message?
426         else if( strchr(Source, '.') )  // TODO: And again, less hack please
427         {
428                 #if 1
429                 for(win = gpWindows; win; win = win->Next)
430                 {
431                         if( win->Server == Server && strcmp(win->Name, Source) == 0 )
432                         {
433                                 break;
434                         }
435                 }
436                 #endif
437                 if( !win ) {
438                         win = &gWindow_Status;
439                 }
440                 
441                 // Set source to the server name (instead of the hostname)
442                 Source = Server->Name;
443         }
444         // Private message
445         else
446         {
447                 for(win = gpWindows; win; win = win->Next)
448                 {
449                         if( win->Server == Server && strcmp(win->Name, Source) == 0 )
450                         {
451                                 break;
452                         }
453                 }
454                 if( !win ) {
455                         win = Window_Create(Server, Dest);
456                 }
457         }
458
459         // Create message cache 
460         _SysDebug("Win (%s) msg: <%s> %s", win->Name, Source, Message);
461         tMessage        *ret;
462         ret = malloc( sizeof(tMessage) + msgLen + 1 + strlen(Source) + 1 );
463         ret->Source = ret->Data + msgLen + 1;
464         strcpy(ret->Source, Source);
465         strcpy(ret->Data, Message);
466         ret->Type = Type;
467         ret->Server = Server;
468         
469         // Append to window message list
470         ret->Next = win->Messages;
471         win->Messages = ret;
472         
473         // Print now if current window
474         if( win == gpCurrentWindow )
475         {
476                 printf("\x1b[s");
477                 printf("\x1b""D");      // Scroll down 1 (free space below)
478                 SetCursorPos(giTerminal_Height-2, 0);
479                  int    prefixlen = strlen(Source) + 3;
480                  int    avail = giTerminal_Width - prefixlen;
481                  int    msglen = strlen(Message);
482                 printf("[%s] %.*s", Source, avail, Message);
483                 while( msglen > avail ) {
484                         msglen -= avail;
485                         Message += avail;
486                         printf("\x1b""D");
487                         SetCursorPos(giTerminal_Height-2, prefixlen);
488                         printf("%.*s", avail, Message);
489                 }
490                 printf("\x1b[u");
491         }
492         
493         return ret;
494 }
495
496 tWindow *Window_Create(tServer *Server, const char *Name)
497 {
498         tWindow *ret, *prev = NULL;
499          int    num = 1;
500         
501         // Get the end of the list
502         // TODO: Cache this instead
503         for( ret = gpCurrentWindow; ret; prev = ret, ret = ret->Next )
504                 num ++;
505         
506         ret = malloc(sizeof(tWindow) + strlen(Name) + 1);
507         ret->Messages = NULL;
508         ret->Server = Server;
509         ret->ActivityLevel = 1;
510         strcpy(ret->Name, Name);
511         
512         if( prev ) {
513                 ret->Next = prev->Next;
514                 prev->Next = ret;
515         }
516         else {  // Shouldn't happen really
517                 ret->Next = gpWindows;
518                 gpWindows = ret;
519         }
520         
521 //      printf("Win %i %s:%s created\n", num, Server->Name, Name);
522         
523         return ret;
524 }
525
526 void Redraw_Screen(void)
527 {
528          int    y = 0;
529         tMessage        *msg;
530
531         printf("\x1B[2J");      // Clear screen
532         printf("\x1B[0;0H");    // Reset cursor
533
534         msg = gpCurrentWindow->Messages;
535         
536         // TODO: Title bar?
537
538         // Note: This renders from the bottom up
539         for( y = giTerminal_Height - 1; y -- && msg; msg = msg->Next)
540         {
541                  int    msglen = strlen(msg->Data);
542                  int    prefix_len = 3 + strlen(msg->Source);
543                  int    line_avail = giTerminal_Width - prefix_len;
544                  int    i = 0, done = 0;
545                 
546                 y -= msglen / line_avail;       // Extra lines (y-- above handles the 1 line case)
547                 SetCursorPos(y, 0);
548                 printf("[%s] ", msg->Source);
549                 
550                 while(done < msglen) {
551                         done += printf("%.*s", line_avail, msg->Data+done);
552                         i ++;
553                         SetCursorPos(y+i, prefix_len);
554                 }
555         }
556
557         // Bottom line is rendered by the prompt
558 }
559
560 void Cmd_PRIVMSG(tServer *Server, const char *Dest, const char *Src, const char *Message)
561 {
562         Message_Append(Server, MSG_TYPE_STANDARD, Dest, Src, Message);
563         //printf("<%s:%s:%s> %s\n", Server->Name, Dest, Src, Message);
564 }
565
566 /**
567  */
568 void ParseServerLine(tServer *Server, char *Line)
569 {
570          int    pos = 0;
571         char    *ident, *cmd;
572
573         _SysDebug("[%s] %s", Server->Name, Line);       
574         
575         // Message?
576         if( *Line == ':' )
577         {
578                 pos ++;
579                 ident = GetValue(Line, &pos);   // Ident (user or server)
580                 cmd = GetValue(Line, &pos);
581                 
582                 // Numeric command
583                 if( isdigit(cmd[0]) && isdigit(cmd[1]) && isdigit(cmd[2]) )
584                 {
585                         char    *user, *message;
586                          int    num;
587                         num  = (cmd[0] - '0') * 100;
588                         num += (cmd[1] - '0') * 10;
589                         num += (cmd[2] - '0') * 1;
590                         
591                         user = GetValue(Line, &pos);
592                         
593                         if( Line[pos] == ':' ) {
594                                 message = Line + pos + 1;
595                         }
596                         else {
597                                 message = GetValue(Line, &pos);
598                         }
599                         
600                         switch(num)
601                         {
602                         case 332:       // Topic
603                                 user = message; // Channel
604                                 message = Line + pos + 1;       // Topic
605                                 Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Topic: %s", message);
606                                 break;
607                         case 333:       // Topic set by
608                                 user = message; // Channel
609                                 message = GetValue(Line, &pos); // User
610                                 GetValue(Line, &pos);   // Timestamp
611                                 Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Set by %s", message);
612                                 break;
613                         case 353:       // /NAMES list
614                                 // <user> = <channel> :list
615                                 // '=' was eaten in and set to message
616                                 user = GetValue(Line, &pos);    // Actually channel
617                                 message = Line + pos + 1;       // List
618                                 Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Names: %s", message);
619                                 break;
620                         case 366:       // end of /NAMES list
621                                 // <user> <channel> :msg
622                                 user = message;
623                                 message = Line + pos + 1;
624                                 Message_Append(Server, MSG_TYPE_SERVER, user, user, message);
625                                 break;
626                         case 372:       // MOTD Data
627                         case 375:       // MOTD Start
628                         case 376:       // MOTD End
629                                 
630                         default:
631                                 //printf("[%s] %i %s\n", Server->Name, num, message);
632                                 Message_Append(Server, MSG_TYPE_SERVER, ident, user, message);
633                                 break;
634                         }
635                 }
636                 else if( strcmp(cmd, "NOTICE") == 0 )
637                 {
638                         char    *class, *message;
639                         
640                         class = GetValue(Line, &pos);
641                         _SysDebug("NOTICE class='%s'", class);
642                         
643                         if( Line[pos] == ':' ) {
644                                 message = Line + pos + 1;
645                         }
646                         else {
647                                 message = GetValue(Line, &pos);
648                         }
649                         
650                         //printf("[%s] NOTICE %s: %s\n", Server->Name, ident, message);
651                         char *ident_bang = strchr(ident, '!');
652                         if( ident_bang ) {
653                                 *ident_bang = '\0';
654                         }
655                         Message_Append(Server, MSG_TYPE_NOTICE, ident, "", message);
656                 }
657                 else if( strcmp(cmd, "PRIVMSG") == 0 )
658                 {
659                         char    *dest, *message;
660                         dest = GetValue(Line, &pos);
661                         
662                         if( Line[pos] == ':' ) {
663                                 message = Line + pos + 1;
664                         }
665                         else {
666                                 message = GetValue(Line, &pos);
667                         }
668
669                         // TODO: Catch when the privmsg is addressed to the user
670
671 //                      Cmd_PRIVMSG(Server, dest, ident, message);
672                         char *ident_bang = strchr(ident, '!');
673                         if( ident_bang ) {
674                                 *ident_bang = '\0';
675                         }
676                         Message_Append(Server, MSG_TYPE_STANDARD, ident, dest, message);
677                 }
678                 else if( strcmp(cmd, "JOIN" ) == 0 )
679                 {
680                         char    *channel;
681                         channel = Line + pos + 1;
682                         
683                         Message_AppendF(Server, MSG_TYPE_JOIN, "", channel, "%s has joined", ident);
684                         //Window_Create(Server, channel);
685                 }
686                 else if( strcmp(cmd, "PART" ) == 0 )
687                 {
688                         char    *channel;
689                         channel = Line + pos + 1;
690                         
691                         Message_AppendF(Server, MSG_TYPE_PART, "", channel, "%s has left", ident);
692                         //Window_Create(Server, channel);
693                 }
694                 else
695                 {
696                         Message_AppendF(Server, MSG_TYPE_SERVER, "", "", "Unknown message %s (%s)", cmd, Line+pos);
697                 }
698         }
699         else {
700                 cmd = GetValue(Line, &pos);
701                 
702                 if( strcmp(cmd, "PING") == 0 ) {
703                         writef(Server->FD, "PONG %s\n", gsHostname);
704                         
705                 }
706                 else {
707                         // Command to client
708                         Message_AppendF(NULL, MSG_TYPE_UNK, "", "", "Client Command: %s", Line);
709                 }
710         }
711 }
712
713 /**
714  * \brief Process incoming lines from the server
715  */
716 int ProcessIncoming(tServer *Server)
717 {       
718         char    *ptr, *newline;
719          int    len;
720         
721         // While there is data in the buffer, read it into user memory and 
722         // process it line by line
723         // ioctl#8 on a TCP client gets the number of bytes in the recieve buffer
724         // - Used to avoid blocking
725         #if NON_BLOCK_READ
726         while( (len = _SysIOCtl(Server->FD, 8, NULL)) > 0 )
727         {
728         #endif
729                 // Read data
730                 len = _SysRead(Server->FD, &Server->InBuf[Server->ReadPos], BUFSIZ - Server->ReadPos);
731                 if( len == -1 ) {
732                         return -1;
733                 }
734                 Server->InBuf[Server->ReadPos + len] = '\0';
735                 
736                 // Break into lines
737                 ptr = Server->InBuf;
738                 while( (newline = strchr(ptr, '\n')) )
739                 {
740                         *newline = '\0';
741                         if( newline[-1] == '\r' )       newline[-1] = '\0';
742                         ParseServerLine(Server, ptr);
743                         ptr = newline + 1;
744                 }
745                 
746                 // Handle incomplete lines
747                 if( ptr - Server->InBuf < len + Server->ReadPos ) {
748                         // Update the read position
749                         // InBuf ReadPos    ptr          ReadPos+len
750                         // | old | new used | new unused |
751                         Server->ReadPos = len + Server->ReadPos - (ptr - Server->InBuf);
752                         // Copy stuff back (moving "new unused" to the start of the buffer)
753                         memcpy(Server->InBuf, ptr, Server->ReadPos);
754                 }
755                 else {
756                         Server->ReadPos = 0;
757                 }
758         #if NON_BLOCK_READ
759         }
760         #endif
761         
762         return 0;
763 }
764
765 /**
766  * \brief Write a formatted string to a file descriptor
767  * 
768  */
769 int writef(int FD, const char *Format, ...)
770 {
771         va_list args;
772          int    len;
773         
774         va_start(args, Format);
775         len = vsnprintf(NULL, 0, Format, args);
776         va_end(args);
777         
778         {
779                 char    buf[len+1];
780                 va_start(args, Format);
781                 vsnprintf(buf, len+1, Format, args);
782                 va_end(args);
783                 
784                 return _SysWrite(FD, buf, len);
785         }
786 }
787
788 /**
789  * \brief Initialise a TCP connection to \a AddressString on port \a PortNumber
790  */
791 int OpenTCP(const char *AddressString, short PortNumber)
792 {
793          int    fd, addrType;
794         char    addrBuffer[8];
795         
796         // Parse IP Address
797         addrType = Net_ParseAddress(AddressString, addrBuffer);
798         if( addrType == 0 ) {
799                 fprintf(stderr, "Unable to parse '%s' as an IP address\n", AddressString);
800                 _SysDebug("Unable to parse '%s' as an IP address\n", AddressString);
801                 return -1;
802         }
803         
804         // Finds the interface for the destination address
805         fd = Net_OpenSocket(addrType, addrBuffer, "tcpc");
806         if( fd == -1 ) {
807                 fprintf(stderr, "Unable to open TCP Client to '%s'\n", AddressString);
808                 _SysDebug("Unable to open TCP client to '%s'\n", AddressString);
809                 return -1;
810         }
811         
812         // Set remote port and address
813 //      printf("Setting port and remote address\n");
814         _SysIOCtl(fd, 5, &PortNumber);
815         _SysIOCtl(fd, 6, addrBuffer);
816         
817         // Connect
818 //      printf("Initiating connection\n");
819         if( _SysIOCtl(fd, 7, NULL) == 0 ) {
820                 // Shouldn't happen :(
821                 fprintf(stderr, "Unable to start connection\n");
822                 return -1;
823         }
824         
825         // Return descriptor
826         return fd;
827 }
828
829 /**
830  * \brief Read a space-separated value from a string
831  */
832 char *GetValue(char *Src, int *Ofs)
833 {
834          int    pos = *Ofs;
835         char    *ret = Src + pos;
836         char    *end;
837         
838         if( !Src )      return NULL;
839         
840         while( *ret == ' ' )    ret ++;
841         
842         end = strchr(ret, ' ');
843         if( end ) {
844                 *end = '\0';
845         }
846         else {
847                 end = ret + strlen(ret) - 1;
848         }
849         
850         end ++ ;
851         while( *ret == ' ' )    end ++;
852         *Ofs = end - Src;
853         
854         return ret;
855 }
856
857 void SetCursorPos(int Row, int Col)
858 {
859         printf("\x1B[%i;%iH", Row, Col);
860 }
861
862 static inline int isdigit(int ch)
863 {
864         return '0' <= ch && ch < '9';
865 }

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