Does the function scandir handle relative paths?
In other words, are the following two pieces of code equivalent?
int NUM = scandir("/home/hello/wait/../pqr/../../hello", &LIST, 0, alphasort);
int NUM = scandir("/home/hello", &LIST, 0, alphasort);
If not, is there some simple buit-in function to convert /home/hello/wait/../pqr/../../hello to /home/hello?
Does the function scandir handle relative paths?
I believe the answer is yes. From posix scandir we know only that:
The scandir() function shall scan the directory dir, [...]
From posix definitions:
3.129 Directory
A file that contains directory entries. No two directory entries in the same directory have the same name
The argument to scandir is char *, so it's a string. And from posix definitions we know that:
3.271 Pathname
A string that is used to identify a file. [...]
Another section named 4.13 Pathname Resolution delves on how pathname is resolved, and tells us that:
If the pathname does not begin with a <slash>, the predecessor of the first filename of the pathname shall be taken to be either the current working directory of the process [...] (such pathnames are referred to as "relative pathnames").
So because scandir takes a pathname to a directory and because of how a pathname is resolved, yes, you can give relative paths to scandir.
In other words
It's not an "other word" - both pathnames you showed are absolute, not relative. Using dot-dot in a pathname doesn't make the pathname relative.
are the following two pieces of code equivalent?
Yes.
is there some simple buit-in function to convert /home/hello/wait/../pqr/../../hello to /home/hello?
There is no "builtin function" to canonicalize a pathname in any compiler I am aware of. There exists a completely normal function provided from POSIX for that purpose and is called realpath(). GNU library may allocated the returned string dynamically and it also has canonicalize_file_name.
Related
I am trying to use the GetShortPathName() function to give me the short version of two paths, but it succeeds in only one path and fails in the other path.
// Get the game directory path
wchar_t GameDirPath[MAX_PATH] = L"\0";
GetCurrentDirectory(MAX_PATH, GameDirPath);
// Get the engine directory path
wchar_t EngineDirPath[MAX_PATH] = L"\0";
wcscat(EngineDirPath, GameDirPath);
wcscat(EngineDirPath, L"\\Assets\\Engine\\");
// Get the short path of the engine directory
wchar_t EngineShortPath[MAX_PATH] = L"\0";
GetShortPathName(EngineDirPath, EngineShortPath, MAX_PATH);
The following gives me the correct short path:
D:\Games\NEEDFO~1\Assets\Engine\
But this one doesn't:
D:\Games\FIFA 97\Assets\Engine\
Note that the two examples exist in the same folder "Games".
In short:
I want to pass the path to "DOSBox.exe" as a parameter but it doesn't accept the windows paths like this "D:\Games\FIFA 97\Assets\Engine", so you must convert it to a DOS path like this "D:\Games\FIFA97~1\Assets\Engine", so, I try to use the GetShortPathName() function to do that mission.
Why does this problem happen, and how can I solve it?
As the documentation explicitly states:
If the specified path is already in its short form and conversion is not needed, the function simply copies the specified path to the buffer specified by the lpszShortPath parameter.
The API behaves as documented. There's nothing that needs to be fixed.
Back to 90s, when DOS OS was largely used, files and directories names were limited to a maximum length of 8 characters (8.3 format; meaning 8 bytes for the file name and 3 for the file extension).
So hello.txt file was admitted, and helloguys.txt (9 chars log) was "illegal".
With Windows this limitation has beeen removed, and short names have been introduced in order to convert paths to DOS compliant format.
Now that we know what a short path is, we can analyze your case. In path
D:\Games\Fifa 97\Assets\Engine\
every token is DOS compliant. So what is the short version of this path? Well, the path itself. And that's why GetShortPathName( ) returns an unchanged path.
You can find a wide description in docs page.
I am currently recoding the "ls" command to learn. However, when I browse files: I may have an error when I try to open the "folder" of the path pointed by the symbolic link. Because it's not a directory (I thought all symbolic links pointed to folders).
How can I check if it points to a directory? (I watch the manuals, stat, dir ..)
I thought all symbolic links pointed to folders
Nope. A symbolic link is an indirect reference to another path. That other path can refer to any kind of file that can be represented in any mounted file system, or to no file at all (i.e. it can be a broken link).
How to check that it points to a directory?
You mention the stat() function, but for reimplementing ls you should mostly be using lstat(), instead. The difference is that when the specified path refers to a symbolic link, stat returns information about the link's target path, whereas lstat returns information about the link itself (including information about the file type, from which you can tell that it is a link).
In the event that you encounter a symbolic link, you can simply check the same path again with stat() to find out what kind of file it points to. stat() will recursively resolve symbolic links to discover the information for the ultimate target, which will be a symbolic link only if it is a broken one. Any way around, you don't need to distinguish between a broken link and any other form of non-directory for your particular purpose.
I just ran into the same problem, and here is my solution:
bool IsDir(const char *path)
{
std::string tmp = path;
tmp += '/';
struct stat statbuf;
return (lstat(tmp.c_str(), &statbuf) >= 0) && S_ISDIR(statbuf.st_mode);
}
the key is the tail / in the path
however, I have no idea whether it's portable
I'm writing a program that iterates through a directory tree depth first (similar to the GNU find program) by recursively constructing paths to each file in the tree and stores the relative paths of encountered files. It also collects some statistics about these files. For this purpose I'm using the stat function.
I've notices that this fails for very deep directory hierarchies, i.e. long file paths, in accordance with stat's documentation.
Now my question is: what alternative approach could I use here that is guaranteed to work for paths of any length? (I don't need working code, just a rough outline would be sufficient).
As you are traversing, open each directory you traverse.
You can then get information about a file in that directory using fstatat. The fstatat function takes an additional parameter, dirfd. If you pass a handle to an open directory in that parameter, the path is interpreted as relative to that directory.
int fstatat(int dirfd, const char *pathname, struct stat *buf,
int flags);
The basic usage is:
int dirfd = open("directory path", O_RDONLY);
struct stat st;
int r = fstatat(dirfd, "relative file path", &st, 0);
You can, of course, also use openat instead of open, as you recurse. And the special value AT_FDCWD can be passed as dirfd to refer to the current working directory.
Caveats
It is easy to get into symlink loops and recurse forever. It is not uncommon to find symlink loops in practice. On my system, /usr/bin/X11 is a symlink to /usr/bin.
Alternatives
There are easier ways to traverse file hierarchies. Use ftw or fts instead, if you can.
I'm trying to use CreateProcess to start a child process, however I keep getting error 2, which, according to the documentation is file not found.
My code looks like this:
if (!(CreateProcess(LPCTSTR("test.exe") ,NULL ,NULL,NULL,FALSE ,0 ,NULL ,NULL ,&producer_si
,&producer)))
{
printf("Create process failed!(%d)\n", GetLastError());
}
Where test.exe is an executable program which I created earlier. The child process is very simple, with the code looking like this:
void _tmain (int argc, TCHAR* argv[])
{
printf("%s\n", "hello!");
}
test.exe is also found in the same folder as the parent process. I'm not understanding why I'm always getting an error code of 2.
Error 2 is ERROR_FILE_NOT_FOUND. As others have told you, you are relying on a relative path when you need to use an absolute path instead.
Also, LPCTSTR("test.exe") is not valid code. If UNICODE is defined, CreateFile() maps to CreateFileW(), and LPCTSTR maps to LPCWSTR ie const wchar_t*. You cannot typecast a char* to a wchar_t* and end up with meaningful data. If you want to use TCHAR-sensitive literals, use the TEXT() macro instead, eg:
if (!CreateProcess(TEXT("full path to\\test.exe"), ...))
Otherwise, forget using TCHAR and just write Ansi-specific or Unicode-specific code instead, depending on your needs:
if (!CreateProcessA("full path to\\test.exe", ...))
if (!CreateProcessW(L"full path to\\test.exe", ...))
test.exe is never being looked up in the directory that the calling exe lives in. It is being looked up in the current directory which is a per-process path variable. Maybe the current directory is not pointed to where test.exe lives. You should also never rely on that because it can change arbitrarily (for example by using the file dialogs, or when the parent process changes it).
The CreateProcess function is very sensitive when it comes to file names, at least in my opinion.
When you specify your exe like that you actually specify it according to the current directory which may not be the same as the directory your main exe is in, which explains the file not found.
One way around is to simply get your current exe's directory with GetModulePath stip away the exe name from that and there you have the same directory, or simply use an absolute path.
According to the CreateProcess documentation the first parameter can be NULL :
The lpApplicationName parameter can be NULL. In that case, the module name must be the first white space–delimited token in the lpCommandLine string.
At least for me it seemed that if you just specify the command line only it works far better than with the application name, and also within the application name you can't handle the commandline.
With QT the MSDN function TEXT() it doesn't work: QTCreator's compiler return:
'Lvar' undeclared (first use in this function)
where var is input of Text(), because of QT whose enable UNICODE and because of that:
#ifdef UNICODE
/*
* NOTE: This tests UNICODE, which is different from the _UNICODE define
* used to differentiate standard C runtime calls.
*/
typedef WCHAR TCHAR;
typedef WCHAR _TCHAR;
/*
* __TEXT is a private macro whose specific use is to force the expansion of a
* macro passed as an argument to the macro TEXT. DO NOT use this
* macro within your programs. It's name and function could change without
* notice.
*/
#define __TEXT(q) L##q
#else
typedef CHAR TCHAR;
typedef CHAR _TCHAR;
#define __TEXT(q) q
#endif
#endif
in particular this passage:
#define __TEXT(q) L##q
in winnt.h included by windows.h
So, to solve this problem, we have to add this:
DEFINES -= UNICODE
in the .pro file of the QTCreator's project and it will work.
I am writing a C program in Linux. Commands like execv() require a path in the form of a C string. Is there a command that will return the current path in the form of a C-style string?
getcwd():
SYNOPSIS
#include <unistd.h>
char *getcwd(char *buf, size_t size);
DESCRIPTION
The getcwd() function shall place an absolute pathname of the current working directory in the array pointed to by buf, and return buf. The pathname copied to the array shall contain no components that are symbolic links. The size argument is the size in bytes of the character array pointed to by the buf argument. If buf is a null pointer, the behavior of getcwd() is unspecified.
RETURN VALUE
Upon successful completion, getcwd() shall return the buf argument. Otherwise, getcwd() shall return a null pointer and set errno to indicate the error. The contents of the array pointed to by buf are then undefined....
The path argument to execv() is the path to the application you wish to execute, not the current working directory (which will be returned by getcwd()) or the shell search path (which will be returned by getenv("PATH")).
Depending on what you're doing, you may get more mileage out of the system() function in the C library rather than the lower-level exec() family.
This is not ANSI C:
#include <unistd.h>
char path[MAXPATHLEN];
getcwd(path, MAXPATHLEN);
printf("pwd -> %s\n", path);
If the path can be a relative path, you should be able to use '.' or './' as the path. I'm not sure if it will work, but you could try it.
You need to grab the environment variable PWD (present working directory).
I'm not sure what the library it is in, but it is a standard Linux header.
I was thinking of getenv() which would help if you also need to run system commands and need the various bin paths located in PATH.