I'm trying to figure out how exactly to use stat() to capture information about a file. What I need is to be able to print several fields of information about a file. So..
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
int main() {
struct stat buf;
stat("file",&buf);
...
cout << st_dev << endl;
cout << st_ino << endl;
cout << st_mode << endl;
cout << st_nlink << endl;
cout << st_uid << endl;
cout << st_gid << endl;
cout << st_rdev << endl;
cout << st_size << endl;
cout << st_blksize << endl;
cout << st_blocks << endl;
cout << st_atime << endl;
cout << st_mtime << endl;
cout << st_ctime << endl;
...
}
I'm thoroughly confused about how to do this. Why is &buf a parameter to stat? I don't care about storing this information in memory, I just need the outputted fields within my c++ program. How do I access the information contained in the struct? Is buf actually supposed to contain the returned information from stat()?
Yes, buf is being used here as an out-parameter. The results are stored in buf and the return value of stat is an error code indicating if the stat operation succeeded or failed.
It is done this way because stat is a POSIX function, designed for C, which does not support out-of-band error reporting mechanisms like exceptions. If stat returned a struct, then it would have no way to indicate errors. Using this out-parameter method also allows the caller to choose where they want to store the results, but that's a secondary feature. It's perfectly fine to pass the address of a normal local variable, just like you have done here.
You access the fields of a struct like you would any other object. I presume you are at least familar with object notation? E.g. the st_dev field within the stat struct called buf is accessed by buf.st_dev. So:
cout << buf.st_dev << endl;
etc.
For another project, I've whipped up a little function that does something similiar to what you need. Take a look at sprintstatf.
Here's an example of usage:
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include "sprintstatf.h"
int
main(int argc, char *argv[])
{
char *outbuf = (char *)malloc(2048 * sizeof(char));
struct stat stbuf;
char *fmt = \
"st_atime (decimal) = \"%a\"\n"
"st_atime (string) = \"%A\"\n"
"st_ctime (decimal) = \"%c\"\n"
"st_ctime (string) = \"%C\"\n"
"st_gid (decimal) = \"%g\"\n"
"st_gid (string) = \"%G\"\n"
"st_ino = \"%i\"\n"
"st_mtime (decimal) = \"%m\"\n"
"st_mtime (string) = \"%M\"\n"
"st_nlink = \"%n\"\n"
"st_mode (octal) = \"%p\"\n"
"st_mode (string) = \"%P\"\n"
"st_size = \"%s\"\n"
"st_uid = \"%u\"\n"
"st_uid = \"%U\"\n";
lstat(argv[1], &stbuf);
sprintstatf(outbuf, fmt, &stbuf);
printf("%s", outbuf);
free(outbuf);
exit(EXIT_SUCCESS);
}
/* EOF */
This question may be way to old to comment but i am posting this as a reference
To get a good understanding about stat() function ,the reason for passing the stat reference and more importantly error handling are explained good in the below link
stat - get file status
You have several errors in your code:
You need &buf, with a single 'f'.
You need to say e.g. buf.st_dev when printing, since st_dev is a field in the struct variable.
Since buf is a local variable on the stack, you're not "saving the values to memory" permanently, it's just as long as that variable is in-scope.
This is how you return multiple values, typically, in C and C++. You pass a pointer to a structure, and the function being called fills in the structure with the values it has computed for you.
buf is the structure that stat loads with the information about the file you pass in the first parameter. You pass &buf here b/c you have buf allocated on the stack as a local variable and you must pass a pointer to the stat function to enable it to load the data.
All variables of st_* are part of the struct stat object and thus must be accessed via your local buf variable as buf.st_uid, etc.
Similar thing is with ctime library. Is designed similar way.
First is to create empty struct.
You have access to object of the struct, but all fields are empty.
Then You use that function (&name-of-created-obiect) and is an adrres to point obiect outside of that function.
Function is designed to store all info to that struct obiect from given reference, and kaboom, you have obiect with ready data to use.
Otherwise, if You don't want use pointer, then you must use
Obiect = function(null);
With pointer
Function(&obiect);
Related
I was curious, so I tried to do this:
First program:
#include <iostream>
using namespace std;
int a = 5;
int main()
{
cout << "Value of a: " << a << endl;
cout << "Address of a: " << &a << endl;
system("pause");
return 0;
}
Output:
Value of a: 5
Address of a: 0x472010
Second program:
#include <iostream>
using namespace std;
int* p = reinterpret_cast<int*>(0x472010);
int main()
{
cout << "Value of p: " << *p << endl;
cout << "The address that the pointer points to: " << p << endl;
cout << endl;
system("pause");
return 0;
}
So, I want to read the value of the variable 'a' with
the 'p' pointer belonging to the another program, and the 'p' pointer point to specific address.
everything is fine until i run the second pragram, as the second program doesn't give the desired results
Output second program:
Value of p: 4661264
The address that the pointer points to: 0x472010
The result does not change if I keep the window of the first program open.
I promise I’m a beginner and I’m trying new things
What am I doing wrong?
You can't directly access another process's memory in the manner you are attempting. Each process runs in its own address space. Your 2nd process is trying to access an (invalid) address within its own address space, not within the address space of the 1st process.
On Windows, to read another process's memory, your 2nd process must obtain a HANDLE to the 1st process, such as from OpenProcess(), and then must use ReadProcessMemory() to read memory from an address within the 1st process.
The alternative is for the 1st process to allocate a block of shared memory which the 2nd process can access directly. On Windows, you can use CreateFileMapping() and MapViewOfFile() for that purpose (see Creating Named Shared Memory on MSDN). On 'Nix systems, you can use shm_open() mmap().
I'm trying to save some string via string_view to second data container but run into some difficulties.
It turns out that string changes its underlying data storage after move()'ing it.
And my question is, why does it happen?
Example:
#include <iostream>
#include <string>
#include <string_view>
using namespace std;
int main() {
string a_str = "abc";
cout << "a_str data pointer: " << (void *) a_str.data() << endl;
string_view a_sv = a_str;
string b_str = move(a_str);
cout << "b_str data pointer: " << (void *) b_str.data() << endl;
cout << "a_sv: " << a_sv << endl;
}
Output:
a_str data pointer: 0x63fdf0
b_str data pointer: 0x63fdc0
a_sv: bc
Thanks for your replies!
What you are seeing is a consequence of short string optimization. In the most basic sense, there is an array in the string object to save a call to new for small strings. Since the array is a member of the class, it has to have it's own address in each object and when you move a string that is in the array, a copy happens.
The string "abc" is short enough for short string optimization. See What are the mechanics of short string optimization in libc++?
If you change it to a longer string you will see the same address.
Today I did a lot of research online about how to create a directory on C++
and found a lot of way to do that, some easier than others.
I tried the _mkdir function using _mkdir("C:/Users/..."); to create a folder. Note that the argument of function will be converted into a const char*.
So far, so good, but when I want to change the path, it does not work (see the code below). I have a default string path "E:/test/new", and I want to create 10 sub-folders: new1, new2, newN, ..., new10.
To do that, I concatenate the string with a number (the counter of the for-loop), converted into char using static_cast, then I transform the string using c_str(), and assign it to a const char* variable.
The compiler has no problem compiling it, but it doesn't work. It prints 10 times "Impossible create folder n". What's wrong?
I probably made a mistake when transforming the string using c_str() to a get a const char*?.
Also, is there a way to create a folder using something else? I looked at CreateDirectory(); (API) but it uses keyword like DWORD HANDLE, etc., that are a little bit difficult to understand for a no-advanced level (I don't know what these mean).
#include <iostream>
#include <Windows.h>
#include<direct.h>
using namespace std;
int main()
{
int stat;
string path_s = "E:/test/new";
for (int i = 1; i <= 10; i++)
{
const char* path_c = (path_s + static_cast<char>(i + '0')).c_str();
stat = _mkdir(path_c);
if (!stat)
cout << "Folder created " << i << endl;
else
cout << "Impossible create folder " << i << endl;
Sleep(10);
}
return 0;
}
If your compiler supports c++17, you can use filesystem library to do what you want.
#include <filesystem>
#include <string>
#include <iostream>
namespace fs = std::filesystem;
int main(){
const std::string path = "E:/test/new";
for(int i = 1; i <= 10; ++i){
try{
if(fs::create_directory(path + std::to_string(i)))
std::cout << "Created a directory\n";
else
std::cerr << "Failed to create a directory\n";\
}catch(const std::exception& e){
std::cerr << e.what() << '\n';
}
}
return 0;
}
The problem is that (path_s + static_cast<char>(i + '0')) creates a temporary object. One whose life-time ends (and is destructed) just after c_str() has been called.
That leaves you with a pointer to a string that no longer exist, and using it in almost any way will lead to undefined behavior.
Instead save the std::string object, and call c_str() just when needed:
std::string path = path_s + std::to_string(i);
_mkdir(path.c_str());
Note that under Linux, you can use the mkdir command as follows:
#include <sys/stat.h>
...
const int dir_err = mkdir("foo", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (-1 == dir_err){
printf("Error creating directory!n");
exit(1);
}
More information on it can be gleaned from reading man 2 mkdir.
I'm developing a application and my idea is store "apps" in files, like executables. Now i have that:
AppWriter.c
#include <vector>
#include <time.h>
#include <functional>
struct PROGRAM
{
std::vector<int> RandomStuff;
std::vector<std::function<void()>> Functions;
std::function<void()> MAIN;
} CODED;
void RANDOMFUNC()
{
srand(time(NULL));
for(int i = 0; i < 40; i++)
CODED.RandomStuff.push_back(rand() % 254);
}
void LOGARRAY()
{
for(int i = 0; i < CODED.RandomStuff.size(); i++)
std::cout << "["<< i + 1 <<"]: "<< CODED.RandomStuff[i] << std::endl;
}
void PROGRAMMAIN()
{
std::cout << "Hello i call random function!" << std::endl;
CODED.Functions[0]();
CODED.Functions[1]();
}
void main()
{
CODED.MAIN = PROGRAMMAIN;
CODED.Functions.push_back(RANDOMFUNC);
CODED.Functions.push_back(LOGARRAY);
std::cout << "Testing MAIN" << std::endl;
CODED.MAIN();
FILE *file = fopen("TEST_PROGRAM.TRI","wb+");
fwrite(&CODED,sizeof(CODED),1,file);
fclose(file);
std::cout << "Program writted correctly!" << std::endl;
_sleep(10000);
}
AppReader.c
#include <iostream>
#include <vector>
#include <time.h>
#include <functional>
struct PROGRAM
{
std::vector<int> RandomStuff;
std::vector<std::function<void()>> Functions;
std::function<void()> MAIN;
} DUMPED;
void main()
{
FILE *file = fopen("TEST_PROGRAM.TRI","rb+");
fseek(file,0,SEEK_END);
int program_len = ftell(file);
rewind(file);
fread(&DUMPED,sizeof(PROGRAM),1,file);
std::cout
<< "Function array size: " << DUMPED.Functions.size() << std::endl
<< "Random Stuff Array size: " << DUMPED.RandomStuff.size() << std::endl;
DUMPED.MAIN();
}
When i run AppReader the functions dont work(Maybe why std::function it's like void pointers?), but in arrays or if i add variables i can see with debugger the data are storaged correctly (for that i tryed the vector of functions), but whatever doesn't work throw's me error on functional file. ¿Any ideas how i can do that?
This is never going to work. At all. Ever. std::function is a complex type. Binary reads and writes don't work for complex types. They never can. You would have to ask for functions in a pre-defined serializable format, like LLVM IR.
Your problem is that you're storing information about functions that exist in one executable, then trying to run them in a separate executable. Other than that, your code does work, but as DeadMG says, you shouldn't be storing complex types in a file. Here's how I modified your code to prove that your code works if run within a single executable:
#include <iostream>
#include <vector>
#include <time.h>
#include <functional>
struct PROGRAM
{
std::vector<int> RandomStuff;
std::vector<std::function<void()>> Functions;
std::function<void()> MAIN;
} CODED;
void RANDOMFUNC()
{
srand(time(NULL));
for(int i = 0; i < 40; i++)
CODED.RandomStuff.push_back(rand() % 254);
}
void LOGARRAY()
{
for(int i = 0; i < CODED.RandomStuff.size(); i++)
std::cout << "["<< i + 1 <<"]: "<< CODED.RandomStuff[i] << std::endl;
}
void PROGRAMMAIN()
{
std::cout << "Hello i call random function!" << std::endl;
CODED.Functions[0]();
CODED.Functions[1]();
}
int main()
{
CODED.MAIN = PROGRAMMAIN;
CODED.Functions.push_back(RANDOMFUNC);
CODED.Functions.push_back(LOGARRAY);
std::cout << "Testing MAIN" << std::endl;
CODED.MAIN();
FILE *file = fopen("TEST_PROGRAM.TRI","wb+");
fwrite(&CODED,sizeof(CODED),1,file);
fclose(file);
std::cout << "Program writted correctly!" << std::endl;
// _sleep(10000);
std::cout << "---------------------\n";
file = fopen("TEST_PROGRAM.TRI","rb+");
fseek(file,0,SEEK_END);
int program_len = ftell(file);
rewind(file);
fread(&CODED,sizeof(PROGRAM),1,file);
std::cout
<< "Function array size: " << CODED.Functions.size() << std::endl
<< "Random Stuff Array size: " << CODED.RandomStuff.size() << std::endl;
CODED.MAIN();
}
The problem is not that you're storing complex types via binary read/write, per se. (Although that is a problem, it's not the cause of the problem you posted this question about.) Your problem is that your data structures are storing information about the functions that exist in your 'writer' executable. Those same functions don't even exist in your 'reader' executable, but even if they did, they likely wouldn't be at the same address. Your data structures are storing, via std::function, pointers to the addresses where the functions exist in your 'writer' executable. When you try to call these non-existent functions in your 'reader' executable, your code happily tries to call them but you get a segfault (or whatever error your OS gives) because that's not the start of a valid function in your 'reader' executable.
Now with regard to writing complex types (e.g. std::vector) directly to a file in binary format: Doing so "works" in the sample code above only because the binary copies of the std::vectors have pointers that, once read back in, still point to valid data from the original std::vectors which you wrote out. Note that you didn't write the std::vector's actual data, you only wrote their metadata, which probably includes things like the length of the vector, the amount of memory currently allocated for the vector, and a pointer to the vector's data. When you read that back, the metadata is correct except for one thing: Any pointers in it are pointing to addresses that were valid when you wrote the data, but which may not be valid now. In the case of the sample code above, the pointers end up pointing to the same (still valid) data from the original vectors. But there's still a problem here: You now have more than one std::vector that thinks they own that memory. When one of them is deleted, it will delete the memory that the other vector expects to still exist. And when the other vector is deleted, it will cause a double-delete. That opens the door to all kinds of UB. E.g. that memory could have been allocated for another purpose by that time, and now the 2nd delete will delete that other purpose's memory, or else the memory has NOT been allocated for another purpose and the 2nd delete may corrupt the heap. To fix this, you'd have to serialize out the essence of each vector, rather than their binary representation, and when reading it back in, you'd have to reconstruct an equivalent copy, rather than simply reconstitute a copy from the binary image of the original.
I am playing with TagLib (on Windows, built with MingW). I am trying to get TagLib to recognize when there is no ID3v1 or ID3v2 information in an MP3 file. According to the TagLib documentation, the ID3v2Tag() function in an MPEG File object should return a NULL pointer when there is no ID3v2 information in the file.
Unfortunately, this is not occurring. I have some test MP3 files I have made that I use in my code (I have made the files available):
blank.mp3 (download), no ID3v1 or ID3v2 information at all. I can confirm this by doing a plain text search for "TAG" and "ID3" in the files binary content.
only_album_id3v2.mp3 (download), has ID3v2 information (only the album is set)
only_album_id3v1.mp3 (download), has ID3v1 information (only the album is set)
Here is my code.
#include <iostream>
#include <mpeg/mpegfile.h>
#include <mpeg/id3v2/id3v2tag.h>
using namespace std;
int main()
{
cout << "Test." << endl;
TagLib::MPEG::File a("tests/other/blank.mp3");
TagLib::MPEG::File b("tests/id3v2/only_album_id3v2.mp3");
TagLib::MPEG::File c("tests/id3v1/only_album_id3v1.mp3");
TagLib::ID3v2::Tag * at = a.ID3v2Tag();
TagLib::ID3v2::Tag * bt = b.ID3v2Tag();
TagLib::ID3v2::Tag * ct = c.ID3v2Tag();
cout << at->album() << endl;
cout << bt->album() << endl;
cout << ct->album() << endl;
cout << "The program is done.";
return 0;
}
Running this program should break, due to a NULL pointer error on cout << at->album() << endl;, but it runs just fine. Also, when I cout << ct << endl;, it returns a memory address.
Here is the output:
Test.
test album id3v2
The program is done.
EDIT:
Here is a new test.
#include <iostream>
#include <mpeg/mpegfile.h>
#include <mpeg/id3v2/id3v2tag.h>
using namespace std;
int main()
{
cout << "Test." << endl;
TagLib::MPEG::File a("tests/other/blank.mp3");
TagLib::MPEG::File b("tests/id3v2/only_album_id3v2.mp3");
TagLib::MPEG::File c("tests/id3v1/only_album_id3v1.mp3");
TagLib::ID3v2::Tag * at = a.ID3v2Tag();
TagLib::ID3v2::Tag * bt = b.ID3v2Tag();
TagLib::ID3v2::Tag * ct = c.ID3v2Tag();
if(at == NULL)
{
cout << "at is NULL.";
}
else
{
cout << "at is not NULL.";
}
cout << endl;
if(bt == NULL)
{
cout << "bt is NULL.";
}
else
{
cout << "bt is not NULL.";
}
cout << endl;
if(ct == NULL)
{
cout << "ct is NULL.";
}
else
{
cout << "ct is not NULL.";
}
cout << endl;
cout << "The program is done.";
return 0;
}
And here is the output.
Test.
at is not NULL.
bt is not NULL.
ct is not NULL.
The program is done.
I examined TagLib's code briefly.
I know nothing about it and never used it, but the code looks buggy to me. Here's why -
In MPEG::File::read(), we are looking for a tag - d->ID3v2Location = findID3v2();. If it doesn't exist, it isn't added to the tags vector. This is the check - if(d->ID3v2Location >= 0).
However, at the end of the function, just before returning, we have -
// Make sure that we have our default tag types available.
ID3v2Tag(true);
ID3v1Tag(true);
Now, Id3v2Tag(create) with a true parameter, actually calls return d->tag.access(ID3v2Index, create);. The access() function is -
template <class T> T *access(int index, bool create)
{
if(!create || tag(index))
return static_cast<T *>(tag(index));
set(index, new T);
return static_cast<T *>(tag(index));
}
So when create is true, we are creating a brand new, empty tag and placing it in the vector (using the set() function).
This means that no matter whether the file contains the tags or not, they are added to the vector. This isn't the documented behavior. Looks like a bug.
I don't know why these two lines are needed there - looking at the history of this file might hint as to why they were added, but I didn't do that.
Anyway, I want to stress that I never actually executed this code. This is based on purely statically reading only very small parts, without being aware of large scale issues.
I think that opening a bug report can't hurt.
Using a null pointer doesn't necessarily result in any error you can see; it's undefined behavior. It might appear to work, or it might do something really really weird.
In this case the compiler is probably generating a call to TagLib::ID3v2::Tag::album with the this pointer set to null, but even this is not guaranteed. What happens inside the function is anyone's guess.
If the function can return NULL, you should be explicitly checking for it and doing something different.
Taglib create an "Empty" ID3v2Tag and ID3v1Tag in the object if the file do not have one.
I am having simmilar problem and hopefully I've found workaround for ID3v1/ID3v2 presence checking.
It's method virtual bool TagLib::Tag::isEmpty() const