<commit_before>#include "Numkey.h"

Numkey::Numkey(){
	if(text = (char *)malloc(DISPLAY_SIZE+1)) memset(text,0,DISPLAY_SIZE+1); //Had to add one more, to avoid some bug
	this->init();
}

Numkey::Numkey(unsigned int width, unsigned int height, int backgroundColor, int textColor, int borderColor){
	if(text = (char *)malloc(DISPLAY_SIZE+1)) memset(text,0,DISPLAY_SIZE+1); //Had to add one more, to avoid some bug
	this->init();

	this->setSize(width,height);
	this->setColors(backgroundColor,textColor,borderColor);
}

Numkey::~Numkey(){
	if(text) free(text);
}

void Numkey::init(){
	Button::init();
	type = 0x32;
	borderWidth = 2;
	charPos = 0;
	setSize(120,180);
	debounceTime = 100;
	autoremove = true;
}

void Numkey::drawFrame(){
  int xPos,yPos,width,height;
  int btnWidth,btnHeight;

  xPos = x;
  yPos = y;
  width = w;
  height = h;
  btnWidth = w / 3;
  btnHeight = h / 5;

  //--display background
  myCanvas->tft->fillRect(xPos,yPos,btnWidth*2,btnHeight,BLACK);
  //--backspace background
  myCanvas->tft->fillRect(xPos+btnWidth*2,yPos,btnWidth,btnHeight,bgColor);
  //--nums background rectangle
  myCanvas->tft->fillRect(xPos,yPos+btnHeight,width,height-btnHeight,bgColor);

	//-- outer border
  for(byte i=borderWidth; i!=0;i--){
    myCanvas->tft->drawRect(xPos,yPos,width,height,borderColor);
    xPos++;
    yPos++;
    width--;
    height--;
  }

  //-- horizontal lines
  for(byte j=1;j<5;j++)
  {
  	for(byte i=0; i<borderWidth;i++){
    	myCanvas->tft->drawHorizontalLine(x,y+btnHeight*j-1+i,width+borderWidth,borderColor);
  	}
  }

  //-- vertical lines
  for(byte j=1;j<3;j++)
  {
  	for(byte i=0; i<borderWidth;i++){
    	myCanvas->tft->drawVerticalLine(x+btnWidth*j-1+i,y+btnHeight,height-btnHeight+borderWidth,borderColor);
  	}
  }

	//-- draw numbers
  xPos = btnWidth/2 - FONT_X;
  yPos = btnHeight/2 - FONT_Y;
  byte xF=0;
  byte yF=1;
  myCanvas->tft->drawString("<-",x+xPos+2*btnWidth-6,y+yPos,fontSize,fgColor);
  for(byte number=1; number<10; number++)
  {
  	myCanvas->tft->drawNumber(number,x+xPos+(xF++)*btnWidth,y+yPos+(yF*btnHeight),fontSize,fgColor);
  	if(number == 3 || number == 6 || number == 9){
  		yF++;
  		xF = 0;
  	}
  }
  myCanvas->tft->drawString(".",x+xPos+(xF++)*btnWidth,y+yPos+(yF*btnHeight),fontSize,fgColor);
  myCanvas->tft->drawNumber(0,x+xPos+(xF++)*btnWidth,y+yPos+(yF*btnHeight),fontSize,fgColor);
  myCanvas->tft->drawString("=",x+xPos+(xF++)*btnWidth,y+yPos+(yF*btnHeight),fontSize,fgColor);
}

void Numkey::append(char* c){
	byte cSize = getTextLength(c);
	byte txtSize = getTextLength(text);//getTextSize();
	byte space = DISPLAY_SIZE - txtSize;

	//Check that there space available to append
	if(txtSize < DISPLAY_SIZE){
		//Serial.print("Space available: ");Serial.println(space);
		for(int i=0; i<space; i++)
		{
			text[i+txtSize] = c[i];
		}
	}
	update();
}

void Numkey::appendNum(unsigned char c){
	//Serial.print("Append num ");Serial.println((byte)c);

	if(c > 9){
		//Serial.print("Not a number");Serial.write(c);
		return; //Exit if c is not a number 0 - 9
	}
	byte txtSize = getTextLength(text);//getTextSize();
	byte space = DISPLAY_SIZE - txtSize;
	//Check that there space available to append
	if(txtSize < DISPLAY_SIZE){
		text[txtSize] = c + 48;
		text[txtSize + 1] = 0;
		update();
	}
}

void Numkey::fitToText(){
  if(*text){
    char* chars = text;
    char size = 0;
    while(*chars){
      *chars++;
      size++;
    }
    w = size * FONT_SPACE * borderWidth + FONT_SPACE;
    h = FONT_Y * borderWidth + FONT_Y;
    //drawString(text,x+5,y+5,2,textColor);
  }
}

long Numkey::getNum(){
	char size = getTextSize();
	long result = 0;
	for(int i = 0; i<size; i++){
		if(text[i] == '.') break;  // Only process integer side
		result = result * 10 + text[i]-'0';
	}
	return result;
}

char* Numkey::getText(){
  return text;
}

byte Numkey::getTextLength(char* c){
	char size = 0;
	if(*c){
    char* chars = c;
    while(*chars){
      *chars++;
      size++;
    }
  }
  return size;
}

byte Numkey::getTextSize(){
  return getTextLength(text);
}

void Numkey::setNum(int num){
	clear();
	char numChar[DISPLAY_SIZE];
	char chars = 0;

	// Extract characters representing the powers of ten
	while(num >= 10)
	{
		numChar[chars++] = num%10;
		num /= 10;
		//Serial.print("num ");Serial.println(num);
	}

	numChar[chars++] = num;

	for(int j = 0; j < chars; j++)//DISPLAY_SIZE; j++)
	{
		text[chars-1-j] = '0'+numChar[j];
		//Serial.print("text[i] ");Serial.println(text[j]);
	}
	text[chars]=0;
	//update();
}

void Numkey::setEventHandler(void (*functionPointer)(Numkey *)){
	eventHandler = functionPointer;
}

/*
void Numkey::setText(char* _text){

	for(int i=0; i<DISPLAY_SIZE-1;i++){
		text[i] = _text[i];
	}
	update();
  //Serial.print("Set text to ");Serial.println(text);
}
*/

void Numkey::deleteChar(){
	int btnWidth = w / 3;
  int btnHeight = h / 5;
	byte textSize = getTextSize();
	//Serial.print("Size is ");Serial.println(textSize);
	if(!textSize) return;

	// last character, forcefully erase it now because update wont
	if(textSize==1){
		myCanvas->tft->fillRect(x+borderWidth,y+borderWidth,(btnWidth*2)-borderWidth,btnHeight-borderWidth*2,BLACK);
	}

	// clear character in last position
	for(int i = textSize-1; i >= 0; i--)
	{
		if(text[i] != 0){
			text[i] = 0;
			break;
		}
	}

	update();
}

void Numkey::clear(){
	byte textSize = getTextSize();
	if(textSize){
		for(int i = textSize-1; i >= 0; i--)
		{
				text[i] = 0;
		}
	}

  int btnWidth = w / 3;
  int btnHeight = h / 5;
	// clear area
	myCanvas->tft->fillRect(x+borderWidth,y+borderWidth,(btnWidth*2)-borderWidth,btnHeight-borderWidth*2,BLACK);
	//update();
}

//Overriden virtual methods

bool Numkey::checkTouch(Point* p){
	int boundX1, boundX2, boundY1, boundY2;
	int btnWidth = w/3;
	int btnHeight = h/5;
	bool pressed = false;
	if(lastMillis + debounceTime < millis()){
		if((p->x > x+borderWidth) && (p->x < x+w-borderWidth) && (p->y > y+borderWidth) && (p->y < y+h-borderWidth)){
			//Serial.println("Numkey event within bounds");
			//backspace coordinates
			boundX1 = x+btnWidth*2;
			boundX2 = x+w;
			boundY1 = y+borderWidth;
			boundY2 = y+btnHeight;
			if((p->x > boundX1) && (p->x < boundX2) && (p->y > boundY1) && (p->y < boundY2)){
				//Serial.println("Backspace pressed");
				deleteChar();
				pressed = true;
			}
			//num coordinates
			for(int r = 1;(r <= 4)&&(!pressed); r++)
			{
				boundY1 = y+(btnHeight)*r+borderWidth;
				boundY2 = y+(btnHeight)*(r+1)-borderWidth;
				for(int c = 1;(c <= 3)&&(!pressed);c++)
				{
					boundX1 = x + (btnWidth)*(c-1) + borderWidth;
					boundX2 = x + (btnWidth)*c - borderWidth;
					int num = 3*(r - 1) + c;

					if((p->x > boundX1) && (p->x < boundX2) && (p->y > boundY1) && (p->y < boundY2)){
						//Serial.print(num); Serial.println();
						//Serial.println("A number has been pressed");

						if(num==11){
							//Serial.println("0 pressed");
							num = 0;
						}

						if(num==10){
							//Serial.println(". pressed");
							append(".");
						}else if(num==12){
							//Serial.println("= pressed");
							entry = getNum();
							if(autoremove){
								if(myCanvas->widgets.peek() == this){ // check if top widget is this numkey
									myCanvas->pop();				  // Remove this numkey from the canvas...
									//this->hide();
								}
							}
							eventHandler(this);		// <<<------ Event handler called when = signed is pressed
						}else{
							appendNum(char(num));//setNum(num);
						}

						pressed = true;
					}
				}// -- columns for loop
			}// --rows for loop
		}// -- if touch within widget area
		touched = !touched;
		lastMillis = millis();
	}// -- debounce
	return false;
}

void Numkey::show(){
	drawFrame();
	update();
	if(!visible) visible = true;
}

void Numkey::show(Button* btn){
	targetButton = btn;
	show();
}

void Numkey::targetEventHandler(){
	targetButton->eventHandler(targetButton);
}

void Numkey::reset(){
	entry = NULL;
}

/* New method to make it easier to use,
 * automatically redraw the canvas when hidden.
 */
