Relative paths from binary file - c

I have the following folder structure:
bin/ <-binary-file is in here
include/
src/
data/
Makefile
In my code, I use relative paths to my data. So "../data/xml/xmlFile.xml". This is fine if I were executing the binary file from the bin/ folder:
brandonto#computer:~/PATH-TO-PROJECT/bin$ ./binary-file
argv[0] = ./binary-file
dirname(argv[0]) = .
But if I were executing the binary from the main folder (or any other folder that is not the bin/ folder):
brandonto#computer:~/PATH-TO-PROJECT$ bin/binary-file
argv[0] = bin/binary-file
dirname(argv[0]) = bin
The xml files would not be found because "../data" would now go up one directory from the main folder (or whatever folder you are in when executing the program).
How could I make it so that the binary file could be executed from any directory on my system?
To make the question a little more clear:
brandonto#brandonto-Aspire-S3-391:~/cpp-workspace/sdl-projects/sdl-space-shooter/bin$ ~/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter
argv[0] = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter
dirname(argv[0]) = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin
brandonto#brandonto-Aspire-S3-391:~/cpp-workspace/sdl-projects/sdl-space-shooter/bin$ cd ..
brandonto#brandonto-Aspire-S3-391:~/cpp-workspace/sdl-projects/sdl-space-shooter$ ~/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter
argv[0] = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin/SpaceShooter
dirname(argv[0]) = /home/brandonto/cpp-workspace/sdl-projects/sdl-space-shooter/bin
Unable to load image ../data/graphics/background/darkPurple.png! SDL_image Error: Couldn't open ../data/graphics/background/darkPurple.png
Unable to load image ../data/graphics/sprites/meteorBrown_big1.png! SDL_image Error: Couldn't open ../data/graphics/sprites/meteorBrown_big1.png
Here, I executed the binary file once from inside the bin/ folder, then once from inside the main folder. The binary ran fine from inside the bin/ folder, but could not find the relative paths to the .png files from inside the main folder.

Probably you are asking a wrong question: the build system has nothing to do with program execution.
However, if you look for an answer, how to make my program to correctly use data, that is located relative to program installation, than here is an answer.
When you program main gets executed, it gets the binary path as the first parameter (index 0). That path can be relative or absolute, but in any case it allows you to find the base directory.
These are also useful links:
How do I find the location of the executable in C?
Finding current executable's path without /proc/self/exe
Here how you can use first argument:
#include <linux/limits.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
int main(int argc, char *argv[])
{
char datadir[PATH_MAX];
strncpy(datadir, argv[0], sizeof(datadir));
dirname(datadir);
strncat(datadir, "/../data", sizeof(datadir));
printf("Data dir: %s\n", datadir);
return 0;
}

I believe that you can find your process id (pid) using the getpid command and perform functions to extract the directory in a manner similar to this question on Ask Ubuntu.

I would have the data associated in some way (organizationally) with the bin directory where the executable resides.
Then, when running the routine, if a complete path is provided (noted by checking arg[0]), then you can find the data directory. If a relative path is provided, then search the search path sequentially until you find the executable, and then you can therefore find the data directory.
No pids needed. (I think this is how Python finds its way, or at least how it used to do so.)

I usually solve this with a program setting. In the good old days I would have these settings in a .ini file which would accompany the executable. Some settings would be configurable from within the program, and all could be edited with a text editor. If the file was missing, or any setting missing, they would be created by default.
For the location of the program's data I would use its full absolute path name. For example it might be
Datapath = D:\os50k
and the program then appends individual file names to the path as necessary.
These days in Windows the System Registry is used for this purpose. However your question is tagged Linux which stores settings in various places, including the program directory.
This question, and this question describe the process more fully.

