I'm trying to create an array of array of strings to prepare them to be shown in a table.
So I have a function that returns a buffer string with the list of some scanned wifi access points, and I'm using strsep to split it by "\n" and then again by "\t".
The loop runs fine until it reaches the end and when the while argument ((line = strsep(&buf, "\n"))) is evaluated it gives a SEGFAULT.
Short Illustrative example asked per #Jabberwocky:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int
wap_scan_count_lines(char* wap_scan)
{
int line_amount = 0;
char *scan = wap_scan;
while(*scan)
{
if ('\n' == *scan){
line_amount++;
}
scan++;
}
return line_amount;
}
int main() {
char ***scan_result, *line=NULL, *item=NULL, *scan=NULL;
scan = strdup("bssid / frequency / signal level / flags / ssid\n"
"a8:6a:bb:e2:d6:ef 5785 -47 [WPA-PSK-CCMP+TKIP][WPA2-PSK-CCMP+TKIP][WPS][ESS] Fibertel WiFi114 5.8GHz");
int wap_scan_size = wap_scan_count_lines(scan);
scan_result = malloc(wap_scan_size * sizeof(**scan_result));
int i = 0;
int item_len = sizeof (*scan_result);
while((line = strsep(&scan, "\n")) != NULL ) {
if(i==0){
i++;
continue;
}
char **scan_line = calloc(5, item_len);
int j = 0;
while ((item = strsep(&line, "\t")) != NULL) {
printf("%s\n", item);
scan_line[j++] = strdup(item);
}
scan_result[i++] = scan_line;
}
return 0;
}
The real function that gives me the problem:
char *** wifi_client_get_wap_list(int *len)
{
char ***scan_result;
char *buf, *buf_free, *cmd, *line, *item;
int ret, items_len;
cmd = strdup("SCAN");
ret = wpa_ctrl_command(cmd, NULL);
if (ret < 0) goto error;
cmd = strdup("SCAN_RESULTS");
ret = wpa_ctrl_command(cmd, &buf); //RETURNS A STRING ON BUF ALLOCATED BY STRDUP
if (ret < 0){
free(buf);
goto error;
}
*len = wap_scan_count_lines(buf); //NUMBER OF LINES IN THE SCAN RESULT
scan_result = calloc(*len, sizeof(**scan_result));
int i = 0, j;
buf_free = buf;
items_len = sizeof (*scan_result);
while ((line = strsep(&buf, "\n"))){ //THIS GIVES THE SEGFAULT AT THE END
// SKIP FIRST LINE WITH HEADERS
if (i==0){
i++;
continue;
}
//if (strcmp(line, "") == 0) {
// break;
//}
//EACH LINE HAS 5 VALUES (bssid, freq, level,flags,ssid)
char **scan_line = calloc(5, items_len);
j = 0;
printf("INNER STEPS:\n");
while((item = strsep(&line, "\t"))){
*(scan_line + j) = strdup(item);
printf("%d ", j);
j++;
}
*(scan_result + i) = scan_line;
printf("\nSTEP: %d\n", i);
i++;
}
free(buf_free);
free(cmd);
return scan_result;
error:
// #TODO: Handle error
if (ret == -2) {
printf("'%s' command timed out.\n", cmd);
} else if (ret < 0) {
printf("'%s' command failed.\n", cmd);
}
free(cmd);
return NULL;
}
Based on https://man7.org/linux/man-pages/man3/strsep.3.html the issue is that the loop will run one more time than you want it to, causing scan_result to overflow.
The relevant parts of the documentation are:
The strsep() function returns a pointer to the token, that is, it
returns the original value of *stringp.
and
If *stringp is NULL, the strsep() function returns NULL and does
nothing else. Otherwise, this function finds the first token in
the string *stringp, that is delimited by one of the bytes in the
string delim. This token is terminated by overwriting the
delimiter with a null byte ('\0'), and *stringp is updated to
point past the token. In case no delimiter was found, the token
is taken to be the entire string *stringp, and *stringp is made
NULL.
In wap_scan_count_lines you count the number of lines that are terminated with '\n'.
In the following 2 lines, you allocate the memory to hold the result based on the number of lines terminated with '\n'.
int wap_scan_size = wap_scan_count_lines(scan);
scan_result = malloc(wap_scan_size * sizeof(**scan_result));
However, the above quoted documentation for strsep() implies that in your simplified example the first wap_scan_size times strsep is called, at the end of the call the result will not be NULL and scan won't be set to NULL during the call. The next time through the call, scan will be set to NULL during the call but the result will not be NULL. This means that the body of the loop will be executed wap_scan_size + 1 times, causing a write past the end of scan_result.
There are at least two possible fixes, depending on whether you actually want to process any line at the end of the input that is not terminated by '\n'.
If you do need to process such lines, which seems more robust to me, particularly given that your simplified example ends with such a line, just allocate one extra entry in scan_result:
scan_result = malloc((wap_scan_size + 1) * sizeof(**scan_result));
If you are quite sure that you do not need to process such lines, but this seems incorrect to me, change:
while((line = strsep(&scan, "\n")) != NULL ) {
to
for(line = strsep(&scan, "\n"); scan != NULL; line = strsep(&scan, "\n") ) {
Related
i'm writing a function (get_next_line) that returns a line read from a file descriptor, so i have the line stored in a char array that i return, now i need to process this line
so i read values into a buffer, whenever there are values read, the buffer should be processed, the newly added values should be joined with the result char array, if however, we encounter a newline, an extra step is required, i append the characters to the resulting array, until i reach the newline, which will be replaced by a null char, then the remaining values in the buffer should be stored in a static array that will append those values to the resulting string next time gnl is called.
the output of my function is correct my i need to deal with the leaks , i solved all of the leaks except the one related to the static char. btw im only allowed to use read() malloc() and free , all the other functions should be made by me (that explains the ft_ before each function in my code) here is my code :
valgrind results for the leaks
#include "get_next_line.h"
char * get_next_line(int fd) {
// // the reminder will contain the values after '\n' because the function should let
// you read the text file one line at a time. for example if the the buffer size is 5 and
// the first line is abc\nz we need to store the 'z' for the next line.
static char *reminder = "";
char buf[BUFFER_SIZE + 1];
int nbytes;
char * line = malloc(BUFFER_SIZE + 1);
if (!line || reminder == NULL) {
return NULL;
}
if (fd < 0 || fd > 999) {
free(line);
return NULL;
}
while ((nbytes = read(fd, buf, BUFFER_SIZE)) > 0) {
buf[nbytes] = '\0';
// ft_strjoin allocates and returns a new
// string, which is the result of the concatenation of ’s1’ and ’s2’.
reminder = ft_strjoin(reminder, buf);
//i modified strchr to returns a pointer to the character after '\n'
if (ft_strchr(reminder, '\n')) {
// each line from the txt file start from the index 0 to '\n'
line = ft_substr(reminder, 0, '\n');
reminder = ft_strchr(reminder, '\n');
return line;
}
}
if (ft_strcmp(reminder, "") == 0 || nbytes < 0 || reminder == NULL) {
free(line);
return NULL;
}
if ((!(ft_strchr(reminder, '\n')))) {
ptr = reminder;
reminder = NULL;
return ptr;
}
if (ft_strchr(reminder, '\n')) {
ptr = ft_substr(reminder, 0, '\n');
reminder = ft_strchr(reminder, '\n');
return ptr;
}
return NULL;
}
// here is my strjoin
char *ft_strjoin(char *s1, char *s2)
{
size_t size_s1;
size_t size_s2;
char *strjoin;
size_s1 = ft_strlen(s1);
size_s2 = ft_strlen(s2);
if (!(strjoin = malloc(size_s1 + size_s2 + 1)))
return (NULL);
ft_strcpy(strjoin, s1);
ft_strcat(strjoin, s2);
if(!(ft_strcmp(s1, "") == 0))
{
free(s1);
}
return (strjoin);
}
I would like to know what would be the equivalent for filling an array like we do in Python, but in C.
Python example code:
arrayname=[0]*10
randomtextvar="test_text"
for i in range(10):
arrayname[i]=randomtextvar
for k in range(10):
print(arrayname[k]+" "+str(k)+ " position")
I haven't find an solution for this, I don't really understand how to set value to a string (char) array position (in C).
EDIT:
#include <stdio.h>
#include <string.h>
int main() {
int c = 0;
char arr[256];
fgets(arr, 256, stdin);
char spl[] = " ";
char *ptr = strtok(arr, spl);
while (ptr != NULL) {
ptr = strtok(NULL, spl);
// HERE I WANT TO ADD THE ptr value to a new array of strings
c++;
}
return 0;
}
You are doing strtok before the while loop and immediately at the start. So, you are trashing the first token on the line.
kaylum pointed out a simple way to save the strings into a fixed array using strdup.
But, I suspect you'd like something as flexible as what python is doing. So, the array of strings can be dynamically grown as you process many input lines using realloc.
Also, in addition, it's sometimes nice to have the last array element be NULL [just like argv].
Here's some refactored code. I've annotated it to explain what is going on:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
char *ptr;
char *bp;
const char *spl = " \n";
char buf[256];
char **arr = NULL;
size_t arrcnt = 0;
size_t arrmax = 0;
// read in all input lines
while (1) {
// get next input line -- stop on EOF
ptr = fgets(buf,sizeof(buf),stdin);
if (ptr == NULL)
break;
// parse the current line
bp = buf;
while (1) {
// get next token on the current line
ptr = strtok(bp,spl);
bp = NULL;
// stop current line if no more tokens on the line
if (ptr == NULL)
break;
// grow the string array [periodically]
// NOTE: using arrmax cuts down on the number of realloc calls
if (arrcnt >= arrmax) {
arrmax += 100;
arr = realloc(arr,sizeof(*arr) * (arrmax + 1));
if (arr == NULL) {
perror("realloc/grow");
exit(1);
}
}
// add current string token to array
// we _must_ use strdup because when the next line is read any
// token data we had previously would get overwritten
arr[arrcnt++] = strdup(ptr);
// add null terminator just like argv -- optional
arr[arrcnt] = NULL;
}
}
// trim the array to the exact number of elements used
arr = realloc(arr,sizeof(*arr) * (arrcnt + 1));
if (arr == NULL) {
perror("realloc/trim");
exit(1);
}
// print the array
for (char **av = arr; *av != NULL; ++av)
printf("%s\n",*av);
// free the array elements
for (char **av = arr; *av != NULL; ++av)
free(*av);
// free the array
free(arr);
// reset counts and pointer
arrmax = 0;
arrcnt = 0;
arr = NULL;
return 0;
}
What I'm trying to do in the following code is to tokenize a string and store every token in a dynamic allocated structure but exclude any duplicates.
This code kind of works, until I enter a string that contains two equal words. For example, the string "this this", will also store the second word even though it's the same. But if I enter "this this is" instead, it removes the second "this", and completely ignores the last word of the string, so that it doesn't get deleted if there's a duplicate in the string.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define dim 70
typedef struct string {
char* token[25];
} string;
int main() {
string* New = malloc(dim*sizeof(string));
char* s;
char* buffer = NULL;
int i = 0, r = 0;
s = malloc(dim * sizeof(char));
fgets(s, dim, stdin);
printf("The string is: %s\n", s);
New->token[i] = malloc(dim*sizeof(char));
New->token[i] = strtok(s, " ");
++i;
while((buffer = strtok(NULL, " ")) && buffer != NULL){
printf("\nbuffer is: %s", buffer);
for(r = 0; r < i; ++r) {
if(strcmp(New->token[r], buffer) != 0 && r == i-1) {
New->token[i] = malloc(strlen(buffer)*sizeof(char)+1);
New->token[i] = buffer;
++i;
}
else if(New->token[r] == buffer) {
break;
}
}
}
printf("\n New string: ");
for(i = 0; New->token[i] != NULL; ++i) {
printf(" %s", New->token[i]);
}
return 0;
}
In my mind this should work fine but I'm really having a hard time finding what I did wrong here. If you need additional info just ask me please, I apologise for any eventual lack of clarity (and for my english).
Complete re-write of this answer to address some fundamentally wrong things I did not see the first time through. See in-line comments in the code at bottom to explain some of the construct changes:
I ran your code exactly as is and saw what you are describing, and other than the note about using strcmp in the other answer, found several lines of code that can be adjusted, or removed to make it do what you described it should:
First, the struct definition creates a pointer to an array of char. Based on what you are doing later in the code, what you need is a simple array of char
typedef struct string {
//char* token[25]; //this create a pointer to array of 25 char
char token[25]; //this is all you need
} string;
As you will see later, this will greatly simplify memory allocation.
some basic problems:
Include the \n newline character in your parsing delimiter. When <enter> is hit as the end of entering the string, a newline is appended, causing the first instance of this and the second instance of this\n to be unequal.
while((buffer = strtok(NULL, " \n")) && buffer != NULL){
^^
This line is creating uninitialized memory.
string* New = malloc(dim*sizeof(string));
A note about using malloc() vs. calloc(): malloc() leaves the memory it creates uninitialized, while calloc() creates a block of memory all initialized to 0.
Memory created using malloc()
Memory created using calloc():
This becomes important in several places in your code, but in particular I see a problem in the last section:
for(i = 0; New->token[i] != NULL; ++i) {
printf(" %s", New->token[i]);
}
If the memory created for New is not initialized, you can get a run-time error when the index i is incremented beyond the area in memory that you have explicitly written to, and loop attempts to test New->token[i]. If New->token[i] contains anything but 0, it will attempt to print that area of memory.
You should also free each instance of memory created in your code with a corresponding call to free().
All of this, and more is addressed in the following re-write of your code:
(tested against this is a string a string.)
typedef struct string {
//char* token[25]; //this create a pointer to array of 25 char
char token[25]; //this is all you need
} string;
int main() {
char* s;
char* buffer = NULL;
int i = 0, r = 0;
string* New = calloc(dim, sizeof(string));//Note: This creates an array of New.
//Example: New[i]
//Not: New->token[i]
s = calloc(dim , sizeof(char));
fgets(s, dim, stdin);
printf("The string is: %s\n", s);
buffer = strtok(s, " \n");
strcpy(New[i].token, buffer); //use strcpy instead of = for strings
//restuctured the parsing loop to a more conventional construct
// when using strtok:
if(buffer)
{
++i;
while(buffer){
printf("\nbuffer is: %s", buffer);
for(r = 0; r < i; ++r) {
if(strcmp(New[r].token, buffer) != 0 && r == i-1) {
strcpy(New[i].token, buffer);
++i;
}
else if(strcmp(New[r].token, buffer)==0) {
break;
}
}
buffer = strtok(NULL, " \n");
}
}
printf("\n New string: ");
for(i = 0; i<dim; i++) {
if(New[i].token) printf(" %s", New[i].token);
}
free(New);
free(s);
return 0;
}
You comparing pointers instead of comparing strings. Replace
}
else if(New->token[r] == buffer) {
break;
With
}
else if(strcmp(New->token[r], buffer) == 0) {
break;
You also need to copy the buffer:
memcpy(New->token[i],buffer,strlen(buffer)+1);
instead of
New->token[i] = buffer;
or replace both lines (along with malloc) with
New->token[i] = strdup(buffer);
And it's better to replace strtok with strtok_r (strtok is not re-entrant).
The structure seems unnecessary.
This uses an array of pointers to store the tokens.
The input can be parsed with strspn and strcspn.
Unique tokens are added to the array of pointers.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DIM 70
int main() {
char* token[DIM] = { NULL};
char s[DIM];
char* buffer = s;
int unique = 0, check = 0;
int match = 0;
int loop = 0;
size_t space = 0;
size_t span = 0;
fgets(s, DIM, stdin);
printf("The string is: %s\n", s);
while ( unique < DIM && *buffer){//*buffer not pointing to zero terminator
space = strspn ( buffer, " \n\t");//leading whitespace
buffer += space;//advance past whitespace
span = strcspn ( buffer, " \n\t");//not whitespace
if ( span) {
printf("\ntoken is: %.*s", (int)span, buffer );//prints span number of characters
}
match = 0;
for ( check = 0; check < unique; ++check) {
if ( 0 == strncmp ( token[check], buffer, span)) {
match = 1;//found match
break;
}
}
if ( ! match) {//no match
token[unique] = malloc ( span + 1);//allocate for token
strncpy ( token[unique], buffer, span);//copy span number of characters
token[unique][span] = 0;//zero terminate
++unique;//add a unique token
}
buffer += span;//advance past non whitespace for next token
}
printf("\n New string: ");
for( loop = 0; loop < unique; ++loop) {
printf(" %s", token[loop]);//print the unique tokens
}
printf("\n");
for( loop = 0; loop < unique; ++loop) {
free ( token[loop]);//free memory
}
return 0;
}
I need to write these two functions:
Precondition: hMy_string is the handle to a valid My_string object.
Postcondition: hMy_string will be the handle of a string object that contains
the next string from the file stream fp according to the following rules.
1) Leading whitespace will be ignored.
2) All characters (after the first non-whitespace character is obtained and included) will be added to the string until a stopping condition
is met. The capacity of the string will continue to grow as needed
until all characters are stored.
3) A stopping condition is met if we read a whitespace character after
we have read at least one non-whitespace character or if we reach
the end of the file.
Function will return SUCCESS if a non-empty string is read successfully.
and failure otherwise. Remember that the incoming string may aleady
contain some data and this function should replace the data but not
necessarily resize the array unless needed.
Status my_string_extraction(MY_STRING hMy_string, FILE* fp);
Precondition: hMy_string is the handle to a valid My_string object.
Postcondition: Writes the characters contained in the string object indicated by the handle hMy_string to the file stream fp.
Function will return SUCCESS if it successfully writes the string and
FAILURE otherwise.
Status my_string_insertion(MY_STRING hMy_string, FILE* fp);
However, I am getting a segmentation fault with my current code:
#include <stdio.h>
#include <stdlib.h>
#include "my_string.h"
Status my_string_extraction(MY_STRING hMy_string, FILE *fp)
{
string *pString = (string *) hMy_string;
int lws = 0;
int exit = 0;
int nws = 0;
int i;
int count = 0;
while(fp != NULL && exit == 0) {
if(pString->size >= pString->capacity) {
char *t_data = (char *)malloc(sizeof(char) * pString->capacity * 2);
if(t_data == NULL) {
return FAILURE;
}
for(i = 0; i < pString->size; i++) {
t_data[i] = pString->data[i];
}
free(pString->data);
pString->data = t_data;
pString->capacity *= 2;
if(getc(fp) == ' ' && lws == 0) {
lws++;
} else if(getc(fp) == ' ' && lws == 1) {
exit++;
} else if(getc(fp) == ' ' && nws > 0) {
exit++;
} else {
pString->data[count] = getc(fp);
count++;
pString->size++;
nws++;
}
fp++;
}
return SUCCESS;
}
Status my_string_insertion(MY_STRING hMy_string, FILE *fp)
{
int i;
string *pString = (string *) hMy_string;
for(i = 0; i < pString->size; i++) {
putc(pString->data[i], fp);
}
if(fp == NULL) {
return FAILURE;
}
return SUCCESS;
}
I am getting a segmentation fault with my current code
Why do you do this:
fp++; // incrementing pointer does not get you the next character in the file
When you try to get next character via getc(fp) that is enough to cause the crash . fp will be invalid pointer at that moment.
Also, I see no restrictions for crossing data boundary:
pString->data[count] = getc(fp);
count++; // count is not restricted from growing
I want to dynamically allocate only a portion of a character array.
So part of an array of size 100 is concrete. Say 10 is permanent memory, the other 90 is dynamic memory.
I made some attempt to read character by character until I decided to give up and take a shortcut idea I thought would work. However I end up getting an error that is
incorrect checksum for freed object - object was probably modified
after being freed
I use this method in a while loop in main and I pretty much free everything after the while loop processes. Because, I have the declaration outside of the while loop. I wanted to read an object in a while loop session since these objects end up being added into a list of objects. However the scope of the while loop causes segmentation problems, it cannot remember anything about the object. (I digress).
Here is my attempt.
Object* read(char* str)
{
Object* object = (Object*)malloc(sizeof(*object));
object->identity[0] = 0;
int capacity = (100 + 1) - (10);
object->name = (char*)malloc(capacity * sizeof(*object->name));
object->value = 0.0;
int length = strlen(str);
if (length > capacity)
object->name = (char*)realloc(object->name, (capacity * 2) * sizeof(*object->name));
int arguments = sscanf(str, "%" STRING_SPACE "s %lf %[^\n]s",
object->identity,
&object->value,
object->name);
if (arguments == MATCHER) {
return object;
} else {
return NULL;
}
return object;
}
In this case, an object has a variable sized name but a fixed amount of space allocated for its identity.
I tried something else with sscanf but realized it will never work because I read the string too late to assign memory to name. See;
/*
int len = 0;
for (char* itemObserve = item->name; *itemObserve; itemObserve++) {
if (len == sizeof(item->name)) {
capacity *= MULTIPLIER;
item->name = (char*)realloc(item->name, capacity * sizeof(*item->name));
}
len++;
}
*/
Here is the code in main, everything undefined is probably irrelevant to the bug:
int main()
{
FILE* stream;
Object* object;
ObjectList* list = initList();
while (true) {
char* line;
char cmd[15] = {0};
char* arg;
char* rest;
printf("> ");
line = getline(stdin);
arg = (char*)malloc(35 * sizeof(*arg));
rest = (char*)malloc(35 * sizeof(*rest));
int arguments = sscanf(line, "%s %s %[^\n]", cmd, arg, rest);
free(line);
line = NULL;
printf("\n");
if (strcmp(cmd, "add") == 0) {
arg = (char*)realloc(arg, (35 * 2) * sizeof(*arg));
sprintf(arg, "%s %s", arg, rest);
if ((object = read(arg)) == NULL) {
continue;
}
objectListAdd(list, object);
} else {
free(rest);
free(arg);
exit(EXIT_SUCCESS);
}
free(rest);
free(arg);
}
freeObject(object);
freeObjectList(list);
return EXIT_SUCCESS;
}
Separate getline function in main file
char* getline(FILE* stream)
{
int capacity = LINE_MAX + 1;
char* buffer = (char*)malloc(capacity * sizeof(*buffer));
int len = 0;
int ch;
while ((ch = fgetc(stream)) != '\n' && (ch != EOF)) {
if (len == capacity) {
capacity *= MULTIPLIER;
buffer = (char*)realloc(buffer, capacity * sizeof(*buffer));
}
buffer[len++] = ch;
}
if (ch == EOF) {
return NULL;
}
buffer[len] = '\0';
if (buffer == NULL)
return NULL;
return buffer;
}
There are other conditionals which work as a kind of command switch but they are irrelevant to the errors my program is exhibiting. This much I have narrowed the problem down to.