void Numkey::hide(){
	//Widget::hide();
	myCanvas->tft->fillRect(x,y,w,h,this->myCanvas->bgColor);
	visible = false;
	this->myCanvas->redraw();
}

void Numkey::update(){
  int btnWidth = w / 3;
  int btnHeight = h / 5;

  if(*text){
    char* chars = text;

		//Count characters to center on the button - Nice trick from the myCanvas->tft2 library
    int size = 0;
    while(*chars){
      *chars++;
      size++;
    }
    //Calculate centered position of the text
    int stringX = getCenterTextX(x,btnWidth*2,size);
    int stringY = getCenterTextY(y,btnHeight);

		// clear area
		myCanvas->tft->fillRect(x+borderWidth,y+borderWidth,(btnWidth*2)-borderWidth,btnHeight-borderWidth*2,BLACK);

    // draw remaining characters
		myCanvas->tft->drawString(text,stringX,stringY,borderWidth,GREEN);
  }
}
<commit_msg>Update Numkey.cpp<commit_after>#include "Numkey.h"

Numkey::Numkey(){
	if(text = (char *)malloc(DISPLAY_SIZE+1)) memset(text,0,DISPLAY_SIZE+1); //Had to add one more, to avoid some bug
	this->init();
}

Numkey::Numkey(unsigned int width, unsigned int height, int backgroundColor, int textColor, int borderColor){
	if(text = (char *)malloc(DISPLAY_SIZE+1)) memset(text,0,DISPLAY_SIZE+1); //Had to add one more, to avoid some bug
	this->init();

	this->setSize(width,height);
	this->setColors(backgroundColor,textColor,borderColor);
}

Numkey::~Numkey(){
	if(text) free(text);
}

void Numkey::init(){
	Button::init();
	type = 0x32;
	borderWidth = 2;
	charPos = 0;
	setSize(120,180);
	debounceTime = 100;
	autoremove = true;
}

void Numkey::drawFrame(){
  int xPos,yPos,width,height;
  int btnWidth,btnHeight;

  xPos = x;
  yPos = y;
  width = w;
  height = h;
  btnWidth = w / 3;
  btnHeight = h / 5;

  //--display background
  myCanvas->tft->fillRect(xPos,yPos,btnWidth*2,btnHeight,BLACK);
  //--backspace background
  myCanvas->tft->fillRect(xPos+btnWidth*2,yPos,btnWidth,btnHeight,bgColor);
  //--nums background rectangle
  myCanvas->tft->fillRect(xPos,yPos+btnHeight,width,height-btnHeight,bgColor);

	//-- outer border
  for(byte i=borderWidth; i!=0;i--){
    myCanvas->tft->drawRect(xPos,yPos,width,height,borderColor);
    xPos++;
    yPos++;
    width--;
    height--;
  }

  //-- horizontal lines
  for(byte j=1;j<5;j++)
  {
  	for(byte i=0; i<borderWidth;i++){
    	myCanvas->tft->drawHorizontalLine(x,y+btnHeight*j-1+i,width+borderWidth,borderColor);
  	}
  }

  //-- vertical lines
  for(byte j=1;j<3;j++)
  {
  	for(byte i=0; i<borderWidth;i++){
    	myCanvas->tft->drawVerticalLine(x+btnWidth*j-1+i,y+btnHeight,height-btnHeight+borderWidth,borderColor);
  	}
  }

	//-- draw numbers
  xPos = btnWidth/2 - FONT_X;
  yPos = btnHeight/2 - FONT_Y;
  byte xF=0;
  byte yF=1;
  myCanvas->tft->drawString("<-",x+xPos+2*btnWidth-6,y+yPos,fontSize,fgColor);
  for(byte number=1; number<10; number++)
  {
  	myCanvas->tft->drawNumber(number,x+xPos+(xF++)*btnWidth,y+yPos+(yF*btnHeight),fontSize,fgColor);
  	if(number == 3 || number == 6 || number == 9){
  		yF++;
  		xF = 0;
  	}
  }
  myCanvas->tft->drawString(".",x+xPos+(xF++)*btnWidth,y+yPos+(yF*btnHeight),fontSize,fgColor);
  myCanvas->tft->drawNumber(0,x+xPos+(xF++)*btnWidth,y+yPos+(yF*btnHeight),fontSize,fgColor);
  myCanvas->tft->drawString("=",x+xPos+(xF++)*btnWidth,y+yPos+(yF*btnHeight),fontSize,fgColor);
}

void Numkey::append(char* c){
	byte cSize = getTextLength(c);
	byte txtSize = getTextLength(text);//getTextSize();
	byte space = DISPLAY_SIZE - txtSize;

	//Check that there space available to append
	if(txtSize < DISPLAY_SIZE){
		//Serial.print("Space available: ");Serial.println(space);
		for(int i=0; i<space; i++)
		{
			text[i+txtSize] = c[i];
		}
	}
	update();
}

void Numkey::appendNum(unsigned char c){
	//Serial.print("Append num ");Serial.println((byte)c);

	if(c > 9){
		//Serial.print("Not a number");Serial.write(c);
		return; //Exit if c is not a number 0 - 9
	}
	byte txtSize = getTextLength(text);//getTextSize();
	byte space = DISPLAY_SIZE - txtSize;
	//Check that there space available to append
	if(txtSize < DISPLAY_SIZE){
		text[txtSize] = c + 48;
		text[txtSize + 1] = 0;
		update();
	}
}

void Numkey::fitToText(){
  if(*text){
    char* chars = text;
    char size = 0;
    while(*chars){
      *chars++;
      size++;
    }
    w = size * FONT_SPACE * borderWidth + FONT_SPACE;
    h = FONT_Y * borderWidth + FONT_Y;
    //drawString(text,x+5,y+5,2,textColor);
  }
}

long Numkey::getNum(){
	char size = getTextSize();
	long result = 0;
	for(int i = 0; i<size; i++){
		if(text[i] == '.') break;  // Only process integer side
		result = result * 10 + text[i]-'0';
	}
	return result;
}

char* Numkey::getText(){
  return text;
}

byte Numkey::getTextLength(char* c){
	char size = 0;
	if(*c){
    char* chars = c;
    while(*chars){
      *chars++;
      size++;
    }
  }
  return size;
}

byte Numkey::getTextSize(){
  return getTextLength(text);
}

void Numkey::setNum(int num){
	clear();
	char numChar[DISPLAY_SIZE];
	char chars = 0;

	// Extract characters representing the powers of ten
	while(num >= 10)
	{
		numChar[chars++] = num%10;
		num /= 10;
		//Serial.print("num ");Serial.println(num);
	}

	numChar[chars++] = num;

	for(int j = 0; j < chars; j++)//DISPLAY_SIZE; j++)
	{
		text[chars-1-j] = '0'+numChar[j];
		//Serial.print("text[i] ");Serial.println(text[j]);
	}
	text[chars]=0;
	//update();
}

void Numkey::setEventHandler(void (*functionPointer)(Numkey *)){
	eventHandler = functionPointer;
}

/*
void Numkey::setText(char* _text){

	for(int i=0; i<DISPLAY_SIZE-1;i++){
		text[i] = _text[i];
	}
	update();
  //Serial.print("Set text to ");Serial.println(text);
}
*/

void Numkey::deleteChar(){
	int btnWidth = w / 3;
  int btnHeight = h / 5;
	byte textSize = getTextSize();
	//Serial.print("Size is ");Serial.println(textSize);
	if(!textSize) return;

	// last character, forcefully erase it now because update wont
	if(textSize==1){
		myCanvas->tft->fillRect(x+borderWidth,y+borderWidth,(btnWidth*2)-borderWidth,btnHeight-borderWidth*2,BLACK);
	}

	// clear character in last position
	for(int i = textSize-1; i >= 0; i--)
	{
		if(text[i] != 0){
			text[i] = 0;
			break;
		}
	}

	update();
}

void Numkey::clear(){
	byte textSize = getTextSize();
	if(textSize){
		for(int i = textSize-1; i >= 0; i--)
		{
			text[i] = 0;
		}
	}

  int btnWidth = w / 3;
  int btnHeight = h / 5;
  // clear area
  if(!autoremove){
		myCanvas->tft->fillRect(x+borderWidth,y+borderWidth,(btnWidth*2)-borderWidth,btnHeight-borderWidth*2,BLACK);
  }else{
		update();
  }
}

//Overriden virtual methods

bool Numkey::checkTouch(Point* p){
	int boundX1, boundX2, boundY1, boundY2;
	int btnWidth = w/3;
	int btnHeight = h/5;
	bool pressed = false;
	if(lastMillis + debounceTime < millis()){
		if((p->x > x+borderWidth) && (p->x < x+w-borderWidth) && (p->y > y+borderWidth) && (p->y < y+h-borderWidth)){
			//Serial.println("Numkey event within bounds");
			//backspace coordinates
			boundX1 = x+btnWidth*2;
			boundX2 = x+w;
			boundY1 = y+borderWidth;
			boundY2 = y+btnHeight;
			if((p->x > boundX1) && (p->x < boundX2) && (p->y > boundY1) && (p->y < boundY2)){
				//Serial.println("Backspace pressed");
				deleteChar();
				pressed = true;
			}
			//num coordinates
			for(int r = 1;(r <= 4)&&(!pressed); r++)
			{
				boundY1 = y+(btnHeight)*r+borderWidth;
				boundY2 = y+(btnHeight)*(r+1)-borderWidth;
				for(int c = 1;(c <= 3)&&(!pressed);c++)
				{
					boundX1 = x + (btnWidth)*(c-1) + borderWidth;
					boundX2 = x + (btnWidth)*c - borderWidth;
					int num = 3*(r - 1) + c;

					if((p->x > boundX1) && (p->x < boundX2) && (p->y > boundY1) && (p->y < boundY2)){
						//Serial.print(num); Serial.println();
						//Serial.println("A number has been pressed");

						if(num==11){
							//Serial.println("0 pressed");
							num = 0;
						}

						if(num==10){
							//Serial.println(". pressed");
							append(".");
						}else if(num==12){
							//Serial.println("= pressed");
							entry = getNum();
							if(autoremove){
								if(myCanvas->widgets.peek() == this){ // check if top widget is this numkey
									myCanvas->pop();				  // Remove this numkey from the canvas...
									//this->hide();
								}
							}
							eventHandler(this);		// <<<------ Event handler called when = signed is pressed
						}else{
							appendNum(char(num));//setNum(num);
						}

						pressed = true;
					}
				}// -- columns for loop
			}// --rows for loop
		}// -- if touch within widget area
		touched = !touched;
		lastMillis = millis();
	}// -- debounce
	return false;
}

