// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2003-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
 */

#include "path.h"
#include "command.h"
#include "input.h"
#include "init.h"

char	*progname;
int	exitcode;
int	expert;
bool	foreign_allowed = false;

static char **projopts;	/* table of project names (cmdline) */
static int nprojopts;	/* number of entries in name table. */

static void
add_project_opt(
	char		*optarg)
{
	nprojopts++;
	projopts = realloc(projopts, sizeof(char*) * nprojopts);
	if (!projopts) {
		perror("realloc");
		exit(1);
	}
	projopts[nprojopts - 1] = optarg;
}

static void
usage(void)
{
	fprintf(stderr,
		_("Usage: %s [-V] [-x] [-f] [-p prog] [-c cmd]... [-d project]... [path]\n"),
		progname);
	exit(1);
}

void
init_cvtnum(
	unsigned int	*blocksize,
	unsigned int	*sectsize)
{
	*blocksize = 4096;
	*sectsize = 512;
}

static void
init_commands(void)
{
	edit_init();
	free_init();
	help_init();
	path_init();
	project_init();
	quot_init();
	quota_init();
	quit_init();
	report_init();
	state_init();
}

/*
 * This function allows xfs_quota commands to iterate across all discovered
 * quota enabled filesystems. Commands that should not iterate all filesystems
 * should specify CMD_FLAG_ONESHOT in their command flags.
 */
static int
filesystem_iterator(
	int	index)
{
	if (index >= fs_count)
		return 0;

	do {
		fs_path = &fs_table[index++];
		/* skip project quota entries */
		if ((fs_path->fs_flags & FS_PROJECT_PATH))
			continue;

		/* only consider foreign filesystems if told so */
		if (!foreign_allowed && (fs_path->fs_flags & FS_FOREIGN))
			continue;

		/* We can use this one */
		break;
	} while (index < fs_count);

	if (fs_path->fs_flags & FS_PROJECT_PATH)
		return 0;
	if (!foreign_allowed && (fs_path->fs_flags & FS_FOREIGN))
		return 0;
	if (index > fs_count)
		return 0;
	return index;
}

static int
init_check_command(
	const cmdinfo_t	*ct)
{
	if (!fs_path)
		return 1;

	/* If it's an XFS filesystem, always run the command. */
	if (!(fs_path->fs_flags & FS_FOREIGN))
		return 1;

	/* If the user specified foreign filesystems are ok (-f), run cmd. */
	if (foreign_allowed &&
	    (ct->flags & CMD_FLAG_FOREIGN_OK))
		return 1;

	/* If cmd not allowed on foreign fs, regardless of -f flag, skip it. */
	if (!(ct->flags & CMD_FLAG_FOREIGN_OK)) {
		fprintf(stderr, _("%s: command is for XFS filesystems only\n"),
			ct->name);
		return 0;
	}

	/* foreign fs, but cmd only allowed via -f flag. Skip it. */
	fprintf(stderr,
		_("%s: foreign filesystem. Invoke xfs_quota with -f to enable.\n"),
		ct->name);
	return 0;
}

static void
init(
	int		argc,
	char		**argv)
{
	int		c;

	progname = basename(argv[0]);
	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);

	while ((c = getopt(argc, argv, "c:d:D:fP:p:t:xV")) != EOF) {
		switch (c) {
		case 'c':	/* commands */
			add_user_command(optarg);
			break;
		case 'd':
			add_project_opt(optarg);
			break;
		case 'f':
			foreign_allowed = true;
			break;
		case 't':
			mtab_file = optarg;
			break;
		case 'D':
			projects_file = optarg;
			break;
		case 'P':
			projid_file = optarg;
			break;
		case 'p':
			progname = optarg;
			break;
		case 'x':
			expert++;
			break;
		case 'V':
			printf(_("%s version %s\n"), progname, VERSION);
			exit(0);
		default:
			usage();
		}
	}

	fs_table_initialise(argc - optind, &argv[optind], nprojopts, projopts);
	free(projopts);

	init_commands();
	add_command_iterator(filesystem_iterator);
	add_check_command(init_check_command);

	/*
	 * Ensure that global commands don't end up with an invalid path pointer
	 * by setting the default device at the first specified on the CLI
	 */
	if (argc != optind)
		fs_path = fs_table_lookup(argv[optind], FS_MOUNT_POINT);
	else
		fs_path = &fs_table[0];
}

int
main(
	int		argc,
	char		**argv)
{
	init(argc, argv);
	command_loop();
	return exitcode;
}
