Get proper case working directory? - c++

To better familiarize myself with C++, I'm redoing an old college OS assignment: program your own shell. I'm using all sorts of Windows.h that I've never known existed. So far I've made good progress but I've noticed something about my cd implementation and my working directory I get back from getcwd.
My cd command does some error checking but ultimately it comes down to chdir(path). Say I'm at C:\ and there exists a folder FOLDER. If I use chdir("folder") then later when I call getcwd(dir, FILENAME_MAX) then I'll get the string C:\folder instead of the case correct string C:\FOLDER. How can I retrieve the working directory with every folder having the proper case?
Note: When I first start my shell and run my pwd command (that solely prints dir from my getcwd call), I get a path that is properly cased. As soon as I start changing the working directory then the casing always matches my strings instead of the actual folder casing.

I think the Windows command prompt just uses GetLongPathName, which returns the path with appropriate casing (however, it doesn't change the drive letter's casing).
If you want an uppercase drive letter, the GetShortPathName function returns the short path with the driver letter capitalized. You can then pass this short path to GetLongPathName, which will turn it into a properly cased long path, but this isn't what cmd does.
You can also use SHGetFileInfo, but it's not the easiest approach.

You can use the GetFullPathName API function to return the proper (case correct) path of the current directory, as in the following example:
TCHAR tchPath[MAX_PATH];
GetFullPathName(TEXT("."), MAX_PATH, tchPath, NULL);

Related

Directory navigation in OPEN clause