If your paths can be determined at build time, (i.e. your project will never need to be installed to another directory,) you can inject the path through the build system as a preprocessor definition. Here's an example with CMake:
file(TO_CMAKE_PATH "${PROJECT_BINARY_DIR}/resources" RESOURCE_DIR) # Normalize Windows/Linux paths
add_custom_command(
TARGET my_target POST_BUILD
COMMAND ${CMAKE_COMMAND} ARGS -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/resources ${RESOURCE_DIR}
)
target_compile_definitions(my_target PUBLIC RESOURCE_DIR=${RESOURCE_DIR})
.
#define VAL(x) #x
#define STR(x) VAL(x)
const char* my_resource = STR(RESOURCE_DIR) "/my_resource.abc";

Related

How to give relative path of script in system function in C?

My .c code is in "src" directory and space.sh is in "bin "directory. In the given code I have given full path of space.sh. when I run the code, its working fine but when I use relative path in the system function it will have error like path not found(system(function commented part)).What is going on wrong here? I want relative path only with respect to my .c code (or relative to "practice" directory)
#include <unistd.h>
#include <stdio.h>
int main(void)
{
//system("../bin/space.sh");
system("/home/amitk/projects/amit_bk/practice/bin/space.sh");
return 0;
}
A path relative to your .c code makes no sense, as the .exe can end up anywhere, for example if you distribute your application without code.
If you use relative paths they will be relative to the current path when the .exe is being run. If it's double-clicked in explorer it will be the same path as the .exe, but it is for example possible to make a shortcut with your .exe as "Target" but with a different "Start in" location, so you should never assume the current path is where the .exe file is.
You can determine the location of the .exe file using argv[0] if you define your main function as int main(int argc, char *argv[]). So if needed you could strip the .exe filename from argv[0] to determine the .exe's path.

GCC cannot recognize the directory path inside a file

The problem I encountered in using GCC is that I cannot use the command make to build my program because some files contain the paths of their actual location.
Say I have a file named "machine.h", its content is target-pisa/pisa.h. At the same time, in the same working directory, I have a folder named "target-pisa", in which there is a file named "pisa.h"; the actual code of the header file "machine.h" is actually inside the file "pisa.h", which is inside the folder named "target-pisa" located in the same working directory as "machine.h".
Assume for some reason I cannot simply copy and paste the code from "pisa.h" to "machine.h"; that is, I have to stick with what is provided by the prof. The make command does not work in this case in my laptop because it cannot interpret target-pisa/pisa.h as a directory path and open the actual header file "pisa.h" according to the path target-pisa/pisa.h provided in the file "machine.h". Instead, git bash interprets target-pisa/pisa.h as C code (if I am not mistaken); see the figure below.
Some additional info that may be helpful:
In machine.h, there is only one line of code as shown below:
target-pisa/pisa.h
I have checked that almost all .c files in the working directory have #include "machine.h".
How can I solve this problem? Please help, I have been stuck in this for a long time. By the way, my friend also used git bash to do this lab and this problem doesn't happen to him.
I tried to reinstall git bash in order to see if the problem can be solved, but it didn't.
All in all, I want to build the program successfully by using make command in git bash.
machine.h needs to have an #include directive to tell the compiler to pull in the nested header.
#include "target-pisa/pisa.h"
Just writing target-pisa/pisa.h by itself isn't valid C code.

Creating CLI apps in C

I am wondering how to make a CLI app in C that "constantly runs". What i mean by that us for example i want to just open terminal and type a keyword and a function in a program executes. Like what "ls" is.
When you type ls it lists contents of the current dir. Likewise i want to make a program that when compiled it executes a certant stuff given the keyword is invoken. I dont want to run the executable with ./example, but rather have the command always available.The compiler i use is gcc. I have read that object file needs to be created but i dont know how to use that.
Thanks
What i mean by that us for example i want to just open terminal and type a keyword and a function in a program executes.
Your program doesn't have to "constantly run" in order for you to be able to invoke it without specifying the path... you just have to make sure that the program is located in one of the directories in your PATH, or conversely, that your PATH environment variable includes the directory where your program is located.
So let's say you want to compile hello.c into a command called hello. Here's the code:
#include <stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}
So you'd compile that like:
> gcc -o hello hello.c
Now you should have an executable file called hello in your current directory, and you can run it like:
> ./hello
But you just want to type hello anywhere, right? So, you can add the current directory to your PATH environment variable, e.g.:
> export PATH=$PATH:$PWD
That adds the value of PWD, an environment variable that contains the current directory, to PATH, which is an environment variable that contains a list of directories where the shell will look for executable programs.
If you want to make that change permanent, you'll need to modify one of the scripts that runs when you start up whatever shell you're using. That's a little beyond the scope of this answer, and there's plenty of advice about how to set up your PATH online, so I'll leave that to you.
Another option, instead of adding the directory that contains hello to your PATH, is to move hello to one of the directories already listed in PATH. You can see the full list by doing this:
> echo $PATH
You haven't said what OS you're using, but if it's anything Unix-like there's probably a /usr/local/bin listed in there. .../bin directories generally hold executable programs, and /usr/local is the directory sub-tree where local additions to the OS go. So you could put your program in /usr/local/bin, and then (assuming /usr/local/bin is in your PATH), hello would always be available.

