c++ filesystem out of root - c++

I`l trying to catch wrong paths in input args in my code and found that behavior.
code example:
#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;
int main() {
fs::path p = "/home";
cout << p << endl; // "/home"
cout << fs::exists(p) << endl; // 1
try {
p = p / "../..";
cout << p << endl; // "/home/../.."
cout << fs::exists(p) << endl; // 1
} catch (...) {
cout << "catched" << endl;
}
p = fs::canonical(p);
cout << p << endl; // "/"
cout << fs::exists(p) << endl; // 1
return 0;
}
How to catch out of bounds of the root with the standard capabilities? Is this a bug or a feature?

Like everyone else said, you cannot "go out of bounds" with relative parent directory (..).
That said, you can check the depth of a given path:
The path can be traversed element-wise via iterators returned by the begin() and end() functions, which views the path in generic format and iterates over root name, root directory, and the subsequent file name elements (directory separators are skipped except the one that identifies the root directory). If the very last element in the path is a directory separator, the last iterator will dereference to an empty element.
Live On Coliru
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
int main()
{
std::cout << std::boolalpha;
for (fs::path p :
{
"/home",
"/home/sehe",
"/home/",
"//home/",
"/home/sehe/",
}) //
{
try {
p = canonical(absolute(p));
std::cout << " -- " << p << " (" << exists(p) << ")\n";
std::cout << "elements:";
for (auto& el : p) {
std::cout << " " << el << ";";
}
std::cout << "\n";
if (std::distance(p.begin(), p.end()) < 3) {
std::cout << "Does not have two unique parents\n";
} else {
p = p / "../..";
std::cout << "Up two: " << p << " (" << exists(p) << ")\n";
}
} catch (std::exception const& e) {
std::cout << "error " << e.what() << "\n";
}
}
}
On my machine prints:
-- "/home" (true)
elements: "/"; "home";
Does not have two unique parents
-- "/home/sehe" (true)
elements: "/"; "home"; "sehe";
Up two: "/home/sehe/../.." (true)
-- "/home" (true)
elements: "/"; "home";
Does not have two unique parents
-- "/home" (true)
elements: "/"; "home";
Does not have two unique parents
-- "/home/sehe" (true)
elements: "/"; "home"; "sehe";
Up two: "/home/sehe/../.." (true)

Related

Can I use the ftw-function for class methods in C++?

I would like to use the ftw-function to recursivly traverse a filesystem structure. Additionally, the method shall be used inside of a class. Also, the entry-function, which is called by nftw(), belongs to the same class. That needs to be the case because the entry-function is supposed to change some class-members, dependent on the files that it finds.
When implementing such an approach, I get an error (see below). Is this an issue of syntax or is it not even possible to forward a pointer to a method to nftw()? In case it is not possible, do you know any alternative way to resursivly traverse a filesystem structure under linux?
class model
{
public:
boost::unordered_map<std::string, int> map;
int configure(const char *name)
{
// ...
ftw("DTModels/", this->ftw_entry, 15);
// ...
return = 0;
}
private:
int ftw_entry(const char *filepath, const struct stat *info, const int typeflag)
{
// Here I want to change the member 'map'
std::string filepath_s = filepath;
std::cout << "FTW_Entry: " << filepath_s << std::endl;
}
};
ERROR:
a pointer to a bound function may only be used to call the function
ftw("DTModels/", this->ftw_entry, 15);
I haven't used ftw in many many years and since you ask for an alternative, take a look at std::filesystem (C++17). Many pre-C++17 installations have it available via boost or experimental. If you use one of the pre-C++17 implementations, you many need to remove some of the stat lines from the below to make it work.
#include <iostream>
//#define I_HAVE_BOOST
#if __cplusplus >= 201703L
#include <filesystem>
namespace fs = std::filesystem;
#elif I_HAVE_BOOST
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
auto& out = std::cout;
void show_dent(const fs::directory_entry& dent) {
static size_t indent=0;
std::string ind(indent, ' ');
fs::file_status lstat = dent.symlink_status();
if( fs::is_symlink(lstat) ) {
fs::path pp = fs::read_symlink(dent);
out << ind << dent << " -> " << pp << "\n";
++indent;
show_dent(fs::directory_entry(pp));
--indent;
} else {
if(fs::is_directory(dent)) {
fs::directory_iterator dit_end;
std::cout << "Directory " << dent << " includes the following files:\n";
++indent;
for(auto dit = fs::directory_iterator(dent); dit != dit_end; ++dit) {
show_dent(*dit);
}
--indent;
} else {
fs::file_status stat = dent.status();
out << ind << dent << "\n"
<< ind << " stat\n"
<< ind << " is_regular_file : " << fs::is_regular_file(stat) << "\n"
<< ind << " is_directory : " << fs::is_directory(stat) << "\n"
<< ind << " is_block_file : " << fs::is_block_file(stat) << "\n"
<< ind << " is_character_file: " << fs::is_character_file(stat) << "\n"
<< ind << " is_fifo : " << fs::is_fifo(stat) << "\n"
<< ind << " is_socket : " << fs::is_socket(stat) << "\n"
<< ind << " is_symlink : " << fs::is_symlink(stat) << "\n"
<< ind << " exists : " << fs::exists(stat) << "\n";
if( fs::is_regular_file(stat) ) {
out
<< ind << " file_size : " << fs::file_size(dent) << "\n";
}
}
}
}
int main(int argc, char* argv[]) {
std::vector<std::string> args(argv+1, argv+argc);
out << std::boolalpha;
for(const auto& file_or_dir : args) {
show_dent(fs::directory_entry(file_or_dir));
}
return 0;
}

How to sort files by descending the file size

I need to output the 5 largest files from the directory. For this, I use a boost filesystem c++. In the process of writing the program, I encountered difficulties. I can output all files from the directory, file size, file creation date and file attributes. In the vector I put the names of the files, but I just can not figure out how to sort by size. I need to output the 5 largest files from the specified directory. I think that you must first sort by file size by descending. That is, from a larger value to a smaller one. And then the scans are not needed. Most likely it needs to be done in a loop. Help me please.
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <boost/filesystem.hpp>
using namespace std;
using namespace boost::filesystem;
void ShowAttributes(DWORD attributes);
void AttribFile(const char* str);
void Attrib();
int main(int argc, char* argv[])
{
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
if (argc < 2)
{
cout << "Using Name Directory" << endl;
return 1;
}
path Part(argv[1]);
try
{
if (exists(Part))
{
if (is_regular_file(Part))
{
cout << Part << " Size " << file_size(Part) << " bytes ";
time_t Time = last_write_time(Part);
cout << asctime(localtime(&Time)) << endl;
}
else if (is_directory(Part))
{
cout << "Directory " << Part << " include:" << endl;
vector<string> vecList;
for (auto j : directory_iterator(Part))
vecList.push_back(j.path().filename().string());
sort(vecList.begin(), vecList.end());
string filePath;
for (auto i : vecList)
{
cout << " " << i;
filePath = Part.parent_path().string() + "/" + i;
if (is_regular_file(filePath))
{
if (Is_Executable_File(filePath))
cout << "*";
cout << " Size " << file_size(filePath) << " bytes ";
time_t Time = last_write_time(Part);
cout << asctime(localtime(&Time)) << endl;
AttribFile(filePath.c_str());
}
cout << endl;
}
}
}
else
cout << Part << " Erroe!" << endl;
}
catch (const filesystem_error& ex)
{
cout << ex.what() << endl;
}
return 0;
}
void ShowAttributes(DWORD attributes)
{
if (attributes & FILE_ATTRIBUTE_ARCHIVE)
cout << " archive" << endl;
if (attributes & FILE_ATTRIBUTE_DIRECTORY)
cout << " directory" << endl;
if (attributes & FILE_ATTRIBUTE_HIDDEN)
cout << " hidden" << endl;
if (attributes & FILE_ATTRIBUTE_NORMAL)
cout << " normal" << endl;
if (attributes & FILE_ATTRIBUTE_READONLY)
cout << " read only" << endl;
if (attributes & FILE_ATTRIBUTE_SYSTEM)
cout << " system" << endl;
if (attributes & FILE_ATTRIBUTE_TEMPORARY)
cout << " temporary" << endl;
}
void AttribFile(const char* str)
{
DWORD attributes;
attributes = GetFileAttributesA(str);
ShowAttributes(attributes);
}
void Attrib()
{
char filename[MAX_PATH];
DWORD attributes;
cout << "Name of file: ";
cin >> filename;
attributes = GetFileAttributesA(filename);
ShowAttributes(attributes);
}
create a class or struct to hold the information you need on each file, e.g.
struct MyFile
{
std::string name;
size_t size;
}
create a vector of these and read all files from your folder
then sort the vector and give a custom comparison (e.g. in form of a lambda), see Sorting a vector of custom objects for details on that
Here's a program based on just the standard library that does what you seem to intend:
Live On Coliru
Update: Using C++11 and Boost Filesystem instead: Live On Coliru
#include <algorithm>
#include <filesystem>
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
namespace fs = std::filesystem;
struct tm *last_modified(fs::path const &p) {
auto ftime = fs::last_write_time(p);
auto cftime = decltype(ftime)::clock::to_time_t(ftime);
return std::localtime(&cftime);
}
bool is_executable(fs::path const& p) {
return fs::perms::none != (fs::status(p).permissions() &
(fs::perms::owner_exec |
fs::perms::group_exec |
fs::perms::others_exec));
}
void report(fs::path const& file) {
if (is_executable(file))
std::cout << "*";
std::cout << file << "\tSize:" << fs::file_size(file);
std::cout << "\tModified:" << std::asctime(last_modified(file));
}
template <typename Accessor, typename Cmp = std::less<> >
static auto compare_by(Accessor&& f, Cmp cmp = {}) {
return [f=std::forward<Accessor>(f),cmp](auto const& a, auto const& b) {
return cmp(f(a), f(b));
};
}
int main(int argc, char *argv[]) {
if (argc < 2) {
std::cout << "Using: " << argv[0] << " [Name|Directory]" << std::endl;
return 1;
}
fs::path filespec(argv[1]);
try {
if (is_regular_file(filespec)) {
// print
report(filespec);
} else if (is_directory(filespec)) {
std::cout << "Directory " << filespec << " include:" << std::endl;
std::vector<fs::directory_entry> const entries { fs::directory_iterator{filespec}, {} };
// filter just files
std::vector<fs::path> files;
std::remove_copy_if(entries.begin(), entries.end(),
back_inserter(files),
[](auto& de) { return de.is_directory(); });
// get the top 5, or fewer
auto b = files.begin(),
top5 = b + std::min(5ul, files.size()),
e = files.end();
// ordered by size, descending
std::partial_sort(b, top5, e,
compare_by([](auto& p) { return fs::file_size(p); }, std::greater<>{}));
files.erase(top5, e);
// print
for (auto& f : files)
report(f);
} else {
std::cout << filespec << " Error!" << std::endl;
}
} catch (const fs::filesystem_error &ex) {
std::cout << ex.what() << std::endl;
}
}
Prints, e.g. for ./a.out /usr/lib:
Directory "/usr/lib/" include:
"/usr/lib/libruby-1.9.1-static.a" Size:3654748 Modified:Wed Nov 19 21:41:25 2014
"/usr/lib/libruby-1.9.1.so.1.9.1" Size:2087600 Modified:Wed Nov 19 21:41:20 2014
"/usr/lib/libruby-1.9.1.so" Size:2087600 Modified:Wed Nov 19 21:41:20 2014
"/usr/lib/libruby-1.9.1.so.1.9" Size:2087600 Modified:Wed Nov 19 21:41:20 2014
"/usr/lib/libc++.so.1" Size:1460461 Modified:Mon Sep 8 20:01:17 2014

Not sure where the segmentation fault is

I'm getting problem with segmentation fault when trying to compile a C++ program, but not sure where the problem lies. I suspect that the problem lies with the .find() ..... could it be the iterator operator < and == which are the comparators for find() that is the issue? I hope that someone can point out to me where they think the problem lies.
The following is part of test01.cpp, where I run it to test the code and use print statements to find out where the problem is:
bool confirmEverythingMatches(const btree<long>& testContainer, const set<long>& stableContainer) {
cout << "Confirms the btree and the set "
"contain exactly the same values..." << endl;
for (long i = kMinInteger; i <= kMaxInteger; i++) {
cout << "Start of for-loop to find iterator for comparisons..." << endl;
if (stableContainer.find(i) != stableContainer.end()) {
cout << "can find i (" << i << ") in stableContainer!" << endl;
} else {
cout << "cannot find i (" << i << ") in stableContainer!" << endl;
}
cout << "In between finding i in stable and testContainers..." << endl;
if (testContainer.find(i) != testContainer.end()) {
cout << "can find i (" << i << ") in testContainer!" << endl;
} else {
cout << "cannot find i (" << i << ") in testContainer!" << endl;
}
cout << "Before assigning the find to boolean variables..." << endl;
bool foundInTree = (testContainer.find(i) != testContainer.end());
cout << "testContainer.find(i) != testContainer.end()" << endl;
bool foundInSet = (stableContainer.find(i) != stableContainer.end());
cout << "stableContainer.find(i) != stableContainer.end()" << endl;
if (foundInTree != foundInSet) {
cout << "- btree and set don't contain the same data!" << endl;
cout << "Mismatch at element: " << i << endl;
return false;
} else {cout << "foundInTree == foundInSet!!!" << i << endl;}
}
cout << "- btree checks out just fine." << endl;
return true;
}
} // namespace close
/**
* Codes for testing various bits and pieces. Most of the code is commented out
* you should uncomment it as appropriate.
**/
int main(void) {
// initialise random number generator with 'random' seed
initRandom();
cout << "after initRandom().." << endl;
// insert lots of random numbers and compare with a known correct container
btree<long> testContainer(99);
cout << "after specifying max node elements in testContainer.." << endl;
set<long> stableContainer;
cout << "after constructing stableContainer.." << endl;
insertRandomNumbers(testContainer, stableContainer, 1000000);
cout << "after inserting random numbers into testContainer and for success inserts, also into stableContainer.." << endl;
btree<long> btcpy = testContainer;
cout << "after copy assigning a copy of testContainer to btcopy.." << endl;
confirmEverythingMatches(btcpy, stableContainer);
cout << "after confirming everything internally matches between testContainer and stableContainer.." << endl;
return 0;
}
The output I get when running the program (No problem when compiling) is this:
Confirms the btree and the set contain exactly the same values...
Start of for-loop to find iterator for comparisons...
cannot find i (1000000) in stableContainer!
In between finding i in stable and testContainers...
ASAN:DEADLYSIGNAL
=================================================================
==7345==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000018 (pc 0x000108d132a8 bp 0x000000000000 sp 0x7fff56eee6f0 T0)
#0 0x108d132a7 in btree<long>::find(long const&) const (test01+0x1000022a7)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (test01+0x1000022a7) in btree<long>::find(long const&) const
==7345==ABORTING
Abort trap: 6
I also got this error when I tried to run it on another machine:
==29936==ERROR: AddressSanitizer failed to allocate 0x200000 (2097152) bytes of SizeClassAllocator32: 12
I found that when it goes into the find(), it will have the segmentation fault:
/**
* Identical in functionality to the non-const version of find,
* save the fact that what's pointed to by the returned iterator
* is deemed as const and immutable.
*
* #param elem the client element we are trying to match.
* #return an iterator to the matching element, or whatever the
* const end() returns if no such match was ever found.
*/
template<typename T> typename btree<T>::const_iterator
btree<T>::find(const T& elem) const {
std::cout << "CONST ITERATOR'S FIND" << std::endl;
Node *tmp_ = root_;
std::cout << "1" << std::endl;
while(true) {
std::cout << "2" << std::endl;
size_t i;
std::cout << "3" << std::endl;
// go through all elements from root to tail
for (i = 0; i < tmp_->__occupied_size_; ++i) {
std::cout << "4" << std::endl;
if (tmp_->__elem_[i] == elem) {
std::cout << "5" << std::endl;
// find the elem, return an iterator
return const_iterator(tmp_, i, this);
std::cout << "6" << std::endl;
} else if (tmp_->__elem_[i] > elem) {
std::cout << "7" << std::endl;
// elem is not in current Node, go to descendants
// for the elem.
if (tmp_->__descendants_ == nullptr) {
std::cout << "8" << std::endl;
return cend();
std::cout << "9" << std::endl;
} else {
std::cout << "10" << std::endl;
tmp_ = tmp_->__descendants_[i];
std::cout << "11" << std::endl;
break;
}
}
}
// handling boundaries cases
if (i == tmp_->__occupied_size_) {
std::cout << "12" << std::endl;
if (tmp_->__descendants_[i] == nullptr) {
std::cout << "13" << std::endl;
return cend();
std::cout << "14" << std::endl;
} else {
std::cout << "15" << std::endl;
tmp_ = tmp_->__descendants_[i];
}
}
}
}
The print statements for this find is:
CONST ITERATOR'S FIND
1
2
3
4
4
7
10
11
2
3
4
7
10
11
ASAN:DEADLYSIGNAL
Ok, so based on the implementation of this find function, I think the problem might be located in
if (tmp_->__descendants_ == nullptr) {
std::cout << "8" << std::endl;
return cend();
std::cout << "9" << std::endl;
} else {
std::cout << "10" << std::endl;
tmp_ = tmp_->__descendants_[i];
std::cout << "11" << std::endl;
break;
}
and then
// handling boundaries cases
if (i == tmp_->__occupied_size_) {
std::cout << "12" << std::endl;
if (tmp_->__descendants_[i] == nullptr) {
std::cout << "13" << std::endl;
return cend();
std::cout << "14" << std::endl;
} else {
std::cout << "15" << std::endl;
tmp_ = tmp_->__descendants_[i];
}
}
So, You are checking if tmp->__descendants_ is not null. If it is not, then you set tmp_ = tmp_->descendants_[i];
Note: you are just checking __descendants_ pointer to be null or not, you are not checking the __descendants_ [i] if it is null!
What if the tmp->__descendants_[i] is null (or gets out of the descendants array)?
If that value is null, then tmp_->occupied_size_ might give you segfault.
Note2: For some reason you are using same index "i" for iterating through __elem_ and __descendants_. I am not sure, how descendants are created, but it might be also a problem here.
This is why debuggers exist. Run your program in the debugger, let the program fail, and then the debugger will tell you where and why it's gone wrong.
It looks like you've got potentially a lot of code to trawl through, which no one here will really want to do as it's not a concise question.
Good luck!

make some crc check code, loop for multiple file (c++)

so i have this code for checking crc file named map.spak and compare the result with my specified crc result which stored in variable "compare"
int main(int iArg, char *sArg[])
{
char sSourceFile[MAX_PATH];
memset(sSourceFile, 0, sizeof(sSourceFile));
CCRC32 crc32;
crc32.Initialize(); //Only have to do this once.
unsigned int iCRC = 0;
strcpy(sSourceFile, "map.spak");
int compare = 399857339;
ifstream checkfile(sSourceFile);
if (checkfile){
cout << "Checking file " << sSourceFile << "..." << endl;
crc32.FileCRC(sSourceFile, &iCRC);
if(iCRC == compare){
cout << "File " << sSourceFile << " complete!\nCRC Result: " << iCRC << endl;
}else{
cout << "File " << sSourceFile << " incomplete!\nCRC Result: " << iCRC << endl;
}
}else{
cout << "File not found!" << endl;
}
system("pause");
return 0;
}
and now i want to make this code for multiple file
let's say the file name list stored in filelist.txt
the filelist.txt structure:
id|filename|specified crc
1|map.spak|399857339
2|monster.spak|274394072
how to make the crc check, loop for each file name
i'm not really good at c++ i only know some algorithm because i know PHP
c++ is too complicated
this is the full source included CRC source Source Code
or pastebin
TestApp.cpp link
I made several changes to your code. I removed guard headers since we use it only in header files. Old-fasioned memset has been replaced by operation on strings. I suspect that you need to pass char* to CCRC32 object hence sSourceFile is still const char*. I compiled code except parts with CCRC32.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include "../CCRC32.H"
int main(int iArg, char *sArg[])
{
std::vector<std::string> filenames;
// TODO - populate filesnames (paths?)
CCRC32 crc32;
crc32.Initialize(); //Only have to do this once.
for (unsigned int i = 0; i < filenames.size(); i++) {
const char* sSourceFile = filenames[i].c_str();
unsigned int iCRC = 0;
int compare = 399857339; // TODO - you need to change this since you are checking several files
std::ifstream checkfile(sSourceFile);
if (checkfile) {
std::cout << "Checking file " << sSourceFile << "..." << std::endl;
crc32.FileCRC(sSourceFile, &iCRC);
if(iCRC == compare){
std::cout << "File " << sSourceFile << " complete!\nCRC Result: " << iCRC << std::endl;
} else {
std::cout << "File " << sSourceFile << " incomplete!\nCRC Result: " << iCRC << std::endl;
}
} else {
std::cout << "File tidak ditemukan!" << std::endl;
}
}
return 0;
}

Counting how many times each word occurs in a file using map. (c++)

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <map>
using namespace std;
int main()
{
ifstream fin;
fin.open("myTextFile.txt");
if ( fin.fail()){
cout << "Could not open input file.";
exit(1);
}
string next;
map <string, int> words;
while (fin >> next){
words[next]++;
}
cout << "\n\n" << "Number of words: " << words[next] << endl;
fin.close();
fin.open("myTextFile.txt");
while (fin >> next){
cout << next << ": " << words[next] << endl;
}
fin.close();
return 0;
}
My main problem is that when a word occurs more than once, it is also listed more then once. i.e if the text starts with "hello hello" then cout produces:
"hello: 2" '\n' "hello: 2"
Also, i'd like not to have to close, and then reopen the file for the second while to be true. It seems like its still at the end of the file from the last while loop.
You need to iterate trough the map, and not open the file a second time.
Look at the code sample provided here.
EDIT: here a code sample that iterates trough a map
// map::begin/end
#include <iostream>
#include <map>
int main ()
{
std::map<char,int> mymap;
std::map<char,int>::iterator it;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
// show content:
for (std::map<char,int>::iterator it=mymap.begin(); it!=mymap.end(); ++it)
std::cout << it->first << " => " << it->second << '\n';
return 0;
}
Here is the output:
a => 200
b => 100
c => 300
You don't need re-open file:
for (auto i = words.begin(); i != words.end(); i++)
{
cout << i->first << " : " << i->second << endl;
}
or simpler:
for (const auto &i : words)
{
cout << i.first << " : " << i.second << endl;
}
You need to iterate over the map after you set it and then you do not need to open the file again, this is trivial example:
int main()
{
std::map<std::string, int> m1 ;
m1["hello"] = 2 ;
m1["world"] = 4 ;
for( const auto &entry : m1 )
{
std::cout << entry.first << " : " << entry.second << std::endl ;
}
}
The expected output would be:
hello : 2
world : 4