void Numkey::show(){
	drawFrame();
	update();
	if(!visible) visible = true;
}

void Numkey::show(Button* btn){
	targetButton = btn;
	show();
}

void Numkey::targetEventHandler(){
	targetButton->eventHandler(targetButton);
}

void Numkey::reset(){
	entry = NULL;
}

/* New method to make it easier to use,
 * automatically redraw the canvas when hidden.
 */
void Numkey::hide(){
	//Widget::hide();
	myCanvas->tft->fillRect(x,y,w,h,this->myCanvas->bgColor);
	visible = false;
	this->myCanvas->redraw();
}

void Numkey::update(){
  int btnWidth = w / 3;
  int btnHeight = h / 5;

  if(*text){
    char* chars = text;

		//Count characters to center on the button - Nice trick from the myCanvas->tft2 library
    int size = 0;
    while(*chars){
      *chars++;
      size++;
    }
    //Calculate centered position of the text
    int stringX = getCenterTextX(x,btnWidth*2,size);
    int stringY = getCenterTextY(y,btnHeight);

		// clear area
		myCanvas->tft->fillRect(x+borderWidth,y+borderWidth,(btnWidth*2)-borderWidth,btnHeight-borderWidth*2,BLACK);

    // draw remaining characters
		myCanvas->tft->drawString(text,stringX,stringY,borderWidth,GREEN);
  }
}
<|endoftext|>


<commit_before> #include <gtest/gtest.h>
#include <exception>
#include <algorithm>

#include <vector>
using std::vector ;

#include <fstream>
using std::ofstream ;

#include <string>
using std::string ;

#include <sofa/helper/system/FileMonitor.h>
using sofa::helper::system::FileEventListener ;
using sofa::helper::system::FileMonitor ;

static std::string getPath(std::string s) {
    return std::string(FRAMEWORK_TEST_RESOURCES_DIR) + std::string("/") + s;
}

void createAFilledFile(const string filename, unsigned int rep){
    ofstream file1 ;
    file1.open(filename.c_str(), ofstream::out) ;

    //throw_when(!file1.is_open()) ;

    string sample = "#include<TODOD> int main(int argc...){ ... }\n}" ;
    for(unsigned int i=0;i<rep;i++){
        file1.write(sample.c_str(), sample.size()) ;
    }
    file1.close();
}

class MyFileListener : public FileEventListener
{
public:
    vector<string> m_files ;

    virtual void fileHasChanged(const std::string& filename){
        //std::cout << "FileHasChanged: " << filename << std::endl ;
        m_files.push_back(filename) ;
    }
};

TEST(FileMonitor, addFileNotExist_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath("nonexisting.txt"), &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileNotExist2_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath(""),"nonexisting.txt", &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileExist_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileTwice_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener);

    // Retry to add an existing file. It should fail.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;

    // The listener should be notified 1 times with the same event.
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, noUpdate_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, updateNoChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    FileMonitor::updates(0) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChange2_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath(""),"existing.txt", &listener) ;
    FileMonitor::updates(0) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChangeTwice_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 100) ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileListenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeFileListener(getPath("existing.txt"), &listener1) ;

    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}

TEST(FileMonitor, listenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeListener(&listener1) ;

    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}
<commit_msg>update FileMonitor_test<commit_after> #include <gtest/gtest.h>
#include <exception>
#include <algorithm>

#include <vector>
using std::vector ;

#include <fstream>
using std::ofstream ;

#include <string>
using std::string ;

#include <sofa/helper/system/FileMonitor.h>
using sofa::helper::system::FileEventListener ;
using sofa::helper::system::FileMonitor ;

static std::string getPath(std::string s) {
    return std::string(FRAMEWORK_TEST_RESOURCES_DIR) + std::string("/") + s;
}

void createAFilledFile(const string filename, unsigned int rep){
    ofstream file1 ;
    file1.open(filename.c_str(), ofstream::out) ;

    //throw_when(!file1.is_open()) ;

    string sample = "#include<TODOD> int main(int argc...){ ... }\n}" ;
    for(unsigned int i=0;i<rep;i++){
        file1.write(sample.c_str(), sample.size()) ;
    }
    file1.close();
}

class MyFileListener : public FileEventListener
{
public:
    vector<string> m_files ;

    virtual void fileHasChanged(const std::string& filename){
        //std::cout << "FileHasChanged: " << filename << std::endl ;
        m_files.push_back(filename) ;
    }
};

TEST(FileMonitor, addFileNotExist_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath("nonexisting.txt"), &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileNotExist2_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath(""),"nonexisting.txt", &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileExist_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileTwice_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener);

    // Retry to add an existing file. It should fail.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;

    // The listener should be notified 1 times with the same event.
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, noUpdate_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, updateNoChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    FileMonitor::updates(0) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChangeTwice_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 100) ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileListenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeFileListener(getPath("existing.txt"), &listener1) ;

    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}

TEST(FileMonitor, listenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeListener(&listener1) ;

    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}

TEST(FileMonitor, fileChange2_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath(""),"existing.txt", &listener) ;
    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;

    sleep(1);   // on osx there is a latency between 0.2 and 0.5s for file events...
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}
<|endoftext|>


<commit_before>#include "Player.h"
#include "Resources.h"
#include "Utils.h"
#include "Network.h"
#include <cmath>

Player::Player(Game* game)
    : Object(game, sf::Vector2f(50, 50), Resources::texturePlayer, sf::Vector2i(2, 1)),
    network(game), game(game)
{
    mapInput();
    state = SELECT_BEAST;
}

Player::~Player()
{
}

void Player::mapInput() {
    inputManager->addMouseMapping(sf::Mouse::Button::Left);
}

void Player::update(float deltaTime) {
    network.update();
    switch(state) {

    case START_MENU:
        startMenu(deltaTime);
        break;
    case SELECT_BEAST:
        selectBeast(deltaTime);
        break;
    case UPGRADE_BEAST:
        upgradeBeast(deltaTime);
        break;
    case CITIES_INITIALITZATION:
        citiesInitialitzation(deltaTime);
        break;
    case BEFORE_CITY_SELECTION:
        beforeCitySelection(deltaTime);
        break;
    case CITY_SELECTION:
        citySelection(deltaTime);
        break;
    case AFTER_CITY_SELECTION:
        afterCitySelection(deltaTime);
        break;
    case MOVE_NEXT_CITY:
        moveNextCity(deltaTime);
        break;
    case MOVING_ANIMATION:
        movingAnimation(deltaTime);
        break;
    case IN_BATTLE:
        inBattle(deltaTime);
        break;
    case BATTLE_ANIMATION:
        battleAnimation(deltaTime);
        break;
    case BATTLE_RESULT:
        battleResult(deltaTime);
        break;
    case ROUND_RESULT:
        roundResult(deltaTime);
        break;
    default:
        break;
    }

}

void Player::draw() {
    network.draw();
}

void Player::startMenu(float deltaTime) {

}

void Player::selectBeast(float deltaTime) {
    numBeasts = 10;
    beasts = Beasts(game, numBeasts, 10, 10, 10, 10, 10);
    state = UPGRADE_BEAST;
}

void Player::upgradeBeast(float deltaTime) {
    state = CITIES_INITIALITZATION;
}

void Player::citiesInitialitzation(float deltaTime) {
    network.addPath(0,1);
    network.addPath(1,3);
    network.addPath(3,2);
    network.addPath(2,4);
    network.addPath(2,5);
    network.addPath(4,5);
    network.addPath(2, 6);
    network.addPath(3, 9);
    network.addPath(6, 7);
    network.addPath(6, 12);
    network.addPath(7, 8);
    network.addPath(7, 9);
    network.addPath(9, 10);
    network.addPath(10, 11);
    network.addPath(11, 12);

    path.clear();
    path.push_back(0);

    state = BEFORE_CITY_SELECTION;
}

void Player::beforeCitySelection(float deltaTime) {
    currentNeighbours = network.getNeighbours(path.back());

    if (currentNeighbours.size() <= 0) {
        pathInitialLength = path.size();
        state = MOVE_NEXT_CITY;
        return;
    }

    //Highlight neighbours
    std::vector<int>::iterator it = currentNeighbours.begin();
    for (; it != currentNeighbours.end(); it++) {
        network.getCity(*it)->highlight();
    }

    state = CITY_SELECTION;
}

void Player::citySelection(float deltaTime) {
    if (inputManager->getMouseState(sf::Mouse::Button::Left) == true) {
        sf::Vector2f mousePos = sf::Vector2f(inputManager->getMousePosition().x, inputManager->getMousePosition().y);
        int result = network.getCityByCoords(mousePos);
        bool inNeighbours = false;

        for (std::vector<int>::iterator it = currentNeighbours.begin(); it != currentNeighbours.end(); it++) {
            if (*it == result) {
                inNeighbours = true;
                break;
            }
        }

        if (inNeighbours) {
            network.destroyNode(path.back());
            path.push_back(result);
            state = AFTER_CITY_SELECTION;
        }
    }
}

void Player::afterCitySelection(float deltaTime) {
    //Unhighlight neighbours
    std::vector<int>::iterator it = currentNeighbours.begin();

    for (; it != currentNeighbours.end(); it++) {
        network.getCity(*it)->unHighlight();
    }

    //Highlight special selected city
    network.getCity(path[path.size()-2])->highlightSpecial();

    state = BEFORE_CITY_SELECTION;
}

void Player::moveNextCity(float deltaTime) {
    cityBeasts = *network.getCity(path.front())->getBeasts();
    numCityBeasts = cityBeasts.beasts.size();
}

