How to correctly manage output of strtok_r? - c

Let's assume I have a char buffer with data separated with char ":";
char pt[256] = "pt:ct:mac";
char *plain_text;
char *cipher_text;
char *mac;
char *next = NULL;
char *tokens = NULL;
const char sep[2] = ":";
tokens = strtok_r(pt, sep, &next);
do
{
if(i == 0)
{
int ln = strlen(tokens);
plain_text = (char*)malloc(ln * 1);
i++;
continue;
}
if(i == 1)
{
int ln = strlen(tokens);
cipher_text = (char*)malloc(ln * 1);
i++;
continue;
}
if(i == 2)
{
int ln = strlen(tokens);
mac = (char*)malloc(ln * 1);
i++;
continue;
}
}
while((tokens = strtok_r(NULL, sep, &next)) != NULL);
free(plain_text);
free(cipher_text);
free(mac);
, so the question is how in the right way to deal with strtok_r output results.
Basically, the main aim is to get the results out of pt string, and put it in the dynamic containers. Since, I don't know the size of plain_text and cipher_text.
Is it the right way to program it?
Apart from that, if do see some minor mistakes or something can be written with better practices please do let me know ;) Thank you!

I would do it with array of pointers.
char pt[256] = "pt:ct:mac";
char *next = NULL;
char *token = NULL;
char *tokens[3] = {NULL};
const char sep[2] = ":";
token = strtok_r(pt, sep, &next);
while(token)
{
int ln = strlen(token);
tokens[i]= (char*)malloc((ln * sizeof(char)) + 1);
strcpy(tokens[i],token);
i++;
token = strtok_r(NULL, sep, &next);
}
for (int i = 0; i< 3 && tokens[i]; i++) {
free(tokens[i]);
tokens[i] = NULL;
}

Related

Copying specific number of characters from a string to another

I have a variable length string that I am trying to divide from plus signs and study on:
char string[] = "var1+vari2+varia3";
for (int i = 0; i != sizeof(string); i++) {
memcpy(buf, string[0], 4);
buf[9] = '\0';
}
since variables are different in size I am trying to write something that is going to take string into loop and extract (divide) variables. Any suggestions ? I am expecting result such as:
var1
vari2
varia3
You can use strtok() to break the string by delimiter
char string[]="var1+vari2+varia3";
const char delim[] = "+";
char *token;
/* get the first token */
token = strtok(string, delim);
/* walk through other tokens */
while( token != NULL ) {
printf( " %s\n", token );
token = strtok(NULL, delim);
}
More info about the strtok() here: https://man7.org/linux/man-pages/man3/strtok.3.html
It seems to me that you don't just want to want to print the individual strings but want to save the individual strings in some buffer.
Since you can't know the number of strings nor the length of the individual string, you should allocate memory dynamic, i.e. use functions like realloc, calloc and malloc.
It can be implemented in several ways. Below is one example. To keep the example simple, it's not performance optimized in anyway.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
char** split_string(const char* string, const char* token, int* num)
{
assert(string != NULL);
assert(token != NULL);
assert(num != NULL);
assert(strlen(token) != 0);
char** data = NULL;
int num_strings = 0;
while(*string)
{
// Allocate memory for one more string pointer
char** ptemp = realloc(data, (num_strings + 1) * sizeof *data);
if (ptemp == NULL) exit(1);
data = ptemp;
// Look for token
char* tmp = strstr(string, token);
if (tmp == NULL)
{
// Last string
// Allocate memory for one more string and copy it
int len = strlen(string);
data[num_strings] = calloc(len + 1, 1);
if (data[num_strings] == NULL) exit(1);
memcpy(data[num_strings], string, len);
++num_strings;
break;
}
// Allocate memory for one more string and copy it
int len = tmp - string;
data[num_strings] = calloc(len + 1, 1);
if (data[num_strings] == NULL) exit(1);
memcpy(data[num_strings], string, len);
// Prepare to search for next string
++num_strings;
string = tmp + strlen(token);
}
*num = num_strings;
return data;
}
int main()
{
char string[]="var1+vari2+varia3";
// Split the string into dynamic allocated memory
int num_strings;
char** data = split_string(string, "+", &num_strings);
// Now data can be used as an array-of-strings
// Example: Print the strings
printf("Found %d strings:\n", num_strings);
for(int i = 0; i < num_strings; ++i) printf("%s\n", data[i]);
// Free the memory
for(int i = 0; i < num_strings; ++i) free(data[i]);
free(data);
}
Output
Found 3 strings:
var1
vari2
varia3
You can use a simple loop scanning the string for + signs:
char string[] = "var1+vari2+varia3";
char buf[sizeof(string)];
int start = 0;
for (int i = 0;;) {
if (string[i] == '+' || string[i] == '\0') {
memcpy(buf, string + start, i - start);
buf[i - start] = '\0';
// buf contains the substring, use it as a C string
printf("%s\n", buf);
if (string[i] == '\0')
break;
start = ++i;
} else {
i++;
}
}
Your code does not have any sense.
I wrote such a function for you. Analyse it as sometimes is good to have some code as a base
char *substr(const char *str, char *buff, const size_t start, const size_t len)
{
size_t srcLen;
char *result = buff;
if(str && buff)
{
if(*str)
{
srcLen = strlen(str);
if(srcLen < start + len)
{
if(start < srcLen) strcpy(buff, str + start);
else buff[0] = 0;
}
else
{
memcpy(buff, str + start, len);
buff[len] = 0;
}
}
else
{
buff[0] = 0;
}
}
return result;
}
https://godbolt.org/z/GjMEqx

