using functions in c (return value) - c

Learning C and having many doubts.
I have a function (lets say function 1) that calls another function (lets say function 2).
Function 2 calculates an array of string.
How can I use this array in function 1?
Some code example:
int find_errors(char* word)
{
char error[100];
/*Given the word, It will find the duplicate chars and store it in the
error array. */
return 0;
}
int find_word(char* word)
{
find_errors (word);
printf("%s\n", error);
return 0;
}

There are at least three possible approaches:
Use a global variable
pass a parameter between them
return a pointer from the function

There are multiple ways to do this.
1) Create a dynamic array and return a pointer to the array. This will require you to manually free the memory for the array at a later time.
#define NUM_ELEMS 50
// In find_error():
char* error = malloc(NUM_ELEMS * sizeof(char));
return error;
// In find_word():
char *error = find_errors();
// do stuff
free(error);
2) Pass a pointer to find_errors that it can use as the error array. This will not require you to manually free the memory.
// In find_word():
char error[NUM_ELEMS];
find_error(error);
3) Use a global array. May make it more difficult for other people to understand your code. Has other potential problems as well.
// In global scope:
char error[NUM_ELEMS];

Your question relates to "call-by-reference" and "call-by-value".
char* getNewValsToSet(void)
{
char* new_vals = (char*) malloc(sizeof(char[5]));
new_vals[4] = '\0';
return new_vals;
}
void setValuesEven(char* vals_to_set)
{
vals_to_set[0] = 'A';
vals_to_set[2] = 'C';
}
void setValuesOdd(char* vals_to_set)
{
vals_to_set[1] = 'B';
vals_to_set[3] = 'D';
}
int main(void)
{
char* some_vals_to_set = getNewValsToSet();
setValsEven(some_vals_to_set);
setValsOdd(some_vals_to_set);
// ... now has vals "ABCD"
free(some_vals_to_set); //cleanup
return 0;
}
If you have "doubts" about learning C, IMHO it's one of the best things you can do (no matter the language in which you work) because it will explain exactly how things work "under-the-hood" (which all high-level languages try to hide to some degree).

You need to declare the error array globally and use it just like you did.
EDIT: using global variables isn't the best practice in most of the cases, like this one.

Here is an example of what you are looking for with an awesome console output. It dynamically allocates the array to hold any number errors (duplicate characters in your case) that may occur.
//Only free errors if result is > 0
int find_errors(char* word, char** errors)
{
int num_errors = 0;
int word_length = strlen(word);
int ARRAY_SIZE = MIN(8, word_length);
char existing[word_length];
int existing_index = 0;
*errors = NULL;
for(int i = 0; i < word_length; i++)
{
char character = word[i];
//Search array
for (int n = 0; n < word_length; ++n ) {
if(n >= existing_index)
{
existing[n] = character;
existing_index++;
break;
}
if (existing[n] == character) {
num_errors++;
if(!*errors)
*errors = (char*)malloc(ARRAY_SIZE * sizeof(char));
//Check if we need to resize array
if(num_errors >= ARRAY_SIZE)
{
ARRAY_SIZE *= 2;
ARRAY_SIZE = MIN(ARRAY_SIZE, word_length);
char *tmp = (char*)malloc(ARRAY_SIZE * sizeof(char));
memcpy(tmp, *errors, (unsigned long)ARRAY_SIZE);
free(*errors);
*errors = tmp;
}
//Set the error character
(*errors)[num_errors - 1] = character;
break;
}
}
}
return num_errors;
}
int find_word(char* word)
{
char* errors;
int errCount = find_errors (word, &errors);
if(errCount > 0)
{
printf("Invalid Characters: ");
for(int i =0; i < errCount; i++)
{
printf("%c ", errors[i]);
}
printf("\n");
free(errors);
}
return 0;
}
int main(int argc, char *argv[])
{
find_word("YWPEIT");
find_word("Hello World");
find_word("XxxxXXxXXoooooooOOOOOOOOOOOOOOOooooooooOOOOOOOOOOOOooooooOOO");
}

Related

C - scanf changes the values ​of an array without direct access

