mirror of
https://review.haiku-os.org/haiku
synced 2025-02-08 22:58:18 +01:00
363 lines
7.6 KiB
C++
363 lines
7.6 KiB
C++
/*
|
|
* Copyright 2010, Alexander Shagarov, alexander.shagarov@gmail.com.
|
|
* Copyright 2005, Stephan Aßmus, superstippi@yellowbites.com.
|
|
* Copyright 2004-2010, Axel Dörfler, axeld@pinc-software.de.
|
|
* Copyright 2002, Sebastian Nozzi.
|
|
*
|
|
* 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 <unistd.h>
|
|
|
|
#include <fs_attr.h>
|
|
#include <Mime.h>
|
|
#include <String.h>
|
|
#include <TypeConstants.h>
|
|
|
|
|
|
/*! Used to present the characters in the raw data view */
|
|
static void
|
|
putCharOrDot(uchar c)
|
|
{
|
|
putchar(isgraph(c) ? c : '.');
|
|
}
|
|
|
|
|
|
/*! Dumps the contents of the attribute in the form of
|
|
raw data. This view is used for the type B_RAW_DATA_TYPE,
|
|
for custom types and for any type that is not directly
|
|
supported by the utility "addattr"
|
|
*/
|
|
static void
|
|
dumpRawData(const char *buffer, size_t size)
|
|
{
|
|
const uint32 kChunkSize = 16;
|
|
uint32 dumpPosition = 0;
|
|
|
|
while (dumpPosition < size) {
|
|
// Position for this line
|
|
printf("0x%06" 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)
|
|
putCharOrDot(buffer[dumpPosition]);
|
|
else
|
|
putchar(' ');
|
|
|
|
dumpPosition++;
|
|
}
|
|
printf("'\n");
|
|
}
|
|
}
|
|
|
|
|
|
static const char*
|
|
type_to_string(uint32 type)
|
|
{
|
|
static char buffer[32];
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static BString
|
|
type_name(uint32 type)
|
|
{
|
|
switch (type) {
|
|
case B_INT8_TYPE:
|
|
return "int8";
|
|
case B_UINT8_TYPE:
|
|
return "uint8";
|
|
case B_INT16_TYPE:
|
|
return "int16";
|
|
case B_UINT16_TYPE:
|
|
return "uint16";
|
|
case B_INT32_TYPE:
|
|
return "int32";
|
|
case B_UINT32_TYPE:
|
|
return "uint32";
|
|
case B_INT64_TYPE:
|
|
return "int64";
|
|
case B_UINT64_TYPE:
|
|
return "uint64";
|
|
case B_FLOAT_TYPE:
|
|
return "float";
|
|
case B_DOUBLE_TYPE:
|
|
return "double";
|
|
case B_BOOL_TYPE:
|
|
return "bool";
|
|
case B_STRING_TYPE:
|
|
return "string";
|
|
case B_MESSAGE_TYPE:
|
|
return "message";
|
|
case B_RAW_TYPE:
|
|
return "raw_data";
|
|
|
|
default:
|
|
return type_to_string(type);
|
|
}
|
|
}
|
|
|
|
|
|
static status_t
|
|
catAttr(const char *attribute, const char *fileName, bool keepRaw,
|
|
bool dataOnly, bool resolveLinks)
|
|
{
|
|
int fd = open(fileName, O_RDONLY | (resolveLinks ? 0 : O_NOTRAVERSE));
|
|
if (fd < 0)
|
|
return errno;
|
|
|
|
attr_info info;
|
|
if (fs_stat_attr(fd, attribute, &info) < 0) {
|
|
close(fd);
|
|
return errno;
|
|
}
|
|
|
|
// limit size of the attribute, only the first 64k will make it on screen
|
|
off_t size = info.size;
|
|
bool cut = false;
|
|
if (size > 64 * 1024) {
|
|
size = 64 * 1024;
|
|
cut = true;
|
|
}
|
|
|
|
char* buffer = (char*)malloc(size);
|
|
if (!buffer) {
|
|
fprintf(stderr, "Could not allocate read buffer!\n");
|
|
close(fd);
|
|
return B_NO_MEMORY;
|
|
}
|
|
|
|
ssize_t bytesRead = fs_read_attr(fd, attribute, info.type, 0, buffer, size);
|
|
if (bytesRead < 0) {
|
|
free(buffer);
|
|
close(fd);
|
|
return errno;
|
|
}
|
|
|
|
if (bytesRead != size) {
|
|
fprintf(stderr, "Could only read %ld bytes from attribute!\n",
|
|
bytesRead);
|
|
free(buffer);
|
|
close(fd);
|
|
return B_ERROR;
|
|
}
|
|
|
|
if (keepRaw) {
|
|
off_t pos = 0;
|
|
ssize_t written = 0;
|
|
while (pos < info.size) {
|
|
// write what we have read so far
|
|
written = write(STDOUT_FILENO, buffer, bytesRead);
|
|
// check for write error
|
|
if (written < bytesRead) {
|
|
if (written >= 0) {
|
|
fprintf(stderr, "Could only write %ld bytes to stream!\n",
|
|
written);
|
|
written = B_ERROR;
|
|
} else {
|
|
fprintf(stderr, "Failed to write to stream: %s\n",
|
|
strerror(written));
|
|
}
|
|
break;
|
|
}
|
|
// read next chunk of data at pos
|
|
pos += bytesRead;
|
|
bytesRead = fs_read_attr(fd, attribute, info.type, pos, buffer,
|
|
size);
|
|
// check for read error
|
|
if (bytesRead < size && pos + bytesRead < info.size) {
|
|
if (bytesRead >= 0) {
|
|
fprintf(stderr, "Could only read %ld bytes from "
|
|
"attribute!\n", bytesRead);
|
|
} else {
|
|
fprintf(stderr, "Failed to read from attribute: %s\n",
|
|
strerror(bytesRead));
|
|
}
|
|
written = B_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
free(buffer);
|
|
if (written > 0)
|
|
written = B_OK;
|
|
close(fd);
|
|
return written;
|
|
}
|
|
|
|
if (!dataOnly)
|
|
printf("%s : %s : ", fileName, type_name(info.type).String());
|
|
|
|
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_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) {
|
|
message.PrintToStream();
|
|
break;
|
|
}
|
|
// supposed to fall through
|
|
}
|
|
|
|
default:
|
|
// The rest of the attributes types are displayed as raw data
|
|
dumpRawData(buffer, size);
|
|
break;
|
|
}
|
|
|
|
free(buffer);
|
|
close(fd);
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
static int
|
|
usage(const char* program, int returnCode)
|
|
{
|
|
// Issue usage message
|
|
fprintf(returnCode == EXIT_SUCCESS ? stdout : stderr,
|
|
"usage: %s [-P] [--raw|-r] <attribute-name> <file1> [<file2>...]\n"
|
|
" -P\t\tDon't resolve links\n"
|
|
" --raw,-r\tGet the raw data of attributes\n"
|
|
" --data,-d\tShow the attribute data only\n", program);
|
|
|
|
return returnCode;
|
|
}
|
|
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
char *program = strrchr(argv[0], '/');
|
|
if (program == NULL)
|
|
program = argv[0];
|
|
else
|
|
program++;
|
|
|
|
const struct option kLongOptions[] = {
|
|
{"raw", no_argument, NULL, 'r'},
|
|
{"data", no_argument, NULL, 'd'},
|
|
{"help", no_argument, NULL, 'h'},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
bool keepRaw = false;
|
|
bool resolveLinks = true;
|
|
bool dataOnly = false;
|
|
|
|
int option;
|
|
while ((option = getopt_long(argc, argv, "rdPh", kLongOptions, NULL))
|
|
!= -1) {
|
|
switch (option) {
|
|
case 'r':
|
|
keepRaw = true;
|
|
break;
|
|
case 'P':
|
|
resolveLinks = false;
|
|
break;
|
|
case 'd':
|
|
dataOnly = true;
|
|
break;
|
|
case 'h':
|
|
return usage(program, EXIT_SUCCESS);
|
|
|
|
default:
|
|
return usage(program, EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
if (optind + 2 > argc)
|
|
return usage(program, EXIT_FAILURE);
|
|
|
|
int succeeded = 0;
|
|
const char* attrName = argv[optind++];
|
|
|
|
while (optind < argc) {
|
|
const char* fileName = argv[optind++];
|
|
status_t status = catAttr(attrName, fileName, keepRaw, dataOnly,
|
|
resolveLinks);
|
|
if (status != B_OK) {
|
|
fprintf(stderr, "%s: \"%s\", attribute \"%s\": %s\n",
|
|
program, fileName, attrName, strerror(status));
|
|
} else
|
|
succeeded++;
|
|
}
|
|
|
|
return succeeded != 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|