How to use serial.available twice in void loop? - arduino-ide

I am using keypad(4x4) with arduino nano and then serially communicating arduino nano and nodemcu. My code for arduino nano is:
#include <SoftwareSerial.h>
#include <Password.h>
#include <Keypad.h>
Password ssid = Password( "1234" );
Password pass = Password( "56789" );
Password connection = Password( "C" );
const byte ROWS = 4; // Four rows
const byte COLS = 4; // Three columns
char hexaKeys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = { 2,3,4,10 };
byte colPins[COLS] = { 11,7,8,9 };
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS );
SoftwareSerial s(5,6);
//void checkPassword();
void setup(){
Serial.begin(9600);
s.begin(9600);
customKeypad.addEventListener(keypadEvent); //add an event listener for this keypad
Serial.println("enter SSID: ");
}
void loop()
{
char customKey = customKeypad.getKey(); //storing pressed key value in a char
if (customKey) {
if(s.available() > 0) {
switch (customKey) {
case '1':
Serial.println(customKey);
s.write(customKey);
break;
case '2':
Serial.println(customKey);
s.write(customKey);
break;
case '3':
Serial.println(customKey);
s.write(customKey);
break;
case '4':
Serial.println(customKey);
s.write(customKey);
break;
case '5':
Serial.println(customKey);
s.write(customKey);
break;
case '6':
Serial.println(customKey);
s.write(customKey);
break;
case '7':
Serial.println(customKey);
s.write(customKey);
break;
case '8':
Serial.println(customKey);
s.write(customKey);
break;
case '9':
Serial.println(customKey);
s.write(customKey);
break;
case '0':
Serial.println(customKey);
s.write(customKey);
//s.write("\nPassword verified\nPress C to connect\n");
break;
case '*':
Serial.println(customKey);
s.write(customKey);
//s.write();
//s.write("\nSSID verified\nEnter your Password\n");
//checkPassword();
break;
case '#':
Serial.println(customKey);
s.write(customKey);
break;
case 'A':
Serial.println(customKey);
s.write(customKey);
break;
case 'B':
Serial.println(customKey);
s.write(customKey);
break;
case 'C':
Serial.println(customKey);
s.write(customKey);
//s.write("\nConnecting to..");
break;
case 'D':
Serial.println(customKey);
s.write(customKey);
break;
}
}
}
}
void keypadEvent(KeypadEvent key){
switch (customKeypad.getState()) {
case PRESSED:
// Serial.println(key);
//s.write(key);
switch (key) {
case '*':
checkPassword();
break;
case '#':
ssid.reset();
pass.reset();
connection.reset();
break;
default:
ssid.append(key);
pass.append(key);
connection.append(key);
}
}
}
void checkPassword(){
if (ssid.evaluate()) {
Serial.println();
Serial.println("SSID verified");
Serial.print("enter password: ");
} else if (pass.evaluate()){
Serial.println();
Serial.println("Password verified");
Serial.println("press C to connect");
} else if (connection.evaluate()) {
Serial.println();
Serial.println("Connecting to....");
} else {
Serial.println("Wrong");
}
}
and my nodemcu code is:
#include <String.h>
#include <SoftwareSerial.h>
#include <ESP8266WiFi.h>
SoftwareSerial s(D6,D5); // (Rx, Tx)
String shab;
String shab2;
String arr[40];
String arr1[40];
const char* shab1="";
const char* shab3="";
std::string str;
int i,k;
void setup() {
s.begin(9600);
Serial.begin(9600);
Serial.print("enter ssid: ");
}
void loop() {
s.write("s");
while(s.available() >0) {
char data=s.read();
shab += data;
Serial.print(data);
}
int len=shab.length();
if (shab.charAt(len-1)=='*') {
for(i=0;i<shab.length();i++) {
arr[i]=shab.substring(0,len-1);
}
shab1 = arr[0].c_str();
Serial.println();
Serial.println(shab1);
Serial.print("Enter ur password: ");
// delay(7000);
// while(true){
while(s.available() >0) {
char data1=s.read();
shab2 += data1;
Serial.print(data1);
//}
int len1=shab2.length();
if(shab2.charAt(len1-1)=='*') {
for(k=0;k<shab2.length();k++) {
arr1[k]=shab2.substring(0,len1-1);
}
shab3 = arr1[0].c_str();
Serial.println();
Serial.println(shab3);
Serial.println("Connecting to wifi: ");
delay(2000);
Serial.println(shab1);
Serial.flush();
WiFi.begin(shab1,shab3);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" IP address: ");
Serial.print(WiFi.localIP());
delay(2000);
// }
}
}
while(1) {
delay(1000);
}
}
}
Now the problem is that when i enter the password from keypad, this while(s.available>0) line when given second time, it does not work. This does not print anything on the serial monitor. Can anyone please help me?

