Compare commits

24 Commits

Author SHA1 Message Date
37d8b7fd0f delete empty file net.h 2026-03-09 12:57:43 -05:00
e68d98a669 update files to match new max column width 100 2026-03-09 12:57:24 -05:00
ce5d66f712 update ColumnLimit 2026-03-09 12:55:21 -05:00
a9ed43b230 update STYLE.md 2026-03-09 12:54:36 -05:00
e8d2e015b3 update README.md 2026-03-09 12:54:07 -05:00
bd2debcf7d write big ugly panic message for when main() freaks out too much 2026-03-09 12:45:24 -05:00
59e34977da oops 2026-03-09 12:32:22 -05:00
0f66b7f4c2 refactor: move display-related functions to display.cc 2026-03-09 12:32:02 -05:00
08b7809c34 refactor: move file operations into file.cc 2026-03-09 12:26:34 -05:00
a1ff9dd62d oops 2026-03-09 12:23:21 -05:00
a2a5f6eb57 refactor: move client/server funcs to net.cc 2026-03-09 12:22:40 -05:00
fa77abd446 copy .clang-format to src/ 2026-03-08 19:39:48 -05:00
da4dfbdd3e move source to src/ 2026-03-08 19:39:02 -05:00
224e59549c oops 2026-03-08 13:37:24 -05:00
5b8dcbdc1a add readme 2026-03-08 13:36:48 -05:00
e5bb323a2f ugh 2026-03-08 13:34:11 -05:00
1368695e74 refactor done? maybe? 2026-03-08 13:11:52 -05:00
53dd322284 refactor pt 2 2026-03-08 12:52:18 -05:00
09fcc17caa refactor wip 2026-03-08 01:29:25 -06:00
54ba2d8067 update ColumnLimit to match STYLE.md 2026-03-07 16:49:21 -06:00
5ca28bf334 add example from scott's initial version 2026-03-07 16:38:48 -06:00
d926b81265 add style guidelines STYLE.md 2026-03-07 16:38:19 -06:00
143ec79d37 add editorconfig and clang-format files 2026-03-07 16:37:55 -06:00
7a0e2de855 refactor: initial commit 2026-03-07 16:26:33 -06:00
20 changed files with 1162 additions and 5 deletions

20
.clang-format Normal file
View File

@@ -0,0 +1,20 @@
{
"BasedOnStyle": "LLVM",
"UseTab": "Always",
"IndentWidth": 8,
"TabWidth": 8,
"ColumnLimit": 100,
"LineEnding": "LF",
"RemoveBracesLLVM": true,
"AlwaysBreakAfterReturnType": "AllDefinitions",
"BreakBeforeBraces": "Custom",
"BraceWrapping": {
"AfterFunction": true,
"AfterClass": false,
"AfterControlStatement": false,
"AfterNamespace": false,
"AfterStruct": false,
"BeforeElse": false,
"BeforeCatch": false
}
}

6
.gitignore vendored
View File

@@ -1,3 +1,3 @@
client.txt old/client.txt
server.txt old/server.txt
main old/main

View File

