Usermode/irc - Rework into multiple files
authorJohn Hodge <[email protected]>
Sat, 17 May 2014 16:20:56 +0000 (00:20 +0800)
committerJohn Hodge <[email protected]>
Sat, 17 May 2014 16:20:56 +0000 (00:20 +0800)
12 files changed:
Usermode/Applications/irc_src/Makefile
Usermode/Applications/irc_src/common.h [new file with mode: 0644]
Usermode/Applications/irc_src/input.c [new file with mode: 0644]
Usermode/Applications/irc_src/input.h [new file with mode: 0644]
Usermode/Applications/irc_src/main.c
Usermode/Applications/irc_src/message.h [new file with mode: 0644]
Usermode/Applications/irc_src/pseudo_curses.c [new file with mode: 0644]
Usermode/Applications/irc_src/pseudo_curses.h [new file with mode: 0644]
Usermode/Applications/irc_src/server.c [new file with mode: 0644]
Usermode/Applications/irc_src/server.h [new file with mode: 0644]
Usermode/Applications/irc_src/window.c [new file with mode: 0644]
Usermode/Applications/irc_src/window.h [new file with mode: 0644]

index 9fab060..f22e82f 100644 (file)
@@ -4,7 +4,8 @@
 
 LDFLAGS += -lnet -lreadline
 
-OBJ = main.o
+OBJ = main.o server.o input.o
+OBJ += window.o pseudo_curses.o
 BIN = irc
 
 -include ../Makefile.tpl
diff --git a/Usermode/Applications/irc_src/common.h b/Usermode/Applications/irc_src/common.h
new file mode 100644 (file)
index 0000000..e374328
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ */
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include "pseudo_curses.h"
+
+typedef struct sServer tServer;
+
+extern void    _SysDebug(const char *format, ...);
+
+extern int     writef(int FD, const char *Format, ...);
+extern int     OpenTCP(const char *AddressString, short PortNumber);
+extern char    *GetValue(char *Src, int *Ofs);
+
+extern void    Redraw_Screen(void);
+extern void    Exit(const char *Reason) __attribute__((noreturn));
+
+#endif
+
diff --git a/Usermode/Applications/irc_src/input.c b/Usermode/Applications/irc_src/input.c
new file mode 100644 (file)
index 0000000..2236152
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ */
+#include "input.h"
+#include "window.h"
+#include "server.h"
+#include <readline.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+// === PROTOTYPES ===
+void   Input_FillSelect(int *nfds, fd_set *rfds);
+void   Input_HandleSelect(int nfds, const fd_set *rfds);
+ int   ParseUserCommand(char *String);
+
+// === GLOBALS ===
+tReadline      *gpInput_ReadlineInfo;
+
+// === CODE ===
+void Input_FillSelect(int *nfds, fd_set *rfds)
+{
+       if( !gpInput_ReadlineInfo ) {
+               gpInput_ReadlineInfo = Readline_Init(1);
+       }
+       
+       FD_SET(0, rfds);
+       if(*nfds < 0+1)
+               *nfds = 0+1;
+}
+
+void Input_HandleSelect(int nfds, const fd_set *rfds)
+{
+       // User input
+       if(FD_ISSET(0, rfds))
+       {
+               char    *cmd = Readline_NonBlock(gpInput_ReadlineInfo);
+               if( cmd )
+               {
+                       if( cmd[0] )
+                       {
+                               ParseUserCommand(cmd);
+                       }
+                       free(cmd);
+                       // Prompt
+                       SetCursorPos(giTerminal_Height-1, 1);
+                       printf("\x1B[2K");      // Clear line
+                       printf("[%s]", Window_GetName(NULL));
+               }
+       }
+}
+
+void Cmd_join(char *ArgString)
+{
+        int    pos=0;
+       char    *channel_name = GetValue(ArgString, &pos);
+       
+       tServer *srv = Window_GetServer(NULL);
+       
+       if( srv )
+       {
+               Windows_SwitchTo( Window_Create(srv, channel_name) );
+               Redraw_Screen();
+               Server_SendCommand(srv, "JOIN :%s", channel_name);
+       }
+}
+
+void Cmd_quit(char *ArgString)
+{
+       const char *quit_message = ArgString;
+       if( quit_message == NULL || quit_message[0] == '\0' )
+               quit_message = "/quit - Acess2 IRC Client";
+       
+       Servers_CloseAll(quit_message);
+       
+       Exit(NULL);     // NULL = user requested
+}
+
+void Cmd_window(char *ArgString)
+{
+        int    pos = 0;
+       char    *window_id = GetValue(ArgString, &pos);
+        int    window_num = atoi(window_id);
+       
+       if( window_num > 0 )
+       {
+               // Get `window_num`th window
+               tWindow *win = Windows_GetByIndex(window_num-1);
+               if( win )
+               {
+                       Windows_SwitchTo( win );
+               }
+               else
+               {
+                       // Otherwise, silently ignore
+               }
+       }
+       else
+       {
+               window_num = 1;
+               for( tWindow *win; (win = Windows_GetByIndex(window_num-1)); window_num ++ )
+               {
+                       Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, NULL, "%i: %s", window_num, Window_GetName(win));
+               }
+       }
+}
+
+const struct {
+       const char *Name;
+       void    (*Fcn)(char *ArgString);
+} caCommands[] = {
+       {"join", Cmd_join},
+       {"quit", Cmd_quit},
+       {"window", Cmd_window},
+       {"win",    Cmd_window},
+       {"w",      Cmd_window},
+};
+const int ciNumCommands = sizeof(caCommands)/sizeof(caCommands[0]);
+
+/**
+ * \brief Handle a line from the prompt
+ */
+int ParseUserCommand(char *String)
+{
+       if( String[0] == '/' )
+       {
+               char    *command;
+                int    pos = 0;
+               
+               command = GetValue(String, &pos)+1;
+
+               // TODO: Prefix matches
+                int    cmdIdx = -1;
+               for( int i = 0; i < ciNumCommands; i ++ )
+               {
+                       if( strcmp(command, caCommands[i].Name) == 0 ) {
+                               cmdIdx = i;
+                               break;
+                       }
+               }
+               if( cmdIdx != -1 ) {
+                       caCommands[cmdIdx].Fcn(String+pos);
+               }
+               else
+               {
+                       Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, NULL, "Unknown command %s", command);
+               }
+       }
+       else
+       {
+               // Message
+               // - Only send if server is valid and window name is non-empty
+               tServer *srv = Window_GetServer(NULL);
+               if( srv && Window_IsChat(NULL) ) {
+                       Window_AppendMessage(NULL, MSG_CLASS_MESSAGE, Server_GetNick(srv), "%s", String);
+                       Server_SendCommand(srv, "PRIVMSG %s :%s\n", Window_GetName(NULL), String);
+               }
+       }
+       
+       return 0;
+}
diff --git a/Usermode/Applications/irc_src/input.h b/Usermode/Applications/irc_src/input.h
new file mode 100644 (file)
index 0000000..dee859e
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ */
+#ifndef _INPUT_H_
+#define _INPUT_H_
+
+#include <acess/sys.h>
+
+extern void    Input_FillSelect(int *nfds, fd_set *rfds);
+extern void    Input_HandleSelect(int nfds, const fd_set *rfds);
+
+#endif
+
index 1ca53c2..e3e2af4 100755 (executable)
@@ -6,94 +6,41 @@
 #include <stdio.h>
 #include <string.h>
 #include <net.h>