The problem is that after Serial.print("Enter ur password: ");, the program immediately evaluates s.available(). Since nothing is available, it goes into the infinite loop at the end of loop() and never gets out of it.
If you want to read incoming data until you receive '*', you can do something like this.
Serial.print("Enter ur password: ");
bool entered = false;
while (entered == false) { // loop until entered becomes true
yield();
while (s.available() > 0) {
char data1 = s.read();
shab2 += data1;
Serial.println(data1);
int len1 = shab2.length();
if (shab2.charAt(len1-1) == '*') {
// do whatever you need to do
entered = true; // now you can get out of the loop
}
}
}

Related

c++ Straustrup calculator02.cpp function Error

I have a small problem with the functions inside calculator02.cpp
there is an error when compile the code
warning: control reaches end of non-void function [-Wreturn-type]130 | }
Yes I know that in any case function must return the value what can I do to solve this problem? in this case I do not have any more cases apart from that ones inside the function itself, same with the get function;
Token Token_stream::get()
{
if (full) { // do we already have a Token ready?
// remove token from buffer
full = false;
return buffer;
}
char ch;
cin >> ch; // note that >> skips whitespace (space, newline, tab, etc.)
switch (ch) {
case ';': // for "print"
case 'q': // for "quit"
case '(': case ')': case '+': case '-': case '*': case '/': case '}': case '{':
return Token(ch); // let each character represent itself
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '9':
{
cin.putback(ch); // put digit back into the input stream
double val;
cin >> val; // read a floating-point number
return Token('8', val); // let '8' represent "a number"
}
default:
error("Bad token");
}
}
double primary()
{
Token t = ts.get();
switch (t.kind) {
case '(': // handle '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected");
return d;
}
case '{':
{
double d = expression();
t = ts.get();
if(t.kind != '}') error("'}' Expected");
return d;
}
case '8': // we use '8' to represent a number
return t.value; // return the number's value
default:
error("primary expected");
}
}

How to write multiple ssid's and passwords to the same json file ESP32

