Let's say I am in 2 differents directory:
On the one hand:
print_text_from_file.c
text_to_be_printed.txt
On the other hand:
src/
print_text_from_file.c
text_to_be_printed.txt
And the source code from the .c:
// can't use argv
int main(void){
FILE *fp = fopen ("text_to_be_printed.txt", "r");
if (fp != NULL) {
// working function which takes fp and prints it as expected
print_from_file(fp);
fclose (fp);
}
else {
printf("The file doesn't exist\n");
}
return 0;
}
And let's say, the file only contains Hello World !
If I compile it
After compiling it from being in the first directory with gcc print_text_from_file.c
and after executing it with ./a.out, the output is:
Hello world ! (exactly what I want)
However if I'm in the second case, I'll compile being on src : gcc print_text_from_file.c
and then execute it with being out of src: ./src/a.out, the output is:
The file doesn't exist.
So basically the challenge is that I can't use argv AND I can't know from where the user will execute the file (so I don't know if using absolute paths would be correct). From anywhere, the program will have to print Hello World !.
You really should read something like Advanced Linux Programming and something about operating systems.
And you might also look into syscalls(2) and credentials(7)
The important notions are working directory, process, glob, unix shell, and PATH
See also chdir(2), path_resolution(7), glob(7), getcwd(3), exec(3), proc(5)
There are several issues here:
gcc generates the a.out file at the working directory. It does not matter whether your build print_text_from_file.c or src/print_text_from_file.c, the a.out file is created at the same place, at the working directory (not at src directory). When you run src/a.out, I don't know what did you actually run, maybe an old artifact.
The file opened by the program calling fopen is relative to the working directory. If you want to open the file src/text_to_be_printed.txt you should give path which is relative to the working directory (i.e. src/text_to_be_printed.txt).
Alternative way is to give the full path of the file. e.g. something like /home/myuser/src/text_to_be_printed.txt; this way, the program will find the input file, not as relative to the working directory but as absolute path.
In order to enable the user to give you the path, you shall change the main function signature to get arguments from the user. It should be: int main(int ac, char **av). The user shall run the command like this ./a.out INPUT_FILE_PATH, where INPUT_FILE_PATH is either relative to the working directory or absolute path.
You can access INPUT_FILE_PATH within your code as av[1]. Code will look like this:
int main(int ac, char **av){
const char *input_path = av[1];
FILE *fp = fopen (input_path, "r");
...
Related
I'm trying to read text from a file (should be pretty easy right?). As far as I recall, the syntax should look something like
FILE *filename;
filename = fopen("filename.txt", "r"); /*when file is the same
folder of the .exe*/
Below is my code. When I run it, I simply get "Error", which is the prompt I wanted in case of an error. I included here a global struct declaration because it's literally the only other thing in the code, even though I'm positive it's not causing any problem with opening the file.
#include <stdio.h>
#include <stdlib.h>
struct list {
char subject[20];
char prof_name[20];
char prof_surname[20];
char period[20];
int credits;
int pass_rate;
};
int main()
{
struct list data[80];
FILE *prof;
prof = fopen("professor.txt", "r");
if (prof == NULL) {
fprintf(stderr, "Error");
exit(EXIT_FAILURE);
}
return 0;
}
The file has the correct name and extension, it's in the same folder as the .exe (I've also tried with the address, it still does the same). I feel like I'm going to get crazy if I look at the code even for just one more minute. There must be something I missed
Regarding the comment "when file is the same folder of the .exe", that is incorrect.
Instead relative paths (like your professor.txt) is relative from the process current working directory. Which might be very different from the location of the .exe file.
My guess is that you're running inside Visual Studio (or other IDE) which places the executable files in a sub-directory. The working directory when running, though, is usually the project root directory.
So either go into the project settings and change the working directory when running the program into the directory where the file is located, or move the file to the actual working directory.
You can use the _getcwd function to get the process working directory, to verify that it is what you believe it is.
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";
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;
}
In Command Prompt, this works: whatever\folder> bezier.exe
but this doesn't: whatever> folder\bezier.exe
My bezier program loads some settings from a local file, so I believe the problem is that the program thinks its directory is whatever\ when it is actually whatever\folder\. I'm calling it from within a C program using CreateProcess(). If I am correct in guessing the problem, is there any way to ensure the program has the right directory for itself?
the main method of bezier.exe:
int main(int argc, char* argv[]) {
char buf[200];
FILE* f = fopen("out.txt","w");
GetCurrentDirectory(200,buf);
fprintf(f,buf);
fclose(f);
SDL_Surface* screen;
SDL_Event e;
SDL_Init(SDL_INIT_VIDEO);
screen = SDL_SetVideoMode(WIDTH, HEIGHT, 32, SDL_FULLSCREEN|SDL_HWSURFACE);
if (screen == NULL)
exit(-1);
SDL_ShowCursor(SDL_DISABLE);
srand(time(NULL));
loadColors(COLOR_FILE);
fill(screen, backColor);
initialiseVars();
while (e.type != SDL_KEYDOWN)
{
//do stuff
}
SDL_Quit();
return 0;
}
Here's the crazy part. With "..> folder\bezier.exe" it doesn't write its path, but it does start a new window. That doesn't make any sense to me, because SDL_SetVideoMode is after writing the path.
You can use GetModuleHandle and GetModuleFileName to find out where your execuatble file is, then use that information to create a file specification for your local settings file.
GetModuleHandle with a NULL argument will give you the handle for the current executable. Then, passing that to GetModuleFileName will give you the fully qualified name of that executable. Just strip off the executable filename from the end and add your configuration file name.
However, that's been a bad idea for a long time now, since Windows provides per-application and per-user settings areas for this sort of stuff - you can generally get those locations with SHGetFolderLocation() and its brethren.
Use the first method only if this is for a personal project. If you plan to release your software to the wild, you should separate executable and configuration information as per Microsoft guidelines.
Regardless of that, it appears you now have the problem that you think the file is not being written to. You need to check that. When you open that file out.txt for write, it does so in the current directory. If you're running in the parent directory (with folder\bezier.exe), it will create it in the parent directory and looking for it in the folder directory is a waste of time.
If you are looking in the directory where you're running the program from, and it's still not being created, there are possible reasons for this. For a start, you should check (ie, capture and output) the return codes from all those f* functions, fopen, fprintf and fclose.
I created a static library in C using Visual Studio. This library contains a function which accesses a text file stored in that current directory. The library was built properly. But the problem is that when I call the function from outside other project it is not loading that text file( I linked the .lib file properly everything else is working except for loading of that file).
Any ideas how to load a text file from .lib file just by relative path??
Thanks in advance..
The following is the library test function definition
int test()
{
FILE *fp = fopen("hello.txt", "r");
if(!fp) printf("File Error");
return 0;
}
The test.lib file is built and created for this.
Just accessing the current folder hello.txt file but when this function is called from other Project. it is saying File Error.
Modify your code to look at the errno:
#include <errno.h>
#include <string.h>
...
if(!fp) printf("File error: %s\n", strerror(errno));
And then look up the meaning of the errno on your operating system to see what's going on.
I'm pretty sure the fact that you're calling this function from a library is a red herring.
What's most likely happening is that your hello.txt file is not in the working directory of the executing process. Go ahead and #include <windows.h> in your project, and use the GetCurrentDirectory function to see what the working directory is when you run your program. Most likely, it's not the same path as your text file.
To remedy this, you can do one of two things: you can change the startup settings of the program (whether that's from Visual Studio or a Windows shortcut) to specify the working directory (called "Start in:" for a Windows shortcut) to be the path to the text file you want to open, or you can figure out what working directory your program has been using and move your text file there instead.
Edit: Also, if you want the application to use its own directory (where the executable file actually resides) you can use the GetModuleFileName function to get the full path of the executable. Of course, you'll have to trim the filename of the program off the end of the string it produces, but that should be a piece of cake.
Check your file path and print an errno, I think you have a static file path