Blank Line Seg. Fault (Writing Own Shell) - c

I'm still learning about writing simple shell.
I want this shell to allow blank lines and comments.
I did some coding and I encountered a problem that if I directly just input enter (blank line), it directly seg.fault core dumped.
I don't know exactly where's the mistake, because I print everything and all seems fine. The only thing that I suspicious in these line
if (args[0] == NULL || !(strncmp(args[0],"#",1))) {
exitstat = 0;
}
I got the args from basic split command function. The weird thing is the comments works just fine.
Below is my functions for read what user input and split them (tokenize if I'm not mistaken). They are really basic because I'm learn those functions from internet tutorial.
char *commandInput() {
char *command = NULL;
ssize_t bufsize = 0;
getline(&command, &bufsize, stdin);
return command;
}
char **splitLine(char *command) {
int bufsize = 64,
int position = 0;
char **tokens = malloc(bufsize * sizeof(char*));
char *token;
token = strtok(command, DELIMITER);
while (token != NULL) {
tokens[position] = token;
position++;
if (position >= bufsize) {
bufsize += 64;
tokens = realloc(tokens, bufsize * sizeof(char*));
}
token = strtok(NULL, DELIMITER);
}
tokens[position] = NULL;
return tokens;
}
Anybody could help me recognize what makes it seg.fault if I enter blank line? Thank you.
EDIT
I used debugger (finally succeed to use it after several trial) and it turns out that the error is located at the line that I didn't expect to cause any problem (see ---UPDATE----).
They way I handle my commandInput function is in main() function, I write
int main () {
......
char * command = NULL
char **args;
command = commandInput();
args= splitLine(command);
------------------ UPDATE!(CAUSING ERROR IF STATEMENT) ---------------
background = 0
numbarguments = 0
// Condition to check whether there is a start program running in backgrond
if (!(strncmp(args[numbarguments - 1], "&",1))) {
background = 1;
args[numbarguments - 1] = NULL;
}
----------------------------------------------
if (args[0] == NULL || !(strncmp(args[0],"#",1))) {
exitstat = 0;
}
....... //(comparing the arguments other than null)
}
So any advice regarding that if condition that causing me seg.fault. Thank you.

The parameter you pass to splitline is modified. strtok has the effect of modifying the string it gets by inserting \0's and returning a pointer to substrings. What strtok returns is not something you can directly store for later use, instead you need to make a copy of it.
token = strtok(command, DELIMITER);
while (token != NULL)
{
tokens[position] = malloc(strlen(token)+1);
strcpy(tokesn[position],token);
...
so in other words it is not enough to allocate the array of pointers to strings, you also need to allocate space to hold strings that you tokenize with strtok.
The code
if (!(strncmp(args[numbarguments - 1], "&",1))) {
background = 1;
args[numbarguments - 1] = NULL;
}
looks wrong, numberarguments is initially 0 so you are comparing args[-1] with "&" then later you assign args[-1] = NULL which probably causes the seg fault.

Related

I have a 'Segmentation Problem' while printing parsed parts of a String

I am writing a simple Shell for school assignment and stuck with a segmentation problem. Initially, my shell parses the user input to remove whitespaces and endofline character, and seperate the words inside the input line to store them in a char **args array. I can seperate the words and can print them without any problem, but when storing the words into a char **args array, and if argument number is greater than 1 and is odd, I get a segmentation error.
I know the problem is absurd, but I stuck with it. Please help me.
This is my parser code and the problem occurs in it:
char **parseInput(char *input){
int idx = 0;
char **parsed = NULL;
int parsed_idx = 0;
while(input[idx]){
if(input[idx] == '\n'){
break;
}
else if(input[idx] == ' '){
idx++;
}
else{
char *word = (char*) malloc(sizeof(char*));
int widx = 0; // Word index
word[widx] = input[idx];
idx++;
widx++;
while(input[idx] && input[idx] != '\n' && input[idx] != ' '){
word = (char*)realloc(word, (widx+1)*sizeof(char*));
word[widx] = input[idx];
idx++;
widx++;
}
word = (char*)realloc(word, (widx+1)*sizeof(char*));
word[widx] = '\0';
printf("Word[%d] --> %s\n", parsed_idx, word);
if(parsed == NULL){
parsed = (char**) malloc(sizeof(char**));
parsed[parsed_idx] = word;
parsed_idx++;
}else{
parsed = (char**) realloc(parsed, (parsed_idx+1)*sizeof(char**));
parsed[parsed_idx] = word;
parsed_idx++;
}
}
}
int i = 0;
while(parsed[i] != NULL){
printf("Parsed[%d] --> %s\n", i, parsed[i]);
i++;
}
return parsed;
}
In your code you have the loop
while(parsed[i] != NULL) { ... }
The problem is that the code never sets any elements of parsed to be a NULL pointer.
That means the loop will go out of bounds, and you will have undefined behavior.
You need to explicitly set the last element of parsed to be a NULL pointer after you parsed the input:
while(input[idx]){
// ...
}
parsed[parsed_idx] = NULL;
On another couple of notes:
Don't assign back to the same pointer you pass to realloc. If realloc fails it will return a NULL pointer, but not free the old memory. If you assign back to the pointer you will loose it and have a memory leak. You also need to be able to handle this case where realloc fails.
A loop like
int i = 0;
while (parsed[i] != NULL)
{
// ...
i++;
}
is almost exactly the same as
for (int i = 0; parsed[i] != NULL; i++)
{
// ...
}
Please use a for loop instead, it's usually easier to read and follow. Also for a for loop the "index" variable (i in your code) will be in a separate scope, and not available outside of the loop. Tighter scope for variables leads to less possible problems.
In C you shouldn't really cast the result of malloc (or realloc) (or really any function returning void *). If you forget to #include <stdlib.h> it could lead to hard to diagnose problems.
Also, a beginner might find the -pedantic switch helpful on your call to the compiler. That switch would have pointed up most of the other suggestions made here. I personally am also a fan of -Wall, though many find it annoying instead of helpful.

Segmentation fault in accessing & modifying string (char*) array in C

I've been getting segmentation faults (with gdb printing "??" on backtraces) on a program I'm trying to compile for a while now and after trying many things (such re-programming a data structure I used which should work now) I still kept getting segfaults although now it gave me a line (which I added a comment onto here).
getMains() is ran multiple times to tokenize different lines from the same file.
I wanted mains to be an array of size 4 but when passing it as "char * mains[4]" I I got a compile error for trying to pass it an array (*)[4] which I've never dealt with beforehand (Just started using C). I'm assuming maybe that could be a problem if I try to access any part that wasn't used, but the problem happens while initializing the indices of the array.
The code I'm trying to get to work, where the "char *** mains" argument is taking in a &(char **) from a separate function "runner" which I want to be edited so I can look at its contents in "runner":
bool getMains(FILE * file, char *** mains)
{
char line[256];
int start = 0;
char * token;
const char * mainDelim = "\t \n\0", * commDelim = "\n\t\0";
if(fgets(line, sizeof(line), file) == NULL)
return false;
while(line[0] == '.')
if(fgets(line, sizeof(line), file) == NULL);
return false;
if(line[0] == '\t' || line[0] == ' ')
{
(*mains)[0] = " ";
start = 1;
}
token = strtok(line, mainDelim);
int i;
for(i = start; token != NULL; ++i)
{
(*mains)[i] = strdup(token); // <- gdb: Segmentation Fault occurs here
if(i % 3 == 2)
token = strtok(NULL, commDelim);
else
token = strtok(NULL, mainDelim);
}
free(token); // Unsure if this was necessary but added in case.
return true;
}
/* Snippet of code running it... */
void runner(FILE * file) {
char ** mains;
if(!getMains(*file, &mains))
return;
while(strcmp(mains[1], "END") != 0){
/* do stuff lookinig through indices 0, 1, 2, & 3 */
if(!getMains(*file, &mains))
break;
}
}
Any tips on this or just generally safely modifying arrays through other functions?
Should I change getMains() into "getMains(FILE * file, char ** mains[4]);" and pass it a &"char * mains[4]") for it to be a set size as wanted? Or would that also produce errors?
You need to allocate memory for mains, it should look like this:
char ** mains;
mains = malloc(some number N * sizeof(char*));
You need something like this if you don't use strdup, which allocates the memory for you:
for (int i = 0; i < N; ++i) {
mains[i] = malloc(some number K);
}
In all cases, do not forget to call free on every pointer you received from malloc or strdup. You can skip this part if the program ends right after you would call free.

Taking of the last word in the line with strtok

Given a file with the following line:
word1 word2 word3 word4
I tried to write the following code:
FILE* f = fopen("my_file.txt", "r");
char line[MAX_BUFFER + 1];
if (fgets(line, MAX_LENGTH_LINE, f) == NULL) {
return NULL;
}
char* word = strtok(line, " ");
for (int i = 0; i < 4; i++) {
printf("%s ", word);
word = strtok(NULL, " ");
}
For prints the "words".
It's working. But, I don't understand something.
How it's acheive the last word word4? (I don't understand it because that after "word4" not exists a space)..
I'm not quite sure what you're asking. Are you asking how the program was able to correctly read word4 from the file even though it wasn't followed by a space? Or are you asking why, when the program printed word4 back out, it didn't seem to print a space after it?
The answer to the first question is that strtok is designed to give you tokens separated by delimiters, not terminated by delimiters. There is no requirement that the last token be followed by a delimiter.
To see the answer to the second question, it may be more clear if we adjust the program and its printout slightly:
char* word = strtok(line, " ");
for (int i = 0; word != NULL; i++) {
printf("%d: \"%s\"\n", i, word);
word = strtok(NULL, " ");
}
I have made two changes here:
The loop runs until word is NULL, that is, as long as strtok finds another word on the line. (This is to make sure we see all the words, and to make sure we're not trying to treat the fourth word specially in any way. If you were trying to treat the fourth word specially in some way, please say so.)
The words are printed back out surrounded by quotes, so that we can see exactly what they contain.
When I run the modified program, I see:
0: "word1"
1: "word2"
2: "word3"
3: "word4
"
That last line looks very strange at first, but the explanation is straightforward. You originally read the line using fgets, which does copy the terminating \n character into the line buffer. So it ends up staying tacked onto word4; that is, the fourth "word" is "word4\n".
For this reason, it's often a good idea to include \n in the set of whitespace delimiter characters you hand to strtok -- that is, you can call strtok(line, " \n") instead. If I do that (in both of the strtok calls), the output changes to
0: "word1"
1: "word2"
2: "word3"
3: "word4"
which may be closer to what you expected.
Your code doesn't check the return value of strtok(), it may be unsafe in some cases.
/* Split string
#content origin string content
#delim delimiter for splitting
#psize pointer pointing at the variable to store token size
#return tokens after splitting
*/
const char **split(char *content, const char *delim, int *psize)
{
char *token;
const char **tokens;
int capacity;
int size = 0;
token = strtok(content, delim);
if (!token)
{
return NULL;
}
// Initialize tokens
tokens = malloc(sizeof(char *) * 64);
if (!tokens)
{
exit(-1);
}
capacity = 64;
tokens[size++] = token;
while ((token = strtok(NULL, delim)))
{
if (size >= capacity)
{
tokens = realloc(tokens, sizeof(char *) * capacity * 2);
if (!tokens)
{
exit(-1);
}
capacity *= 2;
}
tokens[size++] = token;
}
// if (size < capacity)
// {
// tokens = realloc(tokens, sizeof(char *) * size);
// if (!tokens)
// {
// exit(-1);
// }
// }
*psize = size;
return tokens;
}

execv() system call creating stacking smashing error

Whenever I used the execv() here in my code, it works and has no errors, but still causes stack smashing to crash the program during runtime. Am I doing anything wrong here?
Here is the function with the execv():
void execute(char *args[], char *cmd[])
{
pid_t pid;
int status;
char bin[10] = "/bin/";
pid = fork();
// child process
if(pid == 0)
{
strcat(bin, cmd[0]);
execv(bin, args);
} else{
perror("error");
while(wait(&status) != pid);
}
}
here is where I am getting args and cmd from. Would it possibly be caused by something I did here?
void parseString(char *command)
{
char **args = malloc(sizeof(char*) * 16);
int i = 0;
int j = 0;
char *cmd[1];
// split each command by semicolons if necessary, then send each sub command to parseString()
if(strchr(command, ';')) {
char *semi_token = strtok(command, ";");
while(semi_token != NULL){
args[i] = semi_token;
semi_token = strtok(NULL, " ");
parseString(args[i]);
i++;
}
} else {
// if no semi colons, split the commandby spaces and call execute() using the args and cmd
char *token = strtok(command, " ");
while(token != NULL)
{
args[i] = token;
args[++i] = NULL;
while(j == 0 && token != NULL) {
cmd[0] = token;
cmd[1] = NULL;
j++;
}
token = strtok(NULL, " ");
}
execute(args, cmd);
}
j = 0;
i = 0;
free(args);
}
function call happens here. command is input from stdin from the user. Only need basic commands all located in /bin/. something like ls -l or cat file.
while(1){
command = getCommand();
parseString(command);
}
You have two serious errors: One that will lead to out-of-bounds writing of an array, and one that will probably lead to that.
The first, the certain out-of-bounds writing, is in the parseString function. First you have the declaration of the cmd variable:
char *cmd[1];
This defines cmd as an array of one element. Then you do
cmd[0] = token;
cmd[1] = NULL;
which writes to two elements of the one-element array. Writing out of bounds leads to undefined behavior.
The second error is in the execute function, and is the one I talked about in my first comment. There you have
char bin[10] = "/bin/";
That defines bin as an array of ten characters, and you fill up six of them (don't forget the string terminator). In the child-process you do
strcat(bin, cmd[0]);
which appends to string in cmd[0] to the string in bin. The problem here is that bin only have space for ten characters, of which six is already used (as explained above). That means there's only space left for four characters. If the command is any longer than that you will also go out of bounds and again have undefined behavior.
The solution to the first error is simply, make cmd an array of two elements. The solution to the second error is to either make bin larger, and not concatenate more than can fit in the array; Or to allocate the array dynamically (not forgetting space for the terminator).
There are also lot of other potential problems with your code, like the limit on 16 pointers for args. And that you don't really parse arguments in the parseString function, every argument is seen as a separate command. And the memory leak in the case there are semicolon-separated "commands". Or that you don't check for or handle errors everywhere needed. And that you use errno even if there's no error.

How would I read a line, parse the information and then attribute it to a struct in C?

I am currently trying to understand how to go through a .txt file in C and I think I have mostly everything worked out but what I need to do is kind of confusing. I need to create an array of Pointers to point to structs.
Each line in my .txt file should have information corresponding to a single struct. Each line should start with a name followed by some float values.
My question is, when I read the lines and parse them using strtok first, how would I get that information in a struct?
second how would I then make the sample pointer at index i point to the struct?
I tried doing the name seperate from the numbers since the numbers need their own special atof conversion since initially it will be a string. However I think this is probably incorrect since I want to read multiple lines, the code I have before the while loop for obtaining the name will only run once so any following lines will not have the name seperated. I can technically delimit my text file as I choose, so maybe I can just seperate the name with a semicolon and the rest spaces?
If this question seems confusing its probably because I am over thinking
Should I be declaring a struct such as : Sample tmp;
I've been reading examples but I can't figure out how to put the information together. Let me know if I declared my array of pointers incorrectly... Which I think I did. I think my the line that says:
sample arr[SIZE] = {NULL}; might be incorrect but I am not sure. if you can help me work out the logic behind all this I would appreciate it. Thanks.
typedef struct sample{
char* name;
int list_len;
float* value_list;
}sample;
void read_and_parse(){
const int SIZE = 1024;
sample* sample = (sample*)malloc(sizeof(sample); //pointer allocation?
FILE* fin;
fin = fopen("record.txt", "r");
if (fin == NULL) {
printf("record.txt could not be opened \n");
exit(1);
}
else {
int i= 0;
sample arr[SIZE] = {NULL}; //here I try to make the array of pointers
char linebuf[SIZE];
token = strtok(linebuf, " "); //grab the first item
while (fgets(linebuf, SIZE, fin) && i<SIZE) {
arr[i] = malloc(sizeof(sample));
arr[i.name] = token;
token = strtok(NULL, " ");
// now parse the linebuf and fill arr[i] with it
i++;
}
Edited: 11/02/2017
any print statements you see are just silly markers I placed for testing and recognizing what is running when I finally get this code compiled
Here is a much better edited version of the code. I think it should work now.
typedef struct sample{
char* name;
int list_len;
float* value_list;
}sample;
void read_and_parse(FILE **fin, sample* arr[]){
const int SIZE = 1024;
if (*fin == NULL) {
printf("record.txt could not be opened \n");
exit(1);
}
else {
printf("successfully opened file\n");
char linebuf[SIZE];
while ( fgets(linebuf, SIZE, fin) ) {
arr[i] = malloc(sizeof(sample));
int floats_per_line = 0;
while(linebuf[i]){
if(linebuf[i] == ' ');
++floats_per_line;
}
arr[i]->list_len = values_per_line;
arr[i]->value_list = (float*)malloc(sizeof(float)*floats_per_line);
arr[i]->name = strdup(strtok(linebuf, ' '));
char* tok;
int j = 0
while(tok = strtok(NULL, ' ')){
arr[i]->value_list[j] = atof(tok);
++j
}
i++;
}
}
fclose(fin);
}
How would I read a line, parse the information and then attribute it to a struct ?
Read with fgets() which converts a line of file input into a string. OP does that well. Then parse the string.
when I read the lines and parse them using strtok first, how (to) get that information in a struct?
Should I be declaring a struct such as : sample tmp;
Pass the string to a helper function to parse it into a sample that can hold any input. So the pointer members of tmp need to point to maximal space.
char name[SIZE];
char f[SIZE/2];
sample tmp = { name, 0, f };
while (i<SIZE && fgets(linebuf, SIZE, fin)) {
if (sample_parse(&tmp, linebuf) == NULL) {
break; // Parsing failed for some reason, perhaps an error message?
}
// Now populate arr[i] with right-sized memory allocations
arr[i].name = strdup(tmp.name); // ToDo: add NULL check
arr[i].list_len = tmp.list_len;
size_t f_size = sizeof *(tmp.value_list) * tmp.list_len;
arr[i].value_list = malloc(f_size); // ToDo: add NULL check
memcpy(arr[i].value_list, tmp.value_list, f_size);
i++;
}
so maybe I can just separate the name with a semicolon and the rest spaces?
Yes. Also allow other white-spaces too.
if I declared my array of pointers incorrectly.
Code does not have an array of pointers anywhere.
Recommend using size_t for array size type.
typedef struct sample {
char* name;
// int list_len;
size_t list_len;
float* value_list;
} sample;
Some untested code for parsing. Parse the line with strtok(). Further parse the number tokens with strtof().
#define sample_NAME_DELIMITER ":"
#define sample_NUMBER_DELIMITER " \n\t\r"
// parse for a name and then 0 or more numbers
static sample *sample_parse(sample *dest, char *linebuf) {
char *s = strtok(linebuf, sample_NAME_DELIMITER);
if (s == NULL) {
return NULL; // no name - TBD on if this is allowed
}
strcpy(dest->name, s);
size_t i = 0;
while ((s = strtok(NULL, sample_NUMBER_DELIMITER)) != NULL) {
char *endptr;
dest->value_list[i] = strtof(s, &endptr);
if (s == endptr || *endptr) {
// conversion failed or extra junk
break;
}
i++;
}
dest->list_len = i;
return dest;
}

Resources