Combining Strings into a List in C

Below is my code for the following issue. I'm trying to take a string of first names and string of last names that are separated by commas and transform them into a list of full names. For example, if firstnames = "John,Jane" and lastnames = "Smith,Doe", then the output should be ["John Smith", "Jane Doe"].
I believe my issue is arising in my use of strtok since first_names[i] = name is giving me an error. Any help on this would be much appreciated!
char **combine_names(char *firstnames, char *lastnames) {
char first_names[50][50];
char last_names[50][50];
int i = 0;
char *name = strtok(firstnames, ",");
while (name != NULL) {
first_names[i] = name;
i++;
name = strtok(NULL, ",");
}
i = 0;
name = strtok(lastnames, ",");
while (name != NULL) {
last_names[i] = name;
i++;
name = strtok(NULL, ",");
}
char **names;
names = malloc(strlen(first_names) * sizeof(char*));
for (int i = 0; i < strlen(first_names); i++) {
names[i] = malloc(51 * sizeof(char));
}
int i = 0;
int j = 0;
int k = 0;
while (first_names[i] != '\0') {
while (first_names[i][j] != '\0') {
names[i][j] = first_names[i][j];
j++;
}
names[i][j] = ' ';
j++;
while (second_names[i][k] != '\0') {
names[i][j] = second_names[i][k];
j++;
k++;
}
names[i][j] = '\0';
i++;
}
names[i] = '\0';
return names;
}
The following line is causing an incompatible pointer error with the first argument. Why is that?
names = malloc(strlen(first_names) * sizeof(char*));
Using strtok() does indeed pose some problems, but the main issue is your allocating names with an invalid expression malloc(strlen(first_names) * sizeof(char*));. first_names is not a C string, strlen(first_names) does not compute the number of entries in the first_names array.
Here is a simpler and safer approach:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **combine_names(const char *firstnames, const char *lastnames) {
int n = 0;
char **names = malloc(sizeof(*names) * (n + 1));
char *p;
if (names == NULL) {
perror("cannot allocate memory\n");
}
while (*firstnames && *lastnames) {
int len1 = strcspn(firstnames, ",");
int len2 = strcspn(lastnames, ",");
int size = len1 + 1 + len2 + 1;
p = malloc(size);
if (p == NULL) {
perror("cannot allocate memory\n");
}
snprintf(p, size, "%.*s%s%.*s",
len1, firstnames,
len1 && len2 ? " " : "",
len2, lastnames);
names = realloc(names, sizeof(*names) * (n + 2));
if (names == NULL) {
perror("cannot allocate memory\n");
}
names[n++] = p;
firstnames += len1 + (firstnames[len1] == ',');
lastnames += len2 + (lastnames[len2] == ',');
}
names[n] = NULL;
return names;
}
Remember that first_names is a double array of characters. That means that first_names[i] is actually a string, or an array of characters. You can't assign directly to an array of characters, instead you have to write to the array character by character. The easiest way to do this is using string copy or
strcpy(first_names[i], name), but strcpy doesn't protect against buffer overflows. One method is to use strncpy, just be careful because this will not guarantee the string is null-terminated when the source string exceeds the size of the destination string. To fix this, use
strncpy(first_names[i], name, 50);
first_names[i][49] = '\0';
Considering the disadvantages of strncpy it's probably best to use a solution similar to #chqrlie.

