1

Let's say the compiled C binary is located at ~/bin/my_cool_c_program.out and I have the following code:

FILE *file = fopen("helloworld.txt");
perror("Open helloworld.txt");

Now I face the problem, that the code creates the helloworld.txt file in the directory where I'm executing the binary from and not relative from where the binary is located. So calling it from ~ results in the creation of ~/helloworld.txt, but I want it to save it to ~/bin/helloworld.txt. I tried replacing it with "./helloworld", but that didn't work, too. When I call from the ~/bin/ directory itself everything works as expected.

And why is it, that the first one results in perror printing Permission denied after the file gets created, while the second one prints Successful?


NOTE: I do check for file == NULL and so on. I just removed it for simplicity.

Sheldon
  • 376
  • 3
  • 14

1 Answers1

1

There is no portable way to do this. If the string pointed to by argv[0] (argv is the second parameter of main function, though any other name may be used), representing the program name, contains path of the program (there is no such a guarantee), then the code below can be used:

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

/* filename separator (depends on the operating system) */
#define SEP '/'

int main (int argc, char *argv[])
{
        FILE *fp;
        const char *filename = "helloworld.txt";
        const char *lastslash;
        if (argc > 0 && (lastslash = strrchr(argv[0], SEP)) != NULL) {
                const int len = lastslash - argv[0] + 1;
                char *const pathname = malloc(len + strlen(filename) + 1);
                if (pathname == NULL) {
                        perror("malloc");
                        return EXIT_FAILURE;
                }
                sprintf(pathname, "%.*s%s", len, argv[0], filename);
                fp = fopen(pathname, "w");
                free(pathname);
                if (fp == NULL) {
                        perror("fopen");
                        return EXIT_FAILURE;
                }
        } else {
                fputs("No path information in argv[0]\n", stderr);
                return EXIT_FAILURE;
        }

        return 0;
}
Lxer Lx
  • 292
  • 1
  • 6
  • Thank you! I'll look into your code but I'll use [this library](https://github.com/gpakosz/whereami) – Sheldon Dec 22 '19 at 22:24
  • If argv[0] starts with "/" (absolute path) this is the path. Otherwise if argv[0] contains "/" (relative path) append it to cwd (assuming it hasn't been changed yet). Otherwise search directories in $PATH for executable argv[0]. From [this](https://stackoverflow.com/a/933996) answer. Looking for the program in `$PATH` will make it work most of the time – Dinu Dec 23 '19 at 00:06