void Player::movingAnimation(float deltaTime) {

}

void Player::inBattle(float deltaTime) {
    BattleManager::resolveBattle(&beasts, &cityBeasts);
    state = BATTLE_ANIMATION;
}

/**
 * Decrements numBeasts and numCityBeasts until they
 * reach the respective beasts.size()
 */
void Player::battleAnimation(float deltaTime) {
     timer += deltaTime;

     if (timer > 0.2) {
         timer = 0;
         int deltaBeasts = (int)std::ceil((numBeasts - beasts.beasts.size())/7.0);
         int deltaCityBeasts = (int)std::ceil((numCityBeasts - cityBeasts.beasts.size())/7.0);

         numBeasts -= deltaBeasts;
         numCityBeasts -= deltaCityBeasts;

         if(deltaBeasts == 0 && deltaCityBeasts == 0) {
             state = BATTLE_RESULT;
         }
     }
}

void Player::battleResult(float deltaTime) {
    if(beasts.beasts.size() <= 0) {
        state = ROUND_RESULT;
    } else {
        path.erase(path.begin());
        state = MOVE_NEXT_CITY;
    }
}

void Player::roundResult(float deltaTime) {
    upgradePoints += (pathInitialLength - path.size())*10;
}
<commit_msg>Set initial city as highlighted<commit_after>#include "Player.h"
#include "Resources.h"
#include "Utils.h"
#include "Network.h"
#include <cmath>

Player::Player(Game* game)
    : Object(game, sf::Vector2f(50, 50), Resources::texturePlayer, sf::Vector2i(2, 1)),
    network(game), game(game)
{
    mapInput();
    state = SELECT_BEAST;
}

Player::~Player()
{
}

void Player::mapInput() {
    inputManager->addMouseMapping(sf::Mouse::Button::Left);
}

void Player::update(float deltaTime) {
    network.update();
    switch(state) {

    case START_MENU:
        startMenu(deltaTime);
        break;
    case SELECT_BEAST:
        selectBeast(deltaTime);
        break;
    case UPGRADE_BEAST:
        upgradeBeast(deltaTime);
        break;
    case CITIES_INITIALITZATION:
        citiesInitialitzation(deltaTime);
        break;
    case BEFORE_CITY_SELECTION:
        beforeCitySelection(deltaTime);
        break;
    case CITY_SELECTION:
        citySelection(deltaTime);
        break;
    case AFTER_CITY_SELECTION:
        afterCitySelection(deltaTime);
        break;
    case MOVE_NEXT_CITY:
        moveNextCity(deltaTime);
        break;
    case MOVING_ANIMATION:
        movingAnimation(deltaTime);
        break;
    case IN_BATTLE:
        inBattle(deltaTime);
        break;
    case BATTLE_ANIMATION:
        battleAnimation(deltaTime);
        break;
    case BATTLE_RESULT:
        battleResult(deltaTime);
        break;
    case ROUND_RESULT:
        roundResult(deltaTime);
        break;
    default:
        break;
    }

}

void Player::draw() {
    network.draw();
}

void Player::startMenu(float deltaTime) {

}

void Player::selectBeast(float deltaTime) {
    numBeasts = 10;
    beasts = Beasts(game, numBeasts, 10, 10, 10, 10, 10);
    state = UPGRADE_BEAST;
}

void Player::upgradeBeast(float deltaTime) {
    state = CITIES_INITIALITZATION;
}

void Player::citiesInitialitzation(float deltaTime) {
    network.addPath(0,1);
    network.addPath(1,3);
    network.addPath(3,2);
    network.addPath(2,4);
    network.addPath(2,5);
    network.addPath(4,5);
    network.addPath(2, 6);
    network.addPath(3, 9);
    network.addPath(6, 7);
    network.addPath(6, 12);
    network.addPath(7, 8);
    network.addPath(7, 9);
    network.addPath(9, 10);
    network.addPath(10, 11);
    network.addPath(11, 12);

    path.clear();
    path.push_back(0);
    network.getCity(0)->highlightSpecial();

    state = BEFORE_CITY_SELECTION;
}

void Player::beforeCitySelection(float deltaTime) {
    currentNeighbours = network.getNeighbours(path.back());

    if (currentNeighbours.size() <= 0) {
        pathInitialLength = path.size();
        state = MOVE_NEXT_CITY;
        return;
    }

    //Highlight neighbours
    std::vector<int>::iterator it = currentNeighbours.begin();
    for (; it != currentNeighbours.end(); it++) {
        network.getCity(*it)->highlight();
    }

    state = CITY_SELECTION;
}

void Player::citySelection(float deltaTime) {
    if (inputManager->getMouseState(sf::Mouse::Button::Left) == true) {
        sf::Vector2f mousePos = sf::Vector2f(inputManager->getMousePosition().x, inputManager->getMousePosition().y);
        int result = network.getCityByCoords(mousePos);
        bool inNeighbours = false;

        for (std::vector<int>::iterator it = currentNeighbours.begin(); it != currentNeighbours.end(); it++) {
            if (*it == result) {
                inNeighbours = true;
                break;
            }
        }

        if (inNeighbours) {
            network.destroyNode(path.back());
            path.push_back(result);
            state = AFTER_CITY_SELECTION;
        }
    }
}

void Player::afterCitySelection(float deltaTime) {
    //Unhighlight neighbours
    std::vector<int>::iterator it = currentNeighbours.begin();

    for (; it != currentNeighbours.end(); it++) {
        network.getCity(*it)->unHighlight();
    }

    //Highlight special selected city
    network.getCity(path[path.size()-2])->highlightSpecial();

    state = BEFORE_CITY_SELECTION;
}

void Player::moveNextCity(float deltaTime) {
    cityBeasts = *network.getCity(path.front())->getBeasts();
    numCityBeasts = cityBeasts.beasts.size();
}

void Player::movingAnimation(float deltaTime) {

}

void Player::inBattle(float deltaTime) {
    BattleManager::resolveBattle(&beasts, &cityBeasts);
    state = BATTLE_ANIMATION;
}

/**
 * Decrements numBeasts and numCityBeasts until they
 * reach the respective beasts.size()
 */
void Player::battleAnimation(float deltaTime) {
     timer += deltaTime;

     if (timer > 0.2) {
         timer = 0;
         int deltaBeasts = (int)std::ceil((numBeasts - beasts.beasts.size())/7.0);
         int deltaCityBeasts = (int)std::ceil((numCityBeasts - cityBeasts.beasts.size())/7.0);

         numBeasts -= deltaBeasts;
         numCityBeasts -= deltaCityBeasts;

         if(deltaBeasts == 0 && deltaCityBeasts == 0) {
             state = BATTLE_RESULT;
         }
     }
}

void Player::battleResult(float deltaTime) {
    if(beasts.beasts.size() <= 0) {
        state = ROUND_RESULT;
    } else {
        path.erase(path.begin());
        state = MOVE_NEXT_CITY;
    }
}

void Player::roundResult(float deltaTime) {
    upgradePoints += (pathInitialLength - path.size())*10;
}
<|endoftext|>


<commit_before>#include <netdb.h>
#include <unistd.h>
#include <sys/fcntl.h>

#include "client.h"
#include "duckchat.h"
#include "raw.h"

// Client connects, logs in, and joins “Common”.
// Client reads lines from the user and parses commands.
// Client correctly sends Say message.
// Client uses select() to wait for input from the user and the server.
// Client correctly sends Join, Leave, Login, and Logout and TODO handles Switch.
// TODO Client correctly sends List and Who.
// TODO Server can accept connections.
// TODO Server handles Login and Logout from users, and keeps records of which users are logged in.
// TODO Server handles Join and Leave from users, keeps records of which channels a user belongs to,
// and keeps records of which users are in a channel.
// TODO Server handles the Say message.
// TODO Server correctly handles List and Who.
// TODO Create copies of your client and server source. Modify them to send invalid packets to your good client
// and server, to see if you can make your client or server crash. Fix any bugs you find.

// Variables
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
int client_socket;
struct addrinfo *server_info;
std::string current_channel;
std::vector<std::string> channels;

// Prints an error message and exits the program.
void Error(const char *msg) {
  std::cerr << msg << std::endl;
  exit(1);
}

void PrintPrompt() {
  std::cout << "> " << std::flush;
}

// Splits strings around spaces.
std::vector<std::string> StringSplit(std::string input) {
  std::istringstream iss(input);
  std::vector<std::string> result{std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};

  return result;
}

// Splits strings around spaces.
std::vector<std::string> SplitString(char *input, char delimiter) {
  std::vector<std::string> result;
  std::string word = "";

  size_t input_size = strlen(input);
  for (size_t i = 0; i < input_size; i++) {
    if (input[i] != delimiter) {
      word += input[i];
    } else {
      result.push_back(word);
      word = "";
    }
  }
  result.push_back(word);

  return result;
}

void StripChar(char *input, char c) {
  size_t size = strlen(input);
  for (size_t i = 0; i < size; i++) {
    if (input[i] == c) {
      input[i] = '\0';
    }
  }
}

// Gets the address info of the server at a the given port and creates the client's socket.
void CreateSocket(char *domain, const char *port) {
  std::cout << "Connecting to " << domain << std::endl;

  struct addrinfo hints;
  struct addrinfo *server_info_tmp;
  int status;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;
  hints.ai_protocol = 0;

  if ((status = getaddrinfo(domain, port, &hints, &server_info_tmp)) != 0) {
    std::cerr << "client: unable to resolve address: " << gai_strerror(status) << std::endl;
    exit(1);
  }

  // getaddrinfo() returns a list of address structures into server_info_tmp.
  // Tries each address until a successful connect().
  // If socket() (or connect()) fails, closes the socket and tries the next address.

  for (server_info = server_info_tmp; server_info != NULL; server_info = server_info->ai_next) {
    if ((client_socket = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol)) < 0) {
      continue;
    }
    if (connect(client_socket, server_info->ai_addr, server_info->ai_addrlen) != -1) {
      fcntl(client_socket, F_SETFL, O_NONBLOCK);
      break; // Success
    }
    close(client_socket);
  }

  if (server_info == NULL) {
    Error("client: all sockets failed to connect");
  }
}

