/*-
 * Copyright (c) 2014-2018 MongoDB, Inc.
 * Copyright (c) 2008-2014 WiredTiger, Inc.
 *	All rights reserved.
 *
 * See the file LICENSE for redistribution information.
 */

#include "util.h"

static int list_get_allocsize(WT_SESSION *, const char *, size_t *);
static int list_print(WT_SESSION *, const char *, bool, bool);
static int list_print_checkpoint(WT_SESSION *, const char *);
static int usage(void);

int
util_list(WT_SESSION *session, int argc, char *argv[])
{
	WT_DECL_RET;
	int ch;
	char *uri;
	bool cflag, vflag;

	cflag = vflag = false;
	uri = NULL;
	while ((ch = __wt_getopt(progname, argc, argv, "cv")) != EOF)
		switch (ch) {
		case 'c':
			cflag = true;
			break;
		case 'v':
			vflag = true;
			break;
		case '?':
		default:
			return (usage());
		}
	argc -= __wt_optind;
	argv += __wt_optind;

	switch (argc) {
	case 0:
		break;
	case 1:
		if ((uri = util_uri(session, *argv, "table")) == NULL)
			return (1);
		break;
	default:
		return (usage());
	}

	ret = list_print(session, uri, cflag, vflag);

	free(uri);
	return (ret);
}

/*
 * list_get_allocsize --
 *	Get the allocation size for this file from the metadata.
 */
static int
list_get_allocsize(WT_SESSION *session, const char *key, size_t *allocsize)
{
	WT_CONFIG_ITEM szvalue;
	WT_CONFIG_PARSER *parser;
	WT_DECL_RET;
	WT_EXTENSION_API *wt_api;
	int tret;
	char *config;

	wt_api = session->connection->get_extension_api(session->connection);
	if ((ret = wt_api->metadata_search(wt_api, session, key, &config)) != 0)
		return (util_err(
		    session, ret, "%s: WT_EXTENSION_API.metadata_search", key));
	if ((ret = wt_api->config_parser_open(wt_api, session, config,
	    strlen(config), &parser)) != 0)
		return (util_err(
		    session, ret, "WT_EXTENSION_API.config_parser_open"));
	if ((ret = parser->get(parser, "allocation_size", &szvalue)) != 0) {
		if (ret == WT_NOTFOUND) {
			*allocsize = 0;
			ret = 0;
		} else
			ret = util_err(session, ret, "WT_CONFIG_PARSER.get");
		if ((tret = parser->close(parser)) != 0)
			(void)util_err(session, tret, "WT_CONFIG_PARSER.close");
		return (ret);
	}
	if ((ret = parser->close(parser)) != 0)
		return (util_err(session, ret, "WT_CONFIG_PARSER.close"));
	*allocsize = (size_t)szvalue.val;
	return (0);
}

/*
 * list_print --
 *	List the high-level objects in the database.
 */
static int
list_print(WT_SESSION *session, const char *uri, bool cflag, bool vflag)
{
	WT_CURSOR *cursor;
	WT_DECL_RET;
	const char *key, *value;
	bool found;

	/* Open the metadata file. */
	if ((ret = session->open_cursor(
	    session, WT_METADATA_URI, NULL, NULL, &cursor)) != 0) {
		/*
		 * If there is no metadata (yet), this will return ENOENT.
		 * Treat that the same as an empty metadata.
		 */
		if (ret == ENOENT)
			return (0);

		return (util_err(session,
		    ret, "%s: WT_SESSION.open_cursor", WT_METADATA_URI));
	}

	found = uri == NULL;
	while ((ret = cursor->next(cursor)) == 0) {
		/* Get the key. */
		if ((ret = cursor->get_key(cursor, &key)) != 0)
			return (util_cerr(cursor, "get_key", ret));

		/*
		 * If a name is specified, only show objects that match.
		 */
		if (uri != NULL) {
			if (!WT_PREFIX_MATCH(key, uri))
				continue;
			found = true;
		}

		/*
		 * !!!
		 * We don't normally say anything about the WiredTiger metadata
		 * and lookaside tables, they're not application/user "objects"
		 * in the database.  I'm making an exception for the checkpoint
		 * and verbose options. However, skip over the metadata system
		 * information for anything except the verbose option.
		 */
		if (!vflag && WT_PREFIX_MATCH(key, WT_SYSTEM_PREFIX))
			continue;
		if (cflag || vflag ||
		    (strcmp(key, WT_METADATA_URI) != 0 &&
		    strcmp(key, WT_LAS_URI) != 0))
			printf("%s\n", key);

		if (!cflag && !vflag)
			continue;

		if (cflag && (ret = list_print_checkpoint(session, key)) != 0)
			return (ret);
		if (vflag) {
			if ((ret = cursor->get_value(cursor, &value)) != 0)
				return (util_cerr(cursor, "get_value", ret));
			printf("%s\n", value);
		}
	}
	if (ret != WT_NOTFOUND)
		return (util_cerr(cursor, "next", ret));
	if (!found) {
		fprintf(stderr, "%s: %s: not found\n", progname, uri);
		return (1);
	}

	return (0);
}