Strtok() not splitting the array properly

I was trying to split the array I received as an argument in a function using strtok() and it simply don't work as expected.
For example I receive this string : "ls -l" and I got only "ls".
Furthermore , I want to store the tokens into an array of strings.
Here is what I have done so far:
int mysystem(char *s) {
int i;
char *tok , *aux;
int conta = 0;
int pid, status;
int j = 0;
tok = strtok(s , " ");
while (tok != NULL) {
tok = strtok(NULL, s);
conta++;
}
char *store[conta];
i = 0;
aux = strtok(s ," ");
while (aux != NULL) {
store[i] = aux;
aux = strtok(NULL, s);
i++;
}
pid = fork();
if (pid == 0)
execvp(store[0], store);
while (j != conta) {
wait (&status);
j++;
}
return 0;
}
This is the main of where I'm passing the string to my function :
int main(int args, char **arg) {
int i;
int s;
int f = 0;
if (args >= 2) {
int length = 0;
for (i = 1; i < args; ++i) {
length += strlen(arg[i]);
}
char *output = (char*)malloc(length + 1);
char *dest = output;
i = 1;
while (i < args) {
dest = strcat (dest,arg[i]);
i++;
if (i < args) {
dest = strcat (dest," ");
}
}
dest = strcat(dest, "\0");
s = mysystem(dest);
free(output);
return s;
}
}
strtok modifies the string, so you can't run it twice on the same string. s has been converted to a series of strings separated by NUL characters. Change to using an array which is "long enough" and just go through s once.

Weird seg faults on consecutive calls to the same array

