Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
Sorry for the long codes. I'm developing a command program that tokenizing the user commands. If I enter "Hello world "hi guys" ",
temp[0] = "Hello"
temp[1] = "world"
temp[2] = "hi guys"
Now I'm struggling with the store_token function. I have to print tokens like this.
tokens[0] = "Hello"
tokens[1] = "world"
tokens[2] = "hi guys"
temp is a temporary bridge that stores cut tokens. So I write like this
(*tokens)[i] = (*temp)[i]
because 'tokens' is char *[], and I want to access the element of 'tokens'
to store the char * of the temp.
Here is some problem. I can't figure out the (*tokens)[i] and I don't know
what it directs.
This is a problem in code readability. You have some code that almost works; but, because you have not invested in readaility, you can't find your error.
I would start by clarifying the "reset / complete the token operation" and "add to token" operations. If any code flow doesn't do one or the other, there's a bug.
for (int i = 0; i < clen; i++) {
if (*command == *"\"" ) {
isnested = !isnested;
continue;
} else if (*command == *"\n") {
toklen = 0;
ntoken++;
} else if (isspace(*command) && !(isnested)) {
if (doublespace) {
*command++;
continue;
} else {
doublespace = true;
ntoken++;
toklen = 0;
}
} else {
doublespace = false;
temp[ntoken][toklen] = *command;
toklen++;
}
*command++;
}
*nr_tokens = ntoken;
Assuming that adding to toklen indicates "add to the token" and toklen = 0 signals a "complete the token" I would rewrite the code
for (int i = 0; i < clen; i++) {
if (*command == *"\"" ) {
isnested = !isnested;
continue;
} else if (*command == *"\n") {
completeToken();
} else if (isspace(*command) && !(isnested)) {
if (doublespace) {
*command++;
continue;
} else {
doublespace = true;
completeToken();
}
} else {
doublespace = false;
addToken(*command);
}
*command++;
}
*nr_tokens = ntoken;
And now a few things are clear.
There are code flows through your algorithm that don't add to the token and don't complete the token. These code flows need simplified and put up front (so everyone can see what isn't important).
You don't add the quotes to a isnested token. Maybe your lexer strips the quotes; but, if it does, there's no token completion when we are exit the quotes.
Your lexer handles new lines without regard to the isnested variable. Odds are it should be adding the newline to the string if inside a set of quotes.
Your lexer is very concerned with double spaces. Normally, if spaces are being stripped, they are stripped without regard to how many exist.
Even in small blocks of code, readability matters. C's history ignores modern readability rules, as the rules came out after the language; but, you would do well to apply them anyway, so you can get the modern readability benefits within the C operating enviornment.
Related
TL;DR -- what sorts of things might cause an execvp call to not fully function/ search the path properly?
I'm on the tail end of building a rudimentary shell with some quality of life features that I've added over time e.g. history, alias's, and completions. I built those features on top of a functional shell that had a working $PATH search for execution e.g. typing in "ls -la" produced the desired behavior. As you might imagine, I accomplished this just using execvp. (This is written in C if it's not already clear)
I have not changed any of my tokenizing logic and have ensured that the file name is correct; in particular, execvp was producing the desired behavior before I had added these features to my REPL. echo "hello" still produces a tokenized char **xyz and the first token is indeed echo, null-terminated, and so on. That is, my call still looks like, with variables filled-in, ... execvp("echo", argv); after which I call perror, which should only trigger when something has gone awry. Each time I just run the above command, though, since I've added in these features, it returns a failure with the no such file or directory --- before I added these features in though, the behavior was as desired. I'll note, though, that running /bin/echo "hello" runs as expected. Examples are WLOG.
I'm not sure where I should even start looking for errors, and my Google-fu has been mostly fruitless: any suggestions?
I'm initially going to omit code because it totals to several hundred lines and a MWE would not be particularly minimal in addition to my desires to keep this general rather than very particular to my code, though I'm not sure what's causing this. My repository is public and up-to-date, and I'm happy to post any code here.
EDIT:
I knew I wasn't explicitly editing the PATH variable, etc., but this block of code was the problem:
// Grab $PATH from env
char *pathvar = getenv("PATH");
if (pathvar) {
char *path;
int i;
// tokenize on colon to get paths
// then use that immediately to
// scandir, and add everything in
// there to the completions system
path = strtok(pathvar, ":");
while (path) {
struct dirent **fListTemp;
int num_files = scandir(path, &fListTemp, NULL, alphasort);
// only adding the names that are completely composed of
// lower case letters; completions are done using a naive
// Trie Node structure that only supports lowercase letters
// for now... e.g. g++ does not work, and the '+' leads to
// a seg-fault. Same holds for . and ..
for (i = 0; i < num_files; i++) {
char *curr = fListTemp[i]->d_name;
if (strcmp(curr, ".")==0 || strcmp(curr, "..")==0){
continue;
} else if (notalpha(curr)) {
continue;
} else {
str_tolower(curr);
tn_insert(completions, curr);
}
}
for (i = 0; i < num_files; i++) {
free(fListTemp[i]);
}
free (fListTemp);
path = strtok(NULL, ":");
}
} else {
fprintf(stderr, "{wsh # init} -- $PATH variable could not be found?");
}
Note that
The getenv() function returns a pointer to the value in the
environment, or NULL if there is no match.
so my original code was indeed tampering with the PATH variable. The solution I came up with quickly was just to create a copy of that string and use that to parse through the PATH instead:
// Grab $PATH from env
char *pathvar = getenv("PATH");
char *pathvar_cpy = strcpy(pathvar_cpy, pathvar);
if (pathvar_cpy) {
char *path;
int i;
path = strtok(pathvar_cpy, ":");
while (path) {
// Scan directory
struct dirent **fListTemp;
int num_files = scandir(path, &fListTemp, NULL, alphasort);
for (i = 0; i < num_files; i++) {
char *curr = fListTemp[i]->d_name;
if (strcmp(curr, ".")==0 || strcmp(curr, "..")==0){
continue;
} else if (notalpha(curr)) {
continue;
} else {
str_tolower(curr);
tn_insert(completions, curr);
}
}
for (i = 0; i < num_files; i++) {
free(fListTemp[i]);
}
free (fListTemp);
path = strtok(NULL, ":");
}
} else {
fprintf(stderr, "{wsh # init} -- $PATH variable could not be found?");
}
I'm trying to use realloc function in C, to dynamically operate on a char array of strings (char**).
I usually get a realloc():invalid old size error after 41st cicle of the for loop and I really can't understand why.
So, thanks to everyone who will help me ^-^
[EDIT] I'm trying to make the post more clear and following your advices, as a "new active member" of this community, so thank you all!
typedef struct _WordsOfInterest { // this is in an header file containing just
char **saved; // the struct and libraries
int index;
} wordsOfInterest;
int main() {
char *token1, *token2, *save1 = NULL, file[LEN], *temp, *word, **tokenArr;
int n=0, ch,ch2, flag=0, size, init=0,position,currEdit,init2=0,tempEdit,size_arr=LEN,
oldIndex=0,totalIndex=0,*editArr,counterTok=0;
wordsOfInterest toPrint;
char **final;
toPrint.index = 0;
toPrint.saved = malloc(sizeof(char*)*LEN);
editArr = malloc(sizeof(int)*LEN);
tokenArr = malloc(sizeof(char*)*LEN);
final = malloc(sizeof(char*)*1);
// external for loop
for(...) {
tokenArr[counterTok] = token1;
// internal while loop
while(...) {
// some code here surely not involved in the issue
} else {
if(init2 == 0) {
currEdit = config(token1,token2);
toPrint.saved[toPrint.index] = token2;
toPrint.index++;
init2 = 1;
} else {
if((abs((int)strlen(token1)-(int)strlen(token2)))<=currEdit) {
if((tempEdit = config(token1,token2)) == currEdit) {
toPrint.saved[toPrint.index] = token2;
toPrint.index++;
if(toPrint.index == size_arr-1) {
size_arr = size_arr*2;
toPrint.saved = realloc(toPrint.saved, size_arr);
}
} else if((tempEdit = config(token1,token2))<currEdit) {
freeArr(toPrint, size_arr);
toPrint.saved[toPrint.index] = token2;
toPrint.index++;
currEdit = tempEdit;
}
}
}
flag = 0;
word = NULL;
temp = NULL;
freeArr(toPrint, size_arr);
}
}
editArr[counterTok] = currEdit;
init2 = 0;
totalIndex = totalIndex + toPrint.index + 1;
final = realloc(final, (sizeof(char*)*totalIndex));
uniteArr(toPrint, final, oldIndex);
oldIndex = toPrint.index;
freeArr(toPrint,size_arr);
fseek(fp2,0,SEEK_SET);
counterTok++;
}
You start with final uninitialized.
char **final;
change it to:
char **final = NULL;
Even if you are starting with no allocation, it needs a valid value (e.g. NULL) because if you don't initialize a local variable to NULL, it gets garbage, and realloc() will think it is reallocating a valid chunk of memory and will fail into Undefined Behaviour. This is probably your problem, but as you have eliminated a lot of code in between the declaration and the first usage of realloc, whe cannot guess what is happening here.
Anyway, if you have indeed initialized it, I cannot say, as you have hidden part of the code, unlistening the recommendation of How to create a Minimal, Reproducible Example
.
There are several reasons (mostly explained there) to provide a full but m inimal, out of the box, failing code. This allows us to test that code without having to provide (and probably solving, all or part) the neccesary code to make it run. If you only post a concept, you cannot expect from us complete, full running, and tested code, degrading strongly the quality of SO answers.
This means you have work to do before posting, not just eliminating what you think is not worth mentioning.
You need to build a sample that, with minimum code, shows the actual behaviour you see (a nonworking complete program) This means eliminating everything that is not related to the problem.
You need (and this is by far more important) to, before sending the code, to test it at your site, and see that it behaves as you see at home. There are many examples that, when eliminated the unrelated code, don't show the commented behaviour.
...and then, without touching anymore the code, send it as is. Many times we see code that has been touched before sending, and the problem dissapeared.
If we need to build a program, we will probably do it with many other mistakes, but not yours, and this desvirtuates the purpose of this forum.
Finally, sorry for the flame.... but it is necessary to make people read the rules.
I am not C programmer but have recently taking interest in it. I am trying to modify a node of a YAML file using the C libyaml library. When I try to modify the node from an event scalar data the compiler doesn't complain but I get segmentation fault errors.
while (!done)
{
/* Get the next token. */
if (!yaml_parser_parse(&parser, &event))
goto parser_error;
//yaml_parser_scan(&parser, &token);
/* Check if this is the stream end. */
if(beginServerNodes && event.type == 8) {
beginServerNodes = 0;
}
if (event.type == YAML_SCALAR_EVENT) {
if(beginServerNodes == 1) {
//I WANT TO MODIFY THIS VALUE
printf("%s\n", event.data.scalar.value);
}
if(strcmp("servers",event.data.scalar.value) == 0) {
beginServerNodes = 1;
}
}
if (event.type == YAML_STREAM_END_EVENT) {
done = 1;
}
/* Emit the token. */
if (!yaml_emitter_emit(&emitter, &event))
goto emitter_error;
}
So while in that loop when I attempt to modify the following value
event.data.scalar.value
It must be of type yaml_char_t
yaml_char_t *newHost = "10.132.16.48:6379:1 redis-001";
event.data.scalar.value = newHost;
event.data.scalar.length = sizeof(newHost);
The compiler doesn't complain and the code run by dies with segementation fault. If have seen the examples in the libyaml test directories but nothing is intuitive as far as simply editing a node, at least not to a C newb like myself.
Libyaml expect that the values of each scalar can be removed via free(). So you need to initialize this value with malloc()ed memory:
const char* newHost = "10.132.16.48:6379:1 redis-001";
event.data.scalar.value = (yaml_char_t*)strdup(newHost);
event.data.scalar.length = strlen(newHost);
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I tried to use C array to write about the code below.
When I entered=>I am a boy.
The desired output is=>boy a am I.
Here is my code below and it does not work.
I have found the bugs for 2 days and I still could not find the problem
int i,j,k,m,start,temp_end,len;
char str[30];
gets(str);
len=strlen(str);
temp_end=len-1;
for(k=len;k>0;k--)
{
if(str[k]==" ")
start=k;
for(j=start+1;j<temp_end;j++)
{
printf("%c",str[j]);
}
printf(" ");
temp_end=k;
}
for(m=0;m<temp_end;m++)
{
printf("%2c.",str[m]);
}
As others have pointed out there are multiple problems with your code.
In the condition if(str[k]==" "), you are using " " to represent a single space character, which is incorrect. For a single space character you need to use ' '. A " " is a string with a single space character.
You are running the for(j=start+1;j<temp_end;j++) { … } loop every time. However this should be run only when a new space, ' ' is found. You can use a flag for this. Same with printf(" ");, and temp_end = k;
printf("%2c.", str[m]) looks to be like a typo. Why %2c.? You should be doing just "%c".
/* Use a flag to know when to print */
/* 0 means don't print, 1 means print */
int pflag = 0;
for(k = len-1; k >0; k--)
{
if(str[k]==' ') {
pflag = 1; /* new space found, update the flag to print */
start=k;
}
if (pflag == 1) /* check flag to see if print needed */
{
for(j=start+1;j<temp_end;j++)
{
printf("%c",str[j]);
}
printf(" ");
temp_end=k;
pflag = 0; /* reset flag after printing */
}
}
for(m=0;m<temp_end;m++)
{
printf("%c",str[m]);
}
Also, do NOT use gets(). Use fgets() or something else.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I tried to build and run the following program, but it breaks down executing. I thought maybe I made a mistake but 0 errors and 0 warnings were shown.
After researching such behavior on stackoverflow, I mostly saw some misplaced semicolons or forgotten address-operators, which I do not see in this source code or am I overlooking something?
Could some C or GCC Guru tell me what is wrong and why?
Operating system is Windows 7, and compiler had enabled:
-pedantic -w -Wextra -Wall -ansi
Here is the source code:
#include <stdio.h>
#include <string.h>
char *split(char * wort, char c)
{
int i = 0;
while (wort[i] != c && wort[i] != '\0') {
++i;
}
if (wort[i] == c) {
wort[i] = '\0';
return &wort[i+1];
} else {
return NULL;
}
}
int main()
{
char *in = "Some text here";
char *rest;
rest = split(in,' ');
if (rest == NULL) {
printf("\nString could not be devided!");
return 1;
}
printf("\nErster Teil: ");
puts(in);
printf("\nRest: ");
puts(rest);
return 0;
}
The expected behavior is that the string "Some text here" is split at its first space ' ' and the expected output would be:
Erster Teil: Some
Rest: text here
You are modifying a string literal, that's undefined behavior. Change this
char* in = "Some text here";
to
char in[] = "Some text here";
This makes in an array and initializes it with "Some text here". You should use const to prevent accidentally having this bug when you define a pointer to a string literal.