-2

I want to execute multiple commands like ./shell ls > test.txt;ls > test1.txt;ls > test2.txt; which should print the output 3 times in mentioned files. However, somehow my code only prints once.

I have separated char buffer by ';' using strtok_r.

Also, I had a look the other example which is similar to my problem: Nested strtok function problem in C

This is my code

void call_system(char *argv[],int argc)
{
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = child_handler;
    sigaction(SIGCHLD, &sa, NULL);

    int pid;
    int background;
    /*two process are created*/
    pid=fork();
    background = 0;

    if(pid<0)
    {
        fprintf(stderr,"unsuccessful fork /n");
        exit(EXIT_SUCCESS);
    }
    else if(pid==0)
    {
        chdir("h/Ravi Griffith/Sem 1-2015/SP/Assignment1/Assignment1");

        char *bname;
        char *path2 = strdup(*argv);
        bname = basename(path2);

        execvp(bname, argv);
    }
    else if(pid>0)
    {
        /*it will wait untill the child process doesn't finish*/
        child_handler(pid);
        exit(EXIT_SUCCESS); 
    }
}

int main(int argc,char *argv[])
{
    if(argc>1)
    {
    /*it will check whether a user has entered exit then the code will be executed successfully.*/
        if(strcmp( argv[1], "exit")==0)
        {
        exit(EXIT_SUCCESS);
        }
    }
    else
    {
        printf("Enter Shell Command -- ");   
        char buffer[80];

        fgets(buffer, sizeof(buffer), stdin);
        //it will replace the newline character with null
        buffer[strlen(buffer) - 1] = '\0';
        char *end_str;
        char* token= strtok_r(buffer, ";", &end_str);

        /* string will be split in individual argument array */
        while(token != NULL)
        {
            int i;
            char *endtoken;
            printf("a = %s\n", token);
            char *array[strlen(buffer)];
            i = 0;
            char *token2 = strtok_r(token," ", &endtoken);  
            while (token2 != NULL)
            {
                array[i++] = token2;
                token2 = strtok_r(NULL, " ", &endtoken);
            }

            if(sizeof(array)>16)
            {
                char *arrow=array[strlen(buffer)-1];
                char *filename;
                filename=array[strlen(buffer)];
                /*printf("%s",arrow);
                printf("%s",filename);*/
                if(strcmp( arrow, ">") == 0)
                {
                    freopen( filename, "w", stdout );
                }
                else if(strcmp( arrow, "<") == 0)
                {
                    freopen( filename, "rb", stdin );
                }
            }   

            splittoarray(buffer,argv);

            call_system(argv,argc);
            token = strtok_r(NULL, ";",&end_str);
        }
    }
}
Community
  • 1
  • 1
Ravi Vyas
  • 137
  • 1
  • 4
  • 19
  • You might want to use `strsep` instead, since you seem to want nested calls to `strtok`, and `strtok` doesn't work that way. – user3386109 Apr 02 '15 at 02:52
  • @user3386109 Can you please give me an example? – Ravi Vyas Apr 02 '15 at 03:05
  • @Jonathan Leffler, any ideas, why its not working – Ravi Vyas Apr 02 '15 at 03:42
  • any ideas guys? please provide some examples if you can as its bit urgent. Thanks – Ravi Vyas Apr 02 '15 at 04:30
  • Your splitting using `strtok_r` is just fine. It's somewhere in your other code. Looks like it's time to get your debugger fired up. I will mention one thing: are you sure that `strlen(buffer) - 1` is the correct index for `arrow`? If I enter `ls > test123456.txt`, that's 19 characters, meaning character 18 would be 'x', but the '>' character is `buffer[3]`, or `array[1][0]`. What is `i`, and why did you need it? What is its value after you exit the loop? Think about it, and I think you'll get the answer. –  Apr 02 '15 at 05:08
  • @ChronoKitsune thanks for answer, well as you said strtok_r is fine, but I execute these commands(i.e. ls) using execvp(). so If I comment, call_system(argv,argc); which calls the method and execute then it works. Is there any solution for it? – Ravi Vyas Apr 02 '15 at 05:16
  • @RaviVyas In your child process (`pid == 0`), you use `execvp`, which means your program was replaced by the process you execute. Then you use `child_handler` in your parent process (`pid > 0`), which appears to wait for the child process to exit. After that, you call `exit(EXIT_SUCCESS);`. In other words, after you execute one process and it exits, you then exit your original process. That's why it only works once. Also, why are you exiting using `EXIT_SUCCESS` even when something fails? I would think `EXIT_FAILURE` would be much more appropriate when an error occurs. –  Apr 02 '15 at 05:39
  • 2
    @ChronoKitsune, No, the `strtok_r` splitting is not "just fine". The pointers assigned as values to the arrays point to memory that may be overwritten with each call to `strtok_r`. (e.g. `array[i++] = token2;`) There is no guarantee the pointer assigned to `array[i++]` will point to anything after the next `strtok_r` call. You must save a copy of the string pointed to by `token2` following each `strtok_r` call. – David C. Rankin Apr 02 '15 at 06:39
  • @DavidC.Rankin Ah, I missed that bit. That's what I get for commenting instead of sleeping. –  Apr 02 '15 at 10:15
  • 1
    Hehehe.. The only reason I pick up on it when asleep is I've been burned by it enough that it is engrained in my subconsious... `:p` – David C. Rankin Apr 02 '15 at 18:52