-#include <readline.h>
-#include <acess/devices/pty.h>
 #include <stdarg.h>
 
-// === TYPES ===
-typedef struct sServer {
-       struct sServer  *Next;
-        int    FD;
-       char    InBuf[BUFSIZ+1];
-        int    ReadPos;
-       char    Name[];
-} tServer;
-
-typedef struct sMessage
-{
-       struct sMessage *Next;
-       time_t  Timestamp;
-       tServer *Server;
-        int    Type;
-       char    *Source;        // Pointer into `Data`
-       char    Data[];
-}      tMessage;
+#include "common.h"
+#include "input.h"
+#include "window.h"
+#include "server.h"
 
-typedef struct sWindow
-{
-       struct sWindow  *Next;
-       tMessage        *Messages;
-       tServer *Server;        //!< Canonical server (can be NULL)
-        int    ActivityLevel;
-       char    Name[]; // Channel name / remote user
-}      tWindow;
-
-enum eMessageTypes
-{
-       MSG_TYPE_NULL,
-       MSG_TYPE_SERVER,        // Server message
-       
-       MSG_TYPE_NOTICE,        // NOTICE command
-       MSG_TYPE_JOIN,  // JOIN command
-       MSG_TYPE_PART,  // PART command
-       MSG_TYPE_QUIT,  // QUIT command
-       
-       MSG_TYPE_STANDARD,      // Standard line
-       MSG_TYPE_ACTION,        // /me
-       
-       MSG_TYPE_UNK
-};
+// === TYPES ===
 
 // === PROTOTYPES ===
  int   main(int argc, const char *argv[], const char *envp[]);
  int   MainLoop(void);
  int   ParseArguments(int argc, const char *argv[]);
- int   ParseUserCommand(char *String);
 // --- 
-tServer        *Server_Connect(const char *Name, const char *AddressString, short PortNumber);
-tMessage       *Message_AppendF(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message, ...) __attribute__((format(__printf__,5,6)));
-tMessage       *Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message);
-tWindow        *Window_Create(tServer *Server, const char *Name);
 void   Redraw_Screen(void);
-
- int   ProcessIncoming(tServer *Server);
 // --- Helpers
-void   SetCursorPos(int Row, int Col);
+void   Exit(const char *Reason);
  int   writef(int FD, const char *Format, ...);
  int   OpenTCP(const char *AddressString, short PortNumber);
 char   *GetValue(char *Str, int *Ofs);
-static inline int      isdigit(int ch);
 
 // === GLOBALS ===
-char   *gsUsername = "user";
-char   *gsHostname = "acess";
-char   *gsRealName = "Acess2 IRC Client";
-char   *gsNickname = "acess";
-tServer        *gpServers;
-tWindow        gWindow_Status = {
-       NULL, NULL, NULL,       // No next, empty list, no server
-       0, {""} // No activity, empty name (rendered as status)
-};
-tWindow        *gpWindows = &gWindow_Status;
-tWindow        *gpCurrentWindow = &gWindow_Status;
- int   giTerminal_Width = 80;
- int   giTerminal_Height = 25;
+const char     *gsExitReason = "No reason [BUG]";
 
 // ==== CODE ====
 void ExitHandler(void)
 {
        printf("\x1B[?1047l");
-       printf("Quit\n");
+       printf("Quit: %s\n", gsExitReason);
+}
+
+void Exit(const char *Reason)
+{
+       gsExitReason = (Reason ? Reason : "User Requested");
+       exit( (Reason ? 1 : 0) );
 }
 
 int main(int argc, const char *argv[], const char *envp[])
@@ -105,40 +52,31 @@ int main(int argc, const char *argv[], const char *envp[])
        
        atexit(ExitHandler);
        
-       if( _SysIOCtl(1, DRV_IOCTL_TYPE, NULL) != DRV_TYPE_TERMINAL ) {
-               fprintf(stderr, "note: assuming 80x25, can't get terminal dimensions\n");
-               giTerminal_Width = 80;
-               giTerminal_Height = 25;
-       }
-       else {
-               struct ptydims  dims;
-               _SysIOCtl(1, PTY_IOCTL_GETDIMS, &dims);
-               giTerminal_Width = dims.W;
-               giTerminal_Height = dims.H;
-       }
+       ACurses_Init();
        
        printf("\x1B[?1047h");
-       printf("\x1B[%i;%ir", 0, giTerminal_Height-1);
+       printf("\x1B[%i;%ir", 1, giTerminal_Height-1);
        
        SetCursorPos(giTerminal_Height-1, 1);
        printf("[(status)] ");
        
        // HACK: Static server entry
        // UCC (University [of Western Australia] Computer Club) IRC Server
-       gWindow_Status.Server = Server_Connect( "UCC", "130.95.13.18", 6667 );
+       tServer *starting_server = Server_Connect( "UCC", "130.95.13.18", 6667 );
        // Freenode (#osdev)
 //     gWindow_Status.Server = Server_Connect( "Freenode", "89.16.176.16", 6667 );
        // Local servers
 //     gWindow_Status.Server = Server_Connect( "VMHost", "10.0.2.2", 6667 );
 //     gWindow_Status.Server = Server_Connect( "BitlBee", "192.168.1.39", 6667 );
        
-       if( !gWindow_Status.Server )
+       if( !starting_server )
                return -1;
        
-       MainLoop();
+       Windows_SetStatusServer(starting_server);
        
-       for( tServer *srv = gpServers; srv; srv = srv->Next )
-               _SysClose(srv->FD);
+       MainLoop();
+
+       Servers_CloseAll("Client closing");
        
        return 0;
 }
@@ -149,73 +87,25 @@ int MainLoop(void)
        printf("[(status)] ");
        fflush(stdout);
        
-       tReadline *readline_info = Readline_Init(1);
-       
        for( ;; )
        {
                fd_set  readfds, errorfds;
-                int    maxFD = 0;
+                int    nfds = 1;
                
                FD_ZERO(&readfds);
                FD_ZERO(&errorfds);
-               FD_SET(0, &readfds);    // stdin
                
-               fflush(stdout);
+               Input_FillSelect(&nfds, &readfds);
+               Servers_FillSelect(&nfds, &readfds, &errorfds);
                
-               // Fill server FDs in fd_set
-               for( tServer *srv = gpServers; srv; srv = srv->Next )
-               {
-                       FD_SET(srv->FD, &readfds);
-                       FD_SET(srv->FD, &errorfds);
-                       if( srv->FD > maxFD )
-                               maxFD = srv->FD;
-               }
+               int rv = _SysSelect(nfds, &readfds, 0, &errorfds, NULL, 0);
+               if( rv < 0 )    break;
                
-               int rv = _SysSelect(maxFD+1, &readfds, 0, &errorfds, NULL, 0);
-               if( rv == -1 )  break;
-               
-               if(FD_ISSET(0, &readfds))
-               {
-                       // User input
-                       char    *cmd = Readline_NonBlock(readline_info);
-                       if( cmd )
-                       {
-                               if( cmd[0] )
-                               {
-                                       ParseUserCommand(cmd);
-                               }
-                               free(cmd);
-                               // Prompt
-                               SetCursorPos(giTerminal_Height-1, 1);
-                               printf("\x1B[2K");      // Clear line
-                               if( gpCurrentWindow->Name[0] )
-                                       printf("[%s:%s] ",
-                                               gpCurrentWindow->Server->Name, gpCurrentWindow->Name);
-                               else
-                                       printf("[(status)] ");
-                       }
-               }
+               // user input
+               Input_HandleSelect(nfds, &readfds);
                
                // Server response
-               for( tServer *srv = gpServers; srv; srv = srv->Next )
-               {
-                       if(FD_ISSET(srv->FD, &readfds))
-                       {
-                               if( ProcessIncoming(srv) != 0 ) {
-                                       // Oops, error
-                                       _SysDebug("ProcessIncoming failed on FD%i (Server %p %s)",
-                                               srv->FD, srv, srv->Name);
-                                       return 1;
-                               }
-                       }
-                       
-                       if(FD_ISSET(srv->FD, &errorfds))
-                       {
-                               _SysDebug("Error on FD%i (Server %p %s)",
-                                       srv->FD, srv, srv->Name);
-                               return 1;
-                       }
-               }
+               Servers_HandleSelect(nfds, &readfds, &errorfds);
        }
        return 0;
 }
