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

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.)

Related

Building a basic symbol table in C

I am currently building a symbol table program using C. It needs to stay as simple as possible while having the required functionality as I am expected to produce a working compiler by the end of the semester. I currently have a working implementation that creates entries into the symbol table from user input but it is not 100% where it needs to be. I just need some guidance based on the feedback I was given from my professor. I understand that there are some things I need to change, I am new to coding in C and I am also trying to learn Python and R at the same time so im a little overwhelmed. I know I need a separate initialize and print function, That there should be no Input or Output in the create function, and that every entry has a scope of 0. where I'm stuck at, is creating the functions for initialize and print without losing the current functionality that I already have. Any help is appreciated. Here is my current implementation of the code:
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
struct ADT {
char name[18]; // lexeme name
char usage;
char type; // I is integer, S is type string, I for identifier
int scope; // scope of where it was declared, inserted for later use
int reference;
};
typedef struct ADT new_type;
new_type table[200];
int i = 0;
int read(char *name, char usage, char type, char scope) { //Read function to read input and check for duplicates
for (int j = sizeof(table) / sizeof(table[0]); j >= 0; --j) {
if (strcmp(table[j].name, name) == 0 &&
table[j].usage == usage &&
table[j].type == type &&
table[j].scope == scope)
return 1; // found
}
return -1; // not found! that's good
}
int create( char *name, char usage, char type, char scope) { //Create function to insert new input into symbol table
strcpy(table[i].name, name);
table[i].usage = usage;
table[i].type = type;
table[i].scope = scope;
if (table[i].usage == 'I' && table[i].type == 'L')
table[i].reference = atoi(name);
else
table[i].reference = -1;
return i++;
}
int initialize(char *name, char usage, char type, char scope) { // Function to initialize the symbol table and clear it. also creates the fred lexeme
create("Fred", 'I', 'I', '0');
}
int print(char *name, char usage, char type, char scope) { // Print function to print the symbol table
printf("Nate's Symbol Table\n");
printf("#\t\tName\tScope\tType\tUsage\tReference\n");
for (int j = 0; j < i; j++) {
if (table[j].name == NULL)
break;
printf("%*d\t\t%*s\t%*d\t%*c\t%*c\t%*d\n", j, table[j].name, table[j].scope, table[j].type, table[j].usage, table[j].reference);
}
}
int main() { // Main function to take input and produce the symbol table lexemes
printf("Course: CSCI 490 Name: Nathaniel Bennett NN: 02 Assignment: A03\n");
printf("\n");
create("Fred", 'I', 'I', 0);
for (int j = 0; j < i; j++) {
if (table[j].name == NULL)
break;
printf("#\t\tName\tScope\tType\tUsage\tReference\n");
printf("%*d\t\t%*s\t%*d\t%*c\t%*c\t%*d\n", j, table[j].name, table[j].scope, table[j].type, table[j].usage, table[j].reference);
}
// keep asking for a lexeme until we type STOP or stop
while (1) {
char lexeme[256];
char nUsage;
char nType;
char nScope;
printf("Enter a lexeme: \n"); //enter lexeme name
scanf("%s", lexeme);
if (strcmp(lexeme, "stop") == 0) break;
printf("Enter its usage: \n");
scanf(" %c", &nUsage);
printf("Enter its type: \n");
scanf(" %c", &nType);
printf("Enter its scope: \n");
scanf(" %c", &nScope);
printf("%s, %c, %c, %c\n", lexeme, nUsage, nType, nScope);
create(lexeme, nUsage, nType, nScope);
for (int j = 0; j < i; j++) {
if (table[j].name == NULL)
break;
printf("%*d\t\t%*s\t%*d\t%*c\t%*c\t%*d\n", j, table[j].name, table[j].scope, table[j].type, table[j].usage, table[j].reference);
}
}
printf("Nate's Symbol Table\n");
printf("#\t\tName\tScope\tType\tUsage\tReference\n");
for (int j = 0; j < i; j++) {
if (table[j].name == NULL)
break;
printf("%*d\t\t%*s\t%*d\t%*c\t%*c\t%*d\n", j, table[j].name, table[j].scope, table[j].type, table[j].usage, table[j].reference);
}
return 0;
}```
...I think we're normally reluctant to get up in people's course assignments, but you seem like you have thought about this for a while Nate.
I can't quite make out what your instructor is suggesting. I do not see I/O in your code for the create() function. Unless the call to strcpy() is considered I/O in their view.
I do see some room for improvement in your print() function though. Your function relies upon a global entity (table) and then it ties your loop both to an imaginary value (what is "i" in your loop initialization?) AND to a condition where your logic asks effectively, "did I run out of table?"
Choose one condition or the other. There is a semantic elegance in simply printing everything you find in the table. You can make the function better if you pass a reference to the table rather than code to the existence of a static global value. So instead of passing all those values to your print() function, how about just one argument? Pass a reference to table, and your function could then be used for other similar dump operations. It becomes more generalized, and that's a good thing.
I would also say this. I prefer using sprintf() to stage my output in a string and then when everything is ready, I output it all at one time. This is easier to inspect and debug.
Also, not related to your assignment I imagine, but be extra-vigilant every time you use scanf() -- it was often my number one suspect whenever I had a bad pointer.
Definitely try to isolate or eliminate calls to chaotic functions like that one.
Keep thinking about how to make your function stronger, keep refactoring. You'll do great!
There are a number of issues. This won't even compile:
read conflicts with the syscall (i.e. rename it)
read has UB (undefined behavior) because it starts the for loop at one beyond the end of the table array
The symbol printing code is replicated everywhere. Better to define a table printing function (e.g. tblprint) and a symbol printing function (e.g. symprint).
The format used to print a symbol uses (incorrectly) variable precision format specifiers (e.g.) %*s expects two arguments: int len,char *str With -Wall as a compile option, these statements are flagged.
AFAICT, ordinary format specifiers work fine.
The if (sym->name == NULL) will never be valid because it is a fixed length array. We need to use a char *.
Using i as a global for the count of the array is misleading. Try something more descriptive (e.g.) tabcount
Using table[i].whatever everywhere is cumbersome. Try using a pointer (e.g. sym->whatever)
initialize [and some others] need a return with a value.
I've used cpp conditionals to denote old code vs new code:
#if 0
// old code
#else
// new code
#endif
Here is the refactored code. It is annotated. It compiles cleanly and passes a rudimentary test:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
struct ADT {
// NOTE/BUG: the if (sym->name == NULL) will fail
#if 0
char name[18]; // lexeme name
#else
const char *name; // lexeme name
#endif
char usage;
// I is integer, S is type string, I for identifier
char type;
// scope of where it was declared, inserted for later use
int scope;
int reference;
};
#if 0
typedef struct ADT new_type;
new_type table[200];
#else
typedef struct ADT ADT;
ADT table[200];
#endif
int tabcount = 0;
// NOTE/BUG: "read" conflicts with a syscall name
#if 0
//Read function to read input and check for duplicates
int
read(char *name, char usage, char type, char scope)
#else
// find_entry -- find a matching entry (if it exists)
int
find_entry(char *name, char usage, char type, char scope)
#endif
{
// NOTE/BUG: this is UB (undefined behavior) because you're starting at one
// past the end of the array
#if 0
for (int j = sizeof(table) / sizeof(table[0]); j >= 0; --j) {
#else
for (int j = tabcount - 1; j >= 0; --j) {
#endif
ADT *sym = &table[j];
if (strcmp(sym->name, name) == 0 &&
sym->usage == usage &&
sym->type == type &&
sym->scope == scope)
return 1;
}
// not found! that's good
return -1;
}
//Create function to insert new input into symbol table
int
create(char *name, char usage, char type, char scope)
{
ADT *sym = &table[tabcount];
// NOTE/BUG: this needs to be a pointer to a string to allow long strings and
// for "if (sym->name == NULL)" to be valid
#if 0
strcpy(sym->name, name);
#else
sym->name = strdup(name);
#endif
sym->usage = usage;
sym->type = type;
sym->scope = scope;
if (sym->usage == 'I' && sym->type == 'L')
sym->reference = atoi(name);
else
sym->reference = -1;
return tabcount++;
}
// Function to initialize the symbol table and clear it. also creates the fred
// lexeme
int
initialize(char *name, char usage, char type, char scope)
{
create("Fred", 'I', 'I', '0');
return 0;
}
void
symprint(ADT *sym)
{
int j = sym - table;
// NOTE/BUG: with (e.g) %*d this is variable precision field -- it requires
// _two_ arguments: <int wid>,<int val>
#if 0
printf("%*d\t\t%*s\t%*d\t%*c\t%*c\t%*d\n",
j, sym->name, sym->scope, sym->type,
sym->usage, sym->reference);
#else
printf("%d\t\t%s\t%d\t%c\t%c\t%d\n",
j, sym->name, sym->scope, sym->type,
sym->usage, sym->reference);
#endif
}
void
tblprint(int title)
{
if (title)
printf("#\t\tName\tScope\tType\tUsage\tReference\n");
for (int j = 0; j < tabcount; j++) {
ADT *sym = &table[j];
if (sym->name == NULL)
break;
symprint(sym);
}
}
// Print function to print the symbol table
int
print(char *name, char usage, char type, char scope)
{
printf("Nate's Symbol Table\n");
tblprint(1);
return 0;
}
// Main function to take input and produce the symbol table lexemes
int
main()
{
printf("Course: CSCI 490 Name: Nathaniel Bennett NN: 02 Assignment: A03\n");
printf("\n");
create("Fred", 'I', 'I', 0);
tblprint(1);
// keep asking for a lexeme until we type STOP or stop
while (1) {
char lexeme[256];
char nUsage;
char nType;
char nScope;
// enter lexeme name
printf("Enter a lexeme: \n");
scanf("%s", lexeme);
if (strcmp(lexeme, "stop") == 0)
break;
printf("Enter its usage: \n");
scanf(" %c", &nUsage);
printf("Enter its type: \n");
scanf(" %c", &nType);
printf("Enter its scope: \n");
scanf(" %c", &nScope);
printf("%s, %c, %c, %c\n", lexeme, nUsage, nType, nScope);
create(lexeme, nUsage, nType, nScope);
tblprint(0);
}
printf("Nate's Symbol Table\n");
tblprint(1);
return 0;
}

Creating a gets function that returns a char* on return pressed using a getchar function

I had this problem for a while. I cannot create a gets(int maxChar) function that allows the user to get a char* using a get_char() function.
My current code:
char* gets(int maxChar) {
char a;
char* b;
int i;
for(i = 0; i<maxChar; i = i + 1){
a = getchar();
if (a != 0x0D) {putchar(a);} else {puts("\r\n");break;}
b[sizeof(b)] = a;
}
if (sizeof(b) > maxChar-1) {puts("\r\n");}
return b;
//if (b[sizeof(b)-1] != '')
}
the get_char() function works perfectly.
Full kernel.c: https://pastebin.com/3bqTbHsv
Change
char* b;
to
char* b = (char *)malloc(maxChar);
and
b[sizeof(b)] = a;
to
b[i] = a;
Also, change
if (sizeof(b) > maxChar-1) {puts("\r\n");}
to
if (i > maxChar-1) {puts("\r\n");}
The changes made are:
You have created a pointer, but have not allocated any memory. Hence the malloc statement.
sizeof(b) will always be 4 for a 32-bit compiler. You need the array index, given by i.
Same as 2.
These are the basic changes you need to make, without any change to your logic.
The following is an educated guess at what you were attempting to do. It includes some explanations, and a calling example:
char* _gets(int maxChar) // change name to avoid conflict with library 'gets'
{
//char a; //addresses concern pointed out in comments.
int a; //variable is used by function that can return EOF (-1)
int i=0; //
char* b = calloc(maxChar + 1, 1);//point pointer to memory (that it owns)
if(b) //test for success before going on
{
for(i = 0; i<maxChar-3; i = i + 1) // -3 to leave room for \r\n\0
{
a = getchar();
if ((a != 0x0D) && (a != EOF))
{
putchar(a);
b[i]=a;// place char into string accumulator
}
else break;// either EOF or <ENTER> occurred
} // no need for else in this implementation as we can handle
// the clean up once the conditions are met by outputting
// user's entries, then preparing your string (array) for return
puts("\r\n");
b[i++]='\r';
b[i++]='\n';
b[i++]='\0';
}
else return NULL;//failed to allocate memory. leave
return b; // return b (now a string)
//if (b[sizeof(b)-1] != '') // not sure what this was for
}
int main(void)
{
char *strArray = _gets(10);//creates memory for strArray
if(strArray) //use only if not NULL
{
printf("The array contains: %s", strArray );
free(strArray );
}
return 0;
}

Trying to figure out why my struct pointer is storing data inaccurately

I am trying to improve my C skills so I apologize if my question is long. I am having a hard time understanding as to why my struct pointer holds the wrong value in my program, I tried to debug it but I am still relatively new to C and was hoping one of you could tell me what I'm doing wrong here and how I could improve my code and what to focus on.
I am making a program that stores user data on this struct and then prints it out.
typedef struct table {
char *firstName;
char *lastName;
int id;
}USER;
This function below stores the first name
void firstName(int *counter, int *check, USER *pt) {
for (int i = *counter; i < *check; i++) {
pt[i].firstName = calloc (MAX_LENGTH, sizeof(pt));
printf("Enter First Name: ");
getchar();
fgets(pt[i].firstName, MAX_LENGTH, stdin);
}
}
This is just my bool function returning true or false
bool isTrue(char *decision) {
if(*decision == 'Y') {
return true;
}
else {
return false;
}
}
And this is my main
int main(int argc, char **argv) {
USER *pt = calloc(1, sizeof(pt));
int counter = 0, check = 0;
char decision = '\0';
while (1) {
printf("Would you like to enter a user?(Y/N):");
fgets(&decision, 2, stdin);
strtok(&decision, "\n"); //remove the newline char
if (!isTrue(&decision)) {
break;
}
if (counter != 0) {
pt = realloc(pt, sizeof(pt) * 10); //the 10 is temporary
}
check = counter + 1; // make sure loop only runs once.
firstName(&counter, &check, pt);
++counter; // increment counter;
}
printStruct(pt, &counter);
return 0;
}
When I run it out sometimes it works fine and returns everything and sometimes it skips a value. This is what I get. It skips the value at pointer index 1 and prints garbage instead.
Would you like to enter a user?(Y/N):N
First name at array 0 is Ermir
First name at array 1 is P#1First name at array 2 is Kevin
First name at array 3 is Blaus
First name at array 4 is Adam
Also I was wondering why is it when I realloc here If i do I get a realloc error when I enter the second name.
if (counter != 0) {
pt = realloc(pt, sizeof(pt) * 10); //realloc(pt, sizeof(pt) * counter + 1) wont work
}
char decision = '\0';
...
fgets(&decision, 2, stdin);
You are only allocating 1 char but are at least reading 2 chars into it. Fix by allocating a sufficiently sized array for decision.
Unrelated but in firstName() pt[i].firstName = calloc (MAX_LENGTH, sizeof(pt)); should be pt[i].firstName = calloc (MAX_LENGTH, 1);

using functions in c (return value)

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");
}

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