⛏️ index : haiku.git

/*
 * 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>
#include <time.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;
}