@@ -228,547 +118,12 @@ int ParseArguments(int argc, const char *argv[])
        return 0;
 }
 
-
-void Cmd_join(char *ArgString)
-{
-        int    pos=0;
-       char    *channel_name = GetValue(ArgString, &pos);
-       
-       if( gpCurrentWindow->Server )
-       {
-               gpCurrentWindow = Window_Create(gpCurrentWindow->Server, channel_name);
-               Redraw_Screen();
-               writef(gpCurrentWindow->Server->FD, "JOIN :%s\n", channel_name);
-       }
-}
-
-void Cmd_quit(char *ArgString)
-{
-       const char *quit_message = ArgString;
-       if( quit_message == NULL || quit_message[0] == '\0' )
-               quit_message = "/quit - Acess2 IRC Client";
-       
-       for( tServer *srv = gpServers; srv; srv = srv->Next )
-       {
-               writef(srv->FD, "QUIT :%s\n", quit_message);
-       }
-       
-       exit(0);
-}
-
-void Cmd_window(char *ArgString)
-{
-        int    pos = 0;
-       char    *window_id = GetValue(ArgString, &pos);
-        int    window_num = atoi(window_id);
-       
-       if( window_num > 0 )
-       {
-               tWindow *win;
-               window_num --;  // Move to base 0
-               // Get `window_num`th window
-               for( win = gpWindows; win && window_num--; win = win->Next );
-               if( win ) {
-                       gpCurrentWindow = win;
-                       Redraw_Screen();
-               }
-               // Otherwise, silently ignore
-       }
-       else
-       {
-               window_num = 1;
-               for( tWindow *win = gpWindows; win; win = win->Next, window_num ++ )
-               {
-                       if( win->Name[0] ) {
-                               Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "",
-                                       "%i: %s/%s", window_num, win->Server->Name, win->Name);
-                       }
-                       else {
-                               Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "",
-                                       "%i: (status)", window_num);
-                       }
-               }
-       }
-}
-
-const struct {
-       const char *Name;
-       void    (*Fcn)(char *ArgString);
-} caCommands[] = {
-       {"join", Cmd_join},
-       {"quit", Cmd_quit},
-       {"window", Cmd_window},
-       {"win",    Cmd_window},
-       {"w",      Cmd_window},
-};
-const int ciNumCommands = sizeof(caCommands)/sizeof(caCommands[0]);
-
-/**
- * \brief Handle a line from the prompt
- */
-int ParseUserCommand(char *String)
-{
-       if( String[0] == '/' )
-       {
-               char    *command;
-                int    pos = 0;
-               
-               command = GetValue(String, &pos)+1;
-
-               // TODO: Prefix matches
-                int    cmdIdx = -1;
-               for( int i = 0; i < ciNumCommands; i ++ )
-               {
-                       if( strcmp(command, caCommands[i].Name) == 0 ) {
-                               cmdIdx = i;
-                               break;
-                       }
-               }
-               if( cmdIdx != -1 ) {
-                       caCommands[cmdIdx].Fcn(String+pos);
-               }
-               else
-               {
-                       Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "", "Unknown command %s", command);
-               }
-       }
-       else
-       {
-               // Message
-               // - Only send if server is valid and window name is non-empty
-               if( gpCurrentWindow->Server && gpCurrentWindow->Name[0] )
-               {
-                       Message_Append(gpCurrentWindow->Server, MSG_TYPE_STANDARD,
-                               gsNickname, gpCurrentWindow->Name, String);
-                       writef(gpCurrentWindow->Server->FD,
-                               "PRIVMSG %s :%s\n", gpCurrentWindow->Name,
-                               String
-                               );
-               }
-       }
-       
-       return 0;
-}
-
-/**
- * \brief Connect to a server
- */
-tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber)
-{
-       tServer *ret;
-       
-       ret = calloc(1, sizeof(tServer) + strlen(Name) + 1);
-       
-       strcpy(ret->Name, Name);
-       
-       // Connect to the remove server
-       ret->FD = OpenTCP( AddressString, PortNumber );
-       if( ret->FD == -1 ) {
-               fprintf(stderr, "%s: Unable to create socket\n", Name);
-               return NULL;
-       }
-       
-       // Append to open list
-       ret->Next = gpServers;
-       gpServers = ret;
-       
-       // Read some initial data
-       Message_Append(NULL, MSG_TYPE_SERVER, Name, "", "Connection opened");
-       ProcessIncoming(ret);
-       
-       // Identify
-       writef(ret->FD, "USER %s %s %s : %s\n", gsUsername, gsHostname, AddressString, gsRealName);
-       writef(ret->FD, "NICK %s\n", gsNickname);
-       Message_Append(NULL, MSG_TYPE_SERVER, Name, "", "Identified");
-       //printf("%s: Identified\n", Name);
-       
-       return ret;
-}
-
-tMessage *Message_AppendF(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message, ...)
-{
-       va_list args;
-        int    len;
-       va_start(args, Message);
-       len = vsnprintf(NULL, 0, Message, args);
-       va_end(args);
-       
-       char    buf[len+1];
-       va_start(args, Message);
-       vsnprintf(buf, len+1, Message, args);
-       va_end(args);
-       
-       return Message_Append(Server, Type, Source, Dest, buf);
-}
-
-tMessage *Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message)
-{
-       tWindow *win = NULL;
-        int    msgLen = strlen(Message);
-       
-       // Server==NULL indicates an internal message
-       if( Server == NULL || Source[0] == '\0' )
-       {
-               win = &gWindow_Status;
-       }
-       // Determine if it's a channel or PM
-       else if( Dest[0] == '#' || Dest[0] == '&' )     // TODO: Better determining here
-       {
-               for(win = gpWindows; win; win = win->Next)
-               {
-                       if( win->Server == Server && strcmp(win->Name, Dest) == 0 )
-                       {
-                               break;
-                       }
-               }
-               if( !win ) {
-                       //win = Window_Create(Server, Dest);
-                       win = &gWindow_Status;  // Stick it in the status window, just in case
-               }
-       }
-       #if 0
-       else if( strcmp(Dest, Server->Nick) != 0 )
-       {
-               // Umm... message for someone who isn't us?
-               win = &gWindow_Status;  // Stick it in the status window, just in case
-       }
-       #endif
-       // Server message?
-       else if( strchr(Source, '.') )  // TODO: And again, less hack please
-       {
-               #if 1
-               for(win = gpWindows; win; win = win->Next)
-               {
-                       if( win->Server == Server && strcmp(win->Name, Source) == 0 )
-                       {
-                               break;
-                       }
-               }
-               #endif
-               if( !win ) {
-                       win = &gWindow_Status;
-               }
-               
-               // Set source to the server name (instead of the hostname)
-               Source = Server->Name;
-       }
-       // Private message
-       else
-       {
-               for(win = gpWindows; win; win = win->Next)
-               {
-                       if( win->Server == Server && strcmp(win->Name, Source) == 0 )
-                       {
-                               break;
-                       }
-               }
-               if( !win ) {
-                       win = Window_Create(Server, Dest);
-               }
-       }
-
-       // Create message cache 
-       _SysDebug("Win (%s) msg: <%s> %s", win->Name, Source, Message);
-       tMessage        *ret;
-       ret = malloc( sizeof(tMessage) + msgLen + 1 + strlen(Source) + 1 );
-       ret->Source = ret->Data + msgLen + 1;
-       strcpy(ret->Source, Source);
-       strcpy(ret->Data, Message);
-       ret->Type = Type;
-       ret->Server = Server;
-       
-       // Append to window message list
-       ret->Next = win->Messages;
-       win->Messages = ret;
-       
-       // Print now if current window
-       if( win == gpCurrentWindow )
-       {
-               printf("\33[s");
-               printf("\33[T");        // Scroll down 1 (free space below)
-               SetCursorPos(giTerminal_Height-2, 1);
-                int    prefixlen = strlen(Source) + 3;
-                int    avail = giTerminal_Width - prefixlen;
-                int    msglen = strlen(Message);
-               printf("[%s] %.*s", Source, avail, Message);
-               while( msglen > avail ) {
-                       msglen -= avail;
-                       Message += avail;
-                       printf("\33[T");
-                       SetCursorPos(giTerminal_Height-2, prefixlen+1);
-                       printf("%.*s", avail, Message);
-               }
-               printf("\x1b[u");
-       }
-       
-       return ret;
-}
-
-tWindow *Window_Create(tServer *Server, const char *Name)
-{
-       tWindow *ret, *prev = NULL;
-        int    num = 1;
-       
-       // Get the end of the list
-       // TODO: Cache this instead
-       for( ret = gpCurrentWindow; ret; prev = ret, ret = ret->Next )
-               num ++;
-       
-       ret = malloc(sizeof(tWindow) + strlen(Name) + 1);
-       ret->Messages = NULL;
-       ret->Server = Server;
-       ret->ActivityLevel = 1;
-       strcpy(ret->Name, Name);
-       
-       if( prev ) {
-               ret->Next = prev->Next;
-               prev->Next = ret;
-       }
-       else {  // Shouldn't happen really
-               ret->Next = gpWindows;
-               gpWindows = ret;
-       }
-       
-//     printf("Win %i %s:%s created\n", num, Server->Name, Name);
-       
-       return ret;
-}
-
 void Redraw_Screen(void)
 {
-        int    y = 0;
-       tMessage        *msg;
-
        printf("\x1B[2J");      // Clear screen
        printf("\x1B[0;0H");    // Reset cursor
 
-       msg = gpCurrentWindow->Messages;
-       
-       // TODO: Title bar?
-
-       // Note: This renders from the bottom up
-       for( y = giTerminal_Height - 1; y -- && msg; msg = msg->Next)
-       {
-                int    msglen = strlen(msg->Data);
-                int    prefix_len = 3 + strlen(msg->Source);
-                int    line_avail = giTerminal_Width - prefix_len;
-                int    i = 0, done = 0;
-               
-               y -= msglen / line_avail;       // Extra lines (y-- above handles the 1 line case)
-               SetCursorPos(y, 1);
-               printf("[%s] ", msg->Source);
-               
-               while(done < msglen) {
-                       done += printf("%.*s", line_avail, msg->Data+done);
-                       i ++;
-                       SetCursorPos(y+i, prefix_len+1);
-               }
-       }
-
-       // Bottom line is rendered by the prompt
-}
-
-void Cmd_PRIVMSG(tServer *Server, const char *Dest, const char *Src, const char *Message)
-{
-       Message_Append(Server, MSG_TYPE_STANDARD, Dest, Src, Message);
-       //printf("<%s:%s:%s> %s\n", Server->Name, Dest, Src, Message);
-}
-
-void ParseServerLine_Numeric(tServer *Server, const char *ident, int Num, char *Line)
-{
-        int    pos = 0;
-       const char *message;
-       const char *user = GetValue(Line, &pos);
-       
-       if( Line[pos] == ':' ) {
-               message = Line + pos + 1;
-       }
-       else {
-               message = GetValue(Line, &pos);
-       }
-       
-       switch(Num)
-       {
-       case 332:       // Topic
-               user = message; // Channel
-               message = Line + pos + 1;       // Topic
-               Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Topic: %s", message);
-               break;
-       case 333:       // Topic set by
-               user = message; // Channel
-               message = GetValue(Line, &pos); // User
-               GetValue(Line, &pos);   // Timestamp
-               Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Set by %s", message);
-               break;
-       case 353:       // /NAMES list
-               // <user> = <channel> :list
-               // '=' was eaten in and set to message
-               user = GetValue(Line, &pos);    // Actually channel
-               message = Line + pos + 1;       // List
-               Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Names: %s", message);
-               break;
-       case 366:       // end of /NAMES list
-               // <user> <channel> :msg
-               user = message;
-               message = Line + pos + 1;
-               Message_Append(Server, MSG_TYPE_SERVER, user, user, message);
-               break;
-       case 372:       // MOTD Data
-       case 375:       // MOTD Start
-       case 376:       // MOTD End
-               
-       default:
-               //printf("[%s] %i %s\n", Server->Name, num, message);
-               Message_Append(Server, MSG_TYPE_SERVER, ident, user, message);
-               break;
-       }
-}
-
-void ParseServerLine_String(tServer *Server, const char *ident, const char *cmd, char *Line)
-{
-        int    pos = 0;
-       _SysDebug("ident=%s,cmd=%s,Line=%s", ident, cmd, Line);
-       if( strcmp(cmd, "NOTICE") == 0 )
-       {
-               const char *class = GetValue(Line, &pos);
-               _SysDebug("NOTICE class='%s'", class);
-               
-               const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
-               
-               //printf("[%s] NOTICE %s: %s\n", Server->Name, ident, message);
-               char *ident_bang = strchr(ident, '!');
-               if( ident_bang ) {
-                       *ident_bang = '\0';
-               }
-               Message_Append(Server, MSG_TYPE_NOTICE, ident, "", message);
-       }
-       else if( strcmp(cmd, "PRIVMSG") == 0 )
-       {
-               const char *dest = GetValue(Line, &pos);
-               const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
-
-               // TODO: Catch when the privmsg is addressed to the user
-
-//             Cmd_PRIVMSG(Server, dest, ident, message);
-               char *ident_bang = strchr(ident, '!');
-               if( ident_bang ) {
-                       *ident_bang = '\0';
-               }
-               Message_Append(Server, MSG_TYPE_STANDARD, ident, dest, message);
-       }
-       else if( strcmp(cmd, "JOIN" ) == 0 )
-       {
-               const char      *channel = Line + pos + 1;
-               
-               Message_AppendF(Server, MSG_TYPE_JOIN, "", channel, "%s has joined", ident);
-               //Window_Create(Server, channel);
-       }
-       else if( strcmp(cmd, "PART" ) == 0 )
-       {
-               const char      *channel = Line + pos + 1;
-               
-               Message_AppendF(Server, MSG_TYPE_PART, "", channel, "%s has left", ident);
-               //Window_Create(Server, channel);
-       }
-       else
-       {
-               Message_AppendF(Server, MSG_TYPE_SERVER, "", "", "Unknown message %s (%s)", cmd, Line);
-       }
-}
-
-/**
- */
-void ParseServerLine(tServer *Server, char *Line)
-{
-        int    pos = 0;
-
-       _SysDebug("[%s] %s", Server->Name, Line);       
-       
-       // Message?
-       if( *Line == ':' )
-       {
-               pos ++;
-               const char *ident = GetValue(Line, &pos);       // Ident (user or server)
-               const char *cmd = GetValue(Line, &pos);
-               
-               // Numeric command
-               if( isdigit(cmd[0]) && isdigit(cmd[1]) && isdigit(cmd[2]) )
-               {
-                        int    num;
-                       num  = (cmd[0] - '0') * 100;
-                       num += (cmd[1] - '0') * 10;
-                       num += (cmd[2] - '0') * 1;
-
-                       ParseServerLine_Numeric(Server, ident, num, Line+pos);
-               }
-               else
-               {
-                       ParseServerLine_String(Server, ident, cmd, Line+pos);
-               }
-       }
-       else {
-               const char *cmd = GetValue(Line, &pos);
-               
-               if( strcmp(cmd, "PING") == 0 ) {
-                       writef(Server->FD, "PONG %s\n", Line+pos);
-               }
-               else {
-                       // Command to client
-                       Message_AppendF(NULL, MSG_TYPE_UNK, "", "", "Client Command: %s", Line);
-               }
-       }
-}
-
-/**
- * \brief Process incoming lines from the server
- */
-int ProcessIncoming(tServer *Server)
-{      
-       char    *ptr, *newline;
-        int    len;
-       
-       // While there is data in the buffer, read it into user memory and 
-       // process it line by line
-       // ioctl#8 on a TCP client gets the number of bytes in the recieve buffer
-       // - Used to avoid blocking
-       #if NON_BLOCK_READ
-       while( (len = _SysIOCtl(Server->FD, 8, NULL)) > 0 )
-       {
-       #endif
-               // Read data
-               len = _SysRead(Server->FD, &Server->InBuf[Server->ReadPos], BUFSIZ - Server->ReadPos);
-               if( len == -1 ) {
-                       return -1;
-               }
-               Server->InBuf[Server->ReadPos + len] = '\0';
-               
-               // Break into lines
-               ptr = Server->InBuf;
-               while( (newline = strchr(ptr, '\n')) )
-               {
-                       *newline = '\0';
-                       if( newline[-1] == '\r' )       newline[-1] = '\0';
-                       ParseServerLine(Server, ptr);
-                       ptr = newline + 1;
-               }
-               
-               // Handle incomplete lines
-               if( ptr - Server->InBuf < len + Server->ReadPos ) {
-                       // Update the read position
-                       // InBuf ReadPos    ptr          ReadPos+len
-                       // | old | new used | new unused |
-                       Server->ReadPos = len + Server->ReadPos - (ptr - Server->InBuf);
-                       // Copy stuff back (moving "new unused" to the start of the buffer)
-                       memcpy(Server->InBuf, ptr, Server->ReadPos);
-               }
-               else {
-                       Server->ReadPos = 0;
-               }
-       #if NON_BLOCK_READ
-       }
-       #endif
-       
-       return 0;
+       Windows_RepaintCurrent();
 }
 
 /**
@@ -863,12 +218,3 @@ char *GetValue(char *Src, int *Ofs)
        return ret;
 }
 
-void SetCursorPos(int Row, int Col)
-{
-       printf("\x1B[%i;%iH", Row, Col);
-}
-
-static inline int isdigit(int ch)
-{
-       return '0' <= ch && ch < '9';
-}
diff --git a/Usermode/Applications/irc_src/message.h b/Usermode/Applications/irc_src/message.h
new file mode 100644 (file)
index 0000000..0b86f12
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ */
+#ifndef _MESSAGE_H_
+#define _MESSAGE_H_
+
+enum eMessageTypes
+{
+       MSG_TYPE_NULL,
+       MSG_TYPE_SERVER,        // Server message
+       
+       MSG_TYPE_NOTICE,        // NOTICE command
+       MSG_TYPE_JOIN,  // JOIN command
+       MSG_TYPE_PART,  // PART command
+       MSG_TYPE_QUIT,  // QUIT command
+       
+       MSG_TYPE_STANDARD,      // Standard line
+       MSG_TYPE_ACTION,        // /me
+       
+       MSG_TYPE_UNK
+};
+
+enum eMessageClass
+{
+       MSG_CLASS_BARE, // source is unused, just gets timestamped
+       MSG_CLASS_CLIENT,       // source optional, prefixed by '-!-'
+       MSG_CLASS_WALL,         // [SOURCE] MSG         -- Server-provided message
+       MSG_CLASS_MESSAGE,      // <SOURCE> MSG
+       MSG_CLASS_ACTION,       // * SOURCE MSG
+};
+
+typedef struct sMessage        tMessage;
+
+//extern tMessage      *Message_AppendF(tServer *Server, int Type, const char *Src, const char *Dst, const char *Fmt, ...) __attribute__((format(__printf__,5,6)));
+//extern tMessage      *Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message);
+
+#endif
+
diff --git a/Usermode/Applications/irc_src/pseudo_curses.c b/Usermode/Applications/irc_src/pseudo_curses.c
new file mode 100644 (file)
index 0000000..bdcb3cf
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ */
+#include "pseudo_curses.h"
+#include <acess/sys.h>
+#include <acess/devices/pty.h>
+#include <stdio.h>
+
+ int   giTerminal_Width = 80;
+ int   giTerminal_Height = 25;
+
+void ACurses_Init(void)
+{
+       if( _SysIOCtl(1, DRV_IOCTL_TYPE, NULL) != DRV_TYPE_TERMINAL ) {
+               _SysDebug("note: assuming 80x25, can't get terminal dimensions");
+               giTerminal_Width = 80;
+               giTerminal_Height = 25;
+       }
+       else {
+               struct ptydims  dims;
+               _SysIOCtl(1, PTY_IOCTL_GETDIMS, &dims);
+               giTerminal_Width = dims.W;
+               giTerminal_Height = dims.H;
+       }
+}
+
+void SetCursorPos(int Row, int Col)
+{
+       printf("\x1B[%i;%iH", Row, Col);
+}
+
diff --git a/Usermode/Applications/irc_src/pseudo_curses.h b/Usermode/Applications/irc_src/pseudo_curses.h
new file mode 100644 (file)
index 0000000..0548e92
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ */
+#ifndef _ACURSES_H_
+#define _ACURSES_H_
+
+extern int     giTerminal_Width;
+extern int     giTerminal_Height;
+
+extern void    ACurses_Init(void);
+extern void    SetCursorPos(int Row, int Col);
+
+#endif
+
diff --git a/Usermode/Applications/irc_src/server.c b/Usermode/Applications/irc_src/server.c
new file mode 100644 (file)
index 0000000..c8bfea5
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "server.h"
+#include "window.h"
+#include <acess/sys.h>
+#include <ctype.h>     // isdigit
+#include <stdio.h>     // vsnprintf
+
+// === PROTOTYPES ===
+tServer        *Server_Connect(const char *Name, const char *AddressString, short PortNumber);
+ int   Server_HandleIncoming(tServer *Server);
+void   ParseServerLine(tServer *Server, char *Line);
+
+// === GLOBALS ===
+const char     *gsUsername = "user";
+const char     *gsHostname = "acess";
+const char     *gsRealName = "Acess2 IRC Client";
+const char     *gsNickname = "acess";
+tServer        *gpServers;
+
+// === CODE ===
+void Servers_FillSelect(int *nfds, fd_set *rfds, fd_set *efds)
+{
+       for( tServer *srv = gpServers; srv; srv = srv->Next )
+       {
+               FD_SET(srv->FD, rfds);
+               FD_SET(srv->FD, efds);
+               if( srv->FD >= *nfds )
+                       *nfds = srv->FD+1;
+       }
+}
+
+void Servers_HandleSelect(int nfds, const fd_set *rfds, const fd_set *efds)
+{
+       for( tServer *srv = gpServers; srv; srv = srv->Next )
+       {
+               if(FD_ISSET(srv->FD, rfds))
+               {
+                        int    rv = Server_HandleIncoming(srv);
+                       if(rv)
+                       {
+                               // Oops, error
+                               _SysDebug("ProcessIncoming failed on FD%i (Server %p %s)",
+                                       srv->FD, srv, srv->Name);
+                               Exit("Processing error");
+                       }
+               }
+               
+               if(FD_ISSET(srv->FD, efds))
+               {
+                       _SysDebug("Error on FD%i (Server %p %s)",
+                               srv->FD, srv, srv->Name);
+                       Exit("Socket error");
+               }
+       }
+}
+
+void Servers_CloseAll(const char *QuitMessage)
+{
+       while( gpServers )
+       {
+               tServer *srv = gpServers;
+               gpServers = srv->Next;
+       
+               Server_SendCommand(srv, "QUIT :%s", QuitMessage);
+               _SysClose(srv->FD);
+               free(srv);
+       }
+}
+
+/**
+ * \brief Connect to a server
+ */
+tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber)
+{
+       tServer *ret;
+       
+       ret = calloc(1, sizeof(tServer) + strlen(Name) + 1);
+       
+       strcpy(ret->Name, Name);
+       
+       // Connect to the remove server
+       ret->FD = OpenTCP( AddressString, PortNumber );
+       if( ret->FD == -1 ) {
+               free(ret);
+               Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Unable to create socket");
+               return NULL;
+       }
+       
+       ret->Nick = strdup(gsNickname);
+       
+       // Append to open list
+       ret->Next = gpServers;
+       gpServers = ret;
+       
+       // Read some initial data
+       Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Unable to create socket");
+       Server_HandleIncoming(ret);
+       
+       // Identify
+       Server_SendCommand(ret, "USER %s %s %s : %s", gsUsername, gsHostname, AddressString, gsRealName);
+       Server_SendCommand(ret, "NICK %s", ret->Nick);
+       Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Identified");
+       
+       return ret;
+}
+
+const char *Server_GetNick(const tServer *Server)
+{
+       return Server->Nick;
+}
+
+void Server_SendCommand(tServer *Server, const char *Format, ...)
+{
+       va_list args;
+        int    len;
+       
+       va_start(args, Format);
+       len = vsnprintf(NULL, 0, Format, args);
+       va_end(args);
+       
+       char    buf[len+1];
+       va_start(args, Format);
+       vsnprintf(buf, len+1, Format, args);
+       va_end(args);
+       
+       _SysWrite(Server->FD, buf, len);
+       _SysWrite(Server->FD, "\n", 1);
+}
+
+void Cmd_PRIVMSG(tServer *Server, const char *Dest, const char *Src, const char *Message)
+{
+       tWindow *win;
+       if( strcmp(Dest, Server->Nick) == 0 ) {
+               win = Windows_GetByName(Server, Src);
+               if(!win)
+                       win = Window_Create(Server, Src);
+       }
+       else {
+               win = Windows_GetByName(Server, Dest);
+               if(!win)
+                       win = Window_Create(Server, Dest);
+       }
+       Window_AppendMessage(win, MSG_CLASS_MESSAGE, Src, "%s", Message);
+}
+
+/**
+ * \brief Process incoming lines from the server
+ */
+int Server_HandleIncoming(tServer *Server)
+{      
+       char    *ptr, *newline;
+        int    len;
+       
+       // While there is data in the buffer, read it into user memory and 
+       // process it line by line
+       // ioctl#8 on a TCP client gets the number of bytes in the recieve buffer
+       // - Used to avoid blocking
+       #if NON_BLOCK_READ
+       while( (len = _SysIOCtl(Server->FD, 8, NULL)) > 0 )
+       {
+       #endif
+               // Read data
+               len = _SysRead(Server->FD, &Server->InBuf[Server->ReadPos], sizeof(Server->InBuf) - Server->ReadPos);
+               if( len == -1 ) {
+                       return -1;
+               }
+               Server->InBuf[Server->ReadPos + len] = '\0';
+               
+               // Break into lines
+               ptr = Server->InBuf;
+               while( (newline = strchr(ptr, '\n')) )
+               {
+                       *newline = '\0';
+                       if( newline[-1] == '\r' )       newline[-1] = '\0';
+                       ParseServerLine(Server, ptr);
+                       ptr = newline + 1;
+               }
+               
+               // Handle incomplete lines
+               if( ptr - Server->InBuf < len + Server->ReadPos ) {
+                       // Update the read position
+                       // InBuf ReadPos    ptr          ReadPos+len
+                       // | old | new used | new unused |
+                       Server->ReadPos = len + Server->ReadPos - (ptr - Server->InBuf);
+                       // Copy stuff back (moving "new unused" to the start of the buffer)
+                       memcpy(Server->InBuf, ptr, Server->ReadPos);
+               }
+               else {
+                       Server->ReadPos = 0;
+               }
+       #if NON_BLOCK_READ
+       }
+       #endif
+       
+       return 0;
+}
+
+void ParseServerLine_Numeric(tServer *Server, const char *ident, int Num, char *Line)
+{
+        int    pos = 0;
+       const char *message;
+       const char *user = GetValue(Line, &pos);
+       const char      *timestamp;
+       
+       if( Line[pos] == ':' ) {
+               message = Line + pos + 1;
+       }
+       else {
+               message = GetValue(Line, &pos);
+       }
+       
+       switch(Num)
+       {
+       case 332:       // Topic
+               user = message; // Channel
+               message = Line + pos + 1;       // Topic
+               Window_AppendMsg_Topic( Windows_GetByNameOrCreate(Server, user), message );
+               break;
+       case 333:       // Topic set by
+               user = message; // Channel
+               message = GetValue(Line, &pos); // User
+               timestamp = GetValue(Line, &pos);       // Timestamp
+               Window_AppendMsg_TopicTime( Windows_GetByNameOrCreate(Server, user), message, timestamp );
+               break;
+       case 353:       // /NAMES list
+               // <user> = <channel> :list
+               // '=' was eaten in and set to message
+               user = GetValue(Line, &pos);    // Actually channel
+               message = Line + pos + 1;       // List
+               // TODO: parse and store
+               Window_AppendMessage( Windows_GetByNameOrCreate(Server, user), MSG_CLASS_CLIENT, "NAMES", message );
+               break;
+       case 366:       // end of /NAMES list
+               // <user> <channel> :msg
+               // - Ignored
+               break;
+       case 372:       // MOTD Data
+       case 375:       // MOTD Start
+       case 376:       // MOTD End
+               
+       default:
+               //printf("[%s] %i %s\n", Server->Name, num, message);
+               Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_WALL, Server->Name, "Unknown %i %s", Num, message);
+               break;
+       }
+}
+
+void ParseServerLine_String(tServer *Server, const char *ident, const char *cmd, char *Line)
+{
+        int    pos = 0;
+       _SysDebug("ident=%s,cmd=%s,Line=%s", ident, cmd, Line);
+       if( strcmp(cmd, "NOTICE") == 0 )
+       {
+               const char *class = GetValue(Line, &pos);
+               _SysDebug("NOTICE class='%s'", class);
+               
+               const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
+               
+               //printf("[%s] NOTICE %s: %s\n", Server->Name, ident, message);
+               char *ident_bang = strchr(ident, '!');
+               if( ident_bang ) {
+                       *ident_bang = '\0';
+               }
+               // TODO: Colour codes
+               Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_WALL, Server->Name, "%s %s", ident, message);
+       }
+       else if( strcmp(cmd, "PRIVMSG") == 0 )
+       {
+               const char *dest = GetValue(Line, &pos);
+               const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
+
+               char *ident_bang = strchr(ident, '!');
+               if( ident_bang ) {
+                       *ident_bang = '\0';
+               }
+               Cmd_PRIVMSG(Server, dest, ident, message);
+       }
+       else if( strcmp(cmd, "JOIN" ) == 0 )
+       {
+               const char      *channel = Line + pos + 1;
+               
+               Window_AppendMsg_Join( Windows_GetByNameOrCreate(Server, channel), ident );
+       }
+       else if( strcmp(cmd, "PART" ) == 0 )
+       {
+               const char      *channel = Line + pos + 1;
+               
+               Window_AppendMsg_Part( Windows_GetByNameOrCreate(Server, channel), ident, "" );
+       }
+       else
+       {
+               Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_BARE, Server->Name, "Unknown command '%s' %s", cmd, Line);
+       }
+}
+
+/**
+ */
+void ParseServerLine(tServer *Server, char *Line)
+{
+        int    pos = 0;
+
+       _SysDebug("[%s] %s", Server->Name, Line);       
+       
+       // Message?
+       if( *Line == ':' )
+       {
+               pos ++;
+               const char *ident = GetValue(Line, &pos);       // Ident (user or server)
+               const char *cmd = GetValue(Line, &pos);
+               
+               // Numeric command
+               if( isdigit(cmd[0]) && isdigit(cmd[1]) && isdigit(cmd[2]) )
+               {
+                        int    num;
+                       num  = (cmd[0] - '0') * 100;
+                       num += (cmd[1] - '0') * 10;
+                       num += (cmd[2] - '0') * 1;
+
+                       ParseServerLine_Numeric(Server, ident, num, Line+pos);
+               }
+               else
+               {
+                       ParseServerLine_String(Server, ident, cmd, Line+pos);
+               }
+       }
+       else {
+               const char *cmd = GetValue(Line, &pos);
+               
+               if( strcmp(cmd, "PING") == 0 ) {
+                       Server_SendCommand(Server, "PONG %s", Line+pos);
+               }
+               else {
+                       // Command to client
+                       Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Server->Name, "UNK Client Command: %s", Line);
+               }
+       }
+}
diff --git a/Usermode/Applications/irc_src/server.h b/Usermode/Applications/irc_src/server.h
new file mode 100644 (file)
index 0000000..0fd4f51
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ */
+#ifndef _SERVER_H_
+#define _SERVER_H_
+
+#include "common.h"
+
+typedef struct sServer {
+       struct sServer  *Next;
+        int    FD;
+       char    InBuf[1024+1];
+        int    ReadPos;
+       char    *Nick;
+       char    Name[];
+} tServer;
+
+extern void    Servers_FillSelect(int *nfds, fd_set *rfds, fd_set *efds);
+extern void    Servers_HandleSelect(int nfds, const fd_set *rfds, const fd_set *efds);
+extern void    Servers_CloseAll(const char *QuitMessage);
+
+extern tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber);
+extern  int    Server_HandleIncoming(tServer *Server);
+
+extern const char      *Server_GetNick(const tServer *Server);
+
+extern void    Server_SendCommand(tServer *Server, const char *Format, ...) __attribute__((format(__printf__,2,3)));
+
+#endif
+
diff --git a/Usermode/Applications/irc_src/window.c b/Usermode/Applications/irc_src/window.c
new file mode 100644 (file)
index 0000000..1314cec
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ */
+#include "window.h"
+#include <stddef.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>     // TODO: replace with calls into ACurses_*
+#include <stdlib.h>
+#include <assert.h>
+
+struct sMessage
+{
+       struct sMessage *Next;
+       time_t  Timestamp;
+       enum eMessageClass      Class;
+       char    *Source;        // Pointer to the end of `Data`
+       char    Data[];
+};
+
+struct sWindow
+{
+       struct sWindow  *Next;
+       tMessage        *Messages;
+       tServer *Server;        //!< Canonical server (can be NULL)
+        int    ActivityLevel;
+       char    Name[]; // Channel name / remote user
+};
+
+// === PROTOTYPES ===
+void   Windows_RepaintCurrent(void);
+ int   Windows_int_PaintMessage(tMessage *Message);
+
+// === GLOBALS ===
+tWindow        gWindow_Status = {
+       NULL, NULL, NULL,       // No next, empty list, no server
+       0, {""} // No activity, empty name (rendered as status)
+};
+tWindow        *gpWindows = &gWindow_Status;
+tWindow        *gpCurrentWindow = &gWindow_Status;
+
+// === CODE ===
+void Windows_RepaintCurrent(void)
+{
+       tMessage *msg = gpCurrentWindow->Messages;
+       
+       // TODO: Title bar?
+
+       // Note: This renders from the bottom up
+       for( int y = giTerminal_Height - 1; y -- && msg; msg = msg->Next)
+       {
+               y -= Windows_int_PaintMessage(msg);
+       }
+
+       // Bottom line is rendered by the prompt
+       
+}
+
+int Windows_int_PaintMessage(tMessage *Message)
+{
+       printf("\33[T");        // Scroll down 1 (free space below)
+       SetCursorPos(giTerminal_Height-2, 1);
+       
+       size_t  prefixlen = 0;
+       prefixlen += printf("%02i:%02i:%02i ", (Message->Timestamp/3600)%24, (Message->Timestamp/60)%60, Message->Timestamp%60);
+       switch(Message->Class)
+       {
+       case MSG_CLASS_BARE:    break;
+       case MSG_CLASS_CLIENT:
+               if(Message->Source)
+                       prefixlen += printf("[%s] ", Message->Source);
+               prefixlen += printf("-!- ");
+               break;
+       case MSG_CLASS_WALL:
+               prefixlen += printf("[%s] ", Message->Source);
+               break;
+       case MSG_CLASS_MESSAGE:
+               prefixlen += printf("<%s> ", Message->Source);
+               break;
+       case MSG_CLASS_ACTION:
+               prefixlen += printf("* %s ", Message->Source);
+               break;
+       }
+        int    avail = giTerminal_Width - prefixlen;
+        int    msglen = strlen(Message->Data);
+       
+       int     nLines = 1;
+       printf("%.*s", avail, Message);
+       while( msglen > avail ) {
+               msglen -= avail;
+               Message += avail;
+               printf("\33[T");
+               SetCursorPos(giTerminal_Height-2, prefixlen+1);
+               printf("%.*s", avail, Message);
+               nLines ++;
+       }
+       
+       return nLines;
+}
+
+void Windows_SwitchTo(tWindow *Window)
+{
+       gpCurrentWindow = Window;
+       Redraw_Screen();
+}
+
+void Windows_SetStatusServer(tServer *Server)
+{
+       gWindow_Status.Server = Server;
+}
+
+tWindow *Windows_GetByIndex(int Index)
+{
+       tWindow *win;
+       for( win = gpWindows; win && Index--; win = win->Next )
+               ;
+       return win;
+}
+
+tWindow *Windows_GetByNameEx(tServer *Server, const char *Name, bool CreateAllowed)
+{
+       tWindow *ret, *prev = NULL;
+        int    num = 1;
+       
+       // Get the end of the list and check for duplicates
+       // TODO: Cache this instead
+       for( ret = &gWindow_Status; ret; prev = ret, ret = ret->Next )
+       {
+               if( ret->Server == Server && strcmp(ret->Name, Name) == 0 )
+               {
+                       return ret;
+               }
+               num ++;
+       }
+       if( !CreateAllowed ) {
+               return NULL;
+       }
+       
+       ret = malloc(sizeof(tWindow) + strlen(Name) + 1);
+       ret->Messages = NULL;
+       ret->Server = Server;
+       ret->ActivityLevel = 1;
+       strcpy(ret->Name, Name);
+       
+       if( prev ) {
+               ret->Next = prev->Next;
+               prev->Next = ret;
+       }
+       else {  // Shouldn't happen really
+               ret->Next = gpWindows;
+               gpWindows = ret;
+       }
+       
+//     printf("Win %i %s:%s created\n", num, Server->Name, Name);
+       
+       return ret;
+}
+
+tWindow *Windows_GetByName(tServer *Server, const char *Name)
+{
+       return Windows_GetByNameEx(Server, Name, false);
+}
+
+tWindow *Window_Create(tServer *Server, const char *Name)
+{
+       return Windows_GetByNameEx(Server, Name, true);
+}
+
+
+tWindow *Window_int_ParseSpecial(const tWindow *Window)
+{
+       if( Window == NULL )
+               return gpCurrentWindow;
+       if( Window == WINDOW_STATUS )
+               return &gWindow_Status;
+       return (tWindow*)Window;
+}
+
+const char *Window_GetName(const tWindow *Window) {
+       return Window_int_ParseSpecial(Window)->Name;
+}
+tServer        *Window_GetServer(const tWindow *Window) {
+       return Window_int_ParseSpecial(Window)->Server;
+}
+bool Window_IsChat(const tWindow *Window) {
+       return Window_int_ParseSpecial(Window) != &gWindow_Status;
+}
+
+// -----------------------------
+//  Messages
+// -----------------------------
+void Window_AppendMessage(tWindow *Window, enum eMessageClass Class, const char *Source, const char *Message, ...)
+{
+       Window = Window_int_ParseSpecial(Window);
+
+       va_list args;
+       
+       va_start(args, Message);
+       size_t len = vsnprintf(NULL, 0, Message, args);
+       va_end(args);
+       
+       tMessage *msg = malloc( sizeof(tMessage) + len+1 + (Source?strlen(Source)+1:0) );
+       assert(msg);
+       
+       msg->Class = Class;
+       msg->Source = (Source ? msg->Data + len+1 : NULL);
+       va_start(args, Message);
+       vsnprintf(msg->Data, len+1, Message, args);
+       va_end(args);
+       
+       msg->Next = Window->Messages;
+       Window->Messages = msg;
+       
+       if( Window == gpCurrentWindow )
+       {
+               // Scroll if needed, and redraw?
+               // - Lazy option of draw at bottom of screen
+               printf("\33[s");        // Save cursor
+               Windows_int_PaintMessage(msg);
+               printf("\x1b[u");       // Restore cursor
+       }
+}
+
+void Window_AppendMsg_Join(tWindow *Window, const char *Usermask)
+{
+       Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined %s", Usermask, Window->Name);
+}
+void Window_AppendMsg_Quit(tWindow *Window, const char *Usermask, const char *Reason)
+{
+       Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined quit (%s)", Usermask, Reason);
+}
+void Window_AppendMsg_Part(tWindow *Window, const char *Usermask, const char *Reason)
+{
+       Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined left %s (%s)", Usermask, Window->Name, Reason);
+}
+void Window_AppendMsg_Topic(tWindow *Window, const char *Topic)
+{
+       Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "Topic of %s is %s", Window->Name, Topic);
+}
+void Window_AppendMsg_TopicTime(tWindow *Window, const char *User, const char *Timestamp)
+{
+       Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "Topic set by %s at %s", User, Timestamp);
+}
+
diff --git a/Usermode/Applications/irc_src/window.h b/Usermode/Applications/irc_src/window.h
new file mode 100644 (file)
index 0000000..da97f3e
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ */
+#ifndef _WINDOW_H_
+#define _WINDOW_H_
+
+#include "common.h"
+#include "message.h"
+#include <stdbool.h>
+
+typedef struct sWindow tWindow;
+extern void    Windows_RepaintCurrent(void);
+
+extern void    Windows_SetStatusServer(tServer *Server);
+extern tWindow *Window_Create(tServer *Server, const char *Name);
+extern tWindow *Windows_GetByIndex(int Index);
+extern tWindow *Windows_GetByName(tServer *Server, const char *Name);
+static inline tWindow  *Windows_GetByNameOrCreate(tServer *Server, const char *Name) {
+       return Window_Create(Server, Name);
+}
+extern void    Windows_SwitchTo(tWindow *Window);
+
+extern void    Window_AppendMessage(tWindow *Window, enum eMessageClass Class, const char *Source, const char *Message, ...)
+       __attribute__((format(__printf__,4,5)));
+extern void    Window_AppendMsg_Join(tWindow *Window, const char *Usermask);
+extern void    Window_AppendMsg_Quit(tWindow *Window, const char *Usermask, const char *Reason);
+extern void    Window_AppendMsg_Part(tWindow *Window, const char *Usermask, const char *Reason);
+extern void    Window_AppendMsg_Topic(tWindow *Window, const char *Topic);
+extern void    Window_AppendMsg_TopicTime(tWindow *Window, const char *User, const char *Timestmap);
+
+extern const char      *Window_GetName(const tWindow *Window);
+extern tServer *Window_GetServer(const tWindow *Window);
+extern bool    Window_IsChat(const tWindow *Window);
+
+#define WINDOW_STATUS  ((void*)-1)
+
+#endif

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