@@ -1,3 +1,27 @@
an attempt at a threaded ncurses network chat in c++ # threaded network chat refactor
the biggest bug right now is that if you start the server without having started it previously and there isn't a server.txt and client.txt from a previous session, it doesn't work at all. It will work when you try it a second time. The code has been refactored and it compiles. Hopefully it won't explode, but that if it does, that just means we have
actual meaningful work to do.
## TODO
- Finalize coding style,
- **see [STYLE.md](STYLE.md)**
- Get everyone's editors set up for clangd/clang-format and git
- Make sure everyone's compiler toolchains work
- Decide who gets what responsibilities
- Distribute appropriate code to people with those responsibilities
- Have everyone upload their respective code to their sections of the gitlab
## Code
Everything is in [src/](src).
A mostly untouched copy of [scott's original code](https://git.therats.win/scott/threaded_network_chat) is also in [old/](old).
A reformatted version of that code is also in [modified-example.cc](modified-example.cc) but it can be ignored.
## Build
```
cd src/
make
```

123
STYLE.md Normal file
View File

@@ -0,0 +1,123 @@
# Project Coding Style
**Subject to change, let's please discuss this.**
This is a less extreme version of the style I generally follow, and unless someone has a much better style I would
like to use this one for code we will be collaborating on. Feel free to ignore this if you plan for your own code to
be entirely your responsibility.
Also don't swear on anything we expect to present to the professor.
**Please install the [C/C++ Extension Pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools-extension-pack) and [clang-format](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format) for Visual Studio Code.**
Follow the instructions in the previous link to set up the correctly.
## TL;DR
Call files `.cc` instead of `.c` or `.cpp` and use `.h` headers for everything.
If you follow the setup instructions you should be fine. Otherwise:
Use [BSD Kernel Normal Form (KNF)](https://en.wikipedia.org/wiki/Indentation_style#BSD_KNF) for function bodies
and [Kernighan & Ritchie (K&R)](https://en.wikipedia.org/wiki/Indentation_style#K&R) style (with a few modifications stated in **General Guidelines**) everywhere else.
## Existing Literature
I recommend skimming over these for inspiration.
- [Names](https://research.swtch.com/names) - Russ Cox
- [Notes on Programming in C](http://doc.cat-v.org/bell_labs/pikestyle) - Rob Pike
## General Guidelines
- Use 8-width tabs, not spaces, for indentation.
- If you copy code from online, copy it by hand.
- Use [K&R style](https://en.wikipedia.org/wiki/Indentation_style#K&R) for indentation and brace placement where possible.
- When writing function bodies:
- return type goes on its own line,
- function name() is on a line below that, and
- open bracket `{` after that.
- `for`, `if`, `while`, etc. statements that only contain one line
- Use `/* comments */` for permanent comments, `// comments` for temporary ones e.g. `TODO`'s or notices
- **Try to make every line 100 characters wide or less.**
- Start source files as `.cc` straight away, even if they're pure **C**.
### C++-specific guidelines
- file extension: `.cc`, `.h`
- C++ strings are fine
- Vectors are fine
- Use `printf` and co., not `std::cout`, unless somehow absolutely necessary
- `fstream`s for input/output/log files are fine
- Use C's `struct`s instead of objects unless you really really need an object.
- Use C-style type casting where possible
TL;DR pretend you're using C with basic modern conveniences. **When in doubt, ask.**
### Naming and Abbreviations
The length of a function name, variable name, et cetera should be directly proportional to its importance and lifetime.
#### Don't
- abbreviate global variables,
- mention the data type in a variable name,
- use a full word where an abbreviation will do
- (especially not in a variable that will die 5 lines later),
- use single-letter abbreviations for anything that lasts more than 15 lines, or
- abbreviate a word when an apt abbreviation does not exist.
#### Do
- abbreviate extremely short-lived variables to one letter,
- e.g. `for (int index=0, index<10, i++)` -> `for (int i=0, i<10, i++)`
- break any rule if following it ruins the readability
## Source File layout
```C++
/* C++ includes */
#include <iostream>
/* C (.h) includes */
#include <stdio.h>
/* local header includes */
#include "unicorns.h"
/* "using" directives */
using namespace std;
/* global constants */
const int life = 42;
/* global variables */
int degrees = 90;
/* function prototypes */
int div_numb(int n, int d);
/* main function body */
int
main(int argc, char *argv[])
{
int numerator = 32;
int denominator = 16;
int result;
puts("What is 32 divided by 16?");
result = div_numb(32, 16);
return 0;
}
/* other function bodies */
int
div_numb(int n, int d)
{
return n / d;
}
```

View File

@@ -0,0 +1,32 @@
root = true
[*]
charset = utf-8
indent_style = tab
indent_size = 8
tab_width = 8
end_of_line = lf
insert_final_newline = true
[*.{c,cc,cpp,h,hpp}]
# Unbraced single-statement blocks
cpp_remove_braces_for_single_line_control_statements = true
resharper_cpp_remove_braces_for_single_statements = true
resharper_cpp_add_braces_for_single_line_if = false
# Enforce BSD KNF for function bracess
cpp_new_line_before_open_brace_function = new_line
resharper_cpp_brace_style = next_line
resharper_cpp_function_definition_braces = next_line
# Enforce K&R for everything else
cpp_new_line_before_open_brace_namespace = same_line
cpp_new_line_before_open_brace_type = same_line
cpp_new_line_before_open_brace_block = same_line
cpp_new_line_before_open_brace_lambda = same_line
cpp_new_line_before_else = false
cpp_new_line_before_catch = false
resharper_cpp_namespace_declaration_braces = end_of_line
resharper_cpp_type_declaration_braces = end_of_line
resharper_cpp_control_transfer_braces = end_of_line

View File

@@ -0,0 +1,20 @@
{
"BasedOnStyle": "LLVM",
"UseTab": "Always",
"IndentWidth": 8,
"TabWidth": 8,
"ColumnLimit": 120,
"LineEnding": "LF",
"RemoveBracesLLVM": true,
"AlwaysBreakAfterReturnType": "AllDefinitions",
"BreakBeforeBraces": "Custom",
"BraceWrapping": {
"AfterFunction": true,
"AfterClass": false,
"AfterControlStatement": false,
"AfterNamespace": false,
"AfterStruct": false,
"BeforeElse": false,
"BeforeCatch": false
}
}

View File

@@ -0,0 +1,21 @@
# BSD KNF funcs + K&R blocks + other stuff
# Requires clang-format >=16
BasedOnStyle: LLVM # defaults
UseTab: Always
IndentWidth: 8
TabWidth: 8
ColumnLimit: 120
LineEnding: LF # unix
RemoveBracesLLVM: true # single-statement blocks
AlwaysBreakAfterReturnType: AllDefinitions # KNF functions
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true # KNF
AfterClass: false # K&R
AfterControlStatement: false # K&R
AfterNamespace: false # K&R
AfterStruct: false # K&R
BeforeElse: false # K&R
BeforeCatch: false # K&R

386
modified-example.cc Normal file
View File

@@ -0,0 +1,386 @@
/*an attempt at pthread and network and ncurses all in the same function
*/
#include <arpa/inet.h>
#include <curses.h>
#include <fstream>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <ostream>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector> //try to make the program work without this without doing anything awful but do it later
using namespace std;
const int PORT_NUM = 8888;
const string IP_ADDRESS = "127.0.0.1";
const int DEFAULT_CUR_X = 2; // the x position of the preferred default cursor
// position for message entry
const int DEFAULT_CUR_Y = 12; // the x position of the preferred default cursor
// position for message entry
int mode = 0; // what mode is this program in. 0 = nothing. 1 = server. 2 = client
int serverSocketDescriptor; // a global variable for storing the server socket
// descriptor.
int clientSocketDescriptor;
vector<int> clientSocketDescriptors; // a global vector for storing client
// socket descriptors
// keep track of the session time using global variables
struct timeval start1, end1;
// also keep track of the amount of data sent as well
int bytesRead, bytesWritten = 0;
const int LOG_LENGTH = 10; // number of text log file lines to display
int linePos = 0; // counter for what current position to use in the file
string logFileName; // the name of the log file
pthread_t client_wait_thread;
int writeToFile(string path, string line, bool incLineNum = true);
void *waitForClient(void *argss);
void *pollForClient();
void *pollForSever(void *args);
// clears ncurses rows in a specific region only
void
clearRows(int startingRow, int endingRow)
{
// preserve the cursor location
int yBefore, xBefore;
getyx(stdscr, yBefore, xBefore);
for (int i = startingRow; i < endingRow; i++) {
move(i, 0);
clrtoeol(); // clear to end of line
}
// restore original cursor location
move(yBefore, xBefore);
}
// display a file using ncurses
int
displayFile(string path, int startLineNum = 0, int numLines = 10)
{
ifstream file(path);
if (!file) {
return 1;
} else {
clearRows(0, numLines + 1); // clear the chat area
int lineNum = 0;
string line;
// print each line directly to the screen
int num = 0;
while (getline(file, line) && num <= numLines + startLineNum + 1) // while there is file content and the
// line number isn't too high
{
if (num >= startLineNum) {
move(lineNum, 0);
printw("%s", line.c_str());
lineNum++; // increment the row number after
// printing each line
}
num++;
}
move(DEFAULT_CUR_Y, DEFAULT_CUR_X);
refresh();
return 0;
}
}
// gets the number of lines in a file. returns -1 if there was an error.
int
linesInFile(string path)
{
ifstream file(path);
if (!file) {
return 1;
} else {
int lineNum = 0;
string line;
int num = 0;
while (getline(file, line))
num++;
return num;
}
}
struct waitClientArgs {
sockaddr_in newSockAddr;
socklen_t newSockAddrSize;
int auxInt; // used in client mode because I didn't want to make another
// struct to keep track of
};
void *
waitForClient(void *argss)
{
waitClientArgs *args = static_cast<waitClientArgs *>(argss);
clientSocketDescriptor = accept(serverSocketDescriptor, (sockaddr *)&args->newSockAddr, &args->newSockAddrSize);
if (clientSocketDescriptor >= 0) {
writeToFile(logFileName, "client connected");
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
delete args; // delete to avoid memory leaks
return pollForClient();
}
void *
pollForClient()
{
char msg[1024];
while (1) {
// receive a message from the client (listen)
memset(&msg, 0, sizeof(msg)); // clear the buffer
bytesRead += recv(clientSocketDescriptor, (char *)&msg, sizeof(msg), 0);
writeToFile(logFileName,
msg); // write the client's message to file
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
return nullptr;
}
// set up a suitable server for this
int
setupServer(int port)
{
// setup a socket and connection tools
sockaddr_in servAddr;
bzero((char *)&servAddr, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(port);
// open stream oriented socket with internet address
// also keep track of the socket descriptor
serverSocketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocketDescriptor < 0) {
// keeps from bricking the terminal if this happens
endwin();
fprintf(stderr, "Error establishing the server socket!\n");
exit(0);
}
// bind the socket to its local address
int bindStatus = bind(serverSocketDescriptor, (struct sockaddr *)&servAddr, sizeof(servAddr));
if (bindStatus < 0) {
// keeps from bricking the terminal if this happens
endwin();
fprintf(stderr, "Error binding socket to local address!\n");
exit(0);
}
writeToFile(logFileName, "Waiting for a client to connect...");
// listen for up to 5 requests at a time
listen(serverSocketDescriptor, 5);
// receive a request from client using accept
// we need a new address to connect with the client
sockaddr_in newSockAddr;
socklen_t newSockAddrSize = sizeof(newSockAddr);
// accept, create a new socket descriptor to
// handle the new connection with client
auto *aaa = new waitClientArgs{}; // it looks stupid but it works
aaa->newSockAddr = newSockAddr;
aaa->newSockAddrSize = newSockAddrSize;
int rc = pthread_create(&client_wait_thread, nullptr, waitForClient, aaa);
pthread_detach(client_wait_thread);
writeToFile(logFileName, "Server started sucessfully");
gettimeofday(&start1, NULL);
return 0;
}
void
closeServer()
{
long e = end1.tv_sec - start1.tv_sec; /* the linter freaks out if you
don't save it as a variable */
// we need to close the socket descriptors after we're all done
gettimeofday(&end1, NULL);
close(clientSocketDescriptor);
close(serverSocketDescriptor);
/* Don't just die silently */
printf("********Session********\n");
printf("Bytes written: %i\nBytes Read: %i\n", bytesWritten, bytesRead);
printf("Elapsed time: %ld\n secs\n", e);
printf("Connection closed...\n");
}
void
closeClient()
{
long e = end1.tv_sec - start1.tv_sec;
gettimeofday(&end1, NULL);
close(clientSocketDescriptor);
printf("********Session********");
printf("Bytes written: %i\nBytes read: %i\n", bytesWritten, bytesRead);
printf("Elapsed time: %ld\n secs\n", e);
printf("Connection closed...\n");
}
void *
pollForSever(void *args)
{
waitClientArgs *aaa = static_cast<waitClientArgs *>(args);
int socketDescriptor = (int)aaa->auxInt;
char msg[1024];
while (1) {
memset(&msg, 0, sizeof(msg)); // clear the buffer
bytesRead += recv(socketDescriptor, (char *)&msg, sizeof(msg), 0);
// not needed for proofs of concept testing
/*if(!strcmp(msg, "exit"))
{
writeToFile(logFileName, msg);
displayFile(logFileName, linePos, LOG_LENGTH);
break;
}*/
// cout << "Server: " << msg << endl;
// printf("Server: %s\n");
writeToFile(logFileName, msg);
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
}
void
setupClient()
{
// we need 2 things: ip address and port number, in that order
// if(argc != 3)
//{
// cerr << "Usage: ip_address port" << endl; exit(0);
// } //grab the IP address and port number
// create a message buffer
char msg[1024];
// setup a socket and connection tools
struct hostent *host = gethostbyname(IP_ADDRESS.c_str());
sockaddr_in sendSockAddr;
bzero((char *)&sendSockAddr, sizeof(sendSockAddr));
sendSockAddr.sin_family = AF_INET;
sendSockAddr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)*host->h_addr_list));
sendSockAddr.sin_port = htons(PORT_NUM);
clientSocketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
// try to connect...
int status = connect(clientSocketDescriptor, (sockaddr *)&sendSockAddr, sizeof(sendSockAddr));
if (status < 0)
writeToFile(logFileName, "Error connecting to socket!");
writeToFile(logFileName, "Connected to the server!");
int bytesRead, bytesWritten = 0;
struct timeval start1, end1;
gettimeofday(&start1, NULL);
auto *aaa = new waitClientArgs{}; // it looks stupid but it works
aaa->auxInt = clientSocketDescriptor;
int rc = pthread_create(&client_wait_thread, nullptr, pollForSever, aaa);
pthread_detach(client_wait_thread);
}
// appends a line of text to the end of a given file. returns 0 if the file
// existed, 1 if it didn't work
int
writeToFile(string path, string line, bool incLineNum)
{
ofstream file;
file.open(path, ios_base::app); // open the file in append mode
if (file.is_open()) {
file << line << endl;
// this probably would help but theres too much broken stuff
// right now to be sure if (incLineNum)
//{
// linePos++;
// }
return 0;
} else {
// do something if it didn't work
return 1; // i guess that's good enough for now
}
}
int
main(int argc, char *argv[])
{
if (argc > 1 && argv[1][0] == 'c') {
// client mode
logFileName = "client.txt";
mode = 2;
} else {
// server mode
logFileName = "server.txt";
mode = 1;
}
// putting this lower down to circumvent the terminal brick when the
// socket was already in use results in all network functonality no
// longer working.
initscr(); // creates stdscr. important step
use_default_colors(); // this apparently tells it to use default
// terminal colors whenever there is no color
// attribute applied
// printw("Starting server...");
// you have to do this to make the scrolling still work correctly if
// there was already content in the log file
linePos = linesInFile(logFileName) - LOG_LENGTH + 1;
char *userInput = new char[1024];
bool exit = false;
if (mode == 2) {
writeToFile(logFileName, "CLIENT MODE");
setupClient();
} else if (mode == 1) {
writeToFile(logFileName, "SERVER MODE");
setupServer(PORT_NUM);
}
while (!exit) {
displayFile(logFileName, linePos, LOG_LENGTH);
// scroll along the screen if and when required so that it stays
// in sync
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
move(12, 0);
printw("> ");
getstr(userInput);
writeToFile(logFileName, userInput);
if (mode == 1)
send(clientSocketDescriptor, (char *)userInput, strlen(userInput), 0);
else
send(clientSocketDescriptor, (char *)userInput, strlen(userInput), 0);
}
endwin();
closeServer();
return 0;
}

3
old/README.md Normal file
View File

@@ -0,0 +1,3 @@
an attempt at a threaded ncurses network chat in c++
the biggest bug right now is that if you start the server without having started it previously and there isn't a server.txt and client.txt from a previous session, it doesn't work at all. It will work when you try it a second time.

20
src/.clang-format Normal file
View File

@@ -0,0 +1,20 @@
{
"BasedOnStyle": "LLVM",
"UseTab": "Always",
"IndentWidth": 8,
"TabWidth": 8,
"ColumnLimit": 100,
"LineEnding": "LF",
"RemoveBracesLLVM": true,
"AlwaysBreakAfterReturnType": "AllDefinitions",
"BreakBeforeBraces": "Custom",
"BraceWrapping": {
"AfterFunction": true,
"AfterClass": false,
"AfterControlStatement": false,
"AfterNamespace": false,
"AfterStruct": false,
"BeforeElse": false,
"BeforeCatch": false
}
}

20
src/Makefile Normal file
View File

@@ -0,0 +1,20 @@
CXX = g++
CXXFLAGS = -pthread -Wall -Wextra -O2 -std=c++17
LDFLAGS = -lcurses
TARGET = ct
CXX_SRCS = main.cc net.cc file.cc display.cc
CXX_OBJS = $(CXX_SRCS:.cc=.o)
all: $(TARGET)
$(TARGET): $(CXX_OBJS)
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(CXX_OBJS) -o $(TARGET) $(LDFLAGS)
%.o: %.cc
$(CXX) $(CXXFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -f *.o $(TARGET)

6
src/README.md Normal file
View File

@@ -0,0 +1,6 @@
# refactor v1
I'm trying to split this up into multiple pieces but that probably involves using fewer global variables and passing the
same data between the functions that use them.
global variables will use `snake_case` and global constants will use `SCREAMING_SNAKE_CASE`.

30
src/chat.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef CHAT_H
#define CHAT_H
#include <string>
#include <netinet/in.h>
#include <pthread.h>
#define CURSOR_DEFAULT_POSITION_X 2
#define CURSOR_DEFAULT_POSITION_Y 12
#define LOG_LENGTH 10
typedef struct __wclient_args_t wclient_args_t;
void *await_client(void *a);
void big_panic_msg(void);
void clear_rows(int s, int e);
int count_lines(std::string p);
int file_disp(std::string p, int lnstart, int lncount = 10);
int log_append(std::string p, std::string ln);
int log_linect(std::string p);
void client_start(std::string ip, int port, int *sock, std::string log, pthread_t *thr);
void client_stop(struct timeval *start, struct timeval *end, int *sock, int *bread, int *bwrit);
void *poll_client(int *sock, int *bread, std::string log, int *lpos);
void *poll_server(void *args);
int
server_start(int port, int *sock, std::string log, pthread_t *thr, struct timeval *ts);
void server_stop(struct timeval *start, struct timeval *end, int *sock, int *bread, int *bwrit);
#endif

50
src/display.cc Normal file
View File

@@ -0,0 +1,50 @@
#include <fstream>
#include <curses.h>
#include "chat.h"
void
clear_rows(int s, int e)
{
/* preserve cursor location */
int x_bef;
int y_bef;
getyx(stdscr, y_bef, x_bef);
for (int i = s; i < e; i++) {
move(i, 0);
clrtoeol();
}
move(y_bef, x_bef);
}
int
file_disp(std::string p, int lnstart, int lncount)
{
std::ifstream f(p);
if (!f)
return 1;
int i = 0;
int ln_no = 0;
std::string ln;
clear_rows(0, lncount + 1); /* clear chat window */
while (getline(f, ln) && i <= ln_no + lnstart + 1) {
if (i >= lnstart) {
move(ln_no, 0);
printw("%s", ln.c_str());
ln_no++;
}
i++;
}
move(CURSOR_DEFAULT_POSITION_Y, CURSOR_DEFAULT_POSITION_X);
refresh();
return 0;
}

41
src/file.cc Normal file
View File

@@ -0,0 +1,41 @@
#include <algorithm>
#include <fstream>
#include <iterator>
#include "chat.h"
int
count_lines(std::string p)
{
int c = 0;
std::ifstream f(p);
if (!f)
return c;
/* Apparently this is the most efficient way to count all lines in a file. How revolting! */
c = std::count(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>(), '\n');
return c;
}
int
log_append(std::string p, std::string ln)
{
std::ofstream f;
f.open(p, std::ios_base::app);
if (!f.is_open())
return 1;
f << ln << std::endl;
f.close();
// "this probably would help but theres too much broken stuff right now to be sure" - scott
// bool inclnum should be added to function if we do decide to use this block:
// if (inclnum)
// line_position++;
return 0;
}

150
src/main.cc Normal file
View File

@@ -0,0 +1,150 @@
#include <fstream>
#include <string>
#include <arpa/inet.h>
#include <bits/types/struct_timeval.h>
#include <curses.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include "chat.h"
typedef enum { NO_MODE = 0, SERVER_MODE = 1, CLIENT_MODE = 2 } chatmode_t;
chatmode_t mode = NO_MODE;
int line_position = 0;
struct timeval time_start;
struct timeval time_end;
int bytes_read;
int bytes_written;
int listen_port = 9999;
std::string listen_ip_address = "127.0.0.1";
std::string log_path;
int socket_descriptor_server;
int socket_descriptor_client;
pthread_t client_wait_thread;
int
main(int argc, char *argv[])
{
int err = 0;
char *usr_in = new char[1024];
bool quit = false;
if (argc > 1 && (!strcmp(argv[1], "client")))
mode = CLIENT_MODE;
else
mode = SERVER_MODE;
initscr();
use_default_colors();
line_position = count_lines(log_path) - LOG_LENGTH + 1;
switch (mode) {
case CLIENT_MODE:
log_append(log_path, "CLIENT MODE");
client_start(listen_ip_address, listen_port, &socket_descriptor_client, log_path,
&client_wait_thread);
break;
case SERVER_MODE:
log_append(log_path, "SERVER MODE");
err = server_start(listen_port, &socket_descriptor_server, log_path,
&client_wait_thread, &time_start);
if (err)
goto panic_no_server_sock;
break;
default:
goto panic_no_mode;
break;
}
while (!quit) {
file_disp(log_path, line_position, LOG_LENGTH);
if (count_lines(log_path) > LOG_LENGTH)
line_position++;
/* prompt for input */
move(CURSOR_DEFAULT_POSITION_Y, 0);
printw("> ");
/* await input */
getstr(usr_in);
log_append(log_path, usr_in);
send(socket_descriptor_client, (char *)usr_in, strlen(usr_in), 0);
}
/* Final exit point if everything goes OK */
endwin();
switch (mode) {
case CLIENT_MODE:
client_stop(&time_start, &time_end, &socket_descriptor_client, &bytes_read,
&bytes_written);
break;
case SERVER_MODE:
server_stop(&time_start, &time_end, &socket_descriptor_server, &bytes_read,
&bytes_written);
break;
default:
goto panic_no_mode;
break;
}
return 0;
panic_no_mode:
big_panic_msg();
if (mode == NO_MODE)
fprintf(stderr, "Fatal: Mode was not set!\n");
else
fprintf(stderr, "Fatal: Mode was set to invalid enum %i\n", mode);
return 1;
panic_no_server_sock:
big_panic_msg();
fprintf(stderr, "Fatal: Could not establish server socket!\n");
return 1;
}
/*
* For when things go really really bad.
* Check if there is a window and if so, close it.
* Spit out a really loud obnoxious error afterwards.
*/
void
big_panic_msg(void)
{
if (!isendwin())
endwin();
fprintf(stderr, "-----FATAL ERROR, THIS SHOULD NEVER HAPPEN-----\n");
usleep(400 * 1000);
fprintf(stderr, "You may ask yourself... how do I work this?\n");
putchar(7);
usleep(400 * 1000);
fprintf(stderr, "And you may find yourself... past the point of return 0;!\n");
putchar(7);
usleep(400 * 1000);
fprintf(stderr, "And you may ask yourself... well, how did I get here?\n");
putchar(7);
}

211
src/net.cc Normal file
View File

@@ -0,0 +1,211 @@
#include <string>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>
#include <unistd.h>
#include "chat.h"
typedef struct __wclient_args_t {
sockaddr_in new_addr;
socklen_t new_addr_siz;
int aux;
int *bread;
int *sock;
std::string log;
int *lpos;
} wclient_args_t;
typedef struct __wserver_args_t {
sockaddr_in new_addr;
socklen_t new_addr_siz;
int aux;
int *bread;
int *lpos;
std::string log;
} wserver_args_t;
/* client_start(listen_ip, listen_port, &socket_descriptor_client, log_path, &client_wait_thread);
*/
void
client_start(std::string ip, int port, int *sock, std::string log, pthread_t *thr)
{
char msg[1024];
int bread;
int bwrit;
sockaddr_in send_addr;
struct timeval time_start;
struct timeval time_end;
struct hostent *host = gethostbyname(ip.c_str());
auto *a = new wclient_args_t{};
bzero((char *)&send_addr, sizeof(send_addr));
send_addr.sin_family = AF_INET;
send_addr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)*host->h_addr_list));
send_addr.sin_port = htons(port);
*sock = socket(AF_INET, SOCK_STREAM, 0);
attempt_client_connection:
int stat = connect(*sock, (sockaddr *)&send_addr, sizeof(send_addr));
if (stat < 0) {
log_append(log, "Error connecting to socket, retrying...");
goto attempt_client_connection;
}
log_append(log, "Connected to the server!");
bread = bwrit = 0;
gettimeofday(&time_start, NULL);
a->aux = *sock;
pthread_create(thr, nullptr, poll_server, a);
pthread_detach(*thr);
}
int
server_start(int port, int *sock, std::string log, pthread_t *thr, struct timeval *ts)
{
auto *a = new wclient_args_t{};
sockaddr_in srv_addr;
sockaddr_in newsock;
socklen_t newsock_siz = sizeof(newsock);
int bstat;
int rc;
/* set up socket and connection tools */
bzero((char *)&srv_addr, sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
srv_addr.sin_port = htons(port);
/* open stream-oriented socket w inet addr and track sock descriptor */
*sock = socket(AF_INET, SOCK_STREAM, 0);
if (*sock < 0)
return 1;
/* bind sock to its local addr */
bstat = bind(*sock, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
if (bstat < 0)
return 1;
log_append(log, "Waiting for a client to connect...");
/* listen for up to 5 simultaneous requests */
listen(*sock, 5);
a->new_addr = newsock;
a->new_addr_siz = newsock_siz;
rc = pthread_create(thr, nullptr, await_client, a);
pthread_detach(*thr);
log_append(log, "Server started successfully.");
gettimeofday(ts, NULL);
return 0;
}
void *
await_client(void *args)
{
wclient_args_t *a = static_cast<wclient_args_t *>(a);
*a->sock = accept(*a->sock, (sockaddr *)&a->new_addr, &a->new_addr_siz);
if (*a->sock >= 0) {
log_append(a->log, "client connected");
if (count_lines(a->log) > LOG_LENGTH)
++*a->lpos;
file_disp(a->log, *a->lpos, LOG_LENGTH);
}
/* get what we need for the return and throw the rest away */
int *s = a->sock;
int *b = a->bread;
std::string l = a->log;
int *p = a->lpos;
delete a;
return poll_client(s, b, l, p);
}
void *
poll_client(int *sock, int *bread, std::string log, int *lpos)
{
char msg[1024];
for (;;) {
/* listen for msg from client */
memset(&msg, 0, sizeof(msg)); /* clear buf */
*bread += recv(*sock, (char *)&msg, sizeof(msg), 0);
log_append(log, msg);
if (count_lines(log) > LOG_LENGTH)
++*lpos;
file_disp(log, *lpos, LOG_LENGTH);
}
return nullptr;
}
void *
poll_server(void *args)
{
wserver_args_t *a = static_cast<wserver_args_t *>(args);
int sock_desc = (int)a->aux;
char msg[1024];
for (;;) {
memset(&msg, 0, sizeof(msg));
*a->bread += recv(sock_desc, (char *)&msg, sizeof(msg), 0);
log_append(a->log, msg);
if (count_lines(a->log) > LOG_LENGTH)
++*a->lpos;
file_disp(a->log, *a->lpos, LOG_LENGTH);
}
delete a;
}
void
server_stop(struct timeval *start, struct timeval *end, int *sock, int *bread, int *bwrit)
{
long e = end->tv_sec - start->tv_sec;
gettimeofday(end, NULL);
close(*sock);
/* Don't just die silently */
puts("********Session********");
printf("Bytes written: %i\nBytes Read: %i\n", *bwrit, *bread);
printf("Elapsed time: %ld\n secs\n", e);
puts("Connection closed...");
}
void
client_stop(struct timeval *start, struct timeval *end, int *sock, int *bread, int *bwrit)
{
long e = end->tv_sec - start->tv_sec;
gettimeofday(end, NULL);
close(*sock);
puts("********Session********");
printf("Bytes written: %i\nBytes read: %i\n", *bwrit, *bread);
printf("Elapsed time: %ld\n secs\n", e);
puts("Connection closed...");
}