I tried really hard to search for a solution to this but I can't think of good enough keywords.
Currently I'm having troubles grasping the concept behind makeargv and it's usage with triple pointers (I have no idea what ***foo means, it doesn't seem to be as easy of a concept as **foo or *foo). So I made my own:
const char **makeargv(char *string, int *numargs) {
string = string + strspn(string, delims);
char *copy = malloc(strlen(string) + 1);
int i;
strcpy(copy, string);
int numtokens;
if (strtok(copy, delims) != NULL) {
for (numtokens = 1; strtok(NULL, delims) != NULL; numtokens++) {}
}
strcpy(copy, string);
const char *results[numtokens+1];
results[0] = strtok(copy, delims);
for (i = 1; i < numtokens; i++) {
results[i] = strtok(NULL, delims);
}
results[numtokens+1] = NULL;
*numargs = numtokens;
return results;
}
Here's the part at where it breaks:
void parse_file(char* filename) {
char* line = malloc(160*sizeof(char));
FILE* fp = file_open(filename);
int i = 0;
int numargs = 0;
int *pointer = &numargs;
while((line = file_getline(line, fp)) != NULL) {
if (strlen(line) == 1){
continue;
}
const char **args = makeargv(line, pointer);
printf("%s\n", args[0]);
printf("%s\n", args[1]);
/* This prints out args[0], but then args[1] causes a seg fault. Even if I replace
the args[1] with another args[0] it still causes a seg fault */
}
fclose(fp);
free(line);
}
I have a working array of strings. However when I try to print out the strings in the array, I can only print 1 of my choice and then it seg faults for any subsequent calls. lets pretend my array of strings is argv[3] = {"Yes", "no", "maybe"}, if i call argv[0], it will let me call "Yes", but any other calls (even if i call argv[0] again) do not work and cause a segfault. I can call any of the elements in the array, but once i call one the rest cease to work causing segfaults.
Help please? D: This is in C.
const char *results[numtokens+1];
This array "results" is a local variable, it is only available inside of "makeargv".
You'd better use malloc:
results = malloc(numtokens+1)
And I believe there is memory leak in your code.
You will not be able to free the memory for "char *copy"
char *copy = malloc(strlen(string) + 1);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **makeargv(char *string, int *numargs) {
static const char *delims = " \t\n";
string = string + strspn(string, delims);
char *copy = malloc(strlen(string) + 1), *p = copy;
strcpy(copy, string);
int numtokens;
for (numtokens = 0; strtok(p, delims); ++numtokens, p = NULL);
char **results = malloc(sizeof(char*)*(numtokens+1));
strcpy(copy, string);
int i;
p = copy;
for (i = 0; i < numtokens; ++i, p = NULL)
results[i] = strtok(p, delims);
results[i] = NULL;
*numargs = numtokens;
return results;
}
FILE *file_open(char *filename){
FILE *fp = fopen(filename, "r");
if(!fp){
perror("file_open");
exit(1);
}
return fp;
}
void parse_file(char* filename) {
char* line = malloc(160*sizeof(char));
FILE* fp = file_open(filename);
int i = 0, numargs = 0;
while(fgets(line, 160, fp)){
if (*line == '\n')
continue;
char **args = makeargv(line, &numargs);
for(i = 0;i<numargs;++i)
printf("%s\n", args[i]);
printf("\n");
if(args[0])
free(args[0]);
free(args);
}
fclose(fp);
free(line);
}
int main(int argc, char *argv[]){
parse_file(argv[1]);
return 0;
}

segmantation fault in c code

I have a problem with a code I'm checking. I get a Segmentation fault (core dumped) and I think the problem is in this part of the code
The code supposed to add a new item to a connected list by users input.
The input should look like this
word_#_1999_#_synonym_#_FORIGEN
thank you in advance
// Add a new word to the dictionary with the format of { devoted_#_2003,_2001,_2008_#_worship_#_AHAVA }
struct Word * addWord(struct Word * dictionary)
{
struct Word *scan = dictionary;
struct Word *newWord = (struct Word *)malloc(sizeof(struct Word));
char *input = (char *)malloc(sizeof(char) * 128);
char *inputBackup = (char *)malloc(sizeof(char) * 128);
char *years = (char *)malloc(sizeof(char) * 128);
int count = 0;
int tempYear;
char *wordOfNewWord;
printf("Please enter a new word into dictionary\n");
scanf("%s", input);
strcpy(inputBackup, input);
// Init newWord
newWord = (struct Word *)malloc(sizeof(struct Word));
newWord->next = NULL;
newWord->numOfYears = 0;
// Check if good
if(countSubstring(input, "_#_") != 3)
{
printf("error\n");
return NULL;
}
// Get the word name
wordOfNewWord = strtok(input, "_#_");
newWord->word = (char *)malloc(sizeof(wordOfNewWord));
strcpy(newWord->word, wordOfNewWord);
// Get the word years
years = strtok(NULL, "#");
newWord->numOfYears = countSubstring(years, ",_") + 1;
newWord->years = (unsigned short *)malloc(sizeof(unsigned short) * newWord->numOfYears);
years = strtok(years, ",_");
tempYear = strtol(years, NULL, 10);
if (tempYear <= 9999 && tempYear > 0)
{
*(newWord->years + count) = tempYear;
}
else
{
printf("error\n");
return NULL;
}
count = 1;
years = strtok(NULL, ",_");
while (years != NULL)
{
tempYear = strtol(years, NULL, 10);
if (tempYear <= 9999 && tempYear > 0)
{
*(newWord->years + count) = tempYear;
}
else
{
printf("error\n");
return NULL;
}
count++;
years = strtok(NULL, ",_");
}
// Get word synonims
strcpy(input, inputBackup);
input = strtok(input, "#");
input = strtok(NULL, "#");
input = strtok(NULL, "#");
newWord->numOfSynonym = countSubstring(input, ",_") + 1;
newWord->synonymWords = (char **)malloc(sizeof(char) * 30 * newWord->numOfSynonym);
input = strtok(input, ",_");
*(newWord->synonymWords) = input;
count = 1;
input = strtok(NULL, ",_");
while (input != NULL)
{
*(newWord->synonymWords + count) = input;
count++;
input = strtok(NULL, ",_");
}
// Get translation
input = (char *)malloc(sizeof(char) * 120);
strcpy(input, inputBackup);
input = strtok(input, "#");
input = strtok(NULL, "#");
input = strtok(NULL, "#");
input = strtok(NULL, "#");
newWord->numOfTrans = countSubstring(input, ",_") + 1;
newWord->tranWords = (char **)malloc(sizeof(char) * 30 * newWord->numOfTrans);
input = strtok(input, ",_");
*(newWord->tranWords) = input;
count = 1;
input = strtok(NULL, ",_");
while (input != NULL)
{
*(newWord->tranWords + count) = input;
count++;
input = strtok(NULL, ",_");
}
// Put the new word in the dictionary
if(findWord(dictionary, newWord->word) == 1)
{
printf("error\n");
return NULL;
}
}
there is the struct
struct Word
{
char *word;
unsigned short * years;
unsigned short numOfYears;
char ** synonymWords;
unsigned short numOfSynonym;
char ** tranWords;
unsigned short numOfTrans;
struct Word *next;
};
For a kickoff, this code doesn't make much sense:
if (tempYear <= 9999 && tempYear > 0)
{
*(newWord->years + count) = tempYear;
}
considering the only time count is used prior to this line is: int count = 0;
Other than that, you seem to be oblivious to the fact that char ** and char * are not the same thing!:
newWord->synonymWords = (char **)malloc(sizeof(char) * 30 * newWord->numOfSynonym);
//and
newWord->tranWords = (char **)malloc(sizeof(char) * 30 * newWord->numOfTrans);
Which are allocating char *, but at the same time, you're doing:
char *wordOfNewWord;
newWord->word = (char *)malloc(sizeof(wordOfNewWord));
Which is actually allocating memory to hold a pointer, depending on the architecture (32 or 64 bits) a pointer generally requires 4 to 8 times as much memory as a char, of which the size is by definition 1.
Please allocate memory according to the suggestions made in the comments:
char **pointers_to_strings = malloc(10*sizeof(*pointers_to_strings));
for(int i=0;i<10;++i)
pointers_to_strings[i] = calloc(101, sizeof(*pointers_to_strings[i]));
To ensure you're always allocating the right amount of memory, required to accomodate whatever type it will hold.
And of course, no malloc or calloc without a free call:
for (int i=0;i<10;++i)
{
free(pointers_to_strings[i]);
pointers_to_strings[i] = NULL;
}
free(pointers_to_strings);
pointers_to_strings = NULL;
the problem is in this lines
char *wordOfNewWord;
newWord->word = (char *)malloc(sizeof(wordOfNewWord));
strcpy(newWord->word, wordOfNewWord);

Resources