Relative paths in C

I have a C program that uses some resources located in the same directory as the executable. When I execute the program from a random working directory (not the directory where the program is located) the resources don't load, because the relative path I use in the code is not the path where the executable is. How can I solve this nicely?
Pass the path of the directory that contains the resources to the program as an argument and either:
change the current directory of the process to the directory (chdir() on Unix and SetCurrentDirectory() on Windows), or
construct absolute paths to the resources
If it is Windows, as the comment on the question suggests, you can obtain the path of the exe using GetModuleFileName(), extract the directory from it and avoid having to provide an argument to the program. Then either of two options listed would allow the program to be executed from anywhere and still locate its resources.
For anyone happening upon this old question in the future as I just did:
The program (at least in linux) keeps the command it was called by as the first argument of int main argument list.
e.g.
In this example we will drill down a couple of directories to get to our program, resulting in the following call command user#PC:~$ ./foo/bar/awesome_program.x86_64.
The program (code below) will print ./foo/bar/awesome_program.x86_64.
Since we have that string as a variable, it should be rather simple to construct relative paths from it, only replacing the end of that string with paths relative to the executable.
working code:
#include <stdio.h>
int main (int argc, char **argv)
{
printf("calling path: %s\n", argv[0]);
return 0;
}

Cross Platform Data File Access

I want a distribute both a data file and a program coded in C which opens the data file and then closes it, and have it portable. The program source would look something like this:
#include <stdio.h>
int main(int argc, char** argv)
{
fclose(fopen("data.dat", "rb"));
return 0;
}
I'm also using Autotools:
$ ls -R
.:
configure.ac
dat
Makefile.am
src
./dat:
data.dat
Makefile.am
./src:
hello.c
Makefile.am
In Linux when installing software the files usually go into specific directories, e.g. hello would go in /usr/local/bin and data.dat would go in /usr/local/share, and the installer can adjust these directories. Therefore the program would have to be adapt to a change in the path of the data file, specifically the datadir variable.
#src/Makefile.am
AM_CPPFLAGS=-DDATADIR='"$(datadir)"'
...
.
//src/hello.c
...
fclose(fopen(DATADIR "/data.dat", "rb+"));
...
However in Windows, software is not installed this way, and all of the different files are usually installed into one directory. To do this, the bindir and datadir could be made to / when running configure, however that would make the fopen argument invalid.
Is there any way to adjust my setup so that the program would refer to the correct path without using #ifdefs?
You can set -DDATADIR='.' on Windows to get the desired behavior. You can use configure.ac to check whether you are compiling on Windows. Here is one way to do it, adapted from the GTK source code:
AC_CANONICAL_HOST
AC_MSG_CHECKING([for native Win32])
case "$host" in
*-*-mingw*)
os_win32=yes
;;
*)
os_win32=no
;;
esac
AC_MSG_RESULT([$os_win32])
if test "$os_win32" = "yes"; then
DATADEF='-DDATADIR=.'
else
DATADEF="-DDATADIR=$daatadir"
fi
AC_SUBST(DATADEF)
Then, add #DATADEF# to your myprogram_CPPFLAGS in Makefile.am.

Resources