It's my first time here on stackoverflow. I hope my question fits.
We started programming C at the university this semester. Unfortunately there are only a few online lectures. But we still have to solve the tasks.
We should program a kind of Hang-Man this time. In other words, guess a hidden word.
I have the following problem. I get a char, but after entering it, the contents of the riddle array change. If I leave out the input it works. I don't understand why this happens because scanf doesn't actually access riddle.
I myself don't know what to do here. I hope someone can tell me what's wrong with the code.
//sry some variables and texts are in german
char* createRiddle(char const* const str){
int laenge = strlen(str);
char temp[laenge+1];
char *t = temp;
strcpy(temp, str);
int te = strcmp(temp, str);
if (te != 0){
printf("ERROR: Bei der Speicherreservierung ist ein Fehler aufgetreten");
exit(0);
}
int i;
for (i=0; i < (int)strlen(temp);i++){
if (str[i] > 65 && str[i] < 90){ //ASCII Großbuchstaben-Bereich prüfen
char verdeckt = '*';
temp[i] = verdeckt;
} else {
temp[i] = str[i];
}
}
return t;
}
//----------------------------
int uncoverLetter(char *riddle, const char *solution, char letter){
printf("RD3: %s\n",riddle);
letter = toupper(letter);
int i;
int treffer = 0;
for (i=0; i < (int)strlen(solution); i++) {
if (letter == solution[i]) { // Buchstabe im Wort?
if (letter != riddle[i]) { //Buchstabe schon aufgedeckt?
riddle[i] = solution[i];
treffer = treffer + 1;
}
}
}
return treffer;
}
//----------
int gamingLoop(const char* solution){
int punkte; //points
printf("Lets GO!\n\n");
char *riddle = createRiddle(solution);
printf("Gesuchtes Wort: %s\n\n",riddle); //Word: *-******* ( = C-Compiler )
int highscore = 0;
while ((strcmp(riddle, solution)) != 0) {
printf("RD1: %s\n",riddle); //Test: What does Riddle look like?
printf("Bitte geben Sie einen Buchstaben ein: "); // pls enter letter
char eingabe;
scanf(" %c", &eingabe); //-----!!Here is the point where things go wrong!!------
printf("RD2: %s\n",riddle); //Test2
int treffer = uncoverLetter(riddle, solution, eingabe);
//----------- probably unimportant for the problem ----------------
//Zufallszahl
int zufz = (rand() % 11) + 1;
int ii = 1;
for (ii=1; ii < 11 ; ii++){
if ( zufz == ii) {
punkte = zufz*100;
}
}
//------------
if (treffer != 0) {
printf("Du hast %d richtige Treffer.\n", treffer);
highscore = highscore + (treffer*punkte);
printf("Punkte: %i\n\n", highscore);
} else {
printf("Du hast leider keinen Treffer.\n");
highscore = highscore - punkte;
printf("Punkte: %d\n\n", highscore);
}
printf("%s\n\n",riddle);
}
return highscore;
}
OUTPUT:
Sry no pic because i dont have 10 rep :(
Link: https://imgur.com/UIeltVR
// R3 in funktion uncoverLetter
I strongly suspect that I made a very stupid mistake, but unfortunately I can't see it myself / can't see it yet.
I look forward to advice and help.
Thank you.
Your problem is in createRiddle, where you create the *** pattern:
char* createRiddle(char const* const str){
int laenge = strlen(str);
char temp[laenge+1];
char *t = temp;
// ... create pattern ...
return t;
}
You return a local array. (t is just an alias to the array temp.) That array will be out of scope when the function exits and therefore invalid.
There are several possible solutions.
Make the caller provide space
Pass in an array that the caller can fill:
void createRiddle(char *temp, char const* const str)
{
// ... create pattern in temp ...
}
Then call it like this:
char riddle[MAX];
createPattern(riddle, solution);
You don't need to return the array here, because it is the same array you provided, only filled. (You could return it if it makes calling easier. You could also return an error code. Use your good judgement.)
Of course, the function and caller need to agree how much space must be provided. (That could be another function parameter or a global constant.)
Allocate memory dynamically
Dynamic memory is allocated on the heap and guaranteed not to be used by anyone else:
char *createRiddle(char const* const str)
{
int laenge = strlen(str);
char *temp = malloc(laenge + 1);
// ... create pattern in temp ...
return temp;
}
Then use it like this:
char *riddle = createRiddle(char const* const str);
// ... play the game ...
free(riddle); // be nice and clean up
Static arrays
Make the array static.
static char temp[laenge+1];
Here, the static keyword means that there is only one array that retains its value between calls. It is really as if you has declared the array as global outside the function, but with the addition that its name is only known to your function.
That's a quick and easy solution, but it fails when your function is recursive or when other parts of your code use the same function. (That's probably not the case in your game, though.)

