-3

I'm creating a program that when launched takes in input a command and some arguments with scanf and calls execvp using those arguments. I'm doing this with strsep. I store the string in an array (char*),then I want to split it and store tokens in a new array (this time it's an array[] so I can use it with the execvp). Arguments saved with scanf should be commands for terminal (as "ls" with "-l" ecc,"pwd"... however variables saved in PATH) so they are separated by " ".

Ex : 

./mystring  
Type arguments : " ls -l "

It was an example only to specify which kind of input 'll be. I'll do the execvp alone,I need help to split the string in tokens. This is the code :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
 fflush(NULL); //to clean streams
 printf("type the command to start (and arguments if required) \n");
 char **st;
 char dest[10];
 scanf("%s",*st);
 int i;
 for (i=0;(dest[i]=strsep(st," "))!=NULL;i++)
    continue;
 for (int c=0;c<i;c++) printf(" arg %d : [%s] ",c,dest[c]);
 return 0;
}

Lines 5 and 6 required to call strsep,the 10 in dest[10] is symbolic.

Line 7 to store the input in st.

Line 9 should split on " " and store command and arguments in dest[I](that I'll pass to execvp).

Line 11 to print what dest has stored.

And this is the sad output :

./mystring 
type the command to start (and arguments if required) 
Segmentation fault: 11

I don't understand how strsep works, someone can help me?

11
  • 1
    Please don't put line numbers in the code like that; it makes it a pain to work with your code. It's probably best to make them into comments, if the numbers are needed. Commented Jun 18, 2018 at 18:24
  • 2
    do char st[20]; and scanf("%19s",st) that'll be better already Commented Jun 18, 2018 at 18:25
  • 2
    fflush(NULL); //to clean streams - Where did you read that this should be done?
    – Ed Heal
    Commented Jun 18, 2018 at 18:30
  • 1
    Why do you need to do this?
    – Ed Heal
    Commented Jun 18, 2018 at 18:59
  • 1
    @EdHeal Well this is a good question,I thought to clean open streams first of use scanf because usually there are a lots of problems with it, it's a strange function and I don't know what it does in particular and I didn't know that it is usless at the beginning of the main.
    – feded
    Commented Jun 18, 2018 at 19:19

1 Answer 1

3

You need to pass strsep() a pointer to the string to be analyzed. That means something more like:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    printf("type the command to start (and arguments if required)\n");
    char *dest[10];
    char line[4096];
    if (fgets(line, sizeof(line), stdin) == 0)
        return 1;
    line[strcspn(line, "\n")] = '\0';  /* Zap trailing newline */
    char *st = line;
    int i;
    for (i = 0; i < 10 && (dest[i] = strsep(&st, " ")) != NULL; i++)
        ;
    for (int c = 0; c < i; c++)
        printf(" arg %d : [%s]\n", c, dest[c]);
    return 0;
}

Note the changes to dest and st — and the addition of spaces to make the code more easily readable (and newlines in the output). I removed the odd and irrelevant fflush(NULL), too — when a program starts, the buffers don't need flushing. The code removes the newline from the input; scanf() would not have included any newline. The notation scanf("%s", *st); in the question would only read one word from the input, even assuming that *st pointed to some allocated memory (which, in the question, it doesn't).

Note that description of strsep() from a macOS man page says:

char *strsep(char **stringp, const char *delim);

The strsep() function locates, in the string referenced by *stringp, the first occurrence of any character in the string delim (or the terminating '\0' character) and replaces it with a '\0'. The location of the next character after the delimiter character (or NULL, if the end of the string was reached) is stored in *stringp. The original value of *stringp is returned.

This implies that you need to pass a pointer to a char * variable, rather than the value of a char ** variable. The macOS man page also includes an example which shows similar code, passing &string to strsep() where char *string = …;. There is sometimes an art to reading a man page — and reading between the lines of the man page.

The code above (now tested) can be run to produce:

$ ./ss53
type the command to start (and arguments if required)
command-name-is-long a short arg
 arg 0 : [command-name-is-long]
 arg 1 : [a]
 arg 2 : [short]
 arg 3 : [arg]
$
4
  • Thank you very much,I have to study what you wrote,I'll be back soon :'),however I never used fgets,is it like scanf?And why return 1 below?
    – feded
    Commented Jun 18, 2018 at 19:10
  • I imagine that line[strcspn(line, "\n")] = '\0'; replace \n with \0. I think that you do this because in this way the string is cleared by possible problems,so if this is correct for (i = 0; i < 10 && (dest[i] = strsep(&st, " ")) != NULL; i++) should split and store on \0 and " ".
    – feded
    Commented Jun 18, 2018 at 19:22
  • 1
    fgets() is the standard C way to read a line of input, which seems likely to be what you want. scanf("%s", str) doesn't read a line; it reads a word. Since fgets() returns NULL on EOF (or an error), I return 1 from main() — without an error message since I'm lazy in this code. The line[strcspn(line, "\n")] = '\0'; does indeed remove a trailing newline; you can use "\r\n" if you prefer, which will look for a CR ('\r') or NL ('\n') in the input, whichever comes first. However, that's usually not necessary. Commented Jun 18, 2018 at 19:26
  • (See also Beginner's Guide Away From scanf()). Commented Jun 18, 2018 at 19:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.