SCENARIO
PROCEDURE A gathers files from a webservice, and copies them to a root folder. Sometimes files are copied in a subfolder of the root, for example:
c:\root\file1
c:\root\file2
c:\root\filea
c:\root\<unique random name>\fileA
I suspect (but I'm not sure) that the webservice runs on linux-system and file names are case-sensitive. So, files are copied on a Windows file-system, and when uppercase/lowercase conflicts occure, files are copied in a subfolder. The subfolder has an unique randomly generated name.
PROCEDURE B scans files in the root and sub-folders in order to archive them. Files correctly archived are deleted. PROCEDURE A and PROCEDURE B don't run simultaneously.
And now my task ... I've to delete empty subfolder of the root.
FIRST SOLUTION (the easiest one)
When procedure B ends, I can scan empty subfolders of the root, end then delete them. Well ...
DWORD DeleteEmptySubFolder(LPCSTR szRootFolder)
{
DWORD dwError = 0;
CString sFolder(szRootFolder);
sFolder += "*.*";
CFileFind find_folder;
BOOL bWorking = find_folder.FindFile(sFolder);
while (bWorking)
{
bWorking = find_folder.FindNextFile();
if(find_folder.IsDots())
continue;
if(find_folder.IsDirectory())
{
if(PathIsDirectoryEmpty(find_folder.GetFilePath()))
if(!RemoveDirectory(find_folder.GetFilePath()))
dwError = GetLastError();
}
}
return dwError;
}
and now here are the problems: I haven't got any control on PROCEDURE B and I don't know when it ends. PROCEDURE B can call a user function after archiving each individual file.
SECOND SOLUTION (adequate but not too efficient)
I can still call the above function
DWORD DeleteEmptySubFolder(LPCSTR szRootFolder)
It's not efficient for sure, it will scan all subfolders of the root for each archived file, but it will delete only empty subfolders.
THIRD SOLUTION (it should work)
When procedure B call user function, I know the root folder and the full path of the archived file. So I can check if the folder of the file is a sub-folder of the root:
#define EQUAL_FOLDER 0
#define A_SUBFOLDER_OF_B 1
#define B_SUBFOLDER_OF_A 2
#define UNRELATED_FOLDER 3
int CompareFolderHiearachy(LPCSTR szFolderA, LPCSTR szFolderB)
{
if(_stricmp(szFolderA, szFolderB))
{
// StrStrI - Windows function (from shlwapi.dll) which finds the first occurrence of a substring within a string (the comparison is not case-sensitive).
if(StrStrI(szFolderA, szFolderB) == szFolderA)
return A_SUBFOLDER_OF_B;
else if(StrStrI(szFolderB, szFolderA) == szFolderB)
return B_SUBFOLDER_OF_A;
else
return UNRELATED_FOLDER;
}
else
return EQUAL_FOLDER;
}
Maybe this solution could work fine in my scenario, but it can only handle cases where folder/file names are consistent. For example:
local disk:
root: C:\folder\
filename: c:\folder\subfolder\fileA
mapped disk:
root: Z:\folder\
filename: Z:\folder\subfolder\fileA
UNC:
root: \\SERVER\folder\
filename: \\SERVER\folder\subfolder\fileA
and now my too generic and abstract question, can I check the hierarchy/realtionship of two folders in the worst scenario ?
\\server\folder1\folder2 (UNC)
z:\folder2 (network drive).
or even worst ....
\\MYPC\folder1\folder2
c:\folder2
Maybe I'm asking a bit perverse question ... but it's quite challenging and intriguing, isn't it ?
Thank you very much.
I improved the thrid solution; it can solve severale situations, but nfortunately it can't handle all the possible cases.
#define ERROR_OCCURED -1
#define EQUAL_FOLDER 0
#define A_SUBFOLDER_OF_B 1
#define B_SUBFOLDER_OF_A 2
#define UNRELATED_FOLDER 3
int CompareFolderHiearachy(LPCSTR szFolderA, LPCSTR szFolderB)
{
char pBuffer[32767];
DWORD dwBufferLength = 32767;
UNIVERSAL_NAME_INFO * unameinfo = reinterpret_cast< UNIVERSAL_NAME_INFO *>(pBuffer);
DWORD dwRetVal = WNetGetUniversalName(szFolderA, UNIVERSAL_NAME_INFO_LEVEL, reinterpret_cast<LPVOID>(pBuffer), &dwBufferLength);
if(dwRetVal != NO_ERROR && dwRetVal != ERROR_NOT_CONNECTED && dwRetVal != ERROR_BAD_DEVICE)
return ERROR_OCCURED;
CString sFolderA(unameinfo->lpUniversalName ? unameinfo->lpUniversalName : szFolderA);
ZeroMemory(pBuffer, dwBufferLength);
dwRetVal = WNetGetUniversalName(szFolderB, UNIVERSAL_NAME_INFO_LEVEL, reinterpret_cast<LPVOID>(pBuffer), &dwBufferLength);
if(dwRetVal != NO_ERROR && dwRetVal != ERROR_NOT_CONNECTED && dwRetVal != ERROR_BAD_DEVICE)
return ERROR_OCCURED;
CString sFolderB(unameinfo->lpUniversalName ? unameinfo->lpUniversalName : szFolderB);
if(_stricmp(sFolderA, sFolderB))
{
// StrStrI - Windows function (from shlwapi.dll) which finds the first occurrence of a substring within a string (the comparison is not case-sensitive).
if(StrStrI(sFolderA, sFolderB) == static_cast<LPCSTR>(sFolderA))
return A_SUBFOLDER_OF_B;
else if(StrStrI(szFolderB, sFolderA) == static_cast<LPCSTR>(sFolderB))
return B_SUBFOLDER_OF_A;
else
return UNRELATED_FOLDER;
}
else
return EQUAL_FOLDER;
}
It can't solve the following:
folder A: \\MY_PC\shared_folder\folderA
folder B: C:\shared_folder
(\\MY_PC\shared_folder and C:\shared_folder are the sane folder)
and:
folder A: \\SERVER\shared_folderA\shared_folderB
folder B: \\SERVER\shared_folderB
Related
I am trying to modify an ActiveX control developed in Visual Studio 2008 to use it for a purpose for which it was not originally designed. I will be reusing at least 90% of its code. Therefore, I would like to begin by creating an identical control that uses different GUIDs. I tried to follow instructions I found here (a very old link, written in 2004), but when I tried to build my project, I got an assertion failure in ctlreg.cpp line 113. Then, I restored all my changed files back to their original states, and for each GUID is the .odl file, I searched for the GUID in my .cpp and .h files and changed it wherever I found it. I also made sure to change my major version number. I still get the assertion failure. What else should I be doing?
Here's the code from ctlreg.cpp from the start of the method containing the assertion to the assertion itself:
BOOL AFXAPI AfxOleRegisterTypeLib(HINSTANCE hInstance, REFGUID tlid,
LPCTSTR pszFileName, LPCTSTR pszHelpDir)
{
BOOL bSuccess = FALSE;
CStringW strPathNameW;
wchar_t *szPathNameW = strPathNameW.GetBuffer(_MAX_PATH);
::GetModuleFileNameW(hInstance, szPathNameW, _MAX_PATH);
strPathNameW.ReleaseBuffer();
LPTYPELIB ptlib = NULL;
// If a filename was specified, replace final component of path with it.
if (pszFileName != NULL)
{
int iBackslash = strPathNameW.ReverseFind('\\');
if (iBackslash != -1)
strPathNameW = strPathNameW.Left(iBackslash+1);
strPathNameW += pszFileName;
}
if (SUCCEEDED(LoadTypeLib(strPathNameW.GetString(), &ptlib)))
{
ASSERT_POINTER(ptlib, ITypeLib);
LPTLIBATTR pAttr;
GUID tlidActual = GUID_NULL;
if (SUCCEEDED(ptlib->GetLibAttr(&pAttr)))
{
ASSERT_POINTER(pAttr, TLIBATTR);
tlidActual = pAttr->guid;
ptlib->ReleaseTLibAttr(pAttr);
}
// Check that the guid of the loaded type library matches
// the tlid parameter.
ASSERT(IsEqualGUID(tlid, tlidActual));
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I tested find all folders under a directory with the environment of visual studio using mfc. Simply make a mfc dlg, add a button, paste the code below. The only variable to give is "product_path" directory with folders, empty folder is ok name like 123,or abc, or ABC.
Testing:
test 1 folders named 123,456.
result:can be found all.
test 2 folders named 123,456,ab.
result:the folder name ab can not found.
test 3 folders named 123,ab,AB.
result:the folder name AB can not found.
/*vector_folder_name used to store found folders*/
vector<CString> vector_folder_name;
/*product_path directory with folders*/
CString product_path=_T("..\\ProductType");
//sprintf(product_path,"..\\%s","ProductType");
if (product_path.Right(1) != "\\")
{
product_path += _T("\\");
}
product_path += _T("*.*");
CFileFind ff;
BOOL ret = ff.FindFile(product_path);
while (ret)
{
ret = ff.FindNextFile();
if (ret != 0)
{
if (ff.IsDirectory() && !ff.IsDots())
{
//CString path = ff.GetFilePath();
CString folder_name = ff.GetFileName();
vector_folder_name.push_back(folder_name);
//TraverseDir(path, vec);
}
//else /*if(!ff.IsDirectory() && !ff.IsDots())*/
//{
// CString name = ff.GetFileName();
// CString path = ff.GetFilePath();
// vector_folder_name.push_back(path);
//}
}
}
//sort(vector_folder_name.begin(),vector_folder_name.end());
sort(vector_folder_name.begin(),vector_folder_name.end(),compare1);
ff.Close(); // do not foget close
Your problem is totally unrelated to uppercase or lower case file names, and there is nothing magic about the situation, but your code is incorrect. It's also unrelated to the compare1 function
FindNextFile returns FALSE upon the last file found and therefore your code simply skips the last file found.
In other words if FindNextFile returns FALSE, it's not an error but it means that you've got the last file in the directory.
This (even simpler) code works:
...
BOOL ret = ff.FindFile(product_path);
while (ret)
{
ret = ff.FindNextFile();
if (ff.IsDirectory() && !ff.IsDots())
{
// it's a directory
CString folder_name = ff.GetFileName();
vector_folder_name.push_back(folder_name);
}
}
...
I've removed your outcommented code.
I'm trying to rename the entries of an archive using the libarchive library.
In particular I'm using the function archive_entry_set_pathname.
Files and empty directories are correctly renamed, but unfortunately this is not working if a directory is not empty: instead of being renamed, a new empty directory with the new name is created as sibling of the target directory, which has the old name.
Relevant code snippet:
...
while (archive_read_next_header(inputArchive, &entry) == ARCHIVE_OK) {
if (file == QFile::decodeName(archive_entry_pathname(entry))) {
// FIXME: not working with non-empty directories
archive_entry_set_pathname(entry, QFile::encodeName(newPath));
}
int header_response;
if ((header_response = archive_write_header(outputArchive, entry)) == ARCHIVE_OK) {
... // write the (new) outputArchive on disk
}
}
What's wrong with non-empty directories?
In an archive, the files are stored with their full path names relative to the root of the archive. Your code only matches the directory entry, you also need to match all entries below that directory and rename them. I'm no Qt expert and I haven't tried this code, but you will get the idea.
QStringLiteral oldPath("foo/");
QStringLiteral newPath("bar/");
while (archive_read_next_header(inputArchive, &entry) == ARCHIVE_OK) {
QString arEntryPath = QFile::decodeName(archive_entry_pathname(entry));
if(arEntryPath.startsWith(oldPath) {
arEntryPath.replace(0, oldPath.length(), newPath);
archive_entry_set_pathname(entry, QFile::encodeName(arEntryPath));
}
int header_response;
if ((header_response = archive_write_header(outputArchive, entry)) == ARCHIVE_OK) {
... // write the (new) outputArchive on disk
}
}
This is for a web server assignment and this is an extra feature that I would like to implement. I would like to ensure that a client cannot specify a file above the servers root directory.
For example, lets say I have a folder "above", and within "above", I have www. I have my root directory set to /above/www in the server, so the server should be able to access anything within /above/www. Lets also say I have some a1.txt in above, so /above/a1.txt, I don't want a client to be able to perform GET /../a1.txt HTTP/1.0 and get access to that file.
I have 90% of the server implemented, I just need to determine if a file is above a given directory in the filesystem tree or if it is below. I can't think of a super easy way of doing that except for counting the ../ strings and if there are more of those than there are folders, I am above my root directory.
I am running linux, and c++11 is acceptable.
Use boost::filesystem::path(relativePath).absolute().parent_path().string() to obtain the absolute path of the directory that the file represented by relativePath lies in. Now, you can simply check that "/above/www" is a prefix of this directory.
My method for completing the task is to use two for loops and strtok.
This may not be the most c++ answer, but it works.
bool within_dir(const char* path, const char* root_dir)
{
assert(path != NULL);
assert(root_dir != NULL);
char *fname_dup, *root_dup, *token;
int root_value, path_value;
// Duplicate char arrays so strtok doesn't damage the originals
fname_dup = (char*)malloc(sizeof(char) * strlen(path) + 1);
assert(fname_dup != NULL);
bzero(fname_dup, sizeof(char) * strlen(path) +1 );
strncpy (fname_dup, path, sizeof(char) * strlen(path));
root_dup = (char*)malloc(sizeof(char) * strlen(root_dir) + 1);
assert(root_dup != NULL);
bzero(root_dup, sizeof(char) * strlen(root_dir)+ 1);
strncpy(root_dup, root_dir, sizeof(char) * strlen(root_dir) );
// Count root directory
for (root_value = 0, token = strtok(root_dup, "/");
token != NULL;
token = strtok(NULL, "/"))
{
if (strcmp(token, "..") == 0)
root_value--;
else
root_value++;
}
// Count and compare path value to root value
for (path_value = 0, token = strtok(fname_dup, "/");
token != NULL;
token = strtok(NULL, "/"))
{
if (strcmp(token, "..") == 0)
{
path_value--;
if (path_value < root_value)
{
free(root_dup);
free(fname_dup);
return false;
}
}
else
path_value++;
}
free(root_dup);
free(fname_dup);
return true;
}
Basically the code runs through and counts up how "positive" or "negative" the root is. Going up a level is negative, and going down a level is positive. Once it knows the value of the root, if the value of the file path ever goes below the value of the root, we know that the filepath must go up at least one layer above the root directory, and therefore is invalid. Since we don't actually care about the actual value, only if it goes up a layer from the root directory, we immediately stop and return false. If the filepath always remains more positive than the root, then the filepath will always be lower on the filetree. No guarantees are made for links.
I am porting a program from Borland C++ Builder to Visual Studio 2013 (C++). The program uses getcurdir to get the current directory of a drive. This function has a parameter drive, but the Microsoft equivalent function getcwd don't have such parameter. How can I do it?
As you tagged visual studio, I assume you'r using windows. Beside that current directory is just one, (i.e. where the executable is located or other if you've moved to) current directory won't be different depending on current drive, I think. Then, in windows you may use the function GetCurrentDirectory from winapi. The prototype is:
DWORD WINAPI GetCurrentDirectory(
_In_ DWORD nBufferLength,
_Out_ LPTSTR lpBuffer
);
You may get details here.
Example:
TCHAR cwd[100];
GetCurrentDirectory(100,cwd);
// now cwd will contain absolute path to current directory
(Yes I know this is an old entry, just for the records if somebody stumbles over the same issue...)
As deeiip already said correctly, in Windows there is only 1 current directory, but cmd.exe fakes DOS behavior when there was 1 current directory per drive.
If you need to access cmd's current directory per drive, use the according hidden environment variables, e.g. "%=C:%".
Here an example application (in C#):
using System;
static class Module1 {
public static void Main(String[] args) {
var myFolder = GetCurrentFolderPerDrive(args[0]); //e.g. "C:"
Console.WriteLine(myFolder);
}
private static string GetCurrentFolderPerDrive(string driveLetter) {
driveLetter = NormalizeDriveLetter(driveLetter);
string myEnvironmentVariable = $"%={driveLetter}%";
string myResult = Environment.ExpandEnvironmentVariables(myEnvironmentVariable);
if (myResult == myEnvironmentVariable) return $"{driveLetter.ToUpperInvariant()}\\"; //No current folder set, return root
return myResult;
}
private static String NormalizeDriveLetter(String driveLetter) {
if (String.IsNullOrWhiteSpace(driveLetter)) throw new ArgumentNullException(nameof(driveLetter), "The drive letter is null, empty or white-space.");
Boolean throwException = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".IndexOf(driveLetter[0]) < 0);
if (!throwException) {
if (driveLetter.Length == 1) {
driveLetter += ':';
} else if (driveLetter.Length != 2) {
throwException = true;
}
}
if (throwException) throw new ArgumentException($"A well-formed drive letter expected, e.g. \"C:\"!\r\nGiven value: \"{driveLetter}\".", nameof(driveLetter));
return driveLetter;
}
}