Is there a way to split an array of strings into subarray of strings on token

Basically, is there any way to split an array of strings into arrays of strings before and after a token ("|") in C.
An example is shown below.
char *input[] = {"hello","I","am","|","a","cool","|","guy"}
//code
and the result is 3 arrays, containing
{"Hello","I","am"}
{"a","cool"}
{"guy"}
I tried strtok but that seems to split a string into pieces, rather than an array of strings into new, separate, sub-arrays of strings. I also do not know exactly how many "|" tokens will be present, and will need an unknown amount of new arrays (safe to say it'd be less than 10). They will be passed to execvp so having it as one string and just remembering where to start and stop looking will not work.
They will be passed to execvp
Assuming the strings include the program to be executed (the 1st parameter to execvp()) and the strings will be used in the order of appearance as per this pointer-array
char *input[] = {"hello","I","am","|","a","cool","|","guy"}
then a possible simple solution without any duplications might look like this:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
char * input[] = {"hello", "I", "am", "|",
"a", "cool", "|",
"guy", "|"}; /* note the additional trailing `"|"`. */
int main(void)
{
char ** pcurrent = input;
char ** pend = pcurrent + sizeof input / sizeof *input;
while (pcurrent < pend)
{
{
char ** ptmp = pcurrent;
while (ptmp < pend && **ptmp != '|')
{
++ptmp;
}
*ptmp = NULL;
}
{
pid_t pid = fork();
if ((pid_t) -1) == pid)
{
perror("fork() failed");
exit(EXIT_FAILURE);
}
if ((pid_t) 0) == pid) /* child */
{
execvp(pcurrent[0], pcurrent);
perror("execvp() failed");
exit(EXIT_FAILURE);
}
/* parent */
pcurrent = ptmp + 1;
}
} /* while (pcurrent < pend) */
} /* int main(void) */
You need manually to split the input array. And dynamically allocate a new place to store result. E.g. as:
#include <stdio.h>
#include <stdbool.h>
int main()
{
char *input[] = {"hello","I","am","|","a","cool","|","guy"};
int inputLength = sizeof(input)/sizeof(input[0]);
printf("inputLength - %d\n", inputLength);
const char ***result2DimArray = malloc(sizeof(char**) * inputLength);
int *result2DimArrayLengths = malloc(sizeof(int) * inputLength);
memset(result2DimArrayLengths, 0, sizeof(int) * inputLength);
const char **currentSection = 0;
int nextSectionNumber = 0;
for(int inputIndex = 0; inputIndex < inputLength; inputIndex++)
{
if(input[inputIndex][0] == '|')
{
currentSection = 0;
}
else
{
if(!currentSection)
{
currentSection = malloc(sizeof(char*) * inputLength);
result2DimArray[nextSectionNumber] = currentSection;
nextSectionNumber++;
}
*currentSection = input[inputIndex];
currentSection++;
result2DimArrayLengths[nextSectionNumber-1]++;
}
}
/*Checking the result*/
printf("total sections - %d\n", nextSectionNumber);
for(int i=0; i<nextSectionNumber;i++)
{
for(int j=0;j<result2DimArrayLengths[i];j++)
{
printf(result2DimArray[i][j]);
printf(", ");
}
puts("");
}
return 0;
}
Here is a solution which doesn't involve dynamic memory allocation.
Before going in to the details ...
I think it's useful when tackling a problem like this to think about how the "strings" are stored in memory. It might look something like in the attached picture. (The memory addresses are completely unrealistic - and there would be null terminators at the end of each string - but you get the idea).
As the picture shows, the vital information we need for each 'sub-array' can be stored in a <char **, int> pair. The char ** is the address of the first "string" in the sub-array; the int is the number of strings it contains.
We can use a struct string_array_t to store this information.
typedef struct {
// Pointer to first string in sub-array
char **p;
// Number of strings in sub-array
int count;
} string_array_t;
We allocate an array of these on the stack; thus no need for malloc() or free() - as long as we allocate enough sub-arrays.
string_array_t string_arrays[MAX_SUB_ARRAYS] = {0};
char *input[] = {"hello", "I", "am", "|", "a", "cool", "|", "guy"};
// Pointer to current sub-array
string_array_t *cur = NULL;
size_t n_sub_arrays = 1;
Initialize our counters and pointers:
int i = 0, j = 0, k = 0;
cur = &string_arrays[0];
size_t n_strings_total = sizeof(input) / sizeof(input[0]);
Then loop over the array.
for (i = 0; i < n_strings_total; i++) {
if (!strcmp(input[i], "|")) {
// Store total number of strings in this sub-array
cur->count = k;
k = 0;
// Switch to next sub-array
cur = &string_arrays[++j];
if (j >= MAX_SUB_ARRAYS) {
fprintf(stderr, "Not enough sub-arrays allocated ...\n");
break;
}
n_sub_arrays++;
continue;
}
if (k == 0) {
cur->p = &input[i];
}
k++;
}
cur->count = k;
Print the results.
printf("Found %zu sub arrays ...\n", n_sub_arrays);
for (i = 0; i < n_sub_arrays; i++) {
string_array_t *cur = &string_arrays[i];
for (j = 0; j < cur->count; j++) {
printf("%s ", *(cur->p++));
}
printf("\n");
}