I am currently testing a piece of code using arduinojson6. My goal is to store multiple ssid's and passwords to the esp32 SPIFF.
The uneddited question contained a piece of code that would append to the file rather than reading doc, deleting /SSID.json, adding to doc serialization and saving the file again like I have now, is also not the solution.
the desired json file would be:
{"information":[{ "SSID":"variable blaat1", "PASS1":"variable Abc1", "NUMBER": "1" },{ "SSID":"variable blaat2", "PASS2":"variable Abc2", "NUMBER": "2" },{ "SSID":"variable blaat3", "PASS3":"variable Abc3", "NUMBER": "3" },{ "SSID":"variable blaat4", "PASS4":"variable Abc4", "NUMBER": "4" },{ "SSID":"variable blaat5", "PASS5":"variable Abc5", "NUMBER": "5" }]}
Instead, when more then 1 value is serialized and appended it will read like this:
{
"information": {},
"test": [
"mooiman\n",
"mooiweer\n"
],
"number": [
1,
2
]
}
Maybe some of you know how to serialize it properly.
The code I test with:
#include <Arduino.h>
#include <WiFi.h>
//#include <time.h>
//#include <ESP32Ping.h>
#include "FS.h"
#include "SPIFFS.h"
//#include <HTTPClient.h>
#include <ArduinoJson.h>
int numberofInputs = 1;
String ssid = "YourSSID";
String passwords = "YourPassword";
String readString;
char FileReadBuff[1024];
DynamicJsonDocument doc(1024);
void readFile(fs::FS &fs, const char * path){
if (SPIFFS.exists("/SSID.json") == false)
{
File file = SPIFFS.open("/SSID.json", FILE_WRITE);
if (!file) {
Serial.println("There was an error opening the file for writing");
return;
}
if (file.print("SSID")) {
Serial.println("File was written");
} else {
Serial.println("File write failed");
}
file.close();
}
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path);
if(!file || file.isDirectory()){
Serial.println("- failed to open file for reading");
return;
}
uint16_t i = 0;
Serial.println("reading");
while (file.available()) {
FileReadBuff[i] = file.read();
i++;
}
file.close();
}
void CleanFile(fs::FS &fs, const char * path, const char * message) {
for( int i = 0; i < sizeof(FileReadBuff); ++i ){
FileReadBuff[i] = (char)0;
}
File file = SPIFFS.open(path, FILE_WRITE);
if (fs.remove(path)) {
Serial.println("\r\n- file cleaned");
} else {
Serial.println("\r\n- Cleaning failed");
}
file.print(path);
}
void appendFile(fs::FS &fs, const char * path, const char * message){
if (SPIFFS.exists("/SSID.json") == false)
{
File file = SPIFFS.open("/SSID.json", FILE_WRITE);
if (!file) {
Serial.println("There was an error opening the file for writing");
return;
}
if (file.print("SSID")) {
Serial.println("File was written");
} else {
Serial.println("File write failed");
}
file.close();
}
Serial.printf("Appending to file: %s\r\n", path);
File file = fs.open(path, FILE_APPEND);
if(!file){
Serial.println("- failed to open file for appending");
return;
}
if(file.println(message)){
Serial.println("- message appended");
} else {
Serial.println("- append failed");
}
file.close();
}
void Deserialization(){
for( int i = 0; i < sizeof(FileReadBuff); ++i ){
FileReadBuff[i] = (char)0;
}
readFile(SPIFFS, "/SSID.json"); //read everything from ssid.json file
const char * JsonFF = FileReadBuff; // put everything in to const char
Serial.print("Json From File:"); Serial.println(JsonFF);
DeserializationError error = deserializeJson(doc, JsonFF);
if(error){
Serial.print(F("deserializeJson() failed: ")); Serial.println(error.f_str()); // if not legit print error
}
if(!error){
String information = doc["information"];
Serial.println(information);
information = "";
}
}
void testjson(){
readString = "";
while(readString.length() < 1) {
while (Serial.available()) {
delay(10); //small delay to allow input buffer to fill
if (Serial.available() > 0) {
char c = Serial.read(); //gets one byte from serial buffer
if (c == ',') {
break;
} //breaks out of capture loop to print readstring
readString += c;
} //makes the string readString
}
if (readString.length() > 0) {
Serial.println(readString); //prints string to serial port out
if (readString.indexOf("READ") >= 0) {
Serial.println("reading file");
readFile(SPIFFS, "/SSID.json");
Serial.println(FileReadBuff);
for( int i = 0; i < sizeof(FileReadBuff); ++i ){
FileReadBuff[i] = (char)0;
}
}
if (readString.indexOf("DES") >= 0) { //DEZ deserialize will result in an error because json file is currently not valid
Serial.println("reading deserialized json");
Deserialization();
}
if (readString.indexOf("CLEAN") >= 0) { //CLEAN cleans the SSID.json file
Serial.println("reading deserialized json");
CleanFile(SPIFFS, "/SSID.json", "");
}
if (readString.indexOf("WRANDOM") >= 0){ //WRANDOM writes a random string to the SSID.json file
readString = "";
Serial.println("Going to write the following input:"); //waiting for user input
while(readString.length() < 1) {
while (Serial.available()) {
delay(10); //small delay to allow input buffer to fill
if (Serial.available() > 0) {
char c = Serial.read(); //gets one byte from serial buffer
if (c == ',') {
break;
} //breaks out of capture loop to print readstring
readString += c;
} //makes the string readString
}
if (readString.length() > 0) {
Serial.println(readString); //prints string to serial port out
`here is the part we're talking about`
CleanFile(SPIFFS, "/SSID.json", "");
JsonObject information = doc.createNestedObject("information");
String SerializedJson = "";
doc["test"].add(readString);
doc["number"].add(numberofInputs);
serializeJsonPretty(doc, SerializedJson);
appendFile(SPIFFS, "/SSID.json", SerializedJson.c_str());
SerializedJson = "";
numberofInputs ++;
return;
}
}
}
}
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
if (SPIFFS.exists("/SSID.json") == false)
{
File file = SPIFFS.open("/SSID.json", FILE_WRITE);
if (!file) {
Serial.println("There was an error opening the file for writing");
return;
}
if (file.print("SSID")) {
Serial.println("File was written");
} else {
Serial.println("File write failed");
}
file.close();
}
WiFi.mode(WIFI_MODE_STA);
WiFi.begin(ssid.c_str(), passwords.c_str());
while (WiFi.status() != WL_CONNECTED) { //Check for the connection
delay(1000);
Serial.print(".");
}
Serial.println("Connected");
}
void loop() {
// put your main code here, to run repeatedly:
readString = ""; //clears variable for new input
Serial.println("Ready for new input: ");
testjson();
}
So when you serial write WRANDOM you'll get promted to put in something.
When that is recieved it will store that in json.
Do that another time.
Next when you serial write READ it will show you the saved /SSID.json.
Thanks in advance.
Note that the DynamicJsonDocument startsof empty.
PS. I know littlefs is the new spiff but lets first try to make this work (or I need to make sepperate files for every ssid+password)
Here is a test sketch you can implement to your project.
I did not had the time to write more detailed sketch than this.
Let me know if you have any question about this
#include <Arduino.h>
#include "FS.h"
#include <LittleFS.h>
#include <ArduinoJson.h>
#define DOC_SIZE 5000
/*
Example JSON :
{
"information":[
{ "SSID":"variable blaat1", "PASS1":"variable Abc1", "NUMBER": "1" },
{ "SSID":"variable blaat2", "PASS2":"variable Abc2", "NUMBER": "2" },
{ "SSID":"variable blaat3", "PASS3":"variable Abc3", "NUMBER": "3" },
{ "SSID":"variable blaat4", "PASS4":"variable Abc4", "NUMBER": "4" },
{ "SSID":"variable blaat5", "PASS5":"variable Abc5", "NUMBER": "5" }
]
}
*/
/*
When you create a JSON array, you don't need to push the index of the object, since you can later
ask for the array size, therefor you know the indexes. Also you don't need to separate the passwords
like PASS1 or PASS2 since every cred is a separate object.
*/
void addCredentials( const char* SSID, const char* PW ){
// Create a static JSON document.
StaticJsonDocument fileDoc(DOC_SIZE);
// Open the file containing the credentials
File credFile = LittleFS.open(FILE_PATH,FILE_READ);
// Check if it opened.
if( !credFile ){ Serial.println("Failed to open file"); return; }
// Deserialize the file
DeserializationError error = deserializeJson(fileDoc, credFile );
// Check if the deserialization has any errors.
if( error ){ Serial.printf("Error on deserialization: %s\n", error.c_str() );
// Get the info array from the JSON.
JsonArray infoArr = fileDoc["information"].as<JsonArray>();
// Create a new object inside the array.
JsonObject newCred = infoArr.createNestedObject();
// Add credentials to the object.
newCred["SSID"] = SSID;
newCred["PASS"] = PW;
// Serialize everythig back to the file.
serializeJson(fileDoc, credFile);
// Close the file.
credFile.close();
}
// Open the file and put the pretty json into the serial directly.
void printFileContent(){
File credFile = LittleFS.open(FILE_PATH,FILE_READ);
if( !credFile ){ Serial.println("Failed to open file"); return; }
serializeJsonPretty(Serial, credFile);
credFile.close();
}
void clearFile(){
if( !LittleFS.exists(FILE_PATH) ){ Serial.println("Credentials file does not exists yet!"); return; }
File credFile = LittleFS.open(FILE_PATH,FILE_READ);
credFile.remove();
}
void getUserInput(){
if( !Serial.available() ){ return; }
// Get the credentials or whatever from the serial
// and call the **addCredentials( const char* SSID, const char* PW );** function.
}
void setup() {
Serial.begin(115200);
}
void loop() {
getUserInput();
}
I got it figured out with help from Dr.Random. It wasn't the full answer but it helped getting the json format and apend to that file correctly. The following code is a working example.
#include <Arduino.h>
#include <WiFi.h>
#include "FS.h"
#include "LittleFS.h"
#include <ArduinoJson.h>
#define FORMAT_LITTLEFS_IF_FAILED true
#define DOC_SIZE 5000
#define FILE_PATH "/SSID.json"
String ssid = "yourssid";
String passwords = "yourpass";
String readString;
char FileReadBuff[1024];
void readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path);
if(!file || file.isDirectory()){
Serial.println("- failed to open file for reading");
return;
}
Serial.println("- read from file:");
while(file.available()){
Serial.write(file.read());
}
file.close();
}
void Showone(int wichone){
DynamicJsonDocument doc(DOC_SIZE);
File credFile = LittleFS.open(FILE_PATH, FILE_READ);// open file for reading
DeserializationError error = deserializeJson(doc, credFile);// deserialize json
if( !credFile ){ Serial.println("Failed to open file"); return; }// if file doesn't exist, return
const char* SSID = doc[wichone]["SSID"]; //
const char* PASS = doc[wichone]["PASS"]; //
Serial.print("SSID and PASS = "); Serial.print(SSID); Serial.print(PASS); Serial.print(" "); Serial.println(" ");
credFile.close();
}
void addCredentials(const char * input){
DynamicJsonDocument doc(DOC_SIZE);// create json doc
String Serialized; // create string to store serialized json
File credFile = LittleFS.open(FILE_PATH, FILE_READ);// open file for reading
if( !credFile ){ Serial.println("Failed to open file"); return; }// if file doesn't exist, return
DeserializationError error = deserializeJson(doc, credFile);// deserialize json
if( error ){ Serial.printf("Error on deserialization: %s\n", error.c_str() );} //error when spiff is empty or not formatted correctly
JsonArray inforArr = doc["information"].as<JsonArray>();// get array from json
JsonObject newCred = doc.createNestedObject();// create new object in json
newCred["SSID"] = input;
newCred["PASS"] = input;
serializeJsonPretty(doc, Serialized);
Serial.print("input = "); Serial.println(input);
Serial.print("Serialized: "); Serial.println(Serialized);
File credFile2 = LittleFS.open(FILE_PATH, FILE_WRITE);// open file for writing
credFile2.print(Serialized);
credFile2.close();
credFile.close();
Serialized = "";
}
void testjson(){
readString = "";
while(readString.length() < 1) {
while (Serial.available()) {
delay(10); //small delay to allow input buffer to fill
if (Serial.available() > 0) {
char c = Serial.read(); //gets one byte from serial buffer
if (c == ',') {
break;
} //breaks out of capture loop to print readstring
readString += c;
} //makes the string readString
}
if (readString.length() > 0) {
Serial.println(readString); //prints string to serial port out
if (readString.indexOf("READ") >= 0) {
Serial.println("reading file");
readFile(LittleFS, "/SSID.json");
Serial.println(FileReadBuff);
for( int i = 0; i < sizeof(FileReadBuff); ++i ){
FileReadBuff[i] = (char)0;
}
}
if (readString.indexOf("SHOW") >= 0) {
readString = "";
Serial.println("Showing the following input(number): "); //waiting for user input
while(readString.length() < 1) {
while (Serial.available()) {
delay(10); //small delay to allow input buffer to fill
if (Serial.available() > 0) {
char c = Serial.read(); //gets one byte from serial buffer
if (c == ',') {
break;
} //breaks out of capture loop to print readstring
readString += c;
} //makes the string readString
}
if (readString.length() > 0) {
Serial.println(readString); //prints string to serial port out
Showone(readString.toInt());
}
}
}
if (readString.indexOf("CLEAN") >= 0) { //CLEAN cleans the SSID.json file
Serial.println("reading deserialized json");
File credFile = LittleFS.open(FILE_PATH, FILE_WRITE);// open file for writing
credFile.print("");
credFile.close();
}
if (readString.indexOf("WRANDOM") >= 0){ //WRANDOM writes a random string to the SSID.json file
readString = "";
Serial.println("Going to write the following input:"); //waiting for user input
while(readString.length() < 1) {
while (Serial.available()) {
delay(10); //small delay to allow input buffer to fill
if (Serial.available() > 0) {
char c = Serial.read(); //gets one byte from serial buffer
if (c == ',') {
break;
} //breaks out of capture loop to print readstring
readString += c;
} //makes the string readString
}
if (readString.length() > 0) {
Serial.println(readString); //prints string to serial port out
addCredentials(readString.c_str());
}
}
}
}
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)){
Serial.println("LittleFS Mount Failed");
return;
}
//if /ssid.json doesn't exist, create it littlefs
if(!LittleFS.exists(FILE_PATH)){
Serial.println("File doesn't exist, creating it");
File credFile = LittleFS.open(FILE_PATH, FILE_WRITE);
if(!credFile){
Serial.println("Failed to create file");
return;
}
credFile.close();
}
WiFi.mode(WIFI_MODE_STA);
WiFi.begin(ssid.c_str(), passwords.c_str());
while (WiFi.status() != WL_CONNECTED) { //Check for the connection
delay(1000);
Serial.print(".");
}
Serial.println("Connected");
}
void loop() {
// put your main code here, to run repeatedly:
readString = ""; //clears variable for new input
Serial.println("Ready for new input: ");
testjson();
}
Any improvements are still desirable.

