c++ - fopen() internally changes my filename? - c++

I use fopen() in my c++ program and I tried to open a .aff file.
I want to parse a file named car_wheel.aff and after if(ifp=fopen(path,"r")) has executed, it seems the fopen() function changes my path variable???
I add some detail to my question to the comment.
code (since variable path is constructed by my code, I put the whole piece of code here, which may seem a bit redundant.)
char* dir = "../kitchen/";
char filename[100];
char* path;
FILE *ifp;
int detail_level;
if(fscanf(fp,"%d %s",&detail_level,filename)!=2)
{
printf("Error: could not parse include.\n");
exit(0);
}
path = (char*)malloc(strlen(dir)+strlen(filename));
strcpy(path, dir);
strcat(path, filename); // path is "../kitchen/car_wheel.aff"
if(detail_level<=gDetailLevel)
{
if(ifp=fopen(path,"r"))
{
viParseFile(ifp);
fclose(ifp);
}
else
{
// jumped here and path became "../kitchen/car_wheel.aff1\002"
if (ifp == NULL) {
perror(path);
exit(EXIT_FAILURE);
}
printf("Error: could not open include file: <%s>.\n",filename);
exit(1);
}
}
I debugged the code in my ide, and it gave the filename char array is
and there is no '1\002' behind my filename variable. What happened??

The problem is here:
path = (char*)malloc(strlen(dir)+strlen(filename));
You don't allocate space for the terminating zero character. Change it to this:
path = (char*)malloc(strlen(dir)+strlen(filename)+1);

Related

save recursion output to text file

I am trying to recursively list the files in a directory. However when I save the output to a text file, it works, but the file contents keep being reset. So the file size will be 2Kb, and then it is reset to 1Kb, 30Kb reset to 1Kb, and so on. The code is not saving all the output to the text file and only some of the last lines are saved to output.txt.
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
#include <Psapi.h>
#include <fstream>
using namespace std;
void listDir(wchar_t * szCurrentDirectory);
int main()
{
// return current directory where app run
DWORD nBufferLength = MAX_PATH;
wchar_t szCurrentDirectory[MAX_PATH];
GetCurrentDirectory(nBufferLength, szCurrentDirectory);
listDir(szCurrentDirectory);
system("pause");
return 0;
}
void listDir(wchar_t * szCurrentDirectory)
{
wchar_t addPath[MAX_PATH] = L"\\*";
WIN32_FIND_DATA FindFileData;
wchar_t buf[MAX_PATH];
swprintf(buf, MAX_PATH,L"%s%s", szCurrentDirectory, addPath);
HANDLE hFind;
hFind = FindFirstFile(buf, &FindFileData);
wofstream myfile("c:/output.txt");
if (myfile.is_open())
{
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
// Check if is a folder or not.
if (FindFileData.dwFileAttributes != FILE_ATTRIBUTE_REPARSE_POINT)
{
// Ignore current folder and parent folder.
if (wcscmp(FindFileData.cFileName, L".") && wcscmp(FindFileData.cFileName, L"..") != 0)
{
if (wcscmp(FindFileData.cFileName, L"$RECYCLE.BIN") != 0)
{
// return current directory where app run
wchar_t filepath[10000];
// Append slash to current directory.
swprintf(filepath, 10000, L"%s%s%s", szCurrentDirectory, L"\\", FindFileData.cFileName);
// Output the file.
myfile << filepath << endl;
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// Recursive
listDir(filepath);
} // if directory
} // if not recycle bin
} // if not . or ..
} if not reparse point
} while (FindNextFile(hFind, &FindFileData) != 0);
FindClose(hFind);
myfile.close();
}
}
}
try to run the app in c: drive and look the output in output.txt. the output keep reset.
Pass the open file as a parameter instead.
Change
void listDir(wchar_t * szCurrentDirectory)
to
void listDir(wofstream & myfile,wchar_t * szCurrentDirectory)
then remove the file opening code from listDir().
Delete this line:
wofstream myfile("c:/output.txt");
And then call it in main() like this:
wofstream myfile("c:/output.txt");
listDir(myfile,szCurrentDirectory);
And finally in listDir() change
listDir(filepath);
to
listDir(myfile,filepath);
The right answer is definitely to open the file once (in the main program), and then pass the stream into into the listdir function.
However ... there is an alternative. It is not appropriate in this case, it might be in similar cases. You can open the file in append mode, so the contents is never overwritten. You just need to change the line where you open the file to:
std::wofstream myfile("c:/output.txt", std::ios_base::app);
The output will be written to the end of the file, preserving the existing contents.
Open the file in main and pass the handle as a second parameter to the listDir function.
Don't open it in listDir