1 Answers1

1

Given the discussion of command line splitting into individual arguments in a nested fashion with strtok_r, I put together the following example to outline its application to your problem. (I have omitted, your specific code not related to splitting, and I've made changes as necessary). The biggest issue with your original splitting code, was that after declaring flexible arrays of pointers to hold pointers to the tokens returned by strtok_r, you simply assign the return of strtok_r to the pointer array without allocating memory and making a copy of the string pointed to by token/token2.

While strtok_r will return a pointer to each token, the memory location pointed to by token can change on the very next call to strtok_r. Therefor, to preserve the token for later use, you need to make a copy of the string pointed to by token/token2 before your next call to strtok_r.

One other issue to note is that after splitting with ;, when you go to split the individual command into their args, you cannot simply pass the strings in your token token array (strtok_r modifies the original). You must make a copy of each string in your token array and pass a pointer to the copy to strtok_r. That way, you preserve the start address to each allocated token string to prevent your program from SegFault'ing when you pass the pointers to free later.

Here is a short example:

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

int main(int argc,char *argv[])
{
    if(argc > 1 && strcmp (argv[1], "exit")==0) {
        exit(EXIT_SUCCESS);
    }

    printf("Enter Shell Command -- ");   
    char buffer[80];

    fgets(buffer, sizeof(buffer), stdin);

    /* replace the newline character with null */
    buffer[strlen(buffer) - 1] = '\0';

    char *cmdarray[strlen (buffer)];
    size_t cmdidx = 0;
    size_t n = 0;
    char *end_str = NULL;
    char* token= strtok_r(buffer, ";", &end_str);

    /* initialize command string array */
    for (n = 0 ; n < strlen (buffer); n++)
        cmdarray[n] = NULL;

    /* split into individual command string */
    while (token)
    {
        cmdarray[cmdidx++] = strdup (token);
        token = strtok_r (NULL, ";", &end_str);
    }

    /* loop to process command strings into args */
    for (n = 0; n < cmdidx; n++)
    {
        size_t i = 0;
        size_t cmdsz = strlen (cmdarray[n]);
        size_t arridx = 0;
        char *token2 = NULL;
        char *endtoken = NULL;
        char *array[cmdsz];
        char *cmdcopy = strdup (cmdarray[n]);
        char *sp = cmdcopy;

        /* initialize argument array */
        for (i = 0; i < cmdsz; i++)
            array[i] = NULL;

        /* split each command string into argument array */
        for (token2 = strtok_r (sp, " ", &endtoken); token2; token2 = strtok_r (NULL, " ", &endtoken))
            array[arridx++] = strdup (token2);

        /* main processing of commands and args here
        * (print example of all values tokenized) 
        */
        printf ("\ncmdarray[%zu] : %s\n", n, cmdarray[n]);
        for (i = 0; i < arridx; i++)
            printf ("  array[%zu] : %s\n", i, array[i]);

        /* free memory allocated to array */
        for (i = 0; i < arridx; i++)
            if (array[i])
                free (array[i]);

        /* free copy of command used to tokenize arguments */
        if (cmdcopy)
            free (cmdcopy);
    }

    /* free command memory */
    n = 0;
    while (cmdarray[n]) free (cmdarray[n++]);

    return 0;
}

Example of Nested Command Splitting

$ ./bin/strtok_r_nested
Enter Shell Command -- ./shell ls > test.txt;ls > test1.txt;ls > test2.txt

cmdarray[0] : ./shell ls > test.txt
  array[0] : ./shell
  array[1] : ls
  array[2] : >
  array[3] : test.txt

cmdarray[1] : ls > test1.txt
  array[0] : ls
  array[1] : >
  array[2] : test1.txt

cmdarray[2] : ls > test2.txt
  array[0] : ls
  array[1] : >
  array[2] : test2.txt

Verify No Leaks

$ valgrind ./bin/strtok_r_nested
==19622== Memcheck, a memory error detector
==19622== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==19622== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==19622== Command: ./bin/strtok_r_nested
==19622==
Enter Shell Command -- ./shell ls > test.txt;ls > test1.txt;ls > test2.txt
  <snip>

==19622==
==19622== HEAP SUMMARY:
==19622==     in use at exit: 0 bytes in 0 blocks
==19622==   total heap usage: 16 allocs, 16 frees, 156 bytes allocated
==19622==
==19622== All heap blocks were freed -- no leaks are possible
==19622==
==19622== For counts of detected and suppressed errors, rerun with: -v
==19622== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85