For my C++ project I need a library to decompress various formats of archives. I found libarchive was the best solution for this, however when I started testing it I ran into a problem. I tried running an untar example from the library over a zip containing files named in Russian and it just refuses to work.
archive_read_support_compression_all(a);
archive_read_support_format_all(a);
archive_read_open_filename(a, filename, 10240)));
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF) {
break;
}
if (r != ARCHIVE_OK) {
if (r == ARCHIVE_WARN) {
warn("archive_read_next_header()", archive_error_string(a)); // This warning goes off
} else {
fail("archive_read_next_header()", archive_error_string(a), 1);
}
}
msg(archive_entry_pathname(entry)); // entry is null
}
}
When I run this over other archive formats containing the same files everything is fime but for zip it fails with
archive_read_next_header() failed: Pathname cannot be converted from UTF-8 to current locale.
What can I do to make this work? I'm generally fine with the names being gibberish at this point
The system I'm running is Ubuntu 20.04 if that's important
Related
I am trying to open a file for this program. I have tried pathing it directly using examples like C:\User... but for some reason it still says it can't find the file. I have looked on the internet as well as youtube to open files using C++ and its pretty straight forward. However I still can't get this .txt file to be read. Maybe it has to do with Visual Studio?
FILE* in_fp, * fopen();
int main() {
if ((in_fp = fopen("TextFile.txt", "r")) == NULL)
printf("ERROR - cannot open front.in \n");
else
{
getChar();
do {
lex();
} while (nextToken != EOF);
}
}
I'm writting a plugin for Steam protocol in C++. I'm using https://github.com/seishun/SteamPP which uses protobufs from https://github.com/SteamRE/SteamKit and generally it works. I can communicate Steam, I can send and receive single messages (including logging in) without problems, but Steam sends often few messages zipped in one message (EMsg::Multi from protobuf) and here is my problem. I cannot unzip them correctly. I can't understand what I'm doing wrong.
std::string unzip(std::string &input) {
auto archive = archive_read_new();
auto result = archive_read_support_filter_all(archive);
assert(result == ARCHIVE_OK);
result = archive_read_support_format_zip(archive);
assert(result == ARCHIVE_OK);
result = archive_read_open_memory(archive, &input[0], input.size());
assert(result == ARCHIVE_OK);
archive_entry* entry;
result = archive_read_next_header(archive, &entry);
if (result != ARCHIVE_OK) {
return "read next header error " + std::to_string(result);
}
assert(result == ARCHIVE_OK);
std::string output;
output.resize(archive_entry_size(entry));
auto length = archive_read_data(archive, &output[0], output.size());
assert(length == output.size());
if (length != output.size()) {
return "hello world" + std::to_string(length);
}
assert(archive_read_next_header(archive, &entry) == ARCHIVE_EOF);
result = archive_read_free(archive);
assert(result == ARCHIVE_OK);
return output;
}
in this function (libarchive) archive_read_data returns -25 which is an error code and next assert throws error. What is wrong? It's working well in C# SteamKit version and also in node.js version. I have tried also Crypto++ Gunzip but it throws an CryptoPP::Gunzip::HeaderErr exception.
CHECK DEBUG GIF
I think you are missing Zlib in your Libarchive, because Steam messages are deflated and you need Zlib to process them. Now libarchive couldn't process them so it returns -25 because of unsupported file type. Try to recompile libarchive with Zlib attached in CMake.
I use libzip to open zip files in my application and in order to ensure good behavior in case of corrupt zip files I manually corrupted a zip file (by removing a few random lines with a text editor) and try to load that file. However this hangs the entire app because zip_fread() never returns.
Is there a where to determine if a zip file is valid before loading it to avoid such situations?
Update
The behavior seems to depend on the version, so I probably only need to update. This is the code I use on Windows, Mac OS and Linux:
int err;
zip *z= zip_open(zipfile.c_str(), 0, &err);
if (!z)
{
if (err == ZIP_ER_NOZIP)
throw std::runtime_error("The file is not a Workbench document.");
else if (err == ZIP_ER_MEMORY)
throw grt::os_error("Cannot allocate enough memory to open document.");
else if (err == ZIP_ER_NOENT)
throw grt::os_error("File not found.");
int len= zip_error_to_str(NULL, 0, 0, err);
std::string msg;
if (len > 0)
{
char *buf= (char*)g_malloc(len+1);
zip_error_to_str(buf, len+1, 0, err);
msg= buf;
g_free(buf);
}
else
msg= "error opening zip archive";
zip_close(z);
throw std::runtime_error(strfmt(_("Cannot open document file: %s"), msg.c_str()));
}
On OS X this fragment does not return an error (I used the same file for all platforms). Instead the following zip_read() call just hangs. On the other platforms zip_read() immediately returns with a result < 0, so it's easy to catch the error there.
I have a project in C++ Builder6. There's an OpenDialog where I upload images to the project. I'd like my project to be safe and because it only accepts .jpg or .bmp images I decided to make a restriction. As far as I'm concerned I can recognize a .jpg file by setting my stream reader to the 4th position. If I find "JFIF" here, It'll be .jpeg file. And so on.
Here's my code
if(OpenDialog1->Execute())
{
TFileStream *stream = new TFileStream(OpenDialog1->FileName, fmOpenRead);
if(stream != NULL)
{
if(stream->Size < 10)
{
delete stream;
return;
}
char str[10];
stream->Read(str, 10);
if(AnsiString(str + 6).SetLength(4)=="JFIF")
{
ShowMessage("It's jpeg");
}
else if ( AnsiString(str).SetLength(2)=="BM") {
ShowMessage("It's bmp");
}
else
{
ShowMessage("It can not be downloaded");
return;
}
}
delete stream;
}
But unfortunately that code raises an exception about JPEG error #41 when I put here a text file with renamed extension.
So my idea doesn't work. The whole question is:
Can I make my program return my error messages without using try-catch method?
By the way, I understand why the exception is being raised, because my jpeg file is empty. But I'd like to handle it using my own system, not the standart exception.
I have a tar file I want to extract with libarchive to a specific directory. How can I make libarchive extract into to any directory I want? At the moment it always extracts into my program's working directory. I looked at this answer but all this does is change the location of an archive entry within the archive, i.e. it still extracts into my program's working directory just at a different sub-directory.
I resolved this issue next way:
(insert this code before calling 'archive_write_header' function)
const char* currentFile = archive_entry_pathname(archiveEntry);
const std::string fullOutputPath = destination + currentFile;
archive_entry_set_pathname(archiveEntry, fullOutputPath.c_str());
where destination is output path.
And it works.
From the libarchive discussion boards:
"It depends, of course, on the archive being extracted.
Typically, you would chdir() to the directory where you want the output to go, then use code similar to that in the Wiki Examples page:
A Complete Extractor Example
or in the untar.c sample program:
untar Example
Of course, if the tar file you're extracting has
interesting filenames (such as "c:\someotherdirectory"),
then you'll need to play with the filenames as you extract.
Note that the examples all use archive_read_next_header()
to get an entry object from the input archive describing
the next entry; you are then free to edit that entry description
in any way you wish -- in particular, you can change the
name, owner, or permissions -- before calling
archive_write_header() to recreate the entry on
disk.
The Examples page in the Wiki above is probably the
best place to start."
A longer C version of Alex's answer. To extract files to a temp_dir, use archive_entry_pathname() and archive_entry_set_pathname() to re-write each entry::
char* dest_file;
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
break;
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(a));
if (r < ARCHIVE_WARN)
return NULL;
asprintf(&dest_file, "%s/%s", temp_dir, archive_entry_pathname(entry));
archive_entry_set_pathname(entry, dest_file);
// printf(" writing %s\n", dest_file);
r = archive_write_header(ext, entry);
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(ext));
else if (archive_entry_size(entry) > 0) {
r = copy_data(a, ext);
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(ext));
if (r < ARCHIVE_WARN)
exit(1);
}
r = archive_write_finish_entry(ext);
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(ext));
if (r < ARCHIVE_WARN)
return NULL;
free(dest_file);
}