FindNextFile Faild with Space Character

I wrote a simple code to do some operation on every file in every folder (subfolders).
It's perfectly works until the path comes with 'SPACE
' character program crashs and INVALID_HANDLE_VALUE has been called. This is function:
int dirListFiles(char* startDir)
{
HANDLE hFind;
WIN32_FIND_DATAA wfd;
char path[MAX_PATH];
sprintf(path, "%s\\*", startDir);
std::string fileName;
std::string s_path = startDir;
std::string fullPath;
fprintf(stdout, "In Directory \"%s\"\n\n", startDir);
if ((hFind = FindFirstFileA(path, &wfd)) == INVALID_HANDLE_VALUE)
{
printf("FindFirstFIle failed on path = \"%s\"\n", path);
abort();
}
BOOL cont = TRUE;
while (cont == TRUE)
{
if ((strncmp(".", wfd.cFileName, 1) != 0) && (strncmp("..", wfd.cFileName, 2) != 0))
{
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
sprintf(path, "%s\\%s", startDir, wfd.cFileName);
dirListFiles(path);
}
else
{
fileName = wfd.cFileName;
fullPath = s_path + "\\" + fileName;
std::string fileExt = PathFindExtension(fullPath.c_str());
if (fileExt == ".cpp")
{
... Some operation on file
}
}
}
cont = FindNextFile(hFind, &wfd);
}
FindClose(hFind);
For example, If FindNextFile wants to Open Program Files (x86) which has space between file name cause error and program exit. What Can I do for supporting spaces? What Is Problem?
Space is legal character in directory and file names.
First I propose to modify slightly your code:
if ((hFind = FindFirstFileA(path, &wfd)) == INVALID_HANDLE_VALUE)
{
printf("FindFirstFIle failed on path = \"%s\". Error %d\n", path, GetLastError());
return 0; // I think you shouldn't abort on error, just skip this dir.
}
Now check error codes reported by your program.
For some paths I have got error #5 (access denied). Examples:
c:\Program Files (x86)\Google\CrashReports\*
c:\ProgramData\Microsoft\Windows Defender\Clean Store\*
c:\Windows\System32\config\*
Got two cases with code #123 (Invalid name) for path names unmanageable by FindFirstFileA. To correct this behavior it would be better to use wide version of function FindFirstFileW. See both answers for c++ folder only search. For new Windows applications you should use wide version of API, converting with MultiByteToWideChar and WideCharToMultiByte if needed.
You have also logic error. Code skips all directories and files starting with dot.

C++ find file on LD_LIBRARY_PATH

In C/C++ Is there an existing way to check to see if a particular file is on a particular path? I imagine syntax that might look like stat() but with additional parms for filename and path.
pathstat( struct stat stResult, const char* filename, const char* path );
using access() function with F_OK flag will check if your file exist. but before that you have to prepare the file path (from filename and path variables ) before pass the file path as input argument in the function access().
In this answer I assume that you use linux as platform
void pathstat( struct stat stResult, const char* filename, const char* path )
char *file;
int path_len;
if (!filename) {
// set error in the stResult
return;
}
path_len = path ? strlen(path) : 0;
file = malloc((strlen(filename) + path_len + 2) * sizeof(char));
// PREPARE THE FILE PATH
if (path && path[0] && path[path_len-1]=='/')
sprintf(file,"%s%s",path,filename);
else if (path && path[0])
sprintf(file,"%s/%s",path,filename);
else
file = strcpy(file,filename);
//NOW CHECK IF THE FILE PATH EXIST WITH ACCESS
if (access(file, F_OK) != -1) {
//file exist
} else {
// file does not exxit
}
free(file);
}

how to skip a directory while reading using dirent.h