I attempted to parse a string into different words, attempted to freed them later but 2 of the strings have the same byte addresses

I am currently trying to parse a string into an array of strings.
So far, I currently believe I've succeeded in splitting up the string by inserting '\0' after every word "chunk".
However, when I attempt to free the string array later, some of my words have the same byte address, and thus, when I try to free one of them, the other gets freed as well.
This is the code for my parser, I apologize for its messy form:
/*
* parser()
*
* Parses a given string into different words and returns a list with the words.
* If there is a non-space and non-alphabetic character an error is recorded.
*/
void parser(char* str, char** actualList, char** freeingList,char* error, int* length){
// initialize variables
bool chara = false;
bool beginning = true;
int size = strlen(str);
bool nonAlphaSpace = false;
// iterate through the entire string
for(int i = 0; i < size; i++){
// if the character is not either a space or an alphabetic character
if(isspace(str[i])==0 && isalpha(str[i])==0 && !nonAlphaSpace){
*error = str[i];
nonAlphaSpace = true;
}
}
// if there was no irregular character
if(!nonAlphaSpace){
for(int j = 0; j < size; j++){
// if the character is the beginning of the current string
if(beginning){
// record this string into the list of words
freeingList[*length] = &str[j];
(*length)++;
// set the status of any alphabetic character being present to false;
chara = false;
// if the current character is an alphabetic character
if(isalpha(str[j])!=0){
chara = true;
}
beginning = false;
}
// if the character is a space
else if(isspace(str[j])!=0){
// if there was a character beforehand
if(chara){
// get the pointer to the next character
char* new = &str[j+1];
// change the current character to a null
str[j] = '\0';
// realign the pointer to the string to rest of the string
str = new;
j = -1;
size = strlen(str);
beginning = true;
}
}
// if the character is an alphabetic character
else{
chara = true;
}
}
// if the last chunk of string left didn't contain any characters
if(!chara){
free(str);
}
// for every word extracted
for(int k = 0; k < *length; k++){
int newSize = strlen(freeingList[k]);
bool first = true;
// get the pointer to the first character in the word, i.e. not the first few spaces
for(int l = 0; l < newSize; l++){
if(isspace(freeingList[k][l])==0 && first){
actualList[k] = &freeingList[k][l];
first = false;
}
}
}
}
}
This is when I attempt to free it:
// free the current collection of strings
for(int j = 0; j < size; j+=2){
free(words[j]);
}
When I input "home or for" into the parser and later try to free it, the address of "home" is 0x7fffffffe840 while the address of "for" is 0x7fffffffe848. This leads me to believe that the freeing of home also frees or causing a SIGABRT error later.
Is this assumption correct? How can I overcome this double freeing?
You should only call free() on pointers returned by malloc(), calloc(), or realloc(). What it looks like you are doing:
char *ptr = malloc(100);
char *ptr2 = &ptr[10];
free(ptr2); // You can't do that.
I suspect you meant to make a copy of the strings. Here's a simplified version:
void parser(char* str, char** actualList, int* length) {
char *start = str; // The start of the current string
int count = 0; // Number of strings copied
while (*str) {
if (isspace(*str)) {
*str = '\0';
actualList[count] = malloc(strlen(start) + 1); // Allocate space for string
strcpy(actualList[count++], start); // Copy string
start = str + 1; // Reset for next string
if (count == *length - 1) break; // Don't overflow pointer array
}
str++;
}
// Grab the final string
actualList[count] = malloc(strlen(start) + 1); // Allocate space for string
strcpy(actualList[count++], start); // Copy string
*length = count;
}
Then call it like:
char input[] = "home or for";
char *words[5];
int max_words = 5;
parser(input, words, &max_words);
// max_words should be 3 now
for (int i = 0; i < max_words; i++) {
printf("%s\n", words[i]);
}
// Clean up
for (int i = 0; i < max_words; i++) {
free(words[i]);
}
Output:
home
or
for
That's not a parser, though. More a tokenizer.
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
bool parser(char const *str, char ***words, size_t *num_words, size_t *error)
{ // ^^^ gaaaah! *)
assert(words);
errno = 0;
size_t length = strlen(str);
for (size_t i = 0; i < length; ++i) {
if (!isalnum(str[i]) && !isspace(str[i])) {
*error = i; // the position is most likely more meaningful than the character
return false; // get outta here!!
}
}
char const *begin;
char const *end;
*num_words = 0;
*words = NULL;
bool in_word = false;
for (size_t i = 0; i <= length; ++i) {
if (!in_word && isalnum(str[i])) { // word begins
begin = str + i;
in_word = true;
}
else if (in_word && !isalnum(str[i])) { // word ended
end = str + i;
char *word = calloc(end - begin + 1, sizeof *word);
if (!word) {
for (size_t i = 0; i < num_words; ++i)
free((*words)[i]);
free(*words);
errno = ENOMEM;
return false;
}
memcpy(word, begin, end - begin);
char **tmp = realloc(*words, (*num_words + 1) * sizeof *tmp);
if (!tmp) {
free(word);
for (size_t i = 0; i < num_words; ++i)
free((*words)[i]);
free(*words);
errno = ENOMEM;
return false;
}
*words = tmp;
tmp[(*num_words)++] = word;
in_word = false;
}
}
return true;
}
int main(void)
{
char const *foo = "slfkja askdfj jk j aksjf lasjdflkjsdlf jask fdjl";
char **words = NULL;
size_t num_words = 0;
size_t error = 0;
if (!parser(foo, &words, &num_words, &error)) {
if (errno == ENOMEM)
fputs("Not enough memory. :(\n\n", stderr);
else fprintf(stderr, "Error at position %zu: \"%s\"\n\n", error, foo + error);
return EXIT_FAILURE;
}
puts("List of words:");
for (size_t i = 0; i < num_words; ++i) {
printf("\"%s\"\n", words[i]);
free(words[i]);
}
free(words);
}
C should be renamed brainf*ck ...
*) Three Star Programmer
you modify the value of str in the body of the function (in the line str = new; (don't use new as an identifier, more if you plan to use this code as C++ code, as new is a reserved word in C++). As you don't call malloc(3) in the function body, it's quite normal you get a problem from free(3), as free requires to be passed a pointer previously generated with malloc (and only once, so you cannot call it twice with the same pointer). This is the reason of you getting SIGABRT and the like. As a general rule, don't call free(3) in a function you don't also call malloc for the same pointer. That use is error prone and you will run into trouble more than once a day if you insist in doing everything in a single function.
better than apologize for the messy form of the code, please, clean it before posting. Post a minimu (meaning the minimum code that shows the error), full (meaning that we can compile it and observe the result you post as failing), verifiable (code that shows it producing the observed result, and not the expected one) and complete (this means we have nothing to do but compile it and run) code (so we can test it failing as you say, without having to correct it first) That way, we can make a diagnostic of what happens in your code. If we need to correct the code just to make it runnable, we can correct the main problem you are observing and be unable to see the mistake. You see? :)
Note about using new as an identifier in C code:
Many Unit Testing frameworks require your code to be compilable as C++ code, so it can be used by the framework (at least Google Test requires this) If you plan to write unit tests for your code, remember that new is a reserved word in C++ for the operator new, and so, your code will produce syntax errors if you try to compile it with a c++ compiler. Better if you don't use it.

C: Returning String From Another Function

I'm new to C / pointers / memory management and am having trouble implementing a few functions for a project I'm working on.
In my builtins.c file, I have a function called printalias that is called to print all the alias names and corresponding values stored in my program. At the end, I want to print one of the alias names by retrieving it via another function called getal.
int x_printalias(int nargs, char *args[]) {
int i = 0;
// Loop through, print names and values
for(i = 0; i< 100; i++)
{
if(alias_names[i][0]!='\0' && !alias_disabled[i])
{
char * var = alias_names[i];
char * val = alias_vals[i];
fprintf(stderr,"%s = %s\n", var, val );
}
}
// This is where I want to retrieve the string from another function
char * hello = "brett";
hello = getal(hello);
fprintf(stderr,"Got alias for brett --> %s",hello);
return 0;
}
My getal function exists in my shellParser.c file and looks like this, generally performing the same looping and returning when it is found:
const char * getal(int nargs, char *args[])
{
fprintf(stderr,"\nRetrieving alias...\n");
int i = 0;
fprintf(stderr, "check1\n" );
fprintf(stderr,"Got args[0]: %s\n", args[0]);
while (alias_names[i][0]!='\0' && i < MAX_ALIAS_LENGTH ) // Find empty slot in variables array
{
fprintf(stderr, "check2\n" );
fprintf(stderr,"I is currently %i and current varible in slot is %s\n",i,alias_names[i]);
//strncpy(hello, variables[i], MAX_VAR_LENGTH); // Variable at current slot
if(strcmp(alias_names[i], args[0]) == 0) // If we have an entry, need to overwrite it
{
fprintf(stderr,"Found alias %s = %s at spot %i\n",args[0],alias_vals[i], i); // Not at end if here
return alias_vals[i];
}
i++;
}
fprintf(stderr, "check3\n" );
// Elided....
return '\0';
}
In the end of my printalias function, I want to test that this getal function is working by calling it on a hardcoded string "brett". However, when I call my printalias function from the command line, it makes it to the "Check 1" print statement and then simply quits without error or return value.
I think this has something to do with my memory management or incorrect declaration of variables with pointers. Can anybody spot something (or a lot of things) that I'm doing wrong here?
You must declarete list of argument for to call getal and it call with these
list.
And pointer of return values getal must be const char*
//....
// This is where I want to retrieve the string from another function
char * hello[] = {"brett"}; // this list argument for getal function
const char *strGetal;
strGetal = getal(1,hello);
fprintf(stderr,"Got alias for brett --> %s",strGetal);
return 0;
}
Example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char** get_all(int argc, char **argv)
{
char *value;
char **values = NULL;
int i;
values = (char**) malloc(sizeof (char) * argc);
if (values == NULL) {
perror("malloc");
return NULL;
}
for (i = 0; i < argc; i++, argv++) {
value = strchr(*argv, ':');
values[i] = (value + 1);
}
return values;
}
int main()
{
char *args[] = {"key:a", "key:b", "key:c"};
char **values;
int i;
values = get_all(3, args);
for (i = 0; i < 3; i++) {
puts(values[i]);
}
return 0;
}

