Below is my functions, its suppose to list some countries
I tried using << fixed but doesn't seems to help
This is my output:
�ѿra
�ѿd Arab Emirates
�ѿnistan
�ѿua and Barbuda
�ѿlla
�ѿia
�ѿia
�ѿrlands Antilles
�ѿa
�ѿctica
�ѿtina
�ѿcan Samoa
My text file is okay, i wonder is there wrong in my casting of data.
This is my CountryData.cpp file
#include <iostream>
#include <string.h>
#include "CountryData.h"
using namespace std;
// ====================================================================
void readData ()
{
FILE * pFile;
NoOfRecordsRead = 0;
char buffer [Line_Char_Buffer_Size];
pFile = fopen (INPUT_FILE_NAME , "r");
if (pFile == NULL)
perror ("Error opening file 'Countries.txt' !");
else
{
while ( !feof (pFile) )
{
char* aLine = get_line (buffer, Line_Char_Buffer_Size, pFile);
if (aLine != NULL)
{
// printf ("%d] aLine => %s\n", NoOfRecordsRead, aLine);
globalCountryDataArray [NoOfRecordsRead++] = createCountryRecord (aLine);
}
}
fclose (pFile);
}
}
// ====================================================================
char* get_line (char *s, size_t n, FILE *f)
{
char *p = fgets (s, n, f);
if (p != NULL)
{
size_t last = strlen (s) - 1;
if (s[last] == '\n')
s[last] = '\0';
}
return p;
}
// ====================================================================
CountryRecordType createCountryRecord (char* aLine)
{
CountryRecordType ctryRec;
char* pch = strtok (aLine, LINE_DATA_DELIMITER);
// 1) Retrieve TLD
strcpy (ctryRec.TLD, pch);
pch = strtok (NULL, LINE_DATA_DELIMITER);
// 2) Retrieve Country
strcpy (ctryRec.Country, pch);
pch = strtok (NULL, LINE_DATA_DELIMITER);
// 3) Retrieve FIPS104
strcpy (ctryRec.FIPS104, pch);
pch = strtok (NULL, LINE_DATA_DELIMITER);
// 4) Retrieve ISO2
strcpy (ctryRec.ISO2, pch);
pch = strtok (NULL, LINE_DATA_DELIMITER);
// 5) Retrieve ISO3
strcpy (ctryRec.ISO3, pch);
pch = strtok (NULL, LINE_DATA_DELIMITER);
// 6) Retrieve ISONo
ctryRec.ISONo = atof (pch);
pch = strtok (NULL, LINE_DATA_DELIMITER);
// 7) Retrieve Capital
strcpy (ctryRec.Capital, pch);
pch = strtok (NULL, LINE_DATA_DELIMITER);
// 8) Retrieve Region
strcpy (ctryRec.Region, pch);
pch = strtok (NULL, LINE_DATA_DELIMITER);
// 9) Retrieve Currency
strcpy (ctryRec.Currency, pch);
pch = strtok (NULL, LINE_DATA_DELIMITER);
// 10) Retrieve CurrencyCode
strcpy (ctryRec.CurrencyCode, pch);
pch = strtok (NULL, LINE_DATA_DELIMITER);
// 11) Retrieve Population
ctryRec.Population = atof (pch);
return (ctryRec);
}
// ====================================================================
char* displayRecordContent (CountryRecordType ctryRec)
{
char * output = ctryRec.Country;
return output;
}
// ====================================================================
void showAllRecords ()
{
int i=0;
string stroutput;
char * result;
for (i=0; i<NoOfRecordsRead; i++)
{
result = displayRecordContent (globalCountryDataArray [i]);
stroutput += result;
stroutput += "\n";
}
cout << fixed << stroutput << endl;
}
// ====================================================================
int findCountryRecord (const char* countryName)
{
int idx = -1;
int found = 0;
while (!found && (++idx < Max_Record_Size))
if (strcmp (globalCountryDataArray [idx].Country, countryName) == 0)
found = 1;
if (found)
return (idx);
else
return (-1);
}
// ====================================================================
char* getCapital (const char* countryName)
{
int idx = findCountryRecord (countryName);
if (idx < 0)
{
printf ("Country '%s' not found!\n", countryName);
return (NULL);
}
else
return (globalCountryDataArray [idx].Capital);
}
// ====================================================================
char* getCurrencyCode (const char* countryName)
{
int idx = findCountryRecord (countryName);
if (idx < 0)
{
printf ("Country '%s' not found!\n", countryName);
return (NULL);
}
else
return (globalCountryDataArray [idx].CurrencyCode);
}
// ====================================================================
main ()
{
readData ();
showAllRecords ();
}
This is my CountryData.h file
#ifndef COUNTRY_DATA_H
#define COUNTRY_DATA_H
// ====================================================================
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
// ====================================================================
#define TLD_LEN 2
#define COUNTRY_LEN 100
#define FIPS104_LEN 2
#define ISO2_LEN 2
#define ISO3_LEN 3
#define CAPITAL_LEN 100
#define REGION_LEN 100
#define CURRENCY_LEN 50
#define CURRENCY_CODE_LEN 3
#define No_Of_Rec_Fields 11
#define Max_Record_Size 250
#define Line_Char_Buffer_Size 400
#define LINE_DATA_DELIMITER ","
#define INPUT_FILE_NAME "Countries.txt"
// ====================================================================
//const char* LINE_DATA_DELIMITER = ",";
//const char* INPUT_FILE_NAME = "Countries.txt";
typedef struct CountryRecord
{
char TLD [TLD_LEN+1]; // Top Level Domain code
char Country [COUNTRY_LEN+1];
char FIPS104 [FIPS104_LEN+1]; // Ctry code according to FIPS104 standard
char ISO2 [ISO2_LEN+1]; // Ctry code according to ISO2 standard
char ISO3 [ISO3_LEN+1]; // Ctry code according to ISO3 standard
double ISONo;
char Capital [CAPITAL_LEN+1];
char Region [REGION_LEN+1]; // E.g. Asia, Europe, etc.
char Currency [CURRENCY_LEN+1]; // Full name of currency
char CurrencyCode [CURRENCY_CODE_LEN+1]; // Currency abbreviation
double Population;
} CountryRecordType;
int NoOfRecordsRead;
CountryRecordType globalCountryDataArray [Max_Record_Size];
// ====================================================================
void readData ();
char* get_line (char *s, size_t n, FILE *f);
CountryRecordType createCountryRecord (char* aLine);
char* displayRecordContent (CountryRecordType ctryRec);
void showAllRecords ();
int findCountryRecord (const char* countryName);
char* getCapital (const char* countryName);
char* getCurrencyCode (const char* countryName);
// ====================================================================
#endif // COUNTRY_DATA_H
This is part of my countries.txt file
AD,Andorra,AN,AD,AND,20.00,Andorra la Vella,Europe,Euro,EUR,67627.00
AE,United Arab Emirates,AE,AE,ARE,784.00,Abu Dhabi,Middle East,UAE Dirham,AED,2407460.00
AF,Afghanistan,AF,AF,AFG,4.00,Kabul,Asia,Afghani,AFA,26813057.00
AG,Antigua and Barbuda,AC,AG,ATG,28.00,Saint John's,Central America and the Caribbean,East Caribbean Dollar,XCD,66970.00
AI,Anguilla,AV,AI,AIA,660.00,The Valley,Central America and the Caribbean,East Caribbean Dollar,XCD,12132.00
AL,Albania,AL,AL,ALB,8.00,Tirana,Europe,Lek,ALL,3510484.00
AM,Armenia,AM,AM,ARM,51.00,Yerevan,Commonwealth of Independent States,Armenian Dram,AMD,3336100.00
AN,Netherlands Antilles,NT,AN,ANT,530.00,Willemstad,Central America and the Caribbean,Netherlands Antillean guilder,ANG,212226.00
AO,Angola,AO,AO,AGO,24.00,Luanda,Africa,Kwanza,AOA,10366031.00
AQ,Antarctica,AY,AQ,ATA,10.00,--,Antarctic Region, , ,0.00
AR,Argentina,AR,AR,ARG,32.00,Buenos Aires,South America,Argentine Peso,ARS,37384816.00
AS,American Samoa,AQ,AS,ASM,16.00,Pago Pago,Oceania,US Dollar,USD,67084.00
AT,Austria,AU,AT,AUT,40.00,Vienna,Europe,Euro,EUR,8150835.00
AU,Australia,AS,AU,AUS,36.00,Canberra,Oceania,Australian dollar,AUD,19357594.00
Since you are passing CountryRecordType by value to displayRecordContent, the pointer you are returning is invalid, and using it results in undefined behavior. This is because you are returning a pointer to the contents held by the copy that was made during the function call, which gets destructed when the function returns.
Modify your displayRecordContent function to use pass by reference instead.
const char* displayRecordContent (const CountryRecordType &ctryRec)
{
const char * output = ctryRec.Country;
return output;
}
I'd rewrite the code pretty much from the ground up, attempting to actually use C++ instead of C with a few bits of C++ mixed in.
#include <vector>
#include <string>
#include <iostream>
#include <iterator>
#include <fstream>
struct CountryRecord {
std::string TLD;
std::string Country;
std::string FIPS104;
std::string ISO2;
std::string ISO3;
double ISONo;
std::string Capital;
std::string Region;
std::string Currency;
std::string CurrencyCode;
double Population;
};
template <class T>
std::istream &get_field(std::istream &is, T &field) {
is >> field;
is.ignore(1);
return is;
}
template<>
std::istream &get_field<std::string>(std::istream &is, std::string &field) {
return std::getline(is, field, ',');
}
std::istream &operator>>(std::istream &is, CountryRecord &r) {
get_field(is, r.TLD);
get_field(is, r.Country);
get_field(is, r.FIPS104);
get_field(is, r.ISO2);
get_field(is, r.ISO3);
get_field(is, r.ISONo);
get_field(is, r.Capital);
get_field(is, r.Region);
get_field(is, r.Currency);
get_field(is, r.CurrencyCode);
return get_field(is, r.Population);
}
std::ostream &operator<<(std::ostream &os, CountryRecord const &r) {
return os << r.Country;
}
int main() {
std::ifstream in("countries.txt");
std::vector<CountryRecord> countries(
(std::istream_iterator<CountryRecord>(in)),
std::istream_iterator<CountryRecord>());
std::copy(countries.begin(), countries.end(),
std::ostream_iterator<CountryRecord>(std::cout, "\n"));
return 0;
}
Related
I am trying to make a program that reads a string from a file in SPIFFS with 4 tab-separated things and then processes it into four char arrays to be used in another function. However, I get the error cannot convert 'char*' to 'char**' in assignment. Is there any idea why? Here's my code:
#include <string.h>
#include "FS.h"
#include "AdafruitIO_WiFi.h"
char *ssid;
char *pass;
char *aiduser;
char *aidkey;
// comment out the following two lines if you are using fona or ethernet
#include "AdafruitIO_WiFi.h"
//AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS);
void setupWifi(char* *aiduser, char* *aidkey, char* *ssid, char* *pass){
#define WIFIFILE "/config.txt"
int addr = 0;
bool spiffsActive = false;
if (SPIFFS.begin()) {
spiffsActive = true;
}
File f = SPIFFS.open(WIFIFILE, "r");
String str;
while (f.position()<f.size())
{
str=f.readStringUntil('\n');
str.trim();
}
// Length (with one extra character for the null terminator)
int str_len = str.length() + 1;
// Prepare the character array (the buffer)
char char_array[str_len];
// Copy it over
str.toCharArray(char_array, str_len);
const char s[2] = {9, 0};
/* get the first token */
aiduser = strtok(char_array, s);
aidpass = strtok(NULL, s);
ssid = strtok(NULL, s);
pass = strtok(NULL, s);
/* walk through other tokens
while( token != NULL ) {
printf( " %s\n", token );
token = strtok(NULL, s);
}*/
// RESULT: A thingy
}
void setup(){
setupWifi(&aiduser, &aidkey, &ssid, &pass);
AdafruitIO_WiFi io(aiduser, aidkey, ssid, pass);}
Also, I can't run the setupWifi function unless it is in setup or loop, but I can't make it in another setup because this is #included into another main file.
You get this error because of this:
void setupWifi(char* *aiduser, char* *aidkey, char* *ssid, char* *pass)
{
...
aiduser = strtok(char_array, s);
aidpass = strtok(NULL, s);
ssid = strtok(NULL, s);
pass = strtok(NULL, s);
}
This variables are double pointers, strtok returns a pointer to char, those
are not compatible types.
Because strtok returns char_array + some_offset and char_array is a local
variable in setupWifi, you need to do a copy for each of them and return the
copy instead. You can do it with strdup.
*aiduser = strdup(strtok(char_array, s));
*aidpass = strdup(strtok(NULL, s));
*ssid = strdup(strtok(NULL, s));
*pass = strdup(strtok(NULL, s));
I encourage you to always check the return value of strdup, because it can
return NULL.1
If your system does not have strdup, then you can write your own:
char *strdup(const char *text)
{
if(text == NULL)
return NULL;
char *copy = calloc(strlen(text) + 1, 1);
if(copy == NULL)
return NULL;
return strcpy(copy, text);
}
One last thing:
void setupWifi(char* *aiduser, char* *aidkey, char* *ssid, char* *pass);
It looks really awkward, never seen declaring double pointer this way. Much
easier to read would be
void setupWifi(char **aiduser, char **aidkey, char **ssid, char **pass);
Fotenotes
1While the syntax is correct, I still consider this bad practice,
because you should always check the return values of functions that return
pointers. If they return NULL, you cannot access the memory. This adds a
little bit of more code, but your program will not die of segfaults and it can
recover from the errors.
I'd also change your function to return 1 on success, 0 otherwise:
int parse_and_set(char *txt, const char *delim, char **var)
{
if(delim == NULL || var == NULL)
return 0;
char *token = strtok(txt, delim);
if(token == NULL)
return 0;
token = strdup(token);
if(token == NULL)
return NULL;
*var = token;
return 1;
}
void init_parse(char ***vars, size_t len)
{
for(size_t i = 0; i < len; ++i)
**(vars + i) = NULL;
}
int cleanup_parse(char ***vars, size_t len, int retval)
{
for(size_t i = 0; i < len; ++i)
{
free(**(vars + i));
**(vars + i) = NULL;
}
}
int setupWifi(char **aiduser, char **aidkey, char **ssid, char **pass)
{
if(aiduser == NULL || aidkey == NULL || ssid == NULL || pass == NULL)
return 0;
...
/* get the token token */
char **vars[] = { aiduser, aidkey, ssid, pass };
size_t len = sizeof vars / sizeof *vars;
init_parse(vars, len);
if(parse_and_set(char_array, s, aiduser) == 0)
return cleanup_parse(vars, len, 0);
if(parse_and_set(NULL, s, aidpass) == 0)
return cleanup_parse(vars, len, 0);
if(parse_and_set(NULL, s, ssid) == 0)
return cleanup_parse(vars, len, 0);
if(parse_and_set(NULL, s, pass) == 0)
return cleanup_parse(vars, len, 0);
...
return 1;
}
For example the file I am parsing contains unicode char u201d ie. ” (accented quote)
How do I replace it with " (Straight quote)?
using c++ and STL i would use a code like this, you still need to save to output buffer to file.. tested on linux.
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
// load file data
char* load_file(const char *filename)
{
FILE *fp;
fp = fopen(filename, "r");
if (!fp)
return NULL;
size_t size;
if ((0 != fseek(fp, 0, SEEK_END)) || (-1 == (size = ftell(fp))))
size = 0;
// set fp at file start
fseek(fp, 0, 0);
char *buffer;
buffer = (char*) malloc(size);
if(!buffer)
{
fclose (fp);
return NULL;
}
if(size != fread(buffer, 1, size, fp))
{
free (buffer);
buffer = NULL;
}
fclose (fp);
return buffer;
}
// replace string
std::string replace(const std::string& str, const std::string& from, const std::string& to)
{
if(str.size() < 1)
return str;
std::string temp_str(str);
size_t start_pos = 0;
while((start_pos = temp_str.find(from, start_pos)) != std::string::npos)
{
temp_str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
return temp_str.c_str();
}
int main(int argc, char** argv)
{
const char* file_name = "test.txt";
char* file_bytes = load_file(file_name);
if(file_bytes == nullptr)
return EXIT_FAILURE;
std::cout << replace(file_bytes, "”", "\"") << std::endl;
return EXIT_SUCCESS;
}
Why does strcat gives me its version of str1? As far as I know there has to be & thing before paramatater in function prototype and implementation if you want to get it editted, but I don't see it here.
char *strcat( char *str1, const char *str2 );
How do I edit this function so that it would only return new string but leave out the ones I give it?
My try
char *strApp(char *dest, const char *src)
{
size_t i,j;
size_t k = 0;
for (i = 0; dest[i] != '\0'; i++);
char rdest[100];
do {
rdest[k] = dest[k];
} while(++k<=i);
for (j = 0; src[j] != '\0'; j++)
rdest[i+j] = src[j];
rdest[i+j] = '\0';
return rdest;
}
It damages second string. Could anyone give me safe and correct version? Thanks in advance.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *strApp(const char *s1, const char *s2)
{
char *pointer = malloc(strlen(s1) + strlen(s2) + 1);
if (pointer == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
return strcat(strcpy(pointer, s1), s2);
}
int main()
{
char *s1 = "original";
char *s2 = " modified";
char *s3 = strApp(s1, s2);
printf("%s\n", s1);
printf("%s\n", s2);
printf("%s\n", s3);
free(s3);
return 0;
}
Just trying to point out you don't need to completely rewrite strcat() to get what you want.
strcat is, by definition, altering the target. If you don't want to, you should make a copy yourself in a target memory location you allocate yourself.
You've tagged your question with both C and C++. I'm providing a C solution. Adjustments may be needed for C++.
#include <stdlib.h>
#include <string.h>
char* strdupcat(const char* s1, const char* s2) {
size_t s1_len = strlen(s1);
size_t s2_len = strlen(s2);
char* s = malloc(s1_len + s2_len + 1);
if (s == NULL)
return NULL;
{
char* s_end = s;
s_end = mempcpy(s_end, s1, s1_len);
s_end = mempcpy(s_end, s2, s2_len);
*s_end = '\0';
}
return s;
}
Example usage:
#include <stdio.h>
int main() {
char* s = strdupcat("abc", "def");
if (s == NULL) {
perror("Can't concatenate");
return EXIT_FAILURE;
}
puts(s);
free(s);
return EXIT_SUCCESS;
}
This function is used similarly to strdup.
DESCRIPTION
The strdupcat() function returns a pointer to a new string which is a duplicate of the string s1 with a duplicate of string s2 appended. Memory for the new string is obtained with malloc(3), and can be freed with free(3).
RETURN VALUE
The strdupcat() function returns a pointer to the duplicated string, or NULL if insufficient memory was available.
ERRORS
ENOMEM Insufficient memory available to allocate the new string.
You can use strerror or perror to obtain an error message when strdupcat() returns NULL.
Here's a version that accepts an arbitrary number of arguments:
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
char* strdupcatmany(int dummy, ...) {
#define strdupcatmany(...) strdupcatmany(0, __VA_ARGS__, NULL)
size_t len = 0;
char* s;
char* s_dst;
const char* s_src;
va_list ap;
va_start(ap, dummy);
while (1) {
s_src = va_arg(ap, const char*);
if (s_src == NULL)
break;
len += strlen(s_src);
}
va_end(ap);
s = malloc(len + 1);
if (s == NULL)
return NULL;
s_dst = s;
va_start(ap, dummy);
while (1) {
s_src = va_arg(ap, const char*);
if (s_src == NULL)
break;
s_dst = stpcpy(s_dst, s_src);
}
va_end(ap);
*s_dst = '\0';
return s;
}
For example,
#include <stdio.h>
int main() {
char* s = strdupcatmany("abc", "def", "ghi");
if (s == NULL) {
perror("Can't concatenate");
return EXIT_FAILURE;
}
puts(s);
free(s);
return EXIT_SUCCESS;
}
Note: I don't know how portable __VA_ARGS__ args is.
I'm using "readline" library to create a console interface for my program. I'm able to autocomplete words using tab, but when I have words that share the same prefix like (car, card, carbon) it always chooses the shortest one. Here's my program (mostly taken from link):
#include <readline/readline.h>
#include <readline/history.h>
#include <stdlib.h>
#include <iostream>
const char *words[] = {"add", "remove", "rm", "update", "child", "children", "wife", "wifes"};
void *xmalloc (int size)
{
void *buf;
buf = malloc (size);
if (!buf)
{
fprintf (stderr, "Error: Out of memory. Exiting.\n");
exit (1);
}
return buf;
}
char *dupstr (const char *str)
{
char *temp;
temp = (char *) xmalloc (strlen (str) + 1);
strcpy (temp, str);
return (temp);
}
char *my_generator (const char *text, int state)
{
static int list_index, len;
const char *name;
if (!state)
{
list_index = 0;
len = strlen (text);
}
while (name = words[list_index])
{
list_index++;
if (strncmp (name, text, len) == 0) return dupstr (name);
}
// If no names matched, then return NULL.
return ((char *) NULL);
}
static char **my_completion (const char *text, int start, int end)
{
char **matches = (char **) NULL;
if (start == 0)
{
matches = rl_completion_matches ((char *) text, &my_generator);
}
else rl_bind_key ('\t', rl_abort);
return matches;
}
int main (int argc, char *argv[])
{
char *buf;
rl_attempted_completion_function = my_completion;
while ((buf = readline(">> ")) != NULL)
{
rl_bind_key ('\t', rl_complete);
if (strcmp (buf, "exit") == 0) break;
else if (buf[0] == '\0') continue;
else
{
std::cout << buf << std::endl;
add_history (buf);
}
}
free (buf);
return 0;
}
Is it possible to list all matches on double tab just like in ubuntu terminal?
I managed to get it to work by commenting out these two lines:
rl_bind_key ('\t', rl_complete);
and:
else rl_bind_key ('\t', rl_abort);
The default completion behaviour of readline works exactly like in ubuntu terminal, one tab to complete and two tabs to list possible completions. Not sure though what's the default completion function that's binded with the tab key, from the documentation i thought it was rl_possible_completions but it didn't give the same results.
Also i added the following line to my_completion function to prevent adding space at the end of the matched word:
rl_completion_append_character = '\0';
I removed dupstrfunction it and replaced it with the native strdup function instead (this has nothing to do with the auto complete problem, it's just to remove unnecessary code).
This is the final code:
#include <readline/readline.h>
#include <readline/history.h>
#include <stdlib.h>
#include <iostream>
const char *words[] = {"add", "remove", "rm", "update", "child", "children", "wife", "wives"};
// Generator function for word completion.
char *my_generator (const char *text, int state)
{
static int list_index, len;
const char *name;
if (!state)
{
list_index = 0;
len = strlen (text);
}
while (name = words[list_index])
{
list_index++;
if (strncmp (name, text, len) == 0) return strdup (name);
}
// If no names matched, then return NULL.
return ((char *) NULL);
}
// Custom completion function
static char **my_completion (const char *text, int start, int end)
{
// This prevents appending space to the end of the matching word
rl_completion_append_character = '\0';
char **matches = (char **) NULL;
if (start == 0)
{
matches = rl_completion_matches ((char *) text, &my_generator);
}
// else rl_bind_key ('\t', rl_abort);
return matches;
}
int main (int argc, char *argv[])
{
char *buf;
rl_attempted_completion_function = my_completion;
while ((buf = readline(">> ")) != NULL)
{
// rl_bind_key ('\t', rl_complete);
if (strcmp (buf, "exit") == 0) break;
else if (buf[0] == '\0')
{
free (buf);
continue;
}
else
{
std::cout << buf << std::endl;
add_history (buf);
}
free (buf);
buf = NULL;
}
if (buf != NULL) free (buf);
return 0;
}
The answer by razzak is almost correct, but this NULL must be added at the end of array of strings:
const char *words[] = {"add", "remove", "rm", "update", "child", "children", "wife", "wives", NULL};
Some changes for nonwarning compilation in my_generator() function:
while ((name = words[list_index++]))
{
if (strncmp (name, text, len) == 0) return strdup (name);
}
I was making the server in c++ to connect to WebSocket but somehow, it was not connecting with the websocket. The WebSocket is showing the connection is closed and there is also some problem in the c++ server, as on second call to the server from WebSocket, it is showing the following error - double free or corruption (out). I have spend a lot of time on it. Here is the code:
c++
#include<iostream>
#include <string>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
using namespace std;
string getConnectionKey(char*);
void acceptConnection(int, const char*);
void readConnection(int);
void bail(char*);
string executeShellCommand(const string&);
string getBase64Encoded(string);
char *getClientKey(char*);
string getSHA1Hash(string);
int main()
{
char srvr_adr[] = "127.0.0.1";
char srvr_port[] = "9099";
struct sockaddr_in adr_srvr;
struct sockaddr_in adr_clnt;
socklen_t len_inet;
int s; // Server Socket
int c; // Client Socket
int z;
char *data;
char readdata[256];
int count = 2;
data = (char*)malloc(sizeof(char)*128);
s = socket(PF_INET, SOCK_STREAM, 0);
if(s == -1)
bail("socket()");
memset(&adr_srvr,0,sizeof(adr_srvr));
adr_srvr.sin_family = AF_INET;
adr_srvr.sin_port = htons(atoi(srvr_port));
if( strcmp(srvr_adr,"*")!=0)
{
adr_srvr.sin_addr.s_addr = inet_addr(srvr_adr);
if(adr_srvr.sin_addr.s_addr == INADDR_NONE)
bail(" INVALID ADRESS \n");
}
else /* WILD ADDRESS*/
adr_srvr.sin_addr.s_addr = INADDR_ANY;
len_inet = sizeof adr_srvr;
z = bind(s,(struct sockaddr*)&adr_srvr, len_inet);
if(z==-1)
bail("bind(2)");
z = listen(s,10);
if(z==-1)
bail("listen(2)");
for(;;)
{
len_inet = sizeof(adr_clnt);
c = accept(s, (struct sockaddr*)&adr_clnt,&len_inet);
if(c==-1)
bail("accept(2)");
readConnection(c);
close(c);
}
return 0;
}
void readConnection(int c)
{
int z;
char readdata[256];
// READING
z = read(c,readdata, sizeof(readdata)-1);
if(z==-1)
bail("read(2)");
else if(strlen(readdata)>0)
printf(" READ \n%s\n", readdata);
string key = getConnectionKey(readdata);
cout<<" KEY "<<key<<endl;
acceptConnection(c, key.c_str());
}
void acceptConnection(int c, const char *key)
{
int z;
char response[] = "HTTP/1.1 101 Switching Protocols\nUpgrade: websocket\nConnection: Upgrade\nSec-WebSocket-Accept: ";
char *output;
output = (char*)malloc( sizeof(char) * ( strlen(key) + strlen(response) + 1) );
strcat(output, response);
strcat(output, key);
cout<<" output "<<output<<endl;
// WRITING
z = write(c, output, strlen(output));
if(z == -1)
bail("write(2)");
printf(" Connection Done \n");
}
string getConnectionKey(char *str)
{
char *start,*end,*key;
int len;
string s("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
// GET CLIENT KEY
key = getClientKey(str);
// Appending the key
s = key + s;
// SHA1 HASH
string out = getSHA1Hash(s);
//hashwrapper *h = new sha1wrapper();
//string out = h->getHashFromString(s);
// BASE 64 ENCODING
string encoded = getBase64Encoded(out);
//encoded = (char*)malloc(sizeof(char)*256);
//strcpy(encoded, getBase64Encoded(out) );
free(key);
//delete h;
return encoded;
}
char *getClientKey(char *str)
{
int len;
char *start,*end,*key;
start = strstr(str, "Sec-WebSocket-Key:");
if(start == NULL)
return false;
start += 17;
end = strstr(start, "==");
if(end == NULL)
return false;
end++;
while( !(*start>=65 && *start<=90 || *start >= 97 && *start<=122 || *start>=48 && *start<=57 || *start == '+' || *start=='/') )
start++;
len = end - start + 1;
key = (char*) malloc( sizeof(char) * (len+1) );
strncpy(key,start,len);
return key;
}
string getBase64Encoded(string s)
{
int len;
string str="";
len = s.length();
char *command;
for(int i=len-1 ; i>=1; i=i-2)
{
str = s.substr(i-1,2) + str;
str = "\\x" + str;
}
if(len%2==1)
{
str = s[0] + str;
str = "\\x" + str;
}
// making the command to be send to shell
str = "printf \"" + str ;
str = str + "\" | base64";
cout<<endl<<" STRING "<<str<<endl;
return executeShellCommand(str);
}
string getSHA1Hash(string str)
{
int len ;
string output;
str = "printf \""+str;
str = str +"\" | sha1sum";
cout<<str<<endl;
output = executeShellCommand(str);
return output.substr(0,output.length()-4);;
}
string executeShellCommand(const string& cmd)
{
FILE *fpipe;
if ( !(fpipe = (FILE*)popen(cmd.c_str(),"r")) )
{ // If fpipe is NULL
perror("Problems with pipe");
exit(1);
}
char buf[256] = "";
string line="";
while ( fgets( buf, sizeof buf, fpipe) )
{
if(strlen(buf)>0)
line.append(buf);
memset(buf, 0, sizeof(buf));
}
// CLOSE THE PIPE
pclose(fpipe);
return line;
}
void bail(char *on_what)
{
if(errno!=0)
{
fputs( strerror(errno), stderr);
fputs( ":", stderr);
}
fputs( on_what, stderr);
fputs("\n",stderr);
}
Here is the Websocket Code:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Chat</title>
</head>
<body>
<h1>WebSocket Chat</h1>
<section id="content"></section>
<input id="message" type="text" tabindex="1"/>
<textarea id="show">
</textarea>
<script src="http://www.google.com/jsapi"></script>
<script>google.load("jquery", "1.3")</script>
<script src="http://jquery-json.googlecode.com/files/jquery.json-2.2.min.js"></script>
<!--script src="http://jquery-websocket.googlecode.com/files/jquery.websocket-0.0.1.js"></script-->
<script src="/js/jquery.websocket-0.0.1.js"></script>
<script type="text/javascript">
/*var ws = $.websocket("ws://127.0.0.1:9099/",
{
events: {
message: function(e)
{
alert("e.data");
$('#content').append(e.data + '<br>')
}
}
});*/
var websocketConnection = new WebSocket("ws://127.0.0.1:9099/");
websocketConnection.onopen = function(ev)
{
showmsg('Connected to the echo service');
};
websocketConnection.onerror = function(ev)
{
showmsg(" ERROR : ".ev.data);
}
websocketConnection.onclose = function(ev)
{
showmsg(" Connection Closed");
};
websocketConnection.onmessage = function(event)
{
showmsg(event.data);
$('#content').append(event.data+"<br>");
};
showmsg(" CURRENT STATE "+websocketConnection.readyState);
if(!websocketConnection)
showmsg(" object null ");
websocketConnection.send("Hello Echo Server");
$('#message').change(function(){
flag = ws.send('message', this.value);
if(!flag)
alert("not send");
this.value = '';
});
function showmsg(content)
{
$('#show').val(content+"<br>");
}
</script>
</body>
</html>
Please Help me out , what is the problem in the C++ and what is the response to be sent to the WebScoket.
This is one problem (in executeShellCommand() function):
command = (char*) malloc( sizeof(char) * cmd.length() );
line = (char*)malloc(sizeof(char)*256);
line[0] = '\0';
//cout<<" COMMAND "<<cmd<<endl;
strcpy(command, cmd.c_str() ); // Writes one beyond the end of the
// 'command' buffer as no space allocated
// for null terminator
You could just pass the cmd.c_str() directly to popen() instead of allocating and populating the command buffer for that purpose:
if ( !(fpipe = (FILE*)popen(cmd.c_str(),"r")) )
I would recommend replacing char* with std::string where possible and allow it to manage memory for you and use stack allocated buffers instead of dynamically allocating buffers if a std::string is not appropriate. For example:
std::string executeShellCommand(const std::string& cmd)
{
FILE *fpipe;
if ( !(fpipe = (FILE*)popen(cmd.c_str(),"r")) )
{ // If fpipe is NULL
perror("Problems with pipe");
exit(1);
}
char buf[256] = "";
std::string line;
while ( fgets( buf, sizeof buf, fpipe) )
{
line += buf;
memset(buf, 0, sizeof(buf));
}
// CLOSE THE PIPE
pclose(fpipe);
return line;
}