haiku/src/bin/listattr.cpp

299 lines
6.5 KiB
C++

/*
* Copyright 2004-2020, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2002, Ryan Fleet.
*
* Distributed under the terms of the MIT license.
*/
#include <String.h>
#include <TypeConstants.h>
#include <Mime.h>
#include <fs_attr.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
/*! Dumps the contents of the attribute in the form of raw data. This view
is used for the type B_RAW_TYPE, for custom types and for any type that
is not directly supported by the utility "addattr".
*/
static void
dump_raw_data(const char *buffer, size_t size)
{
const uint32 kChunkSize = 16;
uint32 dumpPosition = 0;
while (dumpPosition < size) {
// Position for this line
printf("\t%04" B_PRIx32 ": ", dumpPosition);
// Print the bytes in form of hexadecimal numbers
for (uint32 i = 0; i < kChunkSize; i++) {
if (dumpPosition + i < size) {
printf("%02x ", (uint8)buffer[dumpPosition + i]);
} else
printf(" ");
}
// Print the bytes in form of printable characters
// (whenever possible)
printf(" ");
for (uint32 i = 0; i < kChunkSize; i++) {
if (dumpPosition < size) {
char c = buffer[dumpPosition];
putchar(isgraph(c) ? c : '.');
} else
putchar(' ');
dumpPosition++;
}
printf("\n");
}
}
static void
show_attr_contents(BNode& node, const char* attribute, const attr_info& info)
{
// limit size of the attribute, only the first kLimit byte will make it on
// screen
int kLimit = 256;
bool cut = false;
off_t size = info.size;
if (size > kLimit) {
size = kLimit;
cut = true;
}
char buffer[kLimit];
ssize_t bytesRead = node.ReadAttr(attribute, info.type, 0, buffer, size);
if (bytesRead != size) {
fprintf(stderr, "Could only read %" B_PRIdOFF " bytes from attribute!\n",
size);
return;
}
buffer[min_c(bytesRead, kLimit - 1)] = '\0';
switch (info.type) {
case B_INT8_TYPE:
printf("%" B_PRId8 "\n", *((int8 *)buffer));
break;
case B_UINT8_TYPE:
printf("%" B_PRIu8 "\n", *((uint8 *)buffer));
break;
case B_INT16_TYPE:
printf("%" B_PRId16 "\n", *((int16 *)buffer));
break;
case B_UINT16_TYPE:
printf("%" B_PRIu16 "\n", *((uint16 *)buffer));
break;
case B_INT32_TYPE:
printf("%" B_PRId32 "\n", *((int32 *)buffer));
break;
case B_UINT32_TYPE:
printf("%" B_PRIu32 "\n", *((uint32 *)buffer));
break;
case B_INT64_TYPE:
printf("%" B_PRId64 "\n", *((int64 *)buffer));
break;
case B_UINT64_TYPE:
printf("%" B_PRIu64 "\n", *((uint64 *)buffer));
break;
case B_FLOAT_TYPE:
printf("%f\n", *((float *)buffer));
break;
case B_DOUBLE_TYPE:
printf("%f\n", *((double *)buffer));
break;
case B_BOOL_TYPE:
printf("%d\n", *((unsigned char *)buffer));
break;
case B_TIME_TYPE:
{
char stringBuffer[256];
struct tm timeInfo;
localtime_r((time_t *)buffer, &timeInfo);
strftime(stringBuffer, sizeof(stringBuffer), "%c", &timeInfo);
printf("%s\n", stringBuffer);
break;
}
case B_STRING_TYPE:
case B_MIME_STRING_TYPE:
case 'MSIG':
case 'MSDC':
case 'MPTH':
printf("%s\n", buffer);
break;
case B_MESSAGE_TYPE:
{
BMessage message;
if (!cut && message.Unflatten(buffer) == B_OK) {
putchar('\n');
message.PrintToStream();
putchar('\n');
break;
}
// supposed to fall through
}
default:
// The rest of the attributes types are displayed as raw data
putchar('\n');
dump_raw_data(buffer, size);
putchar('\n');
break;
}
}
static const char *
get_type(type_code type)
{
static char buffer[32];
switch (type) {
case B_MIME_STRING_TYPE:
return "MIME String";
case B_RAW_TYPE:
return "Raw Data";
case B_STRING_TYPE:
return "Text";
case B_INT64_TYPE:
return "Int-64";
case B_UINT64_TYPE:
return "Uint-64";
case B_INT32_TYPE:
return "Int-32";
case B_UINT32_TYPE:
return "Uint-32";
case B_INT16_TYPE:
return "Int-16";
case B_UINT16_TYPE:
return "Uint-16";
case B_INT8_TYPE:
return "Int-8";
case B_UINT8_TYPE:
return "Uint-8";
case B_BOOL_TYPE:
return "Boolean";
case B_FLOAT_TYPE:
return "Float";
case B_DOUBLE_TYPE:
return "Double";
case B_MINI_ICON_TYPE:
return "Mini Icon";
case B_LARGE_ICON_TYPE:
return "Icon";
default:
{
int32 missed = 0, shift = 24;
uint8 value[4];
for (int32 i = 0; i < 4; i++, shift -= 8) {
value[i] = uint8(type >> shift);
if (value[i] < ' ' || value[i] > 127) {
value[i] = '.';
missed++;
}
}
if (missed < 2) {
sprintf(buffer, "'%c%c%c%c'", value[0], value[1], value[2],
value[3]);
} else
sprintf(buffer, "0x%08" B_PRIx32, type);
return buffer;
}
}
}
int
main(int argc, char *argv[])
{
const char *program = strrchr(argv[0], '/');
if (program == NULL)
program = argv[0];
else
program++;
bool printContents = false;
if (argc > 2 && (!strcmp(argv[1], "--long") || !strcmp(argv[1], "-l"))) {
printContents = true;
argc--;
argv++;
}
if (argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) {
printf("usage: %s [-l|--long] 'filename' ['filename' ...]\n"
" -l, --long Shows the attribute contents as well.\n", program);
return argc == 2 ? 0 : 1;
}
off_t total = 0;
for (int i = 1; i < argc; ++i) {
BNode node(argv[i]);
status_t status = node.InitCheck();
if (status < B_OK) {
fprintf(stderr, "%s: initialization failed for \"%s\": %s\n",
program, argv[i], strerror(status));
return 0;
}
printf("File: %s\n", argv[i]);
const int kTypeWidth = 12;
const int kSizeWidth = 10;
const int kNameWidth = 36;
const int kContentsWidth = 21;
printf("%*s %*s %-*s%s\n", kTypeWidth, "Type", kSizeWidth, "Size",
kNameWidth, "Name", printContents ? "Contents" : "");
BString separator;
separator.SetTo('-', kTypeWidth + kSizeWidth + kNameWidth
+ (printContents ? kContentsWidth : 0));
puts(separator.String());
char name[B_ATTR_NAME_LENGTH];
while (node.GetNextAttrName(name) == B_OK) {
attr_info attrInfo;
status = node.GetAttrInfo(name, &attrInfo);
if (status >= B_OK) {
printf("%*s", kTypeWidth, get_type(attrInfo.type));
printf("% *" B_PRId64 " ", kSizeWidth, attrInfo.size);
printf("\"%s\"", name);
if (printContents) {
// padding
int length = kNameWidth - 2 - strlen(name);
if (length > 0)
printf("%*s", length, "");
show_attr_contents(node, name, attrInfo);
} else
putchar('\n');
total += attrInfo.size;
} else {
fprintf(stderr, "%s: stat failed for \"%s\": %s\n",
program, name, strerror(status));
}
}
}
printf("\n%" B_PRId64 " bytes total in attributes.\n", total);
return 0;
}