wxwidgets/samples/archive/archive.cpp
Vadim Zeitlin 4f4c5fcfdf Use nullptr instead of NULL in the code and documentation
This is a combination of running clang-tidy with modernize-use-nullptr
check for some ports (GTK, X11, OSX) and manual changes to the ports for
which it couldn't be used easily (MSW, DFB) and also manually updating
the docs.

Also replace NULL with null or nullptr in the comments as this is more
consistent with the use of nullptr in the code and makes it simpler to
grep for the remaining occurrences of NULL itself.

And also use null in the assert messages.

Only a few occurrences of "NULL" are still left in non-C files, mostly
corresponding to unclear comments or string output which it might not be
safe to change.
2022-10-18 01:25:25 +02:00

353 lines
11 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: samples/archive/archive.cpp
// Purpose: A sample application to manipulate archives
// Author: Tobias Taschner
// Created: 2018-02-07
// Copyright: (c) 2018 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/archive.h"
#include "wx/app.h"
#include "wx/cmdline.h"
#include "wx/filename.h"
#include "wx/scopedptr.h"
#include "wx/vector.h"
#include "wx/wfstream.h"
#include "wx/zipstrm.h"
class ArchiveApp: public wxAppConsole
{
public:
ArchiveApp()
{
m_forceZip64 = false;
m_archiveClassFactory = nullptr;
m_filterClassFactory = nullptr;
}
virtual void OnInitCmdLine(wxCmdLineParser& parser) override;
virtual bool OnCmdLineParsed(wxCmdLineParser& parser) override;
virtual int OnRun() override;
private:
enum ArchiveCommandType
{
CMD_CREATE,
CMD_LIST,
CMD_EXTRACT,
CMD_NONE
};
struct ArchiveCommandDesc
{
ArchiveCommandType type;
const char* id;
const char* desc;
};
static const ArchiveCommandDesc s_cmdDesc[];
ArchiveCommandType m_command;
wxString m_archiveFileName;
wxVector<wxString> m_fileNames;
bool m_forceZip64;
// At most one of these pointers is non-null.
const wxArchiveClassFactory* m_archiveClassFactory;
const wxFilterClassFactory* m_filterClassFactory;
int DoCreate();
int DoList();
int DoExtract();
bool CopyStreamData(wxInputStream& inputStream, wxOutputStream& outputStream, wxFileOffset size);
};
const ArchiveApp::ArchiveCommandDesc ArchiveApp::s_cmdDesc[] =
{
{CMD_CREATE, "c", "create"},
{CMD_LIST, "l", "list"},
{CMD_EXTRACT, "x", "extract"},
{CMD_NONE, 0, 0}
};
// Helper function iterating over all factories of the given type (assumed to
// derive from wxFilterClassFactoryBase) and returning a string containing all
// comma-separated values returned from their GetProtocols() called with the
// given protocol type.
template <typename T>
void AppendAllSupportedOfType(wxString& all, wxStreamProtocolType type)
{
for (const T* factory = T::GetFirst(); factory; factory = factory->GetNext())
{
const wxChar* const* exts = factory->GetProtocols(type);
while (*exts)
{
if (!all.empty())
all+= ", ";
all += *exts++;
}
}
}
// Returns all supported protocols or extensions (depending on the type
// parameter value) for both archive and filter factories.
wxString GetAllSupported(wxStreamProtocolType type)
{
wxString all;
AppendAllSupportedOfType<wxArchiveClassFactory>(all, type);
AppendAllSupportedOfType<wxFilterClassFactory>(all, type);
return all;
}
void ArchiveApp::OnInitCmdLine(wxCmdLineParser& parser)
{
wxAppConsole::OnInitCmdLine(parser);
parser.AddParam("command");
parser.AddParam("archive_name");
parser.AddParam("file_names", wxCMD_LINE_VAL_STRING,
wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL);
parser.AddSwitch("", "force-zip64", "Force ZIP64 format when creating ZIP archives");
parser.AddUsageText("\ncommand:");
for (const ArchiveCommandDesc* cmdDesc = s_cmdDesc; cmdDesc->type != CMD_NONE; cmdDesc++)
parser.AddUsageText(wxString::Format(" %s: %s", cmdDesc->id, cmdDesc->desc));
parser.AddUsageText("\nsupported formats: " + GetAllSupported(wxSTREAM_PROTOCOL));
}
bool ArchiveApp::OnCmdLineParsed(wxCmdLineParser& parser)
{
m_command = CMD_NONE;
wxString command = parser.GetParam();
for (const ArchiveCommandDesc* cmdDesc = s_cmdDesc; cmdDesc->type != CMD_NONE; cmdDesc++)
{
if (cmdDesc->id == command)
m_command = cmdDesc->type;
}
if(m_command == CMD_NONE)
{
wxLogError("Invalid command: %s", command);
return false;
}
m_archiveFileName = parser.GetParam(1);
for (size_t i = 2; i < parser.GetParamCount(); i++)
m_fileNames.push_back(parser.GetParam(i));
m_forceZip64 = parser.FoundSwitch("force-zip64") == wxCMD_SWITCH_ON;
return wxAppConsole::OnCmdLineParsed(parser);
}
bool ArchiveApp::CopyStreamData(wxInputStream& inputStream, wxOutputStream& outputStream, wxFileOffset size)
{
wxChar buf[128 * 1024];
int readSize = 128 * 1024;
wxFileOffset copiedData = 0;
for (;;)
{
if (size != -1 && copiedData + readSize > size)
readSize = size - copiedData;
inputStream.Read(buf, readSize);
size_t actuallyRead = inputStream.LastRead();
outputStream.Write(buf, actuallyRead);
if (outputStream.LastWrite() != actuallyRead)
{
wxLogError("Failed to output data");
return false;
}
if (size == -1)
{
if (inputStream.Eof())
break;
}
else
{
copiedData += actuallyRead;
if (copiedData >= size)
break;
}
}
return true;
}
int ArchiveApp::DoCreate()
{
if (m_fileNames.empty())
{
wxLogError("Need at least one file to add to archive");
return -1;
}
wxTempFileOutputStream fileOutputStream(m_archiveFileName);
if (m_archiveClassFactory)
{
wxScopedPtr<wxArchiveOutputStream> archiveOutputStream(m_archiveClassFactory->NewStream(fileOutputStream));
if (m_archiveClassFactory->GetProtocol().IsSameAs("zip", false) && m_forceZip64)
reinterpret_cast<wxZipOutputStream*>(archiveOutputStream.get())->SetFormat(wxZIP_FORMAT_ZIP64);
for(wxVector<wxString>::iterator fileName = m_fileNames.begin(); fileName != m_fileNames.end(); fileName++)
{
wxFileName inputFileName(*fileName);
wxPrintf("Adding %s...\n", inputFileName.GetFullName());
wxFileInputStream inputFileStream(*fileName);
if (!inputFileStream.IsOk())
{
wxLogError("Could not open file");
return 1;
}
if (!archiveOutputStream->PutNextEntry(inputFileName.GetFullName(), wxDateTime::Now(), inputFileStream.GetLength()))
break;
if (!CopyStreamData(inputFileStream, *archiveOutputStream, inputFileStream.GetLength()))
return 1;
}
if (!archiveOutputStream->Close())
return 1;
}
else // use m_filterClassFactory
{
if (m_fileNames.size() != 1)
{
wxLogError("Filter-based formats only support archives consisting of a single file");
return -1;
}
wxScopedPtr<wxFilterOutputStream> filterOutputStream(m_filterClassFactory->NewStream(fileOutputStream));
wxFileInputStream inputFileStream(*m_fileNames.begin());
if (!inputFileStream.IsOk())
{
wxLogError("Could not open file");
return 1;
}
if (!CopyStreamData(inputFileStream, *filterOutputStream, inputFileStream.GetLength()))
return 1;
}
fileOutputStream.Commit();
wxPrintf("Created archive\n");
return 0;
}
int ArchiveApp::DoList()
{
wxFileInputStream fileInputStream(m_archiveFileName);
if (!fileInputStream.IsOk())
return 1;
if (m_archiveClassFactory)
{
wxScopedPtr<wxArchiveInputStream> archiveStream(m_archiveClassFactory->NewStream(fileInputStream));
wxPrintf("Archive: %s\n", m_archiveFileName);
wxPrintf("Length Date Time Name\n");
wxPrintf("---------- ---------- -------- ----\n");
wxFileOffset combinedSize = 0;
int entryCount = 0;
for (wxArchiveEntry* entry = archiveStream->GetNextEntry(); entry;
entry = archiveStream->GetNextEntry())
{
combinedSize += entry->GetSize();
entryCount++;
wxPrintf("%10lld %s %s %s\n",
entry->GetSize(),
entry->GetDateTime().FormatISODate(),
entry->GetDateTime().FormatISOTime(),
entry->GetName());
}
wxPrintf("---------- -------\n");
wxPrintf("%10lld %d files\n", combinedSize, entryCount);
}
else
{
wxPrintf("Archive \"%s\" contains a single file named \"%s\"\n",
m_archiveFileName, wxFileName(m_archiveFileName).GetName());
}
return 0;
}
int ArchiveApp::DoExtract()
{
wxFileInputStream fileInputStream(m_archiveFileName);
if (!fileInputStream.IsOk())
return 1;
if (m_archiveClassFactory)
{
wxScopedPtr<wxArchiveInputStream> archiveStream(m_archiveClassFactory->NewStream(fileInputStream));
wxPrintf("Extracting from: %s\n", m_archiveFileName);
for (wxArchiveEntry* entry = archiveStream->GetNextEntry(); entry;
entry = archiveStream->GetNextEntry())
{
wxPrintf("Extracting: %s...\n", entry->GetName());
wxTempFileOutputStream outputFileStream(entry->GetName());
if (!CopyStreamData(*archiveStream, outputFileStream, entry->GetSize()))
return 1;
outputFileStream.Commit();
}
wxPrintf("Extracted all files\n");
}
else
{
wxScopedPtr<wxFilterInputStream> filterStream(m_filterClassFactory->NewStream(fileInputStream));
wxPrintf("Extracting single file from: %s\n", m_archiveFileName);
wxTempFileOutputStream outputFileStream(wxFileName(m_archiveFileName).GetName());
if (!CopyStreamData(*filterStream, outputFileStream, -1))
return 1;
outputFileStream.Commit();
wxPrintf("Extracted successfully\n");
}
return 0;
}
int ArchiveApp::OnRun()
{
m_archiveClassFactory = wxArchiveClassFactory::Find(m_archiveFileName, wxSTREAM_FILEEXT);
if (!m_archiveClassFactory)
{
m_filterClassFactory = wxFilterClassFactory::Find(m_archiveFileName, wxSTREAM_FILEEXT);
if (!m_filterClassFactory)
{
wxLogError("File \"%s\" has unsupported extension, supported ones are: %s",
m_archiveFileName, GetAllSupported(wxSTREAM_FILEEXT));
return -1;
}
}
int result = -1;
switch (m_command) {
case CMD_CREATE:
result = DoCreate();
break;
case CMD_LIST:
result = DoList();
break;
case CMD_EXTRACT:
result = DoExtract();
break;
default:
break;
}
return result;
}
wxIMPLEMENT_APP(ArchiveApp);