cgccfopen

How to open a file no matter where I run my program from?


fopen always tries to load files from the working path. To reproduce the bug, I wrote a demo code below:

#include<stdio.h>
int main(void) {
    FILE* my_file = NULL;
    const char* file_name = "some_file.dat";
    errno_t errcode = fopen_s(&my_file, file_name, "rb");
    if (errcode != 0) {
        printf("Cannot open the file `%s`. Error code = %d", file_name, errcode);
    }
    else {
        puts("Success!!");
    }
    return errcode;
}
  1. I have a file d:\path1\some_file.dat and a directory d:\path2. Then I compile the code above to the program named D:\path1\myprogram.exe.
  2. Input command
    cd d:\path1
    myprogram
    
    and the program will print "Success!!".
  3. Input command
    cd d:\path2
    ..\path1\myprogram
    
    and the program will print "Cannot open the file some_file.dat. Error code = 2".

My question is how to successfully open the file no matter where I run my program from. If fopen can't do this, is there a library that can do it?

Reply to the comments:

I know that fopen can load files from absolute paths but I want to make my program portable. myprogram.exe and some_file.dat are always in the same path.


Solution

  • Assuming that the file is in the same folder as the executable.
    You can use the argv[0] passed to the program – which is the executable name.
    I have shown it run three ways: two from the console, and one from GUI file manager.

    #include <stdio.h>
    #include <string.h>
    
    #define SLASH_CHAR  '\\'    // Windows
    //#define SLASH_CHAR  '/'    // Linux
    
    int main(int argc, char *argv[]) {
    
        const char* file_name = "some_file.dat";    // desired filename
        printf("argv[0] = %s\n", argv[0]);          // the executable
        
        // allocate enough memory to create a file name
        char *fname = malloc(strlen(argv[0]) + strlen(file_name) + 1);
        if(fname == NULL)
            return 1;
    
        // find the last path separator in the executable (if any)
        char *slash_ptr = strrchr(argv[0], SLASH_CHAR);
        if (slash_ptr == NULL) {
            // just use the file name
            strcpy(fname, file_name);
        }
        else {
            // create a new file name
            strcpy(fname, argv[0]);
            size_t slash_ind = slash_ptr - argv[0];
            strcpy(fname + slash_ind + 1, file_name);
        }
        
        printf("fname = %s\n", fname);
        free(fname);
        getchar();
        return 0;
    }
    

    Run from the console current directory

    argv[0] = test
    fname = some_file.dat
    

    Run in the console from a parallel folder

    argv[0] = ..\temp\test
    fname = ..\temp\some_file.dat
    

    Run from Windows file manager

    argv[0] = F:\TEMP\test.exe
    fname = F:\TEMP\some_file.dat