Question

This is one step of a set of exercises I'm doing. The program I write should take more than two arguments. The use of the first argument is not implemented yet. The rest of the arguments are a list of directories.

In this step what I have to do is to create an instance of cat for each directory given in arguments, take the contents of the all files of each directory using cat and print the content. I should be able to handle paths such as /home/directory and /home/directory/ both (with the last / or without)

Currently what I am doing is trying to run cat with argument /home/directory/* so that it will read all the files in the given directory and return the content of them. This is my code:

#include "Step1.h"

int main(int argc,char* argv[])
{
    if(argc < 3)
    {
        printf("Usage: ./Step1 <exclusions file> <folder1> <folder2> <folder3> ...\n");
        return -1;
    }

    int i;
    for(i=2; i<argc; i++)
    {
        int catpipe[2];
        if(pipe(catpipe))
        {
            printf("Error in pipe\n");
            return -1;
        }
        pid_t pid = fork();
        if(pid < 0)
        {
            printf("Error in fork\n");
            return -1;
        }
        if(!pid)
        {
            dup2(catpipe[1],1); // cat pipe is used to get the output of cat program into this program.
            close(catpipe[1]);
            close(catpipe[0]);
            char* args[3];
            args[0] = "/bin/cat";
            int length = strlen(argv[i]);
            char* path;
            if(argv[i][length - 1] != '/') // the path given does not have the ending / 
            {
                path = malloc(length + 3);
                strcpy(path,argv[i]);
                path[length] = '/';      //append / at the end
                path[length+1] = '*';    // append * at the end
                path[length+2] = '\0';
            }
            else
            {
                path = malloc(length + 2); // the path contains the ending /
                strcpy(path,argv[i]);
                path[length] = '*';        // append * at the end
                path[length+1] = '\0';
            }                 
            args[1] = path;
            args[2] = NULL;
            printf("%s\n",path);
            execvp("/bin/cat",args);
        }
        close(catpipe[1]);
        char buffer[200];
        int total = read(catpipe[0],buffer,200); // read the output of cat program and print it. 
        buffer[total]='\0';
        printf("The buffer contains: %s\n",buffer);
    }
    return 0;
}

I ran this code as follows:

mod@mod-Inspiron-N5110:~/project$ ./Step1 exclusions ./testdirectory1 ./testdirectory2/

and the result I got is:

/bin/cat: ./testdirectory1/*: No such file or directory
The buffer contains: 
The buffer contains: ./testdirectory2/*

mod@mod-Inspiron-N5110:~/project$ /bin/cat: ./testdirectory2/*: No such file or directory

mod@mod-Inspiron-N5110:~/project$

but when I do:

mod@mod-Inspiron-N5110:~/project$ /bin/cat ./testdirectory1/*

The result is:

Testline 1 of testfile1 in testdirectory1
Test line 1 of testfile1 in testdirectory1
Testline 1 of testfile2 in testdirectory1
Testline 1 of testfile3 in testdirectory1

Please help me to get this result with my program.

Thanks in advance

No correct solution

OTHER TIPS

I think I figured out the answer for my own question. I'm posting this in belief that somebody just like me would find it useful one day. But I'm not very good at C yet. So if this answer is wrong or if there's some mistake/weakness in this, feel free to correct it.

The key for my answer is using the function wordexp(). It can be used to expand the given path: tilde, regular expressions, variables, and all. So this is my answer:

#include "Step1.h"

int main(int argc,char* argv[])
{
    if(argc < 3)
    {
        printf("Usage: ./Step1 <exclusions file> <folder1> <folder2> <folder3> ...\n");
        return -1;
    }

    int i;
    for(i=2; i<argc; i++)
    {
        int catpipe[2];
        if(pipe(catpipe))
        {
            printf("Error in pipe\n");
            return -1;
        }
        pid_t pid = fork();
        if(pid < 0)
        {
            printf("Error in fork\n");
            return -1;
        }
        if(!pid)
        {
            dup2(catpipe[1],1); // cat pipe is used to get the output of cat program into this program.
            close(catpipe[1]);
            close(catpipe[0]);

            int length = strlen(argv[i]);
            char* path;
            if(argv[i][length - 1] != '/') // the path given does not have the ending / 
            {
                path = malloc(length + 3);
                strcpy(path,argv[i]);
                path[length] = '/';      //append / at the end
                path[length+1] = '*';    // append * at the end
                path[length+2] = '\0';
            }
            else
            {
                path = malloc(length + 2); // the path contains the ending /
                strcpy(path,argv[i]);
                path[length] = '*';        // append * at the end
                path[length+1] = '\0';
            }            

        wordexp_t p;
        char **w;
            wordexp(path, &p, 0);
        char** args = malloc((p.we_wordc + 2) * sizeof(char*));
            args[0] = "/bin/cat";   
        w = p.we_wordv;
        int j;
        for (j = 0; j < p.we_wordc; j++)
        args[j+1] = w[j];                
            args[p.we_wordc + 1] = NULL;

            execvp("/bin/cat",args);
        }
        close(catpipe[1]);
        char buffer[1024];
        int total = read(catpipe[0],buffer,1024); // read the output of cat program and print it. 
        buffer[total]='\0';
        printf("The buffer contains: %s\n",buffer);
    }
    return 0;
}

So basically what you get wrong in your code is that you assume that if you give /directory/* as an argument to cat, cat will expand it as "many files", whereas cat understands that you want to open a file called *.

The simple way to have a * expansion would be to call cat through a shell, which will be doing the expansion from * to "many files".

To achieve so, you can replace your execvp() with a system(…) call, which is basically execl("sh", "sh", "-c", …, NULL) where … is your command (i.e. you could simply call "/bin/sh", "-c", "/bin/cat", "/directory/*") and there * should be expanded by the shell.

Or you can work your own recursive directory walk algorithm, such as that one.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top