* Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <Entry.h>
#include <package/hpkg/PackageContentHandler.h>
#include <package/hpkg/PackageEntry.h>
#include <package/hpkg/PackageEntryAttribute.h>
#include <package/hpkg/PackageInfoAttributeValue.h>
#include <package/hpkg/PackageReader.h>
#include <package/hpkg/StandardErrorOutput.h>
#include <package/hpkg/v1/PackageContentHandler.h>
#include <package/hpkg/v1/PackageEntry.h>
#include <package/hpkg/v1/PackageEntryAttribute.h>
#include <package/hpkg/v1//PackageReader.h>
#include <package/PackageInfo.h>
#include "package.h"
#include "PackageInfoPrinter.h"
using namespace BPackageKit;
using BPackageKit::BHPKG::BErrorOutput;
using BPackageKit::BHPKG::BPackageInfoAttributeValue;
using BPackageKit::BHPKG::BStandardErrorOutput;
struct VersionPolicyV1 {
typedef BPackageKit::BHPKG::V1::BPackageContentHandler
PackageContentHandler;
typedef BPackageKit::BHPKG::V1::BPackageEntry PackageEntry;
typedef BPackageKit::BHPKG::V1::BPackageEntryAttribute
PackageEntryAttribute;
typedef BPackageKit::BHPKG::V1::BPackageReader PackageReader;
static inline uint64 PackageDataSize(
const BPackageKit::BHPKG::V1::BPackageData& data)
{
return data.UncompressedSize();
}
static inline status_t InitReader(PackageReader& packageReader,
const char* fileName)
{
return packageReader.Init(fileName);
}
};
struct VersionPolicyV2 {
typedef BPackageKit::BHPKG::BPackageContentHandler PackageContentHandler;
typedef BPackageKit::BHPKG::BPackageEntry PackageEntry;
typedef BPackageKit::BHPKG::BPackageEntryAttribute PackageEntryAttribute;
typedef BPackageKit::BHPKG::BPackageReader PackageReader;
static inline uint64 PackageDataSize(
const BPackageKit::BHPKG::BPackageData& data)
{
return data.Size();
}
static inline status_t InitReader(PackageReader& packageReader,
const char* fileName)
{
return packageReader.Init(fileName,
BPackageKit::BHPKG
::B_HPKG_READER_DONT_PRINT_VERSION_MISMATCH_MESSAGE);
}
};
enum ListMode {
LIST_ALL,
LIST_PATHS_ONLY,
LIST_META_INFO_ONLY
};
template<typename VersionPolicy>
struct PackageContentListHandler : VersionPolicy::PackageContentHandler {
PackageContentListHandler(bool listEntries, bool listAttributes)
:
fPrinter(),
fLevel(0),
fListEntries(listEntries),
fListAttribute(listEntries && listAttributes)
{
}
virtual status_t HandleEntry(typename VersionPolicy::PackageEntry* entry)
{
if (!fListEntries)
return B_OK;
fLevel++;
int indentation = (fLevel - 1) * 2;
printf("%*s", indentation, "");
printf("%-*s", indentation < 32 ? 32 - indentation : 0, entry->Name());
printf(" %8llu",
(unsigned long long)VersionPolicy::PackageDataSize(entry->Data()));
struct tm* time = localtime(&entry->ModifiedTime().tv_sec);
printf(" %04d-%02d-%02d %02d:%02d:%02d",
1900 + time->tm_year, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec);
mode_t mode = entry->Mode();
if (S_ISREG(mode))
printf(" -");
else if (S_ISDIR(mode))
printf(" d");
else if (S_ISLNK(mode))
printf(" l");
else
printf(" ?");
char buffer[4];
printf("%s", _PermissionString(buffer, mode >> 6,
(mode & S_ISUID) != 0));
printf("%s", _PermissionString(buffer, mode >> 3,
(mode & S_ISGID) != 0));
printf("%s", _PermissionString(buffer, mode, false));
if (S_ISLNK(mode))
printf(" -> %s", entry->SymlinkPath());
printf("\n");
return B_OK;
}
virtual status_t HandleEntryAttribute(
typename VersionPolicy::PackageEntry* entry,
typename VersionPolicy::PackageEntryAttribute* attribute)
{
if (!fListAttribute)
return B_OK;
int indentation = fLevel * 2;
printf("%*s<", indentation, "");
printf("%-*s %8llu", indentation < 31 ? 31 - indentation : 0,
attribute->Name(),
(unsigned long long)VersionPolicy::PackageDataSize(
attribute->Data()));
uint32 type = attribute->Type();
if (isprint(type & 0xff) && isprint((type >> 8) & 0xff)
&& isprint((type >> 16) & 0xff) && isprint(type >> 24)) {
printf(" '%c%c%c%c'", int(type >> 24), int((type >> 16) & 0xff),
int((type >> 8) & 0xff), int(type & 0xff));
} else
printf(" %#" B_PRIx32, type);
printf(">\n");
return B_OK;
}
virtual status_t HandleEntryDone(
typename VersionPolicy::PackageEntry* entry)
{
if (!fListEntries)
return B_OK;
fLevel--;
return B_OK;
}
virtual status_t HandlePackageAttribute(
const BPackageInfoAttributeValue& value)
{
if (value.attributeID == B_PACKAGE_INFO_NAME)
printf("package-attributes:\n");
if (!fPrinter.PrintAttribute(value)) {
printf("*** Invalid package attribute section: unexpected "
"package attribute id %d encountered\n", value.attributeID);
return B_BAD_DATA;
}
return B_OK;
}
virtual void HandleErrorOccurred()
{
}
private:
static const char* _PermissionString(char* buffer, uint32 mode, bool sticky)
{
buffer[0] = (mode & 0x4) != 0 ? 'r' : '-';
buffer[1] = (mode & 0x2) != 0 ? 'w' : '-';
if ((mode & 0x1) != 0)
buffer[2] = sticky ? 's' : 'x';
else
buffer[2] = '-';
buffer[3] = '\0';
return buffer;
}
static void _PrintPackageVersion(const BPackageVersionData& version)
{
printf("%s", BPackageVersion(version).ToString().String());
}
private:
PackageInfoPrinter fPrinter;
int fLevel;
bool fListEntries;
bool fListAttribute;
};
template<typename VersionPolicy>
struct PackageContentListPathsHandler : VersionPolicy::PackageContentHandler {
PackageContentListPathsHandler()
:
fPathComponents()
{
}
virtual status_t HandleEntry(typename VersionPolicy::PackageEntry* entry)
{
fPathComponents.Add(entry->Name());
printf("%s\n", fPathComponents.Join("/").String());
return B_OK;
}
virtual status_t HandleEntryAttribute(
typename VersionPolicy::PackageEntry* entry,
typename VersionPolicy::PackageEntryAttribute* attribute)
{
return B_OK;
}
virtual status_t HandleEntryDone(
typename VersionPolicy::PackageEntry* entry)
{
fPathComponents.Remove(fPathComponents.CountStrings() - 1);
return B_OK;
}
virtual status_t HandlePackageAttribute(
const BPackageInfoAttributeValue& value)
{
return B_OK;
}
virtual void HandleErrorOccurred()
{
}
private:
BStringList fPathComponents;
};
template<typename VersionPolicy>
static void
do_list(const char* packageFileName, bool listAttributes, ListMode listMode,
bool ignoreVersionError)
{
BStandardErrorOutput errorOutput;
typename VersionPolicy::PackageReader packageReader(&errorOutput);
status_t error = VersionPolicy::InitReader(packageReader, packageFileName);
if (error != B_OK) {
if (ignoreVersionError && error == B_MISMATCHED_VALUES)
return;
exit(1);
}
switch (listMode) {
case LIST_PATHS_ONLY:
{
PackageContentListPathsHandler<VersionPolicy> handler;
error = packageReader.ParseContent(&handler);
break;
}
case LIST_ALL:
case LIST_META_INFO_ONLY:
{
PackageContentListHandler<VersionPolicy> handler(
listMode != LIST_META_INFO_ONLY, listAttributes);
error = packageReader.ParseContent(&handler);
}
}
if (error != B_OK)
exit(1);
exit(0);
}
int
command_list(int argc, const char* const* argv)
{
ListMode listMode = LIST_ALL;
bool listAttributes = false;
while (true) {
static struct option sLongOptions[] = {
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 }
};
opterr = 0;
int c = getopt_long(argc, (char**)argv, "+ahip", sLongOptions, NULL);
if (c == -1)
break;
switch (c) {
case 'a':
listAttributes = true;
break;
case 'i':
listMode = LIST_META_INFO_ONLY;
break;
case 'h':
print_usage_and_exit(false);
break;
case 'p':
listMode = LIST_PATHS_ONLY;
break;
default:
print_usage_and_exit(true);
break;
}
}
if (optind + 1 != argc)
print_usage_and_exit(true);
const char* packageFileName = argv[optind++];
if (!BString(packageFileName).EndsWith(".hpkg")) {
struct ErrorListener : BPackageInfo::ParseErrorListener {
virtual void OnError(const BString& msg, int line, int col)
{
fprintf(stderr, "%s:%d:%d: %s\n", fPath, line, col,
msg.String());
}
const char* fPath;
} errorListener;
errorListener.fPath = packageFileName;
BPackageInfo info;
if (info.ReadFromConfigFile(BEntry(packageFileName), &errorListener)
!= B_OK) {
return 1;
}
printf("package-attributes:\n");
PackageInfoPrinter().PrintPackageInfo(info);
return 0;
}
BHPKG::BStandardErrorOutput errorOutput;
do_list<VersionPolicyV2>(packageFileName, listAttributes, listMode, true);
do_list<VersionPolicyV1>(packageFileName, listAttributes, listMode, false);
return 0;
}