How can I read from a parent directory using the OPEN clause? Let's say
OPEN (11,file='../inf/input.dat',form='formatted',status='old')
, which doesn't work. I get:
forrtl: severe (29): file not found, unit 11, file /home/cg/compile/comp/../inf/input.dat
I would like to read from the parent directory just before inf. Is that possible?
Unfortunately there is no unique way to do this, since paths are OS dependent. In order to this in a robust way you might need to define a function that look for the OS while preprocessing (cf. compilation flags e.g. here).
For *nix systems (Unix, including OSX, and Linux) the option you provided should suffice
../
in the path goes to the previous directory.
However in windows there is no way that I know to go in the above directory (I don't have a Windows system with me at the moment).
However you can workaround this limitation with the GetModuleFileName API function. (note that this will not work in the systems above)
CHARACTER*(*) pathname ! full name
INTEGER L ! length
L= GetModuleFileName(NULL,pathname,LEN(pathname))
Fullname will now contain the full path where you run your program, so you can do all sort of string operation you want.
If you want to go above one level
Idx = index(trim(pathname), '/', .True.)
Finds the index of the last '/' character in the pathname (you might need to look for the one before the last).
outfile_path=pathname(:idx)+'/inf/input.dat'
will be now the path you want.

Windows up one directory from current path

I have been searching for hours but cant find a solution to this as yet. Apologies it is probably really simple.
My program is using CreateDirectory to create a new directory and then set the path to it to receive a number of data files:
if (CreateDirectory(dateTime.c_str(), NULL) || ERROR_ALREADY_EXISTS == GetLastError())
{
SetCurrentDirectory(dateTime.c_str());
}
Once all the data files have been generated I would like to move back up one directory without specifying the absolute path. Something equivalent to cd.. or ../ Does anyone know the best way to do this?
One possible approach is to get the current directory (GetCurrentDirectory) before changing to a new one and once complete, then change back the desired directory; akin to a push/pop.
In the sample I've left out error checking and buffer size requirements for simplicity.
TCHAR resetDir[1024] = {};
GetCurrentDirectory(1024, resetDir);
//... Do some work, change directories etc...
// Reset the directory
SetCurrentDirectory(resetDir);
Side note: the current directory when the process is launched is not necessarily the same as the directory the process image is in (the exe path).
Relative changes can be done with a simple
SetCurrentDirectory(_T(".."));
Although basing the relative from the current directory would also work (and may be preferable);
SetCurrentDirectory((currentDir + _T("\\..")).c_str());
Internally, cd command ends using SetCurrentDirectory. So to get something equivalent to cd.. or cd ../ you can simply use:
cr = ::SetCurrentDirectory("..");
cr should be non zero if it succeded and 0 if it failed. In the latter case use GetLastError to get further information.

Calls to system() do not behave the same way as in command prompt (cmd)

I need to cut&paste a folder into another folder through code in C++. But some directory names are problematic, such as the ones which have japanese symbols. However, the same commands introduced through cmd all work fine.
system("move dirName dirName2"); //work
system("move ディレクトリ dirName2"); //does not work (system cannot find the specified file)
system("move ディレクトリ.txt dirName2"); //work
Funny enough, if the item which has the japanese symbols is a file and not a folder, the operation works fine even using calls to system().
I have no idea why the second call to system() does not work or how to solve it.
PS: I'm working with Windows7.
"move dirName dirName2", it is a const char* type, while the Japanese chars are not ASII chars, you should use the unicode API here, try:
_wsystem(L"move ディレクトリ dirName2")
It is likely that you need to use _wsystem instead to accomodate the wide characters. See the relevant MSDN pagefor details, but the syntax of the call is the same.

Diagnosing QDir::rmdir failure

I’m using the following code to delete an empty folder on Linux:
bool removeFolder (const QString& path)
{
QDir dir(path);
assert(dir.exists());
return dir.rmdir(".");
}
For some reason it sometimes returns false (for specific folders, but those folders don’t seem to be wrong in any way). If I subsequently use ::rmdir from <unistd.h> to remove the same folder, it succeeds.
How can I tell why QDir::rmdir is failing?
This never happened on Windows so far, QDir::rmdir just works.
Confirming: works on windown, fails on linux.
Reading the "rmdir" doc in <unistd>, here https://pubs.opengroup.org/onlinepubs/007904875/functions/rmdir.html, it says there that "If the path argument refers to a path whose final component is either dot or dot-dot, rmdir() shall fail." So what's probably happening is that QDir::rmdir() is calling the unistd rmdir() function in linux, and this one fails with ".".
I tried to just use the full absolute path ( QDir::rmdir(absolutePath) ) and it worked; however, i see basically no point in using QDir::rmdir() over unistd's rmdir(), so i''ll stick w/ the unistd rmdir() from now on.
note: QDir::removeRecursively() is a different story: it seems to work okay, and it's way more convenient than going through opendir() and then successive readdir()'s (or the nftw(...FTW_DEPTH...) thingie).
I had the same problem but on Windows, I could not delete an empty directory with QDir().rmdir(path);. This happened on some older hard drive so may be the ancient file system was to blame. But I found a hack:
QFile(path).setPermissions(QFile::WriteOther); // this works even for dirs
bool success = QDir().rmdir(path);
Of course, you should revert the permissions back to original values if the deletion was unsuccessful anyway, but that's a different story.
Try to use this one:
dir.rmdir(dir.absolutePath())

Executing a command from C++, What is expected in argv[0]?

I am using execv() to run commands from /bin/ such as 'ls', 'pwd', 'echo' from my c++ program, and I am wondering what value I should provide in argv[0];
const char * path = getPath();
char ** argv = getArgs();
execv(path,argv);
argv[0] is supposed to be the program name. It's passed to the program's main function. Some programs differentiate their behavior depending on what string argv[0] is. For example the GNU bash shell will disable some of its features if called using sh instead of bash. Best give it the same value that you pass to path.
In linux, argv[0] is the process name displayed by the top utility (which it probably gets from reading entries in /proc/)
argv[0] should be the full path of the command that you want to run.
I know that this is not the answer you're looking for but is there a specific reason why you're doing this? The reason I ask is that most if not all of the actions people normally run with either system() or execv() are available in libraries on either Windows or Unix and are safer, faster and less likely to suffer from circumstantial errors. By that I mean, for example, when the PATH changes and suddenly your code stops working.
If you're passing in a string, either in whole or in part, and running it then you also leave yourself open to a user gaining access to the system by entering a command that could be damaging. E.g. imagine you've implemented a file search using find /home -name and your user types in:
"%" -exec rm {} \;
Ouch!