-1

I am having trouble with my C program that is meant to print out the path to a given file from the root. Examples:

./pathto
/users/cs/a1066199/
./pathto .
/users/cs/a1066199/
./pathto file.txt
/users/cs/a1066199/file.txt
./pathto ..
/users/cs/
./pathto ../teaching/somefile.c
/users/cs/teaching/somefile.c

The algorithm being used is below, I have to implement it with recursion:

let d be a directory.
open the parent of d (ie d/..)
loop
    Get next entry, e, in the parent diretory
    get the status of e, using stat, into es
    until es.device==d.device and es.inum==d.inum
endloop

I have coded the program and run the program, but while the first time works, any subsequent calls of the recurisve function seg faults.

I am not sure what/where the problem is.

Any help is appreciated.

Code:

#define _BSD_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<dirent.h>
#include<string.h>

char* compare(char currDir[1024], char newDir[1024], struct stat D){
    int found = 0;
    struct stat Dnew;
    struct stat ES;
    struct dirent* direntp;
    DIR* dirp;
    char* p;
    char dirName[1024];
    char filePath[1024];
    char newDir2[1024];
    char rootPath[1024];

    dirp = opendir(newDir);//open parent directory
    while ((direntp = readdir(dirp)) != NULL){//read parent
        //printf("%s\n", direntp->d_name);
        strcpy(filePath, newDir);//create path to the file in the parent
        strcat(filePath, "/");
        strcat(filePath, direntp->d_name);
        if(stat(filePath, &ES) == -1){//read into stat
            perror("stat");
        }
        if(ES.st_dev == D.st_dev){//check es.device == d.device
            //printf("st_dev are the same\n");
            if(ES.st_ino == D.st_ino){//check es.inum == d.inum
                //printf("st_ino are the same\n");
                printf("The child Dir is %s\n", direntp->d_name);//print this if they are the same
                found = 1;
                if(ES.st_mode & S_IFDIR){//if its a directory, put / on the end
                    strcpy(dirName, direntp->d_name);
                    strcat(dirName, "/");
                } else {
                    strcpy(dirName, direntp->d_name);
                }
            } else {
                found = 0;
            }
        } else {
            found = 0;
        }
    }
    closedir(dirp);

    if(D.st_ino == 2){
        //strcpy(dirName, "/");//if root, return /
        return "/";
    } else {
        dirp = opendir(newDir);
        while ((direntp = readdir(dirp)) != NULL){
            if (strcmp(direntp->d_name, "..") == 0){//find ..
                strcpy(filePath, newDir);
                strcat(filePath, "/");
                strcat(filePath, direntp->d_name);
                if(stat(filePath, &Dnew) == -1){//read .. into Dnew
                    //perror("stat");
                }
            }
        }
        closedir(dirp);

        strcpy(newDir2, newDir);//make new dir
        strcat(newDir2, "/..");

        printf("%s\n", newDir);
        printf("%s\n", newDir2);

        strcpy(rootPath, "");
        /*strncpy(rootPath, */p = compare(newDir, newDir2, Dnew)/*, 1024)*/;//call recursivly
        while(*p != '\0'){
            strcat(rootPath, *p);
            p++;
        }
        strcat(rootPath,dirName);
        return rootPath;
    }
}

int main(int argc, char* argv[]){
    struct stat D;
    //struct stat E;
    //struct stat ES;
    //struct dirent* direntp;
    //DIR* dirp;
    //char filePath[1024];
    char* p;
    char rootPath[1024];
    char currDir[1024];
    char newDir[1024];

    strcpy(currDir, argv[1]);
    if(stat(argv[1], &D) == -1){
        //perror("stat");
    }

    strcpy(newDir, currDir);
    strcat(newDir, "/..");

    printf("%s\n", currDir);
    printf("%s\n", newDir);

    /*strncpy(rootPath, */p = compare(currDir, newDir, D)/*, 1024)*/;
    while(*p != '\0'){
        strcat(rootPath, *p);
        p++;
    }
    printf("%s", rootPath);
    /*if(D.st_mode & S_IFDIR){
        printf("/\n");
    }*/

    return 0;
}

