Relative paths in C - 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;
}

Related

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.

Passing relative path on OSX in C

I'm trying to make a program which runs an executable in its folder on my Mac.
Considering that my program is compiled in the same folder as the source (/Users/Marcello/Documents/C/Test/Test/Test.c), the program would look something like this:
int main(int argc, const char * argv[])
{
printf("Hello, world!\n");
if(execl("/Users/Marcello/Documents/C/Test/Test/HelloWorld", "HelloWorld", NULL))
printf("ERROR\n");
return 0;
}
Everything works fine if I give the absolute path, but it won't work anymore when I try to pass the relative path to the folder (passing "HelloWorld" instead of "/Users/Marcello/Documents/C/Test/Test/HelloWorld").
I noticed this happens because, without other indications, the program will try to search for HelloWorld in the shell's folder instead of the project's folder. This happens as well with functions such as fopen(), so I tried thinking of solutions; the problem is, I want to get this code into a program that everybody could download and install wherever they like, an I would like it to be cross-platform too, but all the solutions I found, such as chdir(), somehow reference to the absolute path of the program, which I shouldn't know in advance.
Can anybody help me find a long-term solution?
The first element of argv contain the relative to path to your program.

Relative paths from binary file

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";

How to call same file as different name for different method?

I was recently at a presentation where one of the speakers stated that he'd used a single CGI file, written in C, that is called by the webserver, but the webserver calls the file by using different names, the CGI file would run a different method.
How can I have a single C file execute different functions within when it is called by different names? Also how do I re-direct the calls for differently named files back to this single file?
Is this possible or was he just full of himself?
If you create the executable with different names but with the same code base, you can take a different branch of the code based the name of the executable used to invoke the program.
Simple example file:
#include <stdio.h>
#include <string.h>
int main1(int argc, char** argv)
{
printf("Came to main1.\n");
return 0;
}
int main2(int argc, char** argv)
{
printf("Came to main2.\n");
return 0;
}
int main(int argc, char** argv)
{
// If the program was invoked using main1, go to main1
if (strstr(argv[0], "main1") != NULL )
{
return main1(argc-1, argv+1);
}
// If the program was invoked using main2, go to main2
if (strstr(argv[0], "main2") != NULL )
{
return main2(argc-1, argv+1);
}
// Don't know what to do.
return -1;
}
Create two different executables from the file.
cc test-262.c -o main1
cc test-262.c -o main2
Then, invoke the program by using the two different executables:
./main1
Output:
Came to main1.
and...
./main2
Output:
Came to main2.
Unix filesystems support the concept of hard and soft links. To create them just type:
ln origfile newfile
to create a hard link, or:
ln -s origfile newfile
to create a soft link.
Soft links are just a special kind of file that contains the path of another file. Most operations in the link transparently result in operating on the target file.
Hard links are lower level. In effect, all files are a link from the pathname to the content. In Unix you can link more than one pathname to the same content. In effect, there's no "original" and "links", all are links. When you delete a file, you're just removing a link, and when the link count goes to zero, the content is removed.
Many unix utilities do this trick. Since the running shell includes the name used to invoque the executable, it's handled just like the 0'th argument of the command line.
When a CGI script is being called by a web server, it receives a considerable amount of information in its environment to let it know how it was called, including:
SCRIPT_NAME, the path to the script from the document root
SCRIPT_FILENAME, the filesystem path to the script (usually the same as argv[0])
REQUEST_URI, the path that was requested by the browser (usually similar to SCRIPT_NAME in the absence of URL rewriting)
QUERY_STRING and PATH_INFO, which contain URL parameters following the script's name
HTTP_*, which contain most of the HTTP headers that were passed in the request
Point is, the script gets a lot of information about how it was called. It could be using any of those to make its decision.
It's possible and actually pretty common.
The first element in the argv array passed to the main function is the "name" of the executable. This can be the full path, or it can be just the last component of the path, or -- if the executable is started with an exec* function call, it can be an arbitrary string. (And Posix allows it to be a null string, as well, but in practice that's pretty rare.)
So there is nothing stopping the executable from looking at argv[0] (having first checked to make sure that argc > 0) and parsing it.
The most typical way to introduce a different name for the executable is to insert a filesystem link with the alternate name (which could be either a hard or a soft-link, but for maintainability soft links are more useful.)
For CGIs, it is not even necessary to examine argv[0], since there are various useful environment variables, including (at least): SCRIPT_NAME.

List a target directory in C

So I am tasked with creating a shell. I have the functions working correctly (e.g. dir, clear, quit, etc.), but I have a question about the 'dir' function. Currently 'dir' works fine. It lists the files of the directory that the program is located in. What I want to do is list the directory of another location. Is there a way to that?
I have yet to create the change directory command. I was wondering if my problem would be solved through that instead. Any help is appreciated.
Side note: The instructions state that I "will need to provide some command line parsing capability to extract the target directory for listing." I have no idea what that is, but maybe someone could enlighten me.
Take a look at GNU's Simple Directory Lister example code, which uses opendir().
To parse command line arguments, take a look at your main() function:
int main(int argc, char* argv[])
{
...
return 0;
}
Use the argv[] pointer and argc integer value to determine the number of and character pointers of arguments.

Resources