I'm using a vector of struct in order to create a kind of minimal filesystem. The entry point is a FileSystem struct manage by MTP but it contain to much data and it also force me to deploy mtp stuff even at high-level.
What I have done is to parse the list of files provided by MTP and file a vector of struct.
here is what I have in the cpp file
std::vector<FileObject*> mtp_wrapper::ListsOfFirstLevel(uint32_t idx) {
std::vector<FileObject*> ListOfObjects;
LIBMTP_file_t *AllFiles;
LIBMTP_file_t *file;
AllFiles = getListOfFiles();
file = AllFiles;
while(file !=NULL) {
LIBMTP_file_t *oldfile;
FSitem = new FileObject();
if(file->parent_id == idx) {
FSitem->filename = file->filename;
FSitem->filesize = file->filesize;
if(file->filetype == LIBMTP_FILETYPE_FOLDER)
FSitem->isFolder = true;
else
FSitem->isFolder = false;
FSitem->itemid = file->item_id;
FSitem->parent_id = file->parent_id;
FSitem->lastmodified = file->modificationdate;
ListOfObjects.push_back(FSitem);
// free(FSitem)
}
oldfile = file;
file = file->next;
LIBMTP_destroy_file_t(oldfile);
}
return ListOfObjects;
}
FileObject is declared in a header file as below
struct FileObject {
uint32_t itemid;
bool isFolder;
char *filename;
uint64_t filesize;
LIBMTP_filetype_t filetype;
uint32_t location;
uint32_t parent_id;
time_t lastmodified;
};
The code to file the FSitem is working fine, FSItem is file correctly but at the end of while, the ListOfOjects is empty. seems like the push_back fail...
ListOfObjects.push_back(FSitem);
// free(FSitem)
Why do you free FSItem ? You have a std::vector of pointers. When you call push_back you copy the pointer to your item inside the vector. The item hasn't moved and hasn't been copied. If you free FSItem , you delete the object, and then the pointer in your vector will be invalid.
Related
I'm writing a program in C++ that behaves like a Twitter Service. The program is supposed to store data in files as follows:
f_username (stores the users that username follows), so it creates a file for each user that follows someone. I have the function load_users() that reads each file to populate all clients (users) information in a vector called client_db (global variable) of type struct (defined below). So, when a f_username file is read, the vectors client_followers and client_following are populated for each user. The function works perfectly, except for the first file it reads. It saves the information in client_followers and client_following in the while loop that reads the file, but the entries for both vectors are deleted somehow after this loop. I added the comments in the code where I'm facing this issue only for the first f_username file it reads.
I believe it has something to do with the fact I'm using pointers, but I have no clue how to fix this issue. This is the first time ever I have this weird logic error. How is possible that it only "deletes" the data from the vectors that is read from first file, but not from the other files (I have 3 files)?
Any help would be really appreciated, I tried my best to explain this problem.
**UPDATE: Problem fixed by replacing the types of vectors client_followers and client_following with a string type without declaring them as pointers, and making sure the actual entries in client_db are updated correctly. **
//Client struct that holds a user's username, followers, and users they follow
struct Client {
std::string username;
bool connected = false;
int following_file_size = 0;
std::vector<Client*> client_followers;
std::vector<Client*> client_following;
ServerReaderWriter<Message, Message>* stream = 0;
bool operator==(const Client& c1) const{
return (username == c1.username);
}
};
void load_users()
{
//load users (followers and following)
DIR *dir;
struct dirent *ent;
if ((dir = opendir ("./")) != NULL) {
while ((ent = readdir (dir)) != NULL) {
std::string filename = ent->d_name;
int index = filename.find("f_");
if(index != std::string::npos)
{
std::ifstream in; //read following file of username
//get username
std::string user = filename.substr(index+2);
int txt_index = user.find(".txt");
user.erase(txt_index,4);
int user_index = find_user(user); //check if user is in client's db already
//client_db is a global variable
if(user_index < 0){ //if not, add client to client_db
Client c;
c.username = user;
client_db.push_back(c);
}
Client* user1 = &client_db[find_user(user)];
std::string username2;
in.open("./" + filename);
while(!in.eof())
{
getline(in,username2);
int following_index = find_user(username2);
if(following_index < 0) //create entry for the user
{
Client c2;
c2.username = username2;
client_db.push_back(c2);
}
Client* user2 = &client_db[find_user(username2)];
//it adds the information for the first file, but entries in client_followers
//and client_following is deleted after this loop ends, how is this possible?
user2->client_followers.push_back(user1);
user1->client_following.push_back(user2);
}
//When I print information of client_following and client_followers, the information obtained from the first file is not printed (e.g. size of client_following is 0).
in.close();
//break;
}
}
closedir (dir);
} else {
/* could not open directory */
//perror ("");
//return EXIT_FAILURE;
return;
}
}
My requirement is to store string data in to .dat or .bin file which human cannot understand it.
I am able to store integers in to file in binary mode but not able to store string in binary mode.
I tried using CFile and CArchive in MFC
Tried using fstream
Tried using File*
But could not succeeded.
Can Anyone help me in doing this?
void CAuthenticationFileDlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);
static int count =0;
DisplayKeys(count);
CString strTotalKeys = m_keys->GetKey1() + m_keys->GetKey2() + m_keys-getkey3() + m_keys->GetKey4();
m_vectKeys.push_back(strTotalKeys);
m_EditKey1.SetFocus();
CFile pFile;
ASSERT (pFile != NULL);
if (!pFile.Open (_T("foo.dat"), CFile::modeReadWrite | CFile::modeCreate|CFile::typeBinary))
{ // Handle error
return;
}
CArchive arStore(&pFile, CArchive::store);
Serialize(arStore);
delete m_keys;
count++;
if(count>0)
{
m_keys = new CKeys;
}
UpdateData(FALSE);
}
I have a class called Ckeys which has 4 Cstring variables.
I am trying to store it's object.
Anyways i s OK. I want to store data into binary format.
Above I mentioned OnintDialog() which is MFC CDialog Virtual function which is invoed before dialog is shown. In which I am trying to read file and displaying it in list control. (I could not achive this)
in Button event I am trying to write the objects data which is given by user.
Use any method to convert/encode the string to unreadable format.
The following methods can save/restore a CString value to/from file:
void WriteString(CString file, CString s)
{
CFile myFile(file, CFile::modeCreate | CFile::modeWrite);
CArchive ar(&myFile, CArchive::store);
int sz = s.GetLength();
ar << sz; // ar.Write(&sz, sizeof(int));
ar.Write(s.GetBuffer(), sz);
ar.Close();
}
CString ReadString(CString file)
{
CString s;
CFile myFile(file, CFile::modeRead);
CArchive ar(&myFile, CArchive::load);
int sz = 0;
ar >> sz; // ar.Read(&sz, sizeof(int));
char* p = s.GetBuffer(sz);
ar.Read(p, sz);
s.ReleaseBuffer(sz);
p = nullptr; // avoid using released buffer
ar.Close();
return s;
}
I'm trying to fetch some data from a pre-defined list.
I have the following struct:
struct FileId
{
int id;
std::string fileName;
bool isValid;
};
I'm creating the following list:
std::list<FileId> filesList;
FileId newFileId;
newFileId.id = 1;
newFileId.fileName = somefile1;
newFileId.isValid = true;
FileId newFileId;
newFileId.id = 2;
newFileId.fileName = somefile2;
newFileId.isValid = false;
I'm trying, for example, to find a file by id, or all the files which are not valid, can it be done without looping the list with an iterator?
I have written a local DataSource because from what I know there are none included in Awesomium, but the thing is that it request everything from the data source html, images etc
And I have no clue on how I should load all types of mime formats.
My current code only supports html/text, where I load the file into binary and send as a response. This does not work for images.
Does anybody know where I should go on from here?
class LocalDataSource :
public Awesomium::DataSource
{
public:
LocalDataSource() { }
virtual ~LocalDataSource() { }
virtual void OnRequest(int request_id, const Awesomium::WebString& path)
{
std::string filepath = Awesomium::ToString(path).insert(0, "./");
std::basic_ifstream<char> is(filepath, std::ios_base::in | std::ios_base::binary);
if (is)
{
is.seekg(0, is.end);
int length = is.tellg();
is.seekg(0, is.beg);
char *buffer = new char[length + 1];
is.read(buffer, length);
buffer[length] = '\0';
is.close();
SendResponse(request_id, strlen(buffer), (unsigned char*)buffer, Awesomium::WSLit("text/html"));
delete[] buffer;
}
else
{
// Error
}
}
};
EDIT:
for now I will load the file relative to the executable and not use DataSource's.
I know this is old, but it was relevant to me, I fixed this the same way as Steven did, I will post the C++ code I used:
bool ResInterceptor::OnFilterNavigation(int origin_process, int origin_routing_id, const Awesomium::WebString& method, const Awesomium::WebURL& url, bool is_main_frame)
{
return false;
}
Awesomium::ResourceResponse* ResInterceptor::OnRequest(Awesomium::ResourceRequest* request)
{
bool isAsset = std::strcmp(ToString(request->url().scheme()).c_str(), "asset")==0;
bool isFile = std::strcmp(ToString(request->url().scheme()).c_str(), "file")==0;
if(!isAsset && !isFile)
{
//if it is neither of these we "may" still intercept the call, this allows for offline-online versions to work
return Awesomium::ResourceInterceptor::OnRequest(request);
}
if(isAsset)
{
//Blah blah, do whatever
}
else if(isFile)
{
//Blah blah, same
}
//As you can see this isn't very, but it worked for my purposes
std::string contentpath = "E:/Location/of/files" + ToString(request->url().path());
Awesomium::WebString datatype;
std::string filename = Awesomium::ToString(request->url().filename());
//I still want to check for the correct mime type
if (has_suffix(filename, ".html")) datatype = Awesomium::WSLit("text/html");
else if(has_suffix(filename, ".js")) datatype = Awesomium::WSLit("text/javascript");
else if(has_suffix(filename, ".css")) datatype = Awesomium::WSLit("text/css");
else if(has_suffix(filename, ".swf")) datatype = Awesomium::WSLit("application/x-shockwave-flash");
else if(has_suffix(filename, ".zip")) datatype = Awesomium::WSLit("application/zip");
else if(has_suffix(filename, ".txt")) datatype = Awesomium::WSLit("text/plain");
else if(has_suffix(filename, ".text")) datatype = Awesomium::WSLit("text/plain");
else if(has_suffix(filename, ".png")) datatype = Awesomium::WSLit("image/png");
else if(has_suffix(filename, ".jpeg")) datatype = Awesomium::WSLit("image/jpeg");
else if(has_suffix(filename, ".jpg")) datatype = Awesomium::WSLit("image/jpeg");
else if(has_suffix(filename, ".webm")) datatype = Awesomium::WSLit("video/webm");
else if(has_suffix(filename, ".mp4")) datatype = Awesomium::WSLit("video/mp4");
else if(has_suffix(filename, ".ogv")) datatype = Awesomium::WSLit("video/ogg");
else if(has_suffix(filename, ".flv")) datatype = Awesomium::WSLit("video/flv");
if(!datatype.IsEmpty())
{
FILE * pFile;
long lSize;
unsigned char * buffer;
size_t result;
pFile = fopen ( contentpath.c_str() , "rb" );
if (pFile!=NULL)
{
// obtain file size:
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
// allocate memory to contain the whole file:
buffer = (unsigned char*) malloc (sizeof(unsigned char)*lSize);
if (buffer == NULL) {fputs ("Memory error",stderr); exit (2);}
// copy the file into the buffer:
result = fread (buffer,1,lSize,pFile);
if (result != lSize) {fputs ("Reading error",stderr); exit (3);}
//This is where the magic happens!!
return Awesomium::ResourceResponse::Create(lSize, buffer, datatype);
// terminate
fclose (pFile);
free (buffer);
}
else
{
//send this off to the default request handler instead of it being a local file
return Awesomium::ResourceInterceptor::OnRequest(request);
}
}else
{
//send this off to the default request handler instead of it being a local file
return Awesomium::ResourceInterceptor::OnRequest(request);
}
}
//Support function
bool ResInterceptor::has_suffix(const std::string &str, const std::string &suffix)
{
return str.size() >= suffix.size() &&
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
as for how I hooked it up, I simply added this line of code:
_web_core = WebCore::Initialize(config);
_web_core->set_resource_interceptor(new ResInterceptor());
This took me a whole night to nail down all because I was passing in a pointer with a variable and not using the "new" keyword directly! I got it now at least!
also note, I tried the exact same code above inside the LocalDataSource and it didn't work for anything except the text files, so I think there is a bug in there, good news is, this works the exact same way, but you get more control over every file request.
Thank you Steven for all the great reference code!
The easy way is to send the contents of a file without sweating mime type detection is to use the static method static ResourceResponse* Awesomium::ResourceResponse::Create.
From the Awesomium docs:
Create a ResourceResponse from a file on disk.
I couldn't figure out a way to map ResourceResponse::Create to DataSource::SendResponse.
As a workaround, you could rewrite your data source as an IResourceInterceptor instead of a DataSource. I wrote up a detailed example in C# on how to use http:// scheme instead of the custom asset:// scheme for embedded resources It should be pretty straightforward to translate the C# to C++. Below is an edited down version of my post (not tested).
using System;
using System.IO;
using System.Reflection;
using Awesomium.Core;
namespace MyApp
{
public class ResourceInterceptor : IResourceInterceptor
{
/// <summary>
/// Intercepts any requests for the EmbeddedResourceDomain base Uri,
/// and returns a response using the embedded resource in this app's assembly/DLL file
/// </summary>
public virtual ResourceResponse OnRequest(ResourceRequest request)
{
ResourceResponse response = null;
string resourceName;
string filePath;
filePath = String.Concat("./", request.Url.AbsolutePath);
filePath = Path.GetFullPath(resourceName.Replace('/', Path.DirectorySeparatorChar));
// cache the resource to a temp file if
if (File.Exists(filePath))
{
response = ResourceResponse.Create(filePath);
}
return response;
}
/// <summary>
/// Optionally blocks any web browser requests by returning true. Not used.
/// </summary>
/// <remarks>
/// This method can implement a whitelist of allowed URLs here by
/// returning true to block any whitelist misses
/// </remarks>
public virtual bool OnFilterNavigation(NavigationRequest request)
{
return false;
}
}
}
Another option might be to hack the HTML content to inject a <base href="file:///c:/my/bin/path" /> element inside the <head> of the document. You would need to modify the href attribute value before loading the content. This may be more work than it is worth.
I've worked myself through the rapidXML sources and managed to read some values. Now I want to change them and save them to my XML file:
Parsing file and set a pointer
void SettingsHandler::getConfigFile() {
pcSourceConfig = parsing->readFileInChar(CONF);
cfg.parse<0>(pcSourceConfig);
}
Reading values from XML
void SettingsHandler::getDefinitions() {
SettingsHandler::getConfigFile();
stGeneral = cfg.first_node("settings")->value();
/* stGeneral = 60 */
}
Changing values and saving to file
void SettingsHandler::setDefinitions() {
SettingsHandler::getConfigFile();
stGeneral = "10";
cfg.first_node("settings")->value(stGeneral.c_str());
std::stringstream sStream;
sStream << *cfg.first_node();
std::ofstream ofFileToWrite;
ofFileToWrite.open(CONF, std::ios::trunc);
ofFileToWrite << "<?xml version=\"1.0\"?>\n" << sStream.str() << '\0';
ofFileToWrite.close();
}
Reading file into buffer
char* Parser::readFileInChar(const char* p_pccFile) {
char* cpBuffer;
size_t sSize;
std::ifstream ifFileToRead;
ifFileToRead.open(p_pccFile, std::ios::binary);
sSize = Parser::getFileLength(&ifFileToRead);
cpBuffer = new char[sSize];
ifFileToRead.read( cpBuffer, sSize);
ifFileToRead.close();
return cpBuffer;
}
However, it's not possible to save the new value. My code is just saving the original file with a value of "60" where it should be "10".
Rgds
Layne
I think this is a RapidXML Gotcha
Try adding the parse_no_data_nodes flag to cfg.parse<0>(pcSourceConfig)
You should definitely be testing that the output file opened correctly and that your write succeeded. At the simplest, you need something like:
if ( ! ofFileToWrite << "<?xml version=\"1.0\"?>\n"
<< sStream.str() << '\0' ) {
throw "write failed";
}
Note that you don't need the '\0' terminator, but it shouldn't do any harm.
Use the following method to add an attribute to a node. The method uses the allocation of memory for strings from rapidxml. So rapidxml takes care of the strings as long as the document is alive. See http://rapidxml.sourceforge.net/manual.html#namespacerapidxml_1modifying_dom_tree for further information.
void setStringAttribute(
xml_document<>& doc, xml_node<>* node,
const string& attributeName, const string& attributeValue)
{
// allocate memory assigned to document for attribute value
char* rapidAttributeValue = doc.allocate_string(attributeValue.c_str());
// search for the attribute at the given node
xml_attribute<>* attr = node->first_attribute(attributeName.c_str());
if (attr != 0) { // attribute already exists
// only change value of existing attribute
attr->value(rapidAttributeValue);
} else { // attribute does not exist
// allocate memory assigned to document for attribute name
char* rapidAttributeName = doc.allocate_string(attributeName.c_str());
// create new a new attribute with the given name and value
attr = doc.allocate_attribute(rapidAttributeName, rapidAttributeValue);
// append attribute to node
node->append_attribute(attr);
}
}