Usermode/IRC - Minor fixes to display
[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;        //!< Canoical 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                 writef(gpCurrentWindow->Server->FD, "JOIN :%s\n", channel_name);
232         }
233 }
234
235 void Cmd_quit(char *ArgString)
236 {
237         const char *quit_message = ArgString;
238         if( quit_message == NULL || quit_message[0] == '\0' )
239                 quit_message = "/quit - Acess2 IRC Client";
240         
241         for( tServer *srv = gpServers; srv; srv = srv->Next )
242         {
243                 writef(srv->FD, "QUIT :%s\n", quit_message);
244         }
245         
246         exit(0);
247 }
248
249 void Cmd_window(char *ArgString)
250 {
251          int    pos = 0;
252         char    *window_id = GetValue(ArgString, &pos);
253          int    window_num = atoi(window_id);
254         
255         if( window_num > 0 )
256         {
257                 tWindow *win;
258                 window_num --;  // Move to base 0
259                 // Get `window_num`th window
260                 for( win = gpWindows; win && window_num--; win = win->Next );
261                 if( win ) {
262                         gpCurrentWindow = win;
263                         Redraw_Screen();
264                 }
265                 // Otherwise, silently ignore
266         }
267         else
268         {
269                 window_num = 1;
270                 for( tWindow *win = gpWindows; win; win = win->Next, window_num ++ )
271                 {
272                         if( win->Name[0] ) {
273                                 Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "",
274                                         "%i: %s/%s", window_num, win->Server->Name, win->Name);
275                         }
276                         else {
277                                 Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "",
278                                         "%i: (status)", window_num);
279                         }
280                 }
281         }
282 }
283
284 const struct {
285         const char *Name;
286         void    (*Fcn)(char *ArgString);
287 } caCommands[] = {
288         {"join", Cmd_join},
289         {"quit", Cmd_quit},
290         {"window", Cmd_window},
291         {"win",    Cmd_window},
292         {"w",      Cmd_window},
293 };
294 const int ciNumCommands = sizeof(caCommands)/sizeof(caCommands[0]);
295
296 /**
297  * \brief Handle a line from the prompt
298  */
299 int ParseUserCommand(char *String)
300 {
301         if( String[0] == '/' )
302         {
303                 char    *command;
304                  int    pos = 0;
305                 
306                 command = GetValue(String, &pos)+1;
307
308                 // TODO: Prefix matches
309                  int    cmdIdx = -1;
310                 for( int i = 0; i < ciNumCommands; i ++ )
311                 {
312                         if( strcmp(command, caCommands[i].Name) == 0 ) {
313                                 cmdIdx = i;
314                                 break;
315                         }
316                 }
317                 if( cmdIdx != -1 ) {
318                         caCommands[cmdIdx].Fcn(String+pos);
319                 }
320                 else
321                 {
322                         Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "", "Unknown command %s", command);
323                 }
324         }
325         else
326         {
327                 // Message
328                 // - Only send if server is valid and window name is non-empty
329                 if( gpCurrentWindow->Server && gpCurrentWindow->Name[0] )
330                 {
331                         Message_Append(gpCurrentWindow->Server, MSG_TYPE_STANDARD,
332                                 gsNickname, gpCurrentWindow->Name, String);
333                         writef(gpCurrentWindow->Server->FD,
334                                 "PRIVMSG %s :%s\n", gpCurrentWindow->Name,
335                                 String
336                                 );
337                 }
338         }
339         
340         return 0;
341 }
342
343 /**
344  * \brief Connect to a server
345  */
346 tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber)
347 {
348         tServer *ret;
349         
350         ret = calloc(1, sizeof(tServer) + strlen(Name) + 1);
351         
352         strcpy(ret->Name, Name);
353         
354         // Connect to the remove server
355         ret->FD = OpenTCP( AddressString, PortNumber );
356         if( ret->FD == -1 ) {
357                 fprintf(stderr, "%s: Unable to create socket\n", Name);
358                 return NULL;
359         }
360         
361         // Append to open list
362         ret->Next = gpServers;
363         gpServers = ret;
364         
365         // Read some initial data
366         Message_Append(NULL, MSG_TYPE_SERVER, Name, "", "Connection opened");
367         ProcessIncoming(ret);
368         
369         // Identify
370         writef(ret->FD, "USER %s %s %s : %s\n", gsUsername, gsHostname, AddressString, gsRealName);
371         writef(ret->FD, "NICK %s\n", gsNickname);
372         Message_Append(NULL, MSG_TYPE_SERVER, Name, "", "Identified");
373         //printf("%s: Identified\n", Name);
374         
375         return ret;
376 }
377
378 tMessage *Message_AppendF(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message, ...)
379 {
380         va_list args;
381          int    len;
382         va_start(args, Message);
383         len = vsnprintf(NULL, 0, Message, args);
384         {
385                 char    buf[len+1];
386                 vsnprintf(buf, len+1, Message, args);
387                 return Message_Append(Server, Type, Source, Dest, buf);
388         }
389 }
390
391 tMessage *Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message)
392 {
393         tWindow *win = NULL;
394          int    msgLen = strlen(Message);
395         
396         // Server==NULL indicates an internal message
397         if( Server == NULL || Source[0] == '\0' )
398         {
399                 win = &gWindow_Status;
400         }
401         // Determine if it's a channel or PM
402         else if( Dest[0] == '#' || Dest[0] == '&' )     // TODO: Better determining here
403         {
404                 for(win = gpWindows; win; win = win->Next)
405                 {
406                         if( win->Server == Server && strcmp(win->Name, Dest) == 0 )
407                         {
408                                 break;
409                         }
410                 }
411                 if( !win ) {
412                         win = Window_Create(Server, Dest);
413                 }
414         }
415         #if 0
416         else if( strcmp(Dest, Server->Nick) != 0 )
417         {
418                 // Umm... message for someone who isn't us?
419                 win = &gWindow_Status;  // Stick it in the status window, just in case
420         }
421         #endif
422         // Server message?
423         else if( strchr(Source, '.') )  // TODO: And again, less hack please
424         {
425                 #if 1
426                 for(win = gpWindows; win; win = win->Next)
427                 {
428                         if( win->Server == Server && strcmp(win->Name, Source) == 0 )
429                         {
430                                 break;
431                         }
432                 }
433                 #endif
434                 if( !win ) {
435                         win = &gWindow_Status;
436                 }
437                 
438                 // Set source to the server name (instead of the hostname)
439                 Source = Server->Name;
440         }
441         // Private message
442         else
443         {
444                 for(win = gpWindows; win; win = win->Next)
445                 {
446                         if( win->Server == Server && strcmp(win->Name, Source) == 0 )
447                         {
448                                 break;
449                         }
450                 }
451                 if( !win ) {
452                         win = Window_Create(Server, Dest);
453                 }
454         }
455
456         // Create message cache 
457         _SysDebug("Win (%s) msg: <%s> %s", win->Name, Source, Message);
458         tMessage        *ret;
459         ret = malloc( sizeof(tMessage) + msgLen + 1 + strlen(Source) + 1 );
460         ret->Source = ret->Data + msgLen + 1;
461         strcpy(ret->Source, Source);
462         strcpy(ret->Data, Message);
463         ret->Type = Type;
464         ret->Server = Server;
465         
466         // Append to window message list
467         ret->Next = win->Messages;
468         win->Messages = ret;
469         
470         // Print now if current window
471         if( win == gpCurrentWindow )
472         {
473                 printf("\x1b[s");
474                 printf("\x1b""D");      // Scroll down 1 (free space below)
475                 SetCursorPos(giTerminal_Height-2, 0);
476                  int    prefixlen = strlen(Source) + 3;
477                  int    avail = giTerminal_Width - prefixlen;
478                  int    msglen = strlen(Message);
479                 printf("[%s] %.*s", Source, avail, Message);
480                 while( msglen > avail ) {
481                         msglen -= avail;
482                         Message += avail;
483                         printf("\x1b""D");
484                         SetCursorPos(giTerminal_Height-2, prefixlen);
485                         printf("%.*s", avail, Message);
486                 }
487                 printf("\x1b[u");
488         }
489         
490         return ret;
491 }
492
493 tWindow *Window_Create(tServer *Server, const char *Name)
494 {
495         tWindow *ret, *prev = NULL;
496          int    num = 1;
497         
498         // Get the end of the list
499         // TODO: Cache this instead
500         for( ret = gpCurrentWindow; ret; prev = ret, ret = ret->Next )
501                 num ++;
502         
503         ret = malloc(sizeof(tWindow) + strlen(Name) + 1);
504         ret->Messages = NULL;
505         ret->Server = Server;
506         ret->ActivityLevel = 1;
507         strcpy(ret->Name, Name);
508         
509         if( prev ) {
510                 ret->Next = prev->Next;
511                 prev->Next = ret;
512         }
513         else {  // Shouldn't happen really
514                 ret->Next = gpWindows;
515                 gpWindows = ret;
516         }
517         
518 //      printf("Win %i %s:%s created\n", num, Server->Name, Name);
519         
520         return ret;
521 }
522
523 void Redraw_Screen(void)
524 {
525          int    y = 0;
526         tMessage        *msg;
527
528         printf("\x1B[2J");      // Clear screen
529         printf("\x1B[0;0H");    // Reset cursor
530
531         msg = gpCurrentWindow->Messages;
532         
533         // TODO: Title bar?
534
535         // Note: This renders from the bottom up
536         for( y = giTerminal_Height - 1; y -- && msg; msg = msg->Next)
537         {
538                  int    msglen = strlen(msg->Data);
539                  int    prefix_len = 3 + strlen(msg->Source);
540                  int    line_avail = giTerminal_Width - prefix_len;
541                  int    i = 0, done = 0;
542                 
543                 y -= msglen / line_avail;       // Extra lines (y-- above handles the 1 line case)
544                 SetCursorPos(y, 0);
545                 printf("[%s] ", msg->Source);
546                 
547                 while(done < msglen) {
548                         done += printf("%.*s", line_avail, msg->Data+done);
549                         i ++;
550                         SetCursorPos(y+i, prefix_len);
551                 }
552         }
553
554         // Bottom line is rendered by the prompt
555 }
556
557 void Cmd_PRIVMSG(tServer *Server, const char *Dest, const char *Src, const char *Message)
558 {
559         Message_Append(Server, MSG_TYPE_STANDARD, Dest, Src, Message);
560         //printf("<%s:%s:%s> %s\n", Server->Name, Dest, Src, Message);
561 }
562
563 /**
564  */
565 void ParseServerLine(tServer *Server, char *Line)
566 {
567          int    pos = 0;
568         char    *ident, *cmd;
569
570 //      _SysDebug("[%s] %s", Server->Name, Line);       
571         
572         // Message?
573         if( *Line == ':' )
574         {
575                 pos ++;
576                 ident = GetValue(Line, &pos);   // Ident (user or server)
577                 cmd = GetValue(Line, &pos);
578                 
579                 // Numeric command
580                 if( isdigit(cmd[0]) && isdigit(cmd[1]) && isdigit(cmd[2]) )
581                 {
582                         char    *user, *message;
583                          int    num;
584                         num  = (cmd[0] - '0') * 100;
585                         num += (cmd[1] - '0') * 10;
586                         num += (cmd[2] - '0') * 1;
587                         
588                         user = GetValue(Line, &pos);
589                         
590                         if( Line[pos] == ':' ) {
591                                 message = Line + pos + 1;
592                         }
593                         else {
594                                 message = GetValue(Line, &pos);
595                         }
596                         
597                         switch(num)
598                         {
599                         case 332:       // Topic
600                                 user = message; // Channel
601                                 message = Line + pos + 1;       // Topic
602                                 Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Topic: %s", message);
603                                 break;
604                         case 333:       // Topic set by
605                                 user = message; // Channel
606                                 message = GetValue(Line, &pos); // User
607                                 GetValue(Line, &pos);   // Timestamp
608                                 Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Set by %s", message);
609                                 break;
610                         case 353:       // /NAMES list
611                                 // <user> = <channel> :list
612                                 // '=' was eaten in and set to message
613                                 user = GetValue(Line, &pos);    // Actually channel
614                                 message = Line + pos + 1;       // List
615                                 Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Names: %s", message);
616                                 break;
617                         case 366:       // end of /NAMES list
618                                 // <user> <channel> :msg
619                                 user = message;
620                                 message = Line + pos + 1;
621                                 Message_Append(Server, MSG_TYPE_SERVER, user, user, message);
622                                 break;
623                         case 372:       // MOTD Data
624                         case 375:       // MOTD Start
625                         case 376:       // MOTD End
626                                 
627                         default:
628                                 //printf("[%s] %i %s\n", Server->Name, num, message);
629                                 Message_Append(Server, MSG_TYPE_SERVER, ident, user, message);
630                                 break;
631                         }
632                 }
633                 else if( strcmp(cmd, "NOTICE") == 0 )
634                 {
635                         char    *class, *message;
636                         
637                         class = GetValue(Line, &pos);
638                         _SysDebug("NOTICE class='%s'", class);
639                         
640                         if( Line[pos] == ':' ) {
641                                 message = Line + pos + 1;
642                         }
643                         else {
644                                 message = GetValue(Line, &pos);
645                         }
646                         
647                         //printf("[%s] NOTICE %s: %s\n", Server->Name, ident, message);
648                         char *ident_bang = strchr(ident, '!');
649                         if( ident_bang ) {
650                                 *ident_bang = '\0';
651                         }
652                         Message_Append(Server, MSG_TYPE_NOTICE, ident, "", message);
653                 }
654                 else if( strcmp(cmd, "PRIVMSG") == 0 )
655                 {
656                         char    *dest, *message;
657                         dest = GetValue(Line, &pos);
658                         
659                         if( Line[pos] == ':' ) {
660                                 message = Line + pos + 1;
661                         }
662                         else {
663                                 message = GetValue(Line, &pos);
664                         }
665
666                         // TODO: Catch when the privmsg is addressed to the user
667
668 //                      Cmd_PRIVMSG(Server, dest, ident, message);
669                         char *ident_bang = strchr(ident, '!');
670                         if( ident_bang ) {
671                                 *ident_bang = '\0';
672                         }
673                         Message_Append(Server, MSG_TYPE_STANDARD, ident, dest, message);
674                 }
675                 else if( strcmp(cmd, "JOIN" ) == 0 )
676                 {
677                         char    *channel;
678                         channel = Line + pos + 1;
679                         Window_Create(Server, channel);
680                 }
681                 else
682                 {
683                         Message_AppendF(Server, MSG_TYPE_SERVER, "", "", "Unknown message %s (%s)", cmd, Line+pos);
684                 }
685         }
686         else {          
687                 cmd = GetValue(Line, &pos);
688                 
689                 if( strcmp(cmd, "PING") == 0 ) {
690                         writef(Server->FD, "PONG %s\n", gsHostname);
691                         
692                 }
693                 else {
694                         // Command to client
695                         Message_AppendF(NULL, MSG_TYPE_UNK, "", "", "Client Command: %s", Line);
696                 }
697         }
698 }
699
700 /**
701  * \brief Process incoming lines from the server
702  */
703 int ProcessIncoming(tServer *Server)
704 {       
705         char    *ptr, *newline;
706          int    len;
707         
708         // While there is data in the buffer, read it into user memory and 
709         // process it line by line
710         // ioctl#8 on a TCP client gets the number of bytes in the recieve buffer
711         // - Used to avoid blocking
712         #if NON_BLOCK_READ
713         while( (len = _SysIOCtl(Server->FD, 8, NULL)) > 0 )
714         {
715         #endif
716                 // Read data
717                 len = _SysRead(Server->FD, &Server->InBuf[Server->ReadPos], BUFSIZ - Server->ReadPos);
718                 if( len == -1 ) {
719                         return -1;
720                 }
721                 Server->InBuf[Server->ReadPos + len] = '\0';
722                 
723                 // Break into lines
724                 ptr = Server->InBuf;
725                 while( (newline = strchr(ptr, '\n')) )
726                 {
727                         *newline = '\0';
728                         if( newline[-1] == '\r' )       newline[-1] = '\0';
729                         ParseServerLine(Server, ptr);
730                         ptr = newline + 1;
731                 }
732                 
733                 // Handle incomplete lines
734                 if( ptr - Server->InBuf < len + Server->ReadPos ) {
735                         // Update the read position
736                         // InBuf ReadPos    ptr          ReadPos+len
737                         // | old | new used | new unused |
738                         Server->ReadPos = len + Server->ReadPos - (ptr - Server->InBuf);
739                         // Copy stuff back (moving "new unused" to the start of the buffer)
740                         memcpy(Server->InBuf, ptr, Server->ReadPos);
741                 }
742                 else {
743                         Server->ReadPos = 0;
744                 }
745         #if NON_BLOCK_READ
746         }
747         #endif
748         
749         return 0;
750 }
751
752 /**
753  * \brief Write a formatted string to a file descriptor
754  * 
755  */
756 int writef(int FD, const char *Format, ...)
757 {
758         va_list args;
759          int    len;
760         
761         va_start(args, Format);
762         len = vsnprintf(NULL, 0, Format, args);
763         va_end(args);
764         
765         {
766                 char    buf[len+1];
767                 va_start(args, Format);
768                 vsnprintf(buf, len+1, Format, args);
769                 va_end(args);
770                 
771                 return _SysWrite(FD, buf, len);
772         }
773 }
774
775 /**
776  * \brief Initialise a TCP connection to \a AddressString on port \a PortNumber
777  */
778 int OpenTCP(const char *AddressString, short PortNumber)
779 {
780          int    fd, addrType;
781         char    addrBuffer[8];
782         
783         // Parse IP Address
784         addrType = Net_ParseAddress(AddressString, addrBuffer);
785         if( addrType == 0 ) {
786                 fprintf(stderr, "Unable to parse '%s' as an IP address\n", AddressString);
787                 _SysDebug("Unable to parse '%s' as an IP address\n", AddressString);
788                 return -1;
789         }
790         
791         // Finds the interface for the destination address
792         fd = Net_OpenSocket(addrType, addrBuffer, "tcpc");
793         if( fd == -1 ) {
794                 fprintf(stderr, "Unable to open TCP Client to '%s'\n", AddressString);
795                 _SysDebug("Unable to open TCP client to '%s'\n", AddressString);
796                 return -1;
797         }
798         
799         // Set remote port and address
800 //      printf("Setting port and remote address\n");
801         _SysIOCtl(fd, 5, &PortNumber);
802         _SysIOCtl(fd, 6, addrBuffer);
803         
804         // Connect
805 //      printf("Initiating connection\n");
806         if( _SysIOCtl(fd, 7, NULL) == 0 ) {
807                 // Shouldn't happen :(
808                 fprintf(stderr, "Unable to start connection\n");
809                 return -1;
810         }
811         
812         // Return descriptor
813         return fd;
814 }
815
816 /**
817  * \brief Read a space-separated value from a string
818  */
819 char *GetValue(char *Src, int *Ofs)
820 {
821          int    pos = *Ofs;
822         char    *ret = Src + pos;
823         char    *end;
824         
825         if( !Src )      return NULL;
826         
827         while( *ret == ' ' )    ret ++;
828         
829         end = strchr(ret, ' ');
830         if( end ) {
831                 *end = '\0';
832         }
833         else {
834                 end = ret + strlen(ret) - 1;
835         }
836         
837         end ++ ;
838         while( *ret == ' ' )    end ++;
839         *Ofs = end - Src;
840         
841         return ret;
842 }
843
844 void SetCursorPos(int Row, int Col)
845 {
846         printf("\x1B[%i;%iH", Row, Col);
847 }
848
849 static inline int isdigit(int ch)
850 {
851         return '0' <= ch && ch < '9';
852 }

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