/*
 * list_print_checkpoint --
 *	List the checkpoint information.
 */
static int
list_print_checkpoint(WT_SESSION *session, const char *key)
{
	WT_BLOCK_CKPT ci;
	WT_CKPT *ckpt, *ckptbase;
	WT_DECL_RET;
	size_t allocsize, len;
	time_t t;
	uint64_t v;

	/*
	 * We may not find any checkpoints for this file, in which case we don't
	 * report an error, and continue our caller's loop.  Otherwise, read the
	 * list of checkpoints and print each checkpoint's name and time.
	 */
	if ((ret = __wt_metadata_get_ckptlist(session, key, &ckptbase)) != 0)
		return (ret == WT_NOTFOUND ? 0 : ret);

	/* We need the allocation size for decoding the checkpoint addr */
	if ((ret = list_get_allocsize(session, key, &allocsize)) != 0)
		return (ret);

	/* Find the longest name, so we can pretty-print. */
	len = 0;
	WT_CKPT_FOREACH(ckptbase, ckpt)
		if (strlen(ckpt->name) > len)
			len = strlen(ckpt->name);
	++len;

	memset(&ci, 0, sizeof(ci));
	WT_CKPT_FOREACH(ckptbase, ckpt) {
		if (allocsize != 0 && (ret = __wt_block_ckpt_decode(
		    session, allocsize, ckpt->raw.data, &ci)) != 0) {
			(void)util_err(session, ret, "__wt_block_ckpt_decode");
			/* continue if damaged */
			ci.root_size = 0;
		}
		/*
		 * Call ctime, not ctime_r; ctime_r has portability problems,
		 * the Solaris version is different from the POSIX standard.
		 */
		t = (time_t)ckpt->sec;
		printf("\t%*s: %.24s", (int)len, ckpt->name, ctime(&t));

		v = ckpt->ckpt_size;
		if (v >= WT_PETABYTE)
			printf(" (%" PRIu64 " PB)\n", v / WT_PETABYTE);
		else if (v >= WT_TERABYTE)
			printf(" (%" PRIu64 " TB)\n", v / WT_TERABYTE);
		else if (v >= WT_GIGABYTE)
			printf(" (%" PRIu64 " GB)\n", v / WT_GIGABYTE);
		else if (v >= WT_MEGABYTE)
			printf(" (%" PRIu64 " MB)\n", v / WT_MEGABYTE);
		else if (v >= WT_KILOBYTE)
			printf(" (%" PRIu64 " KB)\n", v / WT_KILOBYTE);
		else
			printf(" (%" PRIu64 " B)\n", v);
		if (ci.root_size != 0) {
			printf("\t\t" "root offset: %" PRIuMAX
			    " (0x%" PRIxMAX ")\n",
			    (uintmax_t)ci.root_offset,
			    (uintmax_t)ci.root_offset);
			printf("\t\t" "root size: %" PRIu32
			    " (0x%" PRIx32 ")\n",
			    ci.root_size, ci.root_size);
			printf("\t\t" "root checksum: %" PRIu32
			    " (0x%" PRIx32 ")\n",
			    ci.root_checksum, ci.root_checksum);
		}
	}

	__wt_metadata_free_ckptlist(session, ckptbase);
	return (0);
}

static int
usage(void)
{
	(void)fprintf(stderr,
	    "usage: %s %s "
	    "list [-cv] [uri]\n",
	    progname, usage_prefix);
	return (1);
}
