#include <string.h>
#include <stdio.h>
#include <unistd.h>
#define MAX_LIMIT 20
int main () {
printf("Shell> ");
char str1[MAX_LIMIT];
fgets(str1, MAX_LIMIT, stdin);
char delim[] =" ";
char *parsed;
int index = 0;
char *cmd[index];
parsed = strtok(str1,delim);
while( parsed != NULL) {
cmd[index] = parsed;
index++;
parsed = strtok(NULL, delim);
}
cmd[index] = NULL;
int wow = fork();
if(wow == 0){
execvp((char*)cmd[0],cmd);
}
return(0);
}
Everything above is working fine except execvp()
cmd[0] receives string 'ls', technically execvp() should display the list but it doesn't.
If I replace cmd[0] with 'ls', it works.
You have two main issues here.
First when you declare your array as char *cmd[index];, the size of the array is not tied to current value of index as index changes. It sets the size to the current value of index which is 0. Creating an array of size 0 invokes undefined behavior. You need to set a fixed size for the array that will be big enough for your needs.
char *cmd[MAX_LIMIT];
The other problem is your choice of delimiters. The fgets function reads a line of text including the newline at the end of the input. So whichever parameter is read last will have a \n at the end of it. To fix this, add \n to the delimiter list.
char delim[] =" \n";
Related
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int n_of_words = 0;
#define MAX_STR_SZ 256
// asking for user input
char string[50];
printf("\nPlease input a string of text.\n\n");
fgets(string, MAX_STR_SZ, stdin);
char * words[n_of_words];
// extracting the first word
words[n_of_words] = strtok(string, " ");
printf("\n%i %s\n", n_of_words, words[n_of_words]);
// looping through the string to extract all other words
while( words[n_of_words] != NULL )
{
n_of_words ++;
words[n_of_words] = strtok(NULL, " ");
printf("\n%i %s\n", n_of_words, words[n_of_words]);
}
sleep(10);
return 0;
}
I'm very new to programming, but I was trying to write a function to extract words from a user inputted string and save them in an array for later use in the program. I added the 2 printf lines of code to see if it was working properly.
I always get a segmentation fault error after the second iteration of the while loop.
Also, somehow this problem didn't present itself when I compiled the same code on the CS50 ide (Cloud9), but it happens in any other case.
Few issues which can be resolved to prevent segmenatation fault:
No string.h header in the source code for strtokfunction
#include <stdio.h>
#include <unistd.h>
Macros are generally declared in the top of the source code and not inside any function
#define MAX_STR_SZ 256
The char string array is of length 50 but the fgets is allowing 256 and can lead to bufferoverflow.
char string[50];
printf("\nPlease input a string of text.\n\n");
fgets(string, MAX_STR_SZ, stdin);
The value of the variable n_of_words is 0. So, the declaration
char * words[n_of_words];
Will not create an array of the desired length.
The root cause of your question lies here:
while( words[n_of_words] != NULL )
{
n_of_words ++;
words[n_of_words] = strtok(NULL, " ");
printf("\n%i %s\n", n_of_words, words[n_of_words]);
}
You are accessing a memory location which was never declared,
n_of_words ++;
words[n_of_words] = strtok(NULL, " "); //words[1] or any index was never declared.
Every C program gets for free a list of the command line parameters, in general declared as int main(int argc, char* argv[]); or int main(int argc, char** argv);
This is precisely what you are trying to replicate with int n_of_words and char* words[n_of_words];
But you are doing it the wrong way.
A first note on this 3 lines from your code:
#define MAX_STR_SZ 256
char string[50];
fgets(string, MAX_STR_SZ, stdin);
You are setting 256 as the limit for fgets() to read, but you have only 50 chars in string. Many times it will work in this case, since you are reading from the keyboard and many of us would not key more than a few words in, but you have a problem. Change the limits.
strtok() is probably not the best one to choose here. A single loop using scanf() could read many lines and break all of then in words skipping over the newlines and such, and you may find it easier to code.
Anyway, back to your code: since you do not know in advance the number of words, you can estimate a limit or allocate memory for the strings one by one, or even in blocks. But
you need to allocate memory for the strings you will have a SegFault
at the moment you try to write in the words[] array.
I changed a minimum of your code so you can see an example, and I fixed the number of strings in a #define similar of what you have written so far.
A simple way to go is declare --- as C does in main() --- words[] as char** and allocate memory for them as soon as you know you have at least one string to record.
But then you need to note that you will have just the pointers. They are still pointing to nothing.
As soon as you have a string to load you need to allocate memory for it, plus 1 byte for the terminating '\0', and then copying the string and saving the address in the corresponding pointer in the words[] array.
See the code.
#define MAX_STR_SZ 256
#define MAX_N_OF_STRINGS 30
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// https://stackoverflow.com/questions/63343800/
// c-program-segfaulting-with-strtok
int main(int argc, char** argv)
{
int n_of_words = 0;
int max_n_of_words = MAX_N_OF_STRINGS;
char** words;
// asking for user input
char string[MAX_STR_SZ];
printf("\nPlease input a string of text: ");
fgets(string, MAX_STR_SZ, stdin);
string[strlen(string) - 1] = 0; // drops the final '\n'
printf("full string was '%s'\n", string);
if (strlen(string) == 0) return -1; // no input
// we have at least one byte
// before anything build words[]
words = (char**)malloc(max_n_of_words * sizeof(char*));
// now words[] points to an array of pointers to char
// extracting the first word
char* a_word = strtok(string, " ");
// looping through the string to extract all other words
do
{
printf("\n%i %s\n", 1+n_of_words, a_word);
words[n_of_words] = malloc(1 + sizeof(a_word));
strcpy(words[n_of_words], a_word);
n_of_words++;
if (n_of_words >= MAX_N_OF_STRINGS) break;
a_word = strtok(NULL, " ");
} while (a_word != NULL);
printf("\n%d words at the end of the loop:\n\n", n_of_words);
for (int i = 0; i < n_of_words; i += 1)
{
printf("%i %s\n", 1 + n_of_words, words[i]);
free(words[i]); // deletes words[i]
}; // for()
free(words); // deletes the array
return 0;
};
As a result:
Please input a string of text: we have at least one byte
full string was 'we have at least one byte'
1 we
2 have
3 at
4 least
5 one
6 byte
6 words at the end of the loop:
1 we
2 have
3 at
4 least
5 one
6 byte
There are a few problems that could lead to a seg fault. First, I get warnings compiling your code:
../main.c: In function 'main':
../main.c:17:25: warning: implicit declaration of function 'strtok' [-Wimplicit-function-declaration]
words[n_of_words] = strtok(string, " ");
^~~~~~
../main.c:17:23: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
words[n_of_words] = strtok(string, " ");
^
../main.c:24:27: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
words[n_of_words] = strtok(NULL, " ");
All of this is because you didn't include the proper header for strtok, namely string.h. This could potentially cause problems because the default return type is assumed to be int, which may not be large enough to hold a pointer.
Second, you are passing an incorrect size to fgets(). The size should be the size of the buffer for holding the result. If the buffer is overflowed, undefined behavior results.
Finally, the words array is declared with a size n_of_words, which is zero at that point. This results in a zero size array. Arrays in C do not automatically grow.
Here is your code with these issues fixed:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
int n_of_words = 0;
#define MAX_STR_SZ 256
// asking for user input
char string[MAX_STR_SZ]; // <--- Use macro to define buffer size
printf("\nPlease input a string of text.\n\n");
fgets(string, sizeof string, stdin);
char * words[MAX_STR_SZ]; // <--- Should never be more words than characters in the buffer
// extracting the first word
words[n_of_words] = strtok(string, " ");
printf("\n%i %s\n", n_of_words, words[n_of_words]);
// looping through the string to extract all other words
while( words[n_of_words] != NULL )
{
n_of_words ++;
words[n_of_words] = strtok(NULL, " ");
printf("\n%i %s\n", n_of_words, words[n_of_words]);
}
sleep(10);
return 0;
}
Currently I'm reading each character from the user and storing it into a char array called str. From there I'm trying to use a pointer to loop through the string until it sees a space, once a space is seen I want to take the characters already and create an array of strings. Is that possible? Reasons why I'm doing this is because I later want to use an execlp function to execute a process after my initial program was executed.
If you want to split the string into tokens separated by delimiters you could use the strtok function.
An example would be:
#include <stdio.h>
#include <string.h>
int main(void)
{
int i, n;
char str[] = "Hello World";
char *token[4], *act_token;
token[0] = strtok(str, " ");
n=1;
while(n<4 && (act_token=strtok(NULL, " ")))
{
token[n] = act_token;
n++;
}
for(i=0;i<n;i++)
{
printf("%d: %s\n", i, token[i]);
}
return 0;
}
Ok, so I'm going to explain my program.
It takes a text file that's setup as such: in pairs, first line being the title of an experiment, and the second line being 10 numbers separated by spaces. It saves the first lines of pairs in *experiments and the second lines of pairs in data. The last line is *** END *** which is what it's supposed to end with.
For some reason *** END *** doesn't end the program. Any ways I can fix this? I'm assuming it's because fgets gives str blank spaces (99 chars total) so that the string in quotes will never be equal to str?
Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int var;
int i=0,j,k;
char seps[] = " ";
char *experiments[20];
int data[10][20];
char str[100]; // make sure that this size is enough to hold the single line
char *ptr, *token;
int no_line=1;
while(fgets(str,100,stdin) != NULL && strcmp(str,"*** END ***"))
{
if(no_line % 2 == 0)
{
k=0;
token = strtok (str, seps);
while (token != NULL)
{
sscanf (token, "%d", &var);
data[i][k++] = var;
token = strtok (NULL, seps);
}
i++;
/*read integer values from the string "str" using sscanf, sscanf can be called in a loop with %d untill it fails */
}
else
{
ptr = strdup(str);
experiments[i] = ptr;
/*strore string in your variable "experiments" , before copying allocate a memory for the each entry */
}
no_line++;
}
for(j=0;j<i;j++)
{
printf("%s",experiments[j]);
for(k=0;k<10;k++)
{
printf("%d ",data[j][k]);
}
printf("\n");
}
}
You're declaring i here ...
int i,j,k;
... and using it here ...
data[i][k++] = var;
Nowhere do you initialize i. Also, why does data need to be a 2D array? Can't it just be a 1D array?
int data[10];
...
data[k++] = var;
From this code, int i seems to be declared, but not initialized?
data[i][k++] = var;
It may be helpful to use Eclipse or Code Block IDE to try small testable codes because it has all sorts of syntax and error checking features.
Well, I declared a global array of chars like this char * strarr[];
in a method I am tokenising a line and try to put everything into that array like this
*line = strtok(s, " ");
while (line != NULL) {
*line = strtok(NULL, " ");
}
seems like this is not working.. How can I fix it?
Thanks
Any number of things could be going wrong with the code you haven't shown us, such as undefined behaviour by strtoking a string constatnt, or getting your parameters wrong when calling the function.
But the most likely problem from the code we can see is the use of *line instead of line, assuming that line is of type char *.
Use the following code as a baseline:
#include <stdio.h>
#include <string.h>
int main (void) {
char str[] = "My name is paxdiablo";
// Start tokenising words.
char *line = strtok (str, " ");
while (line != NULL) {
// Print current token and get next word.
printf ("[%s]\n", line);
line = strtok(NULL, " ");
}
return 0;
}
This outputs:
[My]
[name]
[is]
[paxdiablo]
and should be easily modifiable into something you can use.
Be aware that, if you're trying to save the character pointers returned from strtok (which would make sense for using *line), they are transitory and will not be what you expect after you're done. That's because modifications are made in-place within the source string. You can do it with something like:
#include <stdio.h>
#include <string.h>
int main (void) {
char *word[4]; // The array of words.
size_t i; // General counter.
size_t nextword = 0; // For preventing array overflow.
char str[] = "My name is paxdiablo";
// Start tokenising.
char *line = strtok (str, " ");
while (line != NULL) {
// If array not full, duplicate string to array and advance index.
if (nextword < sizeof(word) / sizeof(*word))
word[nextword++] = strdup (line);
// Get next word.
line = strtok(NULL, " ");
}
// Print out all stored words.
for (i = 0; i < nextword; i++)
printf ("[%s]\n", word[i]);
return 0;
}
Note the specific size of the word array in that code above. The use of char * strarr[] in your code, along with the message tentative array definition assumed to have one element is almost certainly where the problem lies.
If your implementation doesn't come with a strdup, you can get a reasonably-priced one here :-)
In my program I am taking user input and parsing it into a 2d char array. The array is declared as:
char parsedText[10][255] = {{""},{""},{""},{""},{""},
{""},{""},{""},{""},{""}};
and I am using fgets to grab the user input and parsing it with sscanf. This all works as I think it should.
After this I want to pass parsedText into execvp, parsedText[0] should contain the path and if any arguments are supplied then they should be in parsedText[1] thru parsedText[10].
What is wrong with execvp(parsedText[0], parsedText[1])?
One thing probably worth mentioning is that if I only supply a command such as "ls" without any arguments it appears to work just fine.
Here is my code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "308shell.h"
int main( int argc, char *argv[] )
{
char prompt[40] = "308sh";
char text[40] = "";
char parsedText[10][40] = {{""},{""},{""},{""},{""},
{""},{""},{""},{""},{""}};
// Check for arguments to change the prompt.
if(argc >= 3){
if(!(strcmp(argv[1], "-p"))){
strcpy(prompt, argv[2]);
}
}
strcat(prompt, "> ");
while(1){
// Display the prompt.
fputs(prompt, stdout);
fflush(stdout);
// Grab user input and parse it into parsedText.
mygetline(text, sizeof text);
parseInput(text, parsedText);
// Check if the user wants to exit.
if(!(strcmp(parsedText[0], "exit"))){
break;
}
execvp(parsedText[0], parsedText[1]);
printf("%s\n%s\n", parsedText[0], parsedText[1]);
}
return 0;
}
char *mygetline(char *line, int size)
{
if ( fgets(line, size, stdin) )
{
char *newline = strchr(line, '\n'); /* check for trailing '\n' */
if ( newline )
{
*newline = '\0'; /* overwrite the '\n' with a terminating null */
}
}
return line;
}
char *parseInput(char *text, char parsedText[][40]){
char *ptr = text;
char field [ 40 ];
int n;
int count = 0;
while (*ptr != '\0') {
int items_read = sscanf(ptr, "%s%n", field, &n);
strcpy(parsedText[count++], field);
field[0]='\0';
if (items_read == 1)
ptr += n; /* advance the pointer by the number of characters read */
if ( *ptr != ' ' ) {
strcpy(parsedText[count], field);
break; /* didn't find an expected delimiter, done? */
}
++ptr; /* skip the delimiter */
}
}
execvp takes a pointer to a pointer (char **), not a pointer to an array. It's supposed to be a pointer to the first element of an array of char * pointers, terminated by a null pointer.
Edit: Here's one (not very good) way to make an array of pointers suitable for execvp:
char argbuf[10][256] = {{0}};
char *args[10] = { argbuf[0], argbuf[1], argbuf[2], /* ... */ };
Of course in the real world your arguments probably come from a command line string the user entered, and they probably have at least one character (e.g. a space) between them, so a much better approach would be to either modify the original string in-place, or make a duplicate of it and then modify the duplicate, adding null terminators after each argument and setting up args[i] to point to the right offset into the string.
You could instead do a lot of dynamic allocation (malloc) every step of the way, but then you have to write code to handle every possible point of failure. :-)