i am trying to recursively open files using the functionality provided in dirent.h
My problem is:
i could not make it to skip directories which failed to open. I want it to open the directories which it can and skip those which it can't and move to the next directory instead of exiting with failure.
What should i do to fix this?
Here is a simple code i tried to use
int acessdirs(const char *path)
{
struct dirent *entry;
DIR *dp;
char fpath[300];
if(dp=opendir(path))
{
while((entry=readdir(dp)))
do things here
}
else
{
std::cout<<"error opening directory";
return 0;
}
return 1;
}
I used this same style on windows 7 and it works fine.But it crashed on windows xp and when i debugged it i found that it crashes while trying to open "system volume information".
I really dont need to access this folder and i was hoping if there is any way to skip it.
Here is my real code:
It is a little bit long.
int listdir(const char *path)
{
struct dirent *entry;
DIR *dp;
if(dp = opendir(path))
{
struct stat buf ;
while((entry = readdir(dp)))
{
std::string p(path);
p += "\\";
p += entry->d_name;
char fpath[300];
if(!stat(p.c_str(), &buf))
{
if(S_ISREG(buf.st_mode))
{
sprintf(fpath,"%s\\%s",path,entry->d_name);
stat(fpath, &buf);
std::cout<<"\n Size of \t"<<fpath<<"\t"<<buf.st_size;
fmd5=MDFile (fpath);
}//inner second if
if(S_ISDIR(buf.st_mode) &&
// the following is to ensure we do not dive into directories "." and ".."
strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..") )
{
listdir(p.c_str());
}
}//inner first if
else
std::cout << "ERROR in stat\n";
}//end while
closedir(dp);
}//first if
else
{
std::cout << "ERROR in opendir\n";
return 0;
}
return 1;
}//listdir()
Your biggest problem seems to be here:
sprintf(fpath,"%s\\%s",path,entry->d_name);
stat(fpath, &buf);
Without seeing fpath's declatation, it's tough to tell for certain, but you're either
Overflowing fpath in the sprintf call, leading to undefined behavior. "System Volume Information" is a long name. You should really use snprintf.
Not checking the return value of the stat call. If it returns -1, I'n not sure what the contents of buf will be.
More importantly, if you can use POSIX stuff, the function ftw is standard, and should provide most of the functionality you're trying to implement here.

Problems with Visual C++: Reading all files in a directory

I'm trying to read all files in a directory. I have the following code:
void scanDirectory(char* dir)
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind = INVALID_HANDLE_VALUE;
char DirSpec[MAX_PATH]; // directory specification
strcpy(DirSpec, dir);
strcat(DirSpec, "\\*");
hFind = FindFirstFile(DirSpec, &FindFileData);
int i = 0;
do {
i++;
printf("%d \n", i);
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
printf(" %s <DIR>\n", FindFileData.cFileName);
}
else
{
printf("File %s\n", FindFileData.cFileName);
}
} while(!FindNextFile(hFind, &FindFileData));
FindClose(hFind);
}
The problem is that when I execute the code it results in an infinite loop. Also the output characters are strange, like "File ".
I think you are not using chars and wide chars in a consequent way. You should either use functions with wide char and wchar_t type or vice versa. (But it was a compile error for me so it may depend on some kind of project settings as well.)
And your exit condition in the while loop is also wrong it should test for FindNextFile and not !FindNextFile. The infinite loop may be because of this condition as if it doesn't find any files it will run forever.
Also you should test for the return value of FindFirstFile and not go into the loop if it doesn't find any files.
You are calling !FindNextFile instead of FindNextFile, also you are not checking why
the FindNextFile fails, so you can't be sure if all the files were processed.
Use something like this.
WIN32_FIND_DATA stFindData;
HANDLE hFind = FindFirstFile(cSearchPattern, &stFindData);
if(hFind != INVALID_HANDLE_VALUE)
{
do
{
// Process File
}
while (FindNextFile(hFind, &stFindData) != 0);
DWORD dwError = GetLastError();
if (dwError != ERROR_NO_MORE_FILES)
{
// Not All Files processed, deal with Error
}
FindClose(hFind);
}
Can't you just use .Net like below:
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(Path);
System.IO.FileInfo[] files = dir.GetFiles();
foreach (System.IO.FileInfo file in files)
{
// Do whatever you need with the file info...
string filename = file.Name;
string fullFilename = file.FullName;
}
This is a c# example but you can use for each in C++ the same. Hope this helps.