How do I use serial.available 2 times in this code?

int input;
int choices;
void setup() {
Serial.begin(9600);
Serial.println("BIN, DEC, HEX, OCT Converter\n");
retry:
Serial.println("Kindly choose a number system to convert (1, 2, 3, 4)");
Serial.println("1. BIN\n2. DEC\n3. HEX\n4. OCT\n");
while (Serial.available() == 0) {}
choices = Serial.parseInt();
if (choices == 1) {
Serial.println("You have chosen BIN, please enter a valid input");
}
else if (choices == 2) {
Serial.println("You have chosen DEC, please enter a valid input");
}
else if (choices == 3) {
Serial.println("You have choses HEX, please enter a valid input");
}
else if (choices == 4) {
Serial.println("You have chosen OCT, please enter a valid input");
}
else {
Serial.println("Invalid choice."); \
goto retry;
}
switch (choices) {
case 1: while (Serial.available() == 0) {}
input = Serial.parseInt();
Serial.print("You have input ");
Serial.print(input);
break;
case 2: while (Serial.available() == 0) {}
input = Serial.parseInt();
Serial.print("You have input ");
Serial.print(input);
break;
}
}
void loop() {
}
In the part switch, when I run the code, it shows only 0 and it doesn't give me the option to enter at the serial monitor again.
I can't seem to figure this out, I'm a beginner who's trying to improve in the semantics of Arduino IDE. Thank you