splitting a full filename into parts

I am creating a function that will split a full unix filename(like /home/earlz/test.bin) into its individual parts. I have got a function, and it works for the first two parts perfect, but after that it produces erroneous output...
strlcpy_char will copy a string using term as the terminator, as well as 0.
If it is terminated with term, then term will be the last character of the string, then null.
returns trg string length...
int strlcpy_char(char *trg,const char *src,int max,char term){
int i;
if(max==0){return 0;}
for(i=0;i<max-1;i++){
if(*src==0){
*trg=0;
return i;
}
if(*src==term){
*trg=term;
trg++;
*trg=0; //null terminate
return i+1;
}
*trg=*src;
src++;
trg++;
}
*trg=0;
return max;
}
.
int get_path_part(char *file,int n,char *buf){
int i;
int current_i=0;
//file is assumed to start with '/'so it skips the first character.
for(i=0;i<=n;i++){
current_i++;
current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');
if(current_i<=1){ //zero length string..
kputs("!"); //just a debug message. This never happens with the example
return -1; //not enough parts to the path
}
}
if(buf[current_i-1]=='/'){
return 1; //is not the last part
}else{
return 0; //is the last part(the file part)
}
}
I use this code to test it:
kputs("test path: ");
kgets(cmd);
kputs("\n");
char *tmp=malloc(256);
int i=0;
get_path_part(cmd,i,tmp);
kputs(tmp);
kputs("\n");
i=1;
get_path_part(cmd,i,tmp);
kputs(tmp);
kputs("\n");
i=2;
get_path_part(cmd,i,tmp);
kputs(tmp);
kputs("\n");
When I try something like "/home/test.bin" it works right outputting
/home
/test.bin
But when I try "/home/earlz/test.bin" I get
/home
/earlz
/arlz
Anyone see the problem in my code, as I've been looking but I just can't see any problem.
Also, before you say "but there is a library for that" I am doing this in an operating system kernel, so I barely have a standard library. I only have parts of string.h and really that's about it for standard.
You overwrite current_i instead of adding it up as you walk through the path.
So
current_i++;
current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');
should really be
current_i += strlcpy_char(buf,&file[current_i+1],MAX_PATH_PART_SIZE,'/');
I think you need to track your current_i for i>1 since the max value returned from the strlcpy has no idea of where you are in the overall file string. does it make sense?
current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');
Don't you need to do something like
tocurrent_i += strlcpy_char...
instead of
tocurrent_i = strlcpy_char...
Does your code have to be re-entrant?
If not use strtok, it is in strings.h
STRTOK(P)
NAME
strtok, strtok_r - split string into tokens
SYNOPSIS
#include <string.h>
char *strtok(char *restrict s1, const char *restrict s2);
char *strtok_r(char *restrict s, const char *restrict sep,
char **restrict lasts);
Sorry for not commenting on your code though :)
If you are using Glib, g_strsplit is very nice and easy to use.
This is how I'd do it
char ** split_into_parts(char *path) {
char ** parts = malloc(sizeof(char *) * 100);
int i = 0;
int j = 0;
if (*path == '/') {
path++;
}
parts[0] = 0;
while (*path) {
if (*path == '/') {
parts[i][j] = 0;
i++;
parts[i] = 0;
j = 0;
} else {
if (parts[i] == 0) {
parts[i] = malloc(sizeof(char) * 100);
}
parts[i][j] = *path;
j++;
}
path++;
}
parts[i+1] = 0;
return parts;
}
Try something like the code I have below.
If you need implementations of standard C functions (like strchr()) try koders.com or just google for strchr.c.
#include <stdio.h>
#include <string.h>
const char *NextToken(const char *pStart, char chSep, char *pToken, size_t nTokMax)
{
const char *pEnd;
size_t nLength;
/* set output to empty */
*pToken=0;
/* make sure input is OK */
if (!pStart || *pStart!=chSep)
return NULL;
/* find end of token */
pEnd = strchr(pStart+1, chSep);
if (pEnd)
nLength = pEnd - pStart;
else
nLength = strlen(pStart);
if (nLength >= nTokMax) /* too big */
return NULL;
strncpy(pToken, pStart, nLength);
pToken[nLength] = 0;
return pEnd;
}
int main()
{
#define BUFFSIZE 256
char cmd[BUFFSIZE];
char tmp[BUFFSIZE];
const char *pStart=cmd;
int i=0;
puts("test path: ");
fgets(cmd, BUFFSIZE, stdin);
puts("");
do {
pStart = NextToken(pStart, '/', tmp, BUFFSIZE);
if (tmp[0])
puts(tmp);
} while (pStart);
return 0;
}

Resources