/*
 * Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de.
 * This file may be used under the terms of the MIT License.
 */
 
 
#include <ctype.h>
#include <errno.h>
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <List.h>
#include <Node.h>
#include <Path.h>
#include <String.h>
#include <fs_attr.h>
#include <fs_index.h>
#include <fs_info.h>
 
 
extern const char *__progname;
static const char *kProgramName = __progname;
 
bool gRecursive = false;		// enter directories recursively
bool gVerbose = false;
char *gAttrPattern;
bool gIsPattern = false;
bool gFromVolume = false;	// copy indices from another volume
BList gAttrList;				// list of indices of that volume
 
 
class Attribute {
public:
	Attribute(const char *name);
	~Attribute();
 
	status_t ReadFromFile(BNode *node);
	status_t WriteToFile(BNode *node);
	status_t RemoveFromFile(BNode *node);
 
	const char	*Name() const { return fName.String(); }
	type_code	Type() const { return fType; }
	size_t		Length() const { return fLength; }
 
protected:
	BString		fName;
	type_code	fType;
	void		*fBuffer;
	size_t		fLength;
};
 
 
Attribute::Attribute(const char *name)
	:
	fName(name),
	fBuffer(NULL),
	fLength(0)
{
}
 
 
Attribute::~Attribute()
{
	free(fBuffer);
}
 
 
status_t
Attribute::ReadFromFile(BNode *node)
{
	attr_info info;
	status_t status = node->GetAttrInfo(fName.String(), &info);
	if (status != B_OK)
		return status;
 
	fType = info.type;
	fLength = info.size;
 
	if ((fBuffer = malloc(fLength)) == NULL)
		return B_NO_MEMORY;
 
	ssize_t bytesRead = node->ReadAttr(fName.String(), fType, 0, fBuffer,
		fLength);
	if (bytesRead < B_OK)
		return bytesRead;
	if (bytesRead < (ssize_t)fLength)
		return B_IO_ERROR;
 
	return B_OK;
}
 
 
status_t
Attribute::WriteToFile(BNode *node)
{
	ssize_t bytesWritten = node->WriteAttr(fName.String(), fType, 0, fBuffer,
		fLength);
	if (bytesWritten < B_OK)
		return bytesWritten;
	if (bytesWritten < (ssize_t)fLength)
		return B_IO_ERROR;
 
	return B_OK;
}
 
 
status_t
Attribute::RemoveFromFile(BNode *node)
{
	return node->RemoveAttr(fName.String());
}
 
 
//	#pragma mark -
 
 
bool
nameMatchesPattern(char *name)
{
	if (!gIsPattern)
		return !strcmp(name,gAttrPattern);
 
	// test the beginning
	int i = 0;
	for(; name[i]; i++) {
		if (gAttrPattern[i] == '*')
			break;
		if (name[i] != gAttrPattern[i])
			return false;
	}
 
	// test the end
	int j = strlen(name) - 1;
	int k = strlen(gAttrPattern) - 1;
	for(; j >= i && k >= i; j--, k--) {
		if (gAttrPattern[k] == '*')
			return true;
		if (name[j] != gAttrPattern[k])
			return false;
	}
	if (gAttrPattern[k] != '*')
		return false;
 
	return true;
}
 
 
bool
isAttrInList(char *name)
{
	for (int32 index = gAttrList.CountItems();index-- > 0;) {
		const char *attr = (const char *)gAttrList.ItemAt(index);
		if (!strcmp(attr, name))
			return true;
	}
	return false;
}
 
 
void
handleFile(BEntry *entry, BNode *node)
{
	// Recurse the directories
	if (gRecursive && entry->IsDirectory()) {
		BDirectory dir(entry);
		BEntry entryIterator;
 
		dir.Rewind();
		while (dir.GetNextEntry(&entryIterator, false) == B_OK) {
			BNode innerNode;
			handleFile(&entryIterator, &innerNode);
		}
 
		// also rewrite the attributes of the directory
	}
 
	char name[B_FILE_NAME_LENGTH];
	entry->GetName(name);
 
	status_t status = node->SetTo(entry);
	if (status != B_OK) {
		fprintf(stderr, "%s: could not open \"%s\": %s\n", kProgramName, name,
			strerror(status));
		return;
	}
 
	// rewrite file attributes
 
	char attrName[B_ATTR_NAME_LENGTH];
	Attribute *attr;
	BList list;
 
	// building list
	node->RewindAttrs();
	while (node->GetNextAttrName(attrName) == B_OK) {
		if (gFromVolume) {
			if (!isAttrInList(attrName))
				continue;
		} else if (!nameMatchesPattern(attrName))
			continue;
 
		attr = new(std::nothrow) Attribute(attrName);
		if (attr == NULL) {
			fprintf(stderr, "%s: out of memory.\n", kProgramName);
			exit(1);
		}
 
		status = attr->ReadFromFile(node);
		if (status != B_OK) {
			fprintf(stderr, "%s: could not read attribute \"%s\" of file "
				"\"%s\": %s\n", kProgramName, attrName, name, strerror(status));
			delete attr;
			continue;
		}
		if (gVerbose) {
			printf("%s: read attribute '%s' (%ld bytes of data)\n", name,
				attrName, attr->Length());
		}
		if (!list.AddItem(attr)) {
			fprintf(stderr, "%s: out of memory.\n", kProgramName);
			exit(1);
		}
 
		if (!gFromVolume) {
			// creates index to that attribute if necessary
			entry_ref ref;
			if (entry->GetRef(&ref) == B_OK) {
				index_info indexInfo;
				if (fs_stat_index(ref.device, attrName, &indexInfo) != B_OK)
					fs_create_index(ref.device, attrName, attr->Type(), 0);
			}
		}
	}
 
	// remove attrs
	for (int32 i = list.CountItems(); i-- > 0;) {
		attr = static_cast<Attribute *>(list.ItemAt(i));
		if (attr->RemoveFromFile(node) != B_OK) {
			fprintf(stderr, "%s: could not remove attribute '%s' from file "
				"'%s'.\n", kProgramName, attr->Name(), name);
		}
	}
 
	// rewrite attrs and empty the list
	while ((attr = static_cast<Attribute *>(list.RemoveItem((int32)0))) != NULL) {
		// write attribute back to file
		status = attr->WriteToFile(node);
		if (status != B_OK) {
			fprintf(stderr, "%s: could not write attribute '%s' to file \"%s\":"
				" %s\n", kProgramName, attr->Name(), name, strerror(status));
		} else if (gVerbose) {
			printf("%s: wrote attribute '%s' (%ld bytes of data)\n", name,
				attr->Name(), attr->Length());
		}
 
		delete attr;
	}
}
 
 
void
copyIndicesFromVolume(const char *path, BEntry &to)
{
	entry_ref ref;
	if (to.GetRef(&ref) != B_OK) {
		fprintf(stderr, "%s: Could not open target volume.\n", kProgramName);
		return;
	}
 
	dev_t targetDevice = ref.device;
	dev_t sourceDevice = dev_for_path(path);
	if (sourceDevice < B_OK) {
		fprintf(stderr, "%s: Could not open source volume: %s\n", kProgramName,
			strerror(sourceDevice));
		return;
	}
 
	DIR *indexDirectory = fs_open_index_dir(sourceDevice);
	if (indexDirectory == NULL)
		return;
 
	while (dirent *index = fs_read_index_dir(indexDirectory)) {
		index_info indexInfo;
		if (fs_stat_index(sourceDevice, index->d_name, &indexInfo) != B_OK) {
			fprintf(stderr, "%s: Could not get information about index "
				"\"%s\": %s\n", kProgramName, index->d_name, strerror(errno));
			continue;
		}
 
		if (fs_create_index(targetDevice, index->d_name, indexInfo.type, 0)
				== -1 && errno != B_FILE_EXISTS && errno != B_BAD_VALUE) {
			fprintf(stderr, "Could not create index '%s' (type = %" B_PRIu32
				"): %s\n", index->d_name, indexInfo.type, strerror(errno));
		} else
			gAttrList.AddItem(strdup(index->d_name));
	}
	fs_close_index_dir(indexDirectory);
}
 
 
void
printUsage(char *cmd)
{
	printf("usage: %s [-rvf] attr <list of filenames and/or directories>\n"
		"  -r\tenter directories recursively\n"
		"  -v\tverbose output\n"
		"  -f\tcreate/update all indices from the source volume,\n\t\"attr\" is "
			"the path to the source volume\n", cmd);
}
 
 
int
main(int argc, char **argv)
{
	char *cmd = argv[0];
 
	if (argc < 3) {
		printUsage(cmd);
		return 1;
	}
 
	while (*++argv && **argv == '-') {
		for (int i = 1; (*argv)[i]; i++) {
			switch ((*argv)[i]) {
				case 'f':
					gFromVolume = true;
					break;
				case 'r':
					gRecursive = true;
					break;
				case 'v':
					gVerbose = true;
					break;
				default:
					printUsage(cmd);
					return(1);
			}
		}
	}
	gAttrPattern = *argv;
	if (strchr(gAttrPattern,'*'))
		gIsPattern = true;
 
	while (*++argv) {
		BEntry entry(*argv);
		BNode node;
 
		if (entry.InitCheck() == B_OK) {
			if (gFromVolume)
				copyIndicesFromVolume(gAttrPattern, entry);
			handleFile(&entry, &node);
		} else
			fprintf(stderr, "%s: could not find \"%s\".\n", kProgramName, *argv);
	}
 
	return 0;
}

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fType.