// Sends a message to all users in on the active channel.
int SendSay(std::string message) {
  struct request_say say;
  memset(&say, 0, sizeof(say));
  say.req_type = REQ_SAY;
  strncpy(say.req_text, message.c_str(), SAY_MAX);
  strncpy(say.req_channel, current_channel.c_str(), CHANNEL_MAX);

  if (sendto(client_socket, &say, sizeof(say), 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to send message\n");
  }

  return 0;
}

// Sends login requests to the server.
int SendLogin(char *username) {
  struct request_login login;
  memset(&login, 0, sizeof(login));
  login.req_type = REQ_LOGIN;
  strncpy(login.req_username, username, USERNAME_MAX);

  size_t message_size = sizeof(struct request_login);

  if (sendto(client_socket, &login, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request login\n");
  }

  return 0;
}

// Sends logout requests to the server.
int SendLogout() {
  struct request_logout logout;
  memset((char *) &logout, 0, sizeof(logout));
  logout.req_type = REQ_LOGOUT;

  size_t message_size = sizeof(struct request_logout);

  if (sendto(client_socket, &logout, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request logout\n");
  }

  return 0;
}

int SendList(){
  struct request_list list;
  list.req_type = REQ_LIST;
  size_t list_size = sizeof(struct request_list);

  if (sendto(client_socket, &list, list_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request list \n");
  }

  return 0;
}

int SendLeave(std::string channel) {
  bool contains_channel = false;
  std::vector<std::string>::iterator it;
  for (it = channels.begin(); it != channels.end(); ++it) {
    std::cout << "checking channel: " << *it << std::endl;
    std::cout << "trying to leave channel: " << channel << std::endl;

    if (*it == channel) {
      contains_channel = true;
      break;
    }
  }

  if (contains_channel) {
    channels.erase(it);

    struct request_leave leave;
    memset((char *) &leave, 0, sizeof(leave));
    leave.req_type = REQ_LEAVE;
    strncpy(leave.req_channel, channel.c_str(), CHANNEL_MAX);

    size_t leave_size = sizeof(struct request_leave);

    if (sendto(client_socket, &leave, leave_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
      Error("client: failed to request leave\n");
    }

    for (it = channels.begin(); it != channels.end(); ++it) {
      std::cout << *it << std::endl;
    }
  }

  return 0;
}

// Sends join requests to the server.
int SendJoin(std::string channel) {
  bool contains_channel = false;
  for (std::vector<std::string>::iterator it = channels.begin(); it != channels.end(); ++it) {
    if (*it == channel) {
      contains_channel = true;
      break;
    }
  }

  if (!contains_channel) {
    channels.push_back(channel);

    struct request_join join;
    memset((char *) &join, 0, sizeof(join));
    join.req_type = REQ_JOIN;
    strncpy(join.req_channel, channel.c_str(), CHANNEL_MAX);

    size_t message_size = sizeof(struct request_join);

    if (sendto(client_socket, &join, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
      Error("client: failed to request join\n");
    }
  }

  return 0;
}

// Switches to a channel the user has already joined.
int SwitchChannel(std::string channel) {
  bool isSubscribed = false;

  if (channels.size() > 0) {
    for (auto c: channels) {
      if (channel == c) {
        current_channel = channel;
        isSubscribed = true;
      }
    }
  }

  if (!isSubscribed) {
    std::cout << "You have not subscribed to channel " << channel << std::endl;
  }

  return 0;
}

void HandleTextList(char *receive_buffer, char *output) {
  struct text_list list;
  memcpy(&list, receive_buffer, sizeof(struct text_list));

  std::string backspaces = "";
  for (int i = 0; i < SAY_MAX; i++) {
    backspaces.append("\b");
  }
  std::cout << backspaces;

  std::cout << "Existing channels:" << std::endl;
  for(int i = 2; i < list.txt_nchannels + 2; i++){
    std::cout << " " << list.txt_channels[i].ch_channel << std::endl;
  }

  PrintPrompt();
  std::cout << output << std::flush;
}

// Handles TXT-SAY server messages.
void HandleTextSay(char *receive_buffer, char *output) {
  struct text_say say;
  memcpy(&say, receive_buffer, sizeof(struct text_say));

  std::string backspaces = "";
  for (int i = 0; i < SAY_MAX; i++) {
    backspaces.append("\b");
  }
  std::cout << backspaces;
  std::cout << "[" << say.txt_channel << "]" << "[" << say.txt_username << "]: " << say.txt_text << std::endl;
  PrintPrompt();
  std::cout << output << std::flush;
}

// Processes the input string to decide what type of command it is.
bool ProcessInput(std::string input) {
  std::vector<std::string> inputs = StringSplit(input);

  if (inputs[0] == "/exit") {
    SendLogout();
    cooked_mode();
    return false;
  } else if (inputs[0] == "/list") {
    SendList();
  } else if (inputs[0] == "/join" && inputs.size() > 1) {
    SendJoin(inputs[1]);
  } else if (inputs[0] == "/leave") {
    SendLeave(inputs[1]);
  } else if (inputs[0] == "/who") {

  } else if (inputs[0] == "/switch" && inputs.size() > 1) {
    SwitchChannel(inputs[1]);
  } else {
    std::cout << "*Unknown command" << std::endl;
  }

  PrintPrompt();
  return true;
}

int main(int argc, char *argv[]) {
  char *domain;
  char *port_str;
  int port_num;
  char *username;
  std::string input;
  char *output = (char *) "";
  fd_set read_set;
  int result;

  char stdin_buffer[SAY_MAX + 1];
  char *stdin_buffer_pointer = stdin_buffer;

  char receive_buffer[kBufferSize];
  memset(&receive_buffer, 0, kBufferSize);

  if (argc < 4) {
    Error("usage: client [server name] [port] [username]");
  }

  domain = argv[1];
  port_str = argv[2];
  port_num = atoi(argv[2]);
  username = argv[3];

  if (strlen(domain) > UNIX_PATH_MAX) {
    Error("client: server name must be less than 108 characters");
  }

  if (port_num < 0 || port_num > 65535) {
    Error("client: port number must be between 0 and 65535");
  }

  if (strlen(username) > USERNAME_MAX) {
    Error("client: username must be less than 32 characters");
  }

  CreateSocket(domain, port_str);

  SendLogin(username);

  current_channel = "Common";
  SendJoin(current_channel);

  if (raw_mode() != 0){
    Error("client: error using raw mode");
  }

  PrintPrompt();

  while (1) {
    FD_ZERO(&read_set);
    FD_SET(client_socket, &read_set);
    FD_SET(STDIN_FILENO, &read_set);

    if ((result = select(client_socket + 1, &read_set, NULL, NULL, NULL)) < 0) {
      Error("client: problem using select");
    }

    if (result > 0) {
      if (FD_ISSET(STDIN_FILENO, &read_set)) {
        // User entered a char.
        char c = (char) getchar();
        if (c == '\n') {
          // Increments pointer and adds NULL char.
          *stdin_buffer_pointer++ = '\0';

          // Resets stdin_buffer_pointer to the start of stdin_buffer.
          stdin_buffer_pointer = stdin_buffer;

          std::cout << "\n" << std::flush;

          // Prevents output from printing on the new prompt after a newline char.
          output = (char *) "";

          input.assign(stdin_buffer, stdin_buffer + strlen(stdin_buffer));

          if (input[0] == '/') {
            if (!ProcessInput(input)) {
              break;
            }
          } else {
            // Sends chat messages.
            SendSay(input);
          }
        } else if (stdin_buffer_pointer != stdin_buffer + SAY_MAX) {
          // Increments pointer and adds char c.
          *stdin_buffer_pointer++ = c;

          std::cout << c << std::flush;

          // Copies pointer into output.
          output = stdin_buffer_pointer;
          // Increments and sets NULL char.
          *output++ = '\0';
          // Copies stdin_buffer into part of output before NULL char.
          output = stdin_buffer;
        }
      } else if (FD_ISSET(client_socket, &read_set)) {
        // Socket has data.
        ssize_t read_size = read(client_socket, receive_buffer, kBufferSize);

        if (read_size != 0) {
          struct text message;
          memcpy(&message, receive_buffer, sizeof(struct text));
          text_t text_type = message.txt_type;

          switch (text_type) {
            case TXT_SAY:
              HandleTextSay(receive_buffer, output);
              break;
            case TXT_LIST:
              HandleTextList(receive_buffer, output);
              break;
            default:
              break;
          }
        }

        memset(&receive_buffer, 0, SAY_MAX);
      } // end of if client_socket

    } // end of if result

  } // end of while

  return 0;
}<commit_msg>Error checking<commit_after>#include <netdb.h>
#include <unistd.h>
#include <sys/fcntl.h>

#include "client.h"
#include "duckchat.h"
#include "raw.h"

// Client connects, logs in, and joins “Common”.
// Client reads lines from the user and parses commands.
// Client correctly sends Say message.
// Client uses select() to wait for input from the user and the server.
// Client correctly sends Join, Leave, Login, and Logout and TODO handles Switch.
// TODO Client correctly sends List and Who.
// TODO Server can accept connections.
// TODO Server handles Login and Logout from users, and keeps records of which users are logged in.
// TODO Server handles Join and Leave from users, keeps records of which channels a user belongs to,
// and keeps records of which users are in a channel.
// TODO Server handles the Say message.
// TODO Server correctly handles List and Who.
// TODO Create copies of your client and server source. Modify them to send invalid packets to your good client
// and server, to see if you can make your client or server crash. Fix any bugs you find.

// Variables
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
int client_socket;
struct addrinfo *server_info;
std::string current_channel;
std::vector<std::string> channels;

// Prints an error message and exits the program.
void Error(const char *msg) {
  std::cerr << msg << std::endl;
  exit(1);
}

void PrintPrompt() {
  std::cout << "> " << std::flush;
}

// Splits strings around spaces.
std::vector<std::string> StringSplit(std::string input) {
  std::istringstream iss(input);
  std::vector<std::string> result{std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};

  return result;
}

// Splits strings around spaces.
std::vector<std::string> SplitString(char *input, char delimiter) {
  std::vector<std::string> result;
  std::string word = "";

  size_t input_size = strlen(input);
  for (size_t i = 0; i < input_size; i++) {
    if (input[i] != delimiter) {
      word += input[i];
    } else {
      result.push_back(word);
      word = "";
    }
  }
  result.push_back(word);

  return result;
}

void StripChar(char *input, char c) {
  size_t size = strlen(input);
  for (size_t i = 0; i < size; i++) {
    if (input[i] == c) {
      input[i] = '\0';
    }
  }
}

// Gets the address info of the server at a the given port and creates the client's socket.
void CreateSocket(char *domain, const char *port) {
  std::cout << "Connecting to " << domain << std::endl;

  struct addrinfo hints;
  struct addrinfo *server_info_tmp;
  int status;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;
  hints.ai_protocol = 0;

  if ((status = getaddrinfo(domain, port, &hints, &server_info_tmp)) != 0) {
    std::cerr << "client: unable to resolve address: " << gai_strerror(status) << std::endl;
    exit(1);
  }

  // getaddrinfo() returns a list of address structures into server_info_tmp.
  // Tries each address until a successful connect().
  // If socket() (or connect()) fails, closes the socket and tries the next address.

  for (server_info = server_info_tmp; server_info != NULL; server_info = server_info->ai_next) {
    if ((client_socket = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol)) < 0) {
      continue;
    }
    if (connect(client_socket, server_info->ai_addr, server_info->ai_addrlen) != -1) {
      fcntl(client_socket, F_SETFL, O_NONBLOCK);
      break; // Success
    }
    close(client_socket);
  }

  if (server_info == NULL) {
    Error("client: all sockets failed to connect");
  }
}

// Sends a message to all users in on the active channel.
int SendSay(std::string message) {
  struct request_say say;
  memset(&say, 0, sizeof(say));
  say.req_type = REQ_SAY;
  strncpy(say.req_text, message.c_str(), SAY_MAX);
  strncpy(say.req_channel, current_channel.c_str(), CHANNEL_MAX);

  if (sendto(client_socket, &say, sizeof(say), 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to send message\n");
  }

  return 0;
}

// Sends login requests to the server.
int SendLogin(char *username) {
  struct request_login login;
  memset(&login, 0, sizeof(login));
  login.req_type = REQ_LOGIN;
  strncpy(login.req_username, username, USERNAME_MAX);

  size_t message_size = sizeof(struct request_login);

  if (sendto(client_socket, &login, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request login\n");
  }

  return 0;
}

// Sends logout requests to the server.
int SendLogout() {
  struct request_logout logout;
  memset((char *) &logout, 0, sizeof(logout));
  logout.req_type = REQ_LOGOUT;

  size_t message_size = sizeof(struct request_logout);

  if (sendto(client_socket, &logout, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request logout\n");
  }

  return 0;
}

int SendList(){
  struct request_list list;
  list.req_type = REQ_LIST;
  size_t list_size = sizeof(struct request_list);

  if (sendto(client_socket, &list, list_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request list \n");
  }

  return 0;
}

int SendLeave(std::string channel) {
  bool contains_channel = false;
  std::vector<std::string>::iterator it;
  for (it = channels.begin(); it != channels.end(); ++it) {
    std::cout << "checking channel: " << *it << std::endl;
    std::cout << "trying to leave channel: " << channel << std::endl;

    if (*it == channel) {
      contains_channel = true;
      break;
    }
  }

  if (contains_channel) {
    channels.erase(it);

    struct request_leave leave;
    memset((char *) &leave, 0, sizeof(leave));
    leave.req_type = REQ_LEAVE;
    strncpy(leave.req_channel, channel.c_str(), CHANNEL_MAX);

    size_t leave_size = sizeof(struct request_leave);

    if (sendto(client_socket, &leave, leave_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
      Error("client: failed to request leave\n");
    }

    for (it = channels.begin(); it != channels.end(); ++it) {
      std::cout << *it << std::endl;
    }
  }

  return 0;
}

// Sends join requests to the server.
int SendJoin(std::string channel) {
  bool contains_channel = false;
  for (std::vector<std::string>::iterator it = channels.begin(); it != channels.end(); ++it) {
    if (*it == channel) {
      contains_channel = true;
      break;
    }
  }

  if (!contains_channel) {
    channels.push_back(channel);

    struct request_join join;
    memset((char *) &join, 0, sizeof(join));
    join.req_type = REQ_JOIN;
    strncpy(join.req_channel, channel.c_str(), CHANNEL_MAX);

    size_t message_size = sizeof(struct request_join);

    if (sendto(client_socket, &join, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
      Error("client: failed to request join\n");
    }
  }

  return 0;
}

// Switches to a channel the user has already joined.
int SwitchChannel(std::string channel) {
  bool isSubscribed = false;

  if (channels.size() > 0) {
    for (auto c: channels) {
      if (channel == c) {
        current_channel = channel;
        isSubscribed = true;
      }
    }
  }

  if (!isSubscribed) {
    std::cout << "You have not subscribed to channel " << channel << std::endl;
  }

  return 0;
}

void HandleTextList(char *receive_buffer, char *output) {
  struct text_list list;
  memcpy(&list, receive_buffer, sizeof(struct text_list));

  std::string backspaces = "";
  for (int i = 0; i < SAY_MAX; i++) {
    backspaces.append("\b");
  }
  std::cout << backspaces;

  std::cout << "Existing channels:" << std::endl;
  for(int i = 2; i < list.txt_nchannels + 2; i++){
    std::cout << " " << list.txt_channels[i].ch_channel << std::endl;
  }

  PrintPrompt();
  std::cout << output << std::flush;
}

// Handles TXT-SAY server messages.
void HandleTextSay(char *receive_buffer, char *output) {
  struct text_say say;
  memcpy(&say, receive_buffer, sizeof(struct text_say));

  std::string backspaces = "";
  for (int i = 0; i < SAY_MAX; i++) {
    backspaces.append("\b");
  }
  std::cout << backspaces;
  std::cout << "[" << say.txt_channel << "]" << "[" << say.txt_username << "]: " << say.txt_text << std::endl;
  PrintPrompt();
  std::cout << output << std::flush;
}

// Processes the input string to decide what type of command it is.
bool ProcessInput(std::string input) {
  std::vector<std::string> inputs = StringSplit(input);

  if (inputs[0] == "/exit") {
    SendLogout();
    cooked_mode();
    return false;
  } else if (inputs[0] == "/list") {
    SendList();
  } else if (inputs[0] == "/join" && inputs.size() > 1) {
    SendJoin(inputs[1]);
  } else if (inputs[0] == "/leave" && inputs.size() > 1) {
    if (input[1] == current_channel){
      SendLeave(inputs[1]);
    } else {
      std::cout << "*Unknown command" << std::endl;
    }
  } else if (inputs[0] == "/who") {

  } else if (inputs[0] == "/switch" && inputs.size() > 1) {
    SwitchChannel(inputs[1]);
  } else {
    std::cout << "*Unknown command" << std::endl;
  }

  PrintPrompt();
  return true;
}

int main(int argc, char *argv[]) {
  char *domain;
  char *port_str;
  int port_num;
  char *username;
  std::string input;
  char *output = (char *) "";
  fd_set read_set;
  int result;

  char stdin_buffer[SAY_MAX + 1];
  char *stdin_buffer_pointer = stdin_buffer;

  char receive_buffer[kBufferSize];
  memset(&receive_buffer, 0, kBufferSize);

  if (argc < 4) {
    Error("usage: client [server name] [port] [username]");
  }

  domain = argv[1];
  port_str = argv[2];
  port_num = atoi(argv[2]);
  username = argv[3];

  if (strlen(domain) > UNIX_PATH_MAX) {
    Error("client: server name must be less than 108 characters");
  }

  if (port_num < 0 || port_num > 65535) {
    Error("client: port number must be between 0 and 65535");
  }

  if (strlen(username) > USERNAME_MAX) {
    Error("client: username must be less than 32 characters");
  }

  CreateSocket(domain, port_str);

  SendLogin(username);

  current_channel = "Common";
  SendJoin(current_channel);

  if (raw_mode() != 0){
    Error("client: error using raw mode");
  }

  PrintPrompt();

  while (1) {
    FD_ZERO(&read_set);
    FD_SET(client_socket, &read_set);
    FD_SET(STDIN_FILENO, &read_set);

    if ((result = select(client_socket + 1, &read_set, NULL, NULL, NULL)) < 0) {
      Error("client: problem using select");
    }

    if (result > 0) {
      if (FD_ISSET(STDIN_FILENO, &read_set)) {
        // User entered a char.
        char c = (char) getchar();
        if (c == '\n') {
          // Increments pointer and adds NULL char.
          *stdin_buffer_pointer++ = '\0';

          // Resets stdin_buffer_pointer to the start of stdin_buffer.
          stdin_buffer_pointer = stdin_buffer;

          std::cout << "\n" << std::flush;

          // Prevents output from printing on the new prompt after a newline char.
          output = (char *) "";

          input.assign(stdin_buffer, stdin_buffer + strlen(stdin_buffer));

          if (input[0] == '/') {
            if (!ProcessInput(input)) {
              break;
            }
          } else {
            // Sends chat messages.
            SendSay(input);
          }
        } else if (stdin_buffer_pointer != stdin_buffer + SAY_MAX) {
          // Increments pointer and adds char c.
          *stdin_buffer_pointer++ = c;

          std::cout << c << std::flush;

          // Copies pointer into output.
          output = stdin_buffer_pointer;
          // Increments and sets NULL char.
          *output++ = '\0';
          // Copies stdin_buffer into part of output before NULL char.
          output = stdin_buffer;
        }
      } else if (FD_ISSET(client_socket, &read_set)) {
        // Socket has data.
        ssize_t read_size = read(client_socket, receive_buffer, kBufferSize);

        if (read_size != 0) {
          struct text message;
          memcpy(&message, receive_buffer, sizeof(struct text));
          text_t text_type = message.txt_type;

          switch (text_type) {
            case TXT_SAY:
              HandleTextSay(receive_buffer, output);
              break;
            case TXT_LIST:
              HandleTextList(receive_buffer, output);
              break;
            default:
              break;
          }
        }

        memset(&receive_buffer, 0, SAY_MAX);
      } // end of if client_socket

    } // end of if result

  } // end of while

  return 0;
}<|endoftext|>


<commit_before>#include <netdb.h>
#include <unistd.h>
#include <sys/fcntl.h>

#include "client.h"
#include "duckchat.h"
#include "raw.h"

// Client connects, logs in, and joins “Common”.
// Client reads lines from the user and parses commands.
// Client correctly sends Say message.
// Client uses select() to wait for input from the user and the server.
// Client correctly sends Join, Leave, Login, and Logout and TODO handles Switch.
// TODO Client correctly sends List and Who.
// TODO Server can accept connections.
// TODO Server handles Login and Logout from users, and keeps records of which users are logged in.
// TODO Server handles Join and Leave from users, keeps records of which channels a user belongs to,
// and keeps records of which users are in a channel.
// TODO Server handles the Say message.
// TODO Server correctly handles List and Who.
// TODO Create copies of your client and server source. Modify them to send invalid packets to your good client
// and server, to see if you can make your client or server crash. Fix any bugs you find.

// Variables
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
int client_socket;
struct addrinfo *server_info;
char *current_channel;
std::vector<char *> channels;

// Prints an error message and exits the program.
void Error(const char *msg) {
  std::cerr << msg << std::endl;
  exit(1);
}

void PrintPrompt() {
  std::cout << "> " << std::flush;
}

// Splits strings around spaces.
std::vector<std::string> StringSplit(std::string input) {
  std::istringstream iss(input);
  std::vector<std::string> result{std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};

  return result;
}

// Splits strings around spaces.
std::vector<std::string> SplitString(char *input, char delimiter) {
  std::vector<std::string> result;
  std::string word = "";

  size_t input_size = strlen(input);
  for (size_t i = 0; i < input_size; i++) {
    if (input[i] != delimiter) {
      word += input[i];
    } else {
      result.push_back(word);
      word = "";
    }
  }
  result.push_back(word);

  return result;
}

void StripChar(char *input, char c) {
  size_t size = strlen(input);
  for (size_t i = 0; i < size; i++) {
    if (input[i] == c) {
      input[i] = '\0';
    }
  }
}

// Gets the address info of the server at a the given port and creates the client's socket.
void CreateSocket(char *domain, const char *port) {
  std::cout << "Connecting to " << domain << std::endl;

  struct addrinfo hints;
  struct addrinfo *server_info_tmp;
  int status;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;
  hints.ai_protocol = 0;

  if ((status = getaddrinfo(domain, port, &hints, &server_info_tmp)) != 0) {
    std::cerr << "client: unable to resolve address: " << gai_strerror(status) << std::endl;
    exit(1);
  }

  // getaddrinfo() returns a list of address structures into server_info_tmp.
  // Tries each address until a successful connect().
  // If socket() (or connect()) fails, closes the socket and tries the next address.

  for (server_info = server_info_tmp; server_info != NULL; server_info = server_info->ai_next) {
    if ((client_socket = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol)) < 0) {
      continue;
    }
    if (connect(client_socket, server_info->ai_addr, server_info->ai_addrlen) != -1) {
      fcntl(client_socket, F_SETFL, O_NONBLOCK);
      break; // Success
    }
    close(client_socket);
  }

  if (server_info == NULL) {
    Error("client: all sockets failed to connect");
  }
}

// Sends a message to all users in on the active channel.
int SendSay(const char *message) {
  struct request_say say;
  memset(&say, 0, sizeof(say));
  say.req_type = REQ_SAY;
  strncpy(say.req_text, message, SAY_MAX);
  strncpy(say.req_channel, current_channel, CHANNEL_MAX);

  if (sendto(client_socket, &say, sizeof(say), 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to send message\n");
  }

  return 0;
}

// Sends login requests to the server.
int SendLogin(char *username) {
  struct request_login login;
  memset(&login, 0, sizeof(login));
  login.req_type = REQ_LOGIN;
  strncpy(login.req_username, username, USERNAME_MAX);

  size_t message_size = sizeof(struct request_login);

  if (sendto(client_socket, &login, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request login\n");
  }

  return 0;
}

// Sends logout requests to the server.
int SendLogout() {
  struct request_logout logout;
  memset((char *) &logout, 0, sizeof(logout));
  logout.req_type = REQ_LOGOUT;

  size_t message_size = sizeof(struct request_logout);

  if (sendto(client_socket, &logout, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request logout\n");
  }

  return 0;
}

// Sends join requests to the server.
int SendJoin(const char *channel) {
  if (std::find(channels.begin(), channels.end(), channel) != channels.end()) {
    struct request_join join;
    memset((char *) &join, 0, sizeof(join));
    join.req_type = REQ_JOIN;
    strncpy(join.req_channel, channel, CHANNEL_MAX);

    size_t message_size = sizeof(struct request_join);

    if (sendto(client_socket, &join, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
      Error("client: failed to request join\n");
    }

    channels.push_back((char *) channel);

    for (auto c : channels) {
      std::cout << "channel in channels: " << c << std::endl;
    }
  }

  return 0;
}

// Switches to a channel the user has already joined.
int SwitchChannel(const char *channel) {
  bool isSubscribed = false;

  if (channels.size() > 0) {
    for (auto c: channels) {
      std::cout << "c: " << c << " channel: " << channel << std::endl;
      if (channel == c) {
        current_channel = (char *) channel;
        isSubscribed = true;
      }
    }
  }

  if (!isSubscribed) {
    std::cout << "You have not subscribed to channel " << channel << std::endl;
  }

  return 0;
}

// Handles TXT-SAY server messages.
void HandleTextSay(char *receive_buffer, char *output) {
  struct text_say say;
  memcpy(&say, receive_buffer, sizeof(struct text_say));

  std::string backspaces = "";
  for (int i = 0; i < SAY_MAX; i++) {
    backspaces.append("\b");
  }
  std::cout << backspaces;
  std::cout << "[" << say.txt_channel << "]" << "[" << say.txt_username << "]: " << say.txt_text << std::endl;
  PrintPrompt();
  std::cout << output << std::flush;
}

// Processes the input string to decide what type of command it is.
bool ProcessInput(std::string input) {
  std::vector<std::string> inputs = StringSplit(input);

  if (inputs[0] == "/exit") {
    SendLogout();
    cooked_mode();
    return false;
  } else if (inputs[0] == "/list") {

  } else if (inputs[0] == "/join" && inputs.size() > 1) {
    SendJoin(inputs[1].c_str());
  } else if (inputs[0] == "/leave") {

  } else if (inputs[0] == "/who") {

  } else if (inputs[0] == "/switch" && inputs.size() > 1) {
    SwitchChannel(inputs[1].c_str());
  } else {
    std::cout << "*Unknown command" << std::endl;
  }

  PrintPrompt();
  return true;
}

int main(int argc, char *argv[]) {
  char *domain;
  char *port_str;
  int port_num;
  char *username;
  char *input;
  char *output = (char *) "";
  fd_set read_set;
  int result;

  char stdin_buffer[SAY_MAX + 1];
  char *stdin_buffer_pointer = stdin_buffer;

  char receive_buffer[kBufferSize];
  memset(&receive_buffer, 0, kBufferSize);

  if (argc < 4) {
    Error("usage: client [server name] [port] [username]");
  }

  domain = argv[1];
  port_str = argv[2];
  port_num = atoi(argv[2]);
  username = argv[3];

  if (strlen(domain) > UNIX_PATH_MAX) {
    Error("client: server name must be less than 108 characters");
  }

  if (port_num < 0 || port_num > 65535) {
    Error("client: port number must be between 0 and 65535");
  }

  if (strlen(username) > USERNAME_MAX) {
    Error("client: username must be less than 32 characters");
  }

  CreateSocket(domain, port_str);

  SendLogin(username);

  current_channel = (char *) "Common";
  channels.push_back(current_channel);
  SendJoin(current_channel);

  if (raw_mode() != 0){
    Error("client: error using raw mode");
  }

  PrintPrompt();

  while (1) {
    FD_ZERO(&read_set);
    FD_SET(client_socket, &read_set);
    FD_SET(STDIN_FILENO, &read_set);

    if ((result = select(client_socket + 1, &read_set, NULL, NULL, NULL)) < 0) {
      Error("client: problem using select");
    }

    if (result > 0) {
      if (FD_ISSET(STDIN_FILENO, &read_set)) {
        // User entered a char.
        char c = (char) getchar();
        if (c == '\n') {
          // Increments pointer and adds NULL char.
          *stdin_buffer_pointer++ = '\0';

          // Resets stdin_buffer_pointer to the start of stdin_buffer.
          stdin_buffer_pointer = stdin_buffer;

          std::cout << "\n" << std::flush;

          // Prevents output from printing on the new prompt after a newline char.
          output = (char *) "";

          input = stdin_buffer;
          if (input[0] == '/') {
            if (!ProcessInput(input)) {
              break;
            }
          } else {
            // Sends chat messages.
            SendSay(input);
          }
        } else if (stdin_buffer_pointer != stdin_buffer + SAY_MAX) {
          // Increments pointer and adds char c.
          *stdin_buffer_pointer++ = c;

          std::cout << c << std::flush;

          // Copies pointer into output.
          output = stdin_buffer_pointer;
          // Increments and sets NULL char.
          *output++ = '\0';
          // Copies stdin_buffer into part of output before NULL char.
          output = stdin_buffer;
        }
      } else if (FD_ISSET(client_socket, &read_set)) {
        // Socket has data.
        ssize_t read_size = read(client_socket, receive_buffer, kBufferSize);

        if (read_size != 0) {
          struct text message;
          memcpy(&message, receive_buffer, sizeof(struct text));
          text_t text_type = message.txt_type;

          switch (text_type) {
            case TXT_SAY:
              HandleTextSay(receive_buffer, output);
              break;
            default:
              break;
          }
        }

        memset(&receive_buffer, 0, SAY_MAX);
      } // end of if client_socket

    } // end of if result

  } // end of while

  return 0;
}<commit_msg>change join print test<commit_after>#include <netdb.h>
#include <unistd.h>
#include <sys/fcntl.h>

#include "client.h"
#include "duckchat.h"
#include "raw.h"

// Client connects, logs in, and joins “Common”.
// Client reads lines from the user and parses commands.
// Client correctly sends Say message.
// Client uses select() to wait for input from the user and the server.
// Client correctly sends Join, Leave, Login, and Logout and TODO handles Switch.
// TODO Client correctly sends List and Who.
// TODO Server can accept connections.
// TODO Server handles Login and Logout from users, and keeps records of which users are logged in.
// TODO Server handles Join and Leave from users, keeps records of which channels a user belongs to,
// and keeps records of which users are in a channel.
// TODO Server handles the Say message.
// TODO Server correctly handles List and Who.
// TODO Create copies of your client and server source. Modify them to send invalid packets to your good client
// and server, to see if you can make your client or server crash. Fix any bugs you find.

// Variables
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
int client_socket;
struct addrinfo *server_info;
char *current_channel;
std::vector<char *> channels;

// Prints an error message and exits the program.
void Error(const char *msg) {
  std::cerr << msg << std::endl;
  exit(1);
}

void PrintPrompt() {
  std::cout << "> " << std::flush;
}

// Splits strings around spaces.
std::vector<std::string> StringSplit(std::string input) {
  std::istringstream iss(input);
  std::vector<std::string> result{std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};

  return result;
}

// Splits strings around spaces.
std::vector<std::string> SplitString(char *input, char delimiter) {
  std::vector<std::string> result;
  std::string word = "";

  size_t input_size = strlen(input);
  for (size_t i = 0; i < input_size; i++) {
    if (input[i] != delimiter) {
      word += input[i];
    } else {
      result.push_back(word);
      word = "";
    }
  }
  result.push_back(word);

  return result;
}

void StripChar(char *input, char c) {
  size_t size = strlen(input);
  for (size_t i = 0; i < size; i++) {
    if (input[i] == c) {
      input[i] = '\0';
    }
  }
}

// Gets the address info of the server at a the given port and creates the client's socket.
void CreateSocket(char *domain, const char *port) {
  std::cout << "Connecting to " << domain << std::endl;

  struct addrinfo hints;
  struct addrinfo *server_info_tmp;
  int status;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;
  hints.ai_protocol = 0;

  if ((status = getaddrinfo(domain, port, &hints, &server_info_tmp)) != 0) {
    std::cerr << "client: unable to resolve address: " << gai_strerror(status) << std::endl;
    exit(1);
  }

  // getaddrinfo() returns a list of address structures into server_info_tmp.
  // Tries each address until a successful connect().
  // If socket() (or connect()) fails, closes the socket and tries the next address.

  for (server_info = server_info_tmp; server_info != NULL; server_info = server_info->ai_next) {
    if ((client_socket = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol)) < 0) {
      continue;
    }
    if (connect(client_socket, server_info->ai_addr, server_info->ai_addrlen) != -1) {
      fcntl(client_socket, F_SETFL, O_NONBLOCK);
      break; // Success
    }
    close(client_socket);
  }

  if (server_info == NULL) {
    Error("client: all sockets failed to connect");
  }
}

// Sends a message to all users in on the active channel.
int SendSay(const char *message) {
  struct request_say say;
  memset(&say, 0, sizeof(say));
  say.req_type = REQ_SAY;
  strncpy(say.req_text, message, SAY_MAX);
  strncpy(say.req_channel, current_channel, CHANNEL_MAX);

  if (sendto(client_socket, &say, sizeof(say), 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to send message\n");
  }

  return 0;
}

// Sends login requests to the server.
int SendLogin(char *username) {
  struct request_login login;
  memset(&login, 0, sizeof(login));
  login.req_type = REQ_LOGIN;
  strncpy(login.req_username, username, USERNAME_MAX);

  size_t message_size = sizeof(struct request_login);

  if (sendto(client_socket, &login, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request login\n");
  }

  return 0;
}

// Sends logout requests to the server.
int SendLogout() {
  struct request_logout logout;
  memset((char *) &logout, 0, sizeof(logout));
  logout.req_type = REQ_LOGOUT;

  size_t message_size = sizeof(struct request_logout);

  if (sendto(client_socket, &logout, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request logout\n");
  }

  return 0;
}

// Sends join requests to the server.
int SendJoin(const char *channel) {
  if (!(std::find(channels.begin(), channels.end(), channel) != channels.end())) {
    struct request_join join;
    memset((char *) &join, 0, sizeof(join));
    join.req_type = REQ_JOIN;
    strncpy(join.req_channel, channel, CHANNEL_MAX);

    size_t message_size = sizeof(struct request_join);

    if (sendto(client_socket, &join, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
      Error("client: failed to request join\n");
    }

    channels.push_back((char *) channel);

    for (std::vector<char *>::iterator it = channels.begin(); it != channels.end(); ++it) {
      std::cout << "channel in channels: " << *it << std::endl;
    }
  }

  return 0;
}

// Switches to a channel the user has already joined.
int SwitchChannel(const char *channel) {
  bool isSubscribed = false;

  if (channels.size() > 0) {
    for (auto c: channels) {
      std::cout << "c: " << c << " channel: " << channel << std::endl;
      if (channel == c) {
        current_channel = (char *) channel;
        isSubscribed = true;
      }
    }
  }

  if (!isSubscribed) {
    std::cout << "You have not subscribed to channel " << channel << std::endl;
  }

  return 0;
}

// Handles TXT-SAY server messages.
void HandleTextSay(char *receive_buffer, char *output) {
  struct text_say say;
  memcpy(&say, receive_buffer, sizeof(struct text_say));

  std::string backspaces = "";
  for (int i = 0; i < SAY_MAX; i++) {
    backspaces.append("\b");
  }
  std::cout << backspaces;
  std::cout << "[" << say.txt_channel << "]" << "[" << say.txt_username << "]: " << say.txt_text << std::endl;
  PrintPrompt();
  std::cout << output << std::flush;
}

// Processes the input string to decide what type of command it is.
bool ProcessInput(std::string input) {
  std::vector<std::string> inputs = StringSplit(input);

  if (inputs[0] == "/exit") {
    SendLogout();
    cooked_mode();
    return false;
  } else if (inputs[0] == "/list") {

  } else if (inputs[0] == "/join" && inputs.size() > 1) {
    SendJoin(inputs[1].c_str());
  } else if (inputs[0] == "/leave") {

  } else if (inputs[0] == "/who") {

  } else if (inputs[0] == "/switch" && inputs.size() > 1) {
    SwitchChannel(inputs[1].c_str());
  } else {
    std::cout << "*Unknown command" << std::endl;
  }

  PrintPrompt();
  return true;
}

int main(int argc, char *argv[]) {
  char *domain;
  char *port_str;
  int port_num;
  char *username;
  char *input;
  char *output = (char *) "";
  fd_set read_set;
  int result;

  char stdin_buffer[SAY_MAX + 1];
  char *stdin_buffer_pointer = stdin_buffer;

  char receive_buffer[kBufferSize];
  memset(&receive_buffer, 0, kBufferSize);

  if (argc < 4) {
    Error("usage: client [server name] [port] [username]");
  }

  domain = argv[1];
  port_str = argv[2];
  port_num = atoi(argv[2]);
  username = argv[3];

  if (strlen(domain) > UNIX_PATH_MAX) {
    Error("client: server name must be less than 108 characters");
  }

  if (port_num < 0 || port_num > 65535) {
    Error("client: port number must be between 0 and 65535");
  }

  if (strlen(username) > USERNAME_MAX) {
    Error("client: username must be less than 32 characters");
  }

  CreateSocket(domain, port_str);

  SendLogin(username);

  current_channel = (char *) "Common";
  channels.push_back(current_channel);
  SendJoin(current_channel);

  if (raw_mode() != 0){
    Error("client: error using raw mode");
  }

  PrintPrompt();

  while (1) {
    FD_ZERO(&read_set);
    FD_SET(client_socket, &read_set);
    FD_SET(STDIN_FILENO, &read_set);

    if ((result = select(client_socket + 1, &read_set, NULL, NULL, NULL)) < 0) {
      Error("client: problem using select");
    }

    if (result > 0) {
      if (FD_ISSET(STDIN_FILENO, &read_set)) {
        // User entered a char.
        char c = (char) getchar();
        if (c == '\n') {
          // Increments pointer and adds NULL char.
          *stdin_buffer_pointer++ = '\0';

          // Resets stdin_buffer_pointer to the start of stdin_buffer.
          stdin_buffer_pointer = stdin_buffer;

          std::cout << "\n" << std::flush;

          // Prevents output from printing on the new prompt after a newline char.
          output = (char *) "";

          input = stdin_buffer;
          if (input[0] == '/') {
            if (!ProcessInput(input)) {
              break;
            }
          } else {
            // Sends chat messages.
            SendSay(input);
          }
        } else if (stdin_buffer_pointer != stdin_buffer + SAY_MAX) {
          // Increments pointer and adds char c.
          *stdin_buffer_pointer++ = c;

          std::cout << c << std::flush;

          // Copies pointer into output.
          output = stdin_buffer_pointer;
          // Increments and sets NULL char.
          *output++ = '\0';
          // Copies stdin_buffer into part of output before NULL char.
          output = stdin_buffer;
        }
      } else if (FD_ISSET(client_socket, &read_set)) {
        // Socket has data.
        ssize_t read_size = read(client_socket, receive_buffer, kBufferSize);

        if (read_size != 0) {
          struct text message;
          memcpy(&message, receive_buffer, sizeof(struct text));
          text_t text_type = message.txt_type;

          switch (text_type) {
            case TXT_SAY:
              HandleTextSay(receive_buffer, output);
              break;
            default:
              break;
          }
        }

        memset(&receive_buffer, 0, SAY_MAX);
      } // end of if client_socket

    } // end of if result

  } // end of while

  return 0;
}<|endoftext|>