Terminal Output:

/usr
/usr/..
The child Dir is usr
/usr/..
/usr/../..
The child Dir is ..
The child Dir is .
./pathto: line 6: 27236 Segmentation fault      (core dumped) ./pathto.o $*
jwodder
  • 54,758
  • 12
  • 108
  • 124

1 Answers1

2

There are a few things in the question code that need to be fixed.

1) Consider the following fragment of question code (which occurs twice in the question code):

        while(*p != '\0'){
        strcat(rootPath, *p);
        p++;
    }

The strcat() function requires the address of two strings. The variable 'p' is defined as a char ptr, which can store such an address. However, '*p' references the first char value of the string, and hence will generally cause a compiler warning warning: passing argument 2 of ‘strcat’ makes pointer from integer without a cast. This alone can, and most likely did, cause a seg-fault. Perhaps the following code would be better?

        size_t len = strlen(rootPath);
        while(*p != '\0')
           { 
           rootpath[len++] = *p++;
           } 
       rootpath[len] = '\0';         

2) Now consider the following:

char* compare(char currDir[1024], char newDir[1024], struct stat D){
    ...
    char rootPath[1024];
    ...

    ... 
    return rootPath;
    }

Obviously, the compare() function has worked hard to calculate the value rootPath, and it is important to return the prized value back to the caller. There is a flaw here though. The variable 'rootPath' lives only inside the braces of the compare() function. Both the variable, and the storage allocated to the variable cannot exist after 'return'. Hence, the compiler will generally warn: warning: function returns address of local variable This will also (eventually) cause a seg-fault.

In order to return the prized value, it must be placed in a storage which can outlast the compare() function. Perhaps something like this:

char* compare(char currDir[1024], char newDir[1024], struct stat D){
    ...
    char rootPath[1024];
    ...

    ... 
    return strdup(rootPath);
    }

The strdup() function will allocate 'heap' memory (which can outlast the function) for the value, returning the address of that memory to the caller. Keep in mind that it is the callers job to call free() to return that memory back to the heap when it is no longer needed.

Mahonri Moriancumer
  • 5,993
  • 2
  • 18
  • 28
  • Repeatedly invoking `strlen` on the same string you already know is *exactly* one char wider than the last time you called it is [textbook painters algorithm](http://en.wikipedia.org/wiki/Painter's_algorithm). Use a second pointer `char *q = rootPath;` and just `*q++ = *p++;` and `*q=0;` after the loop. The rest of this answer I can get on board with. – WhozCraig Jun 14 '14 at 03:25
  • Yah. given context it may as well start with 0 for one of those contexts, not `strlen()`, but that's irrelevant for the actual goal, getting rid of a `n(n+1)/2` scanning scenario. +1. thanks. – WhozCraig Jun 14 '14 at 03:37
  • Thanks for those, they stopped the segfaults. However, the output of my program is still incorrect for any directory but the first one. If I input /usr/bin/, my output is `/usr/bin /usr/bin/.. The child Dir is bin /usr/bin/.. /usr/bin/../.. The child Dir is .. The child Dir is . /bin/` This gives the wrong answer. I think the problem is somehow related the way my program stores the directories its been to, but I cannot find where it is messing up. – MatthewH01 Jun 14 '14 at 05:38
  • @WhozCraig I should have put these tags in my last post (Only joined last night)... – MatthewH01 Jun 14 '14 at 06:28
  • Never Mind, got it working. in the line `if (strcmp(direntp->d_name, "..") == 0){//find ..`, I should have looked for . instead of .. Another error I found was that I was adding the cstring pointed to by p to rootPath, but forgot to clear rootPath, which resulted on 'rubbish' appearing on other computers I tested in on. – MatthewH01 Jun 14 '14 at 10:36