Why does my current_user pointer reset?

current_user pointer works within functions of System.cpp, but resets afterwards. The current_user seems to be updated locally when it's set within the function createUser(string name) when it's called in the switch statement. However, when I try to call it outside the function it seems to have not been updated at all. Not entirely sure what's going on.
Main.cpp
#include <iostream>
#include "System.h"
#include "User.h"
using namespace std;
int main()
{
System s;
s.run();
// Works
User a("Test");
User b("Test 2");
User c("Test 3");
User* current = &a;
cout << "The current user is: " << current->getName() << endl;
current = &b;
cout << "Now it's: " << current->getName() << endl;
current = &c;
cout << "The final current user is: " << current->getName() << endl;
// Does not work
cout << "Current user: " << s.getCurrentUser()->getName() << endl;
}
System.h
#ifndef SYSTEM_H
#define SYSTEM_H
#include "User.h"
#include "Group.h"
#include "MessageBuffer.h"
#include "Banner.h"
#include <iostream>
#include <vector>
class System
{
public:
System();
char validInput(std::string inputIn);
bool validUsername(std::string nameIn);
bool userExists(std::string nameIn);
void createUser(std::string nameIn);
void run();
User* getCurrentUser();
private:
User* current_user;
std::vector<User> user_list;
std::vector<Group> group_list;
};
#endif // SYSTEM_H
System.cpp
// Program 1: TigerBook Social Network
// File: System.cpp
// Description: Class Implimentation of the System Class. Instantiates objects that must be initialized and handles
// basic user screw-ups (choosing and option out of bounds).
#include <iostream>
#include "System.h"
using namespace std;
// Function: Default System Constructor
// Inputs: None
// Description: Default constructor for the class.
System::System()
{
}
User* System::getCurrentUser()
{
return current_user;
}
// Function: validInput
// Inputs: string inputIn
// Outputs: char value of input at 0
// Description: Determines whether the input is valid.
char System::validInput(string inputIn)
{
if (inputIn.length() == 1)
{
return inputIn[0];
}
else
{
return '0';
}
}
// Function: validUsername
// Inputs: string username
// Outputs: true if valid, false if not
// Description: Determines whether the username is valid
bool System::validUsername(string nameIn)
{
if (nameIn.empty() || nameIn.length() < 2 || (nameIn.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") != string::npos))
{
cerr << "\n\t*** ERROR: Invalid user name, please try again! ***" << endl;
return false;
}
else
{
return true;
}
}
// Function: userExists
// Inputs: string username
// Outputs: true if exists, false if not
// Description: Determines whether the username exists in user_list.
bool System::userExists(string nameIn)
{
return false;
}
// Function: createUser
// Inputs: string username
// Outputs: void
// Description: Creates new user and adds it to user_list.
void System::createUser(string nameIn)
{
User u(nameIn);
user_list.push_back(u);
current_user = &u;
}
// Function: run
// Inputs: None
// Outputs: void
// Description: Program driver, handles basic user input and screw-ups
void System::run()
{
//current_user = NULL;
Banner banner("The TigerBook Social Network!");
cout << banner.getBanner() << endl;
bool quit = false;
string input;
while(!quit)
{
cout << "\nCreate new user (n), Broadcast (b), Multicast (m), Unicast (u), Wall page (w), Home page (h), Create new group (g), " << endl;
cout << "Join a group (j), Switch user (s), Quit (q)\n" << endl;
cout << "Choose an option: ";
getline(cin, input);
if (current_user == NULL && (input != "N" && input != "n" && input != "Q" && input != "q"))
{
cerr << "\n\t*** ERROR: There is no current user, please create a new user! ***" << endl;
continue;
}
switch (validInput(input))
{
// Create new user
case 'N':
case 'n':
{
string username;
cout << "\nPlease enter user name: ";
getline(cin, username);
if (!validUsername(username))
{
continue;
}
else if (userExists(username))
{
cerr << "\n\t*** ERROR: The user \"" + username + "\" already exists, please try again! ***" << endl;
continue;
}
else
{
createUser(username);
cout << "\nCurrent user: " << getCurrentUser()->getName() << endl; // test current_user
}
break;
}
case 'B':
case 'b':
{
break;
}
case 'M':
case 'm':
{
break;
}
case 'U':
case 'u':
{
break;
}
case 'W':
case 'w':
{
break;
}
case 'H':
case 'h':
{
break;
}
case 'G':
case 'g':
{
break;
}
case 'J':
case 'j':
{
break;
}
case 'S':
case 's':
{
break;
}
case 'Q':
case 'q':
{
quit = true;
banner.setBanner("Thank you for using TigerBook Social Network!");
cout << banner.getBanner() << endl << endl;
break;
}
default:
{
cerr << "\n\t*** ERROR: Invalid input, please try again! ***" << endl;
}
} // End of switch statement
} // End of loop
}
User.h
#ifndef USER_H
#define USER_H
#include <string>
class User
{
public:
User();
User(std::string nameIn);
std::string getName();
void setName(std::string nameIn);
private:
std::string name;
};
#endif // USER_H
User.cpp
// Program 1: TigerBook Social Network
// File: User.cpp
// Description: Class implementation for User class
#include "User.h"
using namespace std;
// Constructor (Default)
User::User()
{
//ctor
}
// Constructor
// Inputs: string that sets name
// Description: Constructs user object and assigns its name.
User::User(string nameIn)
{
name = nameIn;
}
// Function: setName
// Inputs: string that sets name
// Outputs: void
// Description: Sets name of user object.
void User::setName(string nameIn)
{
name = nameIn;
}
// Function: getName
// Inputs: none
// Outputs: Name of user
// Description: Returns the name of the user object.
string User::getName()
{
return name;
}
You have:
void System::createUser(string nameIn)
{
User u(nameIn);
user_list.push_back(u);
current_user = &u;
}
Here, you are storing pointer to a local variable. The pointer becomes a dangling pointer as soon as the function returns. Any use of current_usr to access the object after the function returns is cause for undefined behavior.
You can use:
void System::createUser(string nameIn)
{
User u(nameIn);
user_list.push_back(u);
current_user = &(user_list.back());
}
Even that is very fragile. It will work if you carefully manage the objects in user_list.
A better option will be not to store a pointer to the object at all. You can, for example, use:
User* System::getCurrentUser()
{
if ( user_list.empty() )
{
return nullptr;
}
return &(user_list.back());
}

Segfault submitting query

I'm testing a wrapper library for the MySQL C API, and I'm trying to insert a row.
I've tested it in GDB, and the line (in my code) that faults appears as follows:
Breakpoint 1, cq_query (con=0x7fffffffe1c0,
query=0x6014a0 "INSERT INTO TaskType(state,parentID,displayName) VALUES(1,19,'boop')") at cqstatic.c:32
32 return mysql_query(con->con, query);
This query string is formatted correctly (I can paste it into the mysql command line, and it runs fine), so it would appear that something is wrong in the connection (con->con is of type void * cast to MYSQL *).
My other functions which do SELECT and UPDATE work fine. Only insert appears to be broken.
Here is my Test Code
#include <stdio.h>
#include <string.h>
#include <cquel.h>
int main(void)
{
struct dbconn con = cq_new_connection("myurl.tld",
"myuser", "mypasswd", "mydb");
cq_init(1024, 128);
char *fields[] = {
"state",
"parentID",
"displayName"
};
char *vals[] = {
"1",
"19",
"boop"
};
struct drow *row = cq_new_drow(3);
cq_drow_set(row, vals);
struct dlist *list = cq_new_dlist(3, fields, "");
cq_dlist_add(list, row);
cq_insert(con, "TaskType", list);
cq_free_dlist(list);
return 0;
}
Insert Function
int cq_insert(struct dbconn con, const char *table, const struct dlist *list)
{
int rc;
char *query, *columns, *values;
const char *fmt = "INSERT INTO %s(%s) VALUES(%s)";
if (table == NULL)
return 1;
if (list == NULL)
return 2;
query = calloc(CQ_QLEN, sizeof(char));
if (query == NULL)
return -1;
columns = calloc(CQ_QLEN/2, sizeof(char));
if (columns == NULL) {
free(query);
return -2;
}
values = calloc(CQ_QLEN/2, sizeof(char));
if (values == NULL) {
free(query);
free(columns);
return -3;
}
rc = cq_dlist_fields_to_utf8(&con, columns, CQ_QLEN/2, *list);
if (rc) {
free(query);
free(columns);
free(values);
return 100;
}
rc = cq_connect(&con);
if (rc) {
free(query);
free(columns);
free(values);
return 200;
}
for (struct drow *r = list->first; r != NULL; r = r->next) {
rc = cq_drow_to_utf8(&con, values, CQ_QLEN/2, *r);
if (rc)
break;
rc = snprintf(query, CQ_QLEN, fmt, table, columns, values);
if (CQ_QLEN <= (size_t) rc) {
rc = -4;
break;
}
rc = cq_query(&con, query);
if (rc) {
rc = 201;
break;
}
}
cq_close_connection(&con);
free(query);
free(columns);
free(values);
return rc;
}
And one of the important helper functions
int cq_fields_to_utf8(struct dbconn *con, char *buf, size_t buflen,
size_t fieldc, char * const *fieldnames, bool usequotes)
{
int rc = 0;
size_t num_left = fieldc, written = 0;
if (num_left == 0)
return 1;
char *temp = calloc(CQ_FMAXLEN+3, sizeof(char));
if (NULL == temp)
return -1;
char *field = calloc((CQ_FMAXLEN+3)*2+1, sizeof(char));
if (NULL == field) {
free(temp);
return -2;
}
/* prevent appending to buffer */
buf[0] = '\0';
cq_connect(con);
for (size_t i = 0; i < fieldc; ++i) {
bool escaped = fieldnames[i][0] == '\\';
const char *orig = escaped ? &fieldnames[i][1] : fieldnames[i];
const char *value;
bool isstr = false;
if (!escaped) {
mysql_real_escape_string(con->con, field, orig, strlen(orig));
value = field;
if (usequotes)
for (size_t j = 0; j < strlen(value); ++j) {
if (!isdigit(value[j])) {
isstr = true;
break;
}
}
} else {
value = orig;
}
const char *a = isstr ? "'" : "";
const char *c = --num_left > 0 ? "," : "";
written += snprintf(temp, CQ_FMAXLEN+3, "%s%s%s%s", a, value, a, c);
if (written >= buflen) {
rc = 2;
break;
}
strcat(buf, temp);
}
cq_close_connection(con);
free(field);
free(temp);
return rc;
}
used when setting up the query string.
Found my own issue. Blame the 10 minute rule.
I didn't look closely enough at cq_insert(), and it is making a double connection, closing the second one, leaving the first one lost, and the pointer pointing to freed memory, so segfault.