/* This file is part of GNU Rush.
   Copyright (C) 2008-2025 Sergey Poznyakoff

   GNU Rush is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   GNU Rush is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Rush.  If not, see <http://www.gnu.org/licenses/>. */

#include <rush.h>
#include "error.h"
#include <parseopt.h>

char *base_name = RUSH_DB;
int  display_header = 1;  /* Display header line */
int forward = 0;
char *format;
unsigned long count = 0;

enum {
	OPT_FORWARD = 256
};

static int
optset_zero(struct parseopt *po, struct optdef *opt, char *arg)
{
	*(int*) opt->opt_ptr = 0;
	return 0;
}

static int
optset_number(struct parseopt *po, struct optdef *opt, char *arg)
{
	char *p;
	count = strtoul(po->po_optname, &p, 10);
	po->po_optname = p-1;
	return 0;
}

static struct optdef options[] = {
	{
		.opt_name = "format",
		.opt_argdoc = N_("STRING"),
		.opt_doc = N_("Use STRING instead of the default format."),
		.opt_set = optset_string_copy,
		.opt_ptr = &format
	},
	{
		.opt_name = "F",
		.opt_flags = OPTFLAG_ALIAS
	},

	{
		.opt_name = "file",
		.opt_argdoc = N_("DIR"),
		.opt_doc = N_("Look for database files in DIR."),
		.opt_set = optset_string_copy,
		.opt_ptr = &base_name
	},
	{
		.opt_name = "f",
		.opt_flags = OPTFLAG_ALIAS
	},

	{
		.opt_name = "forward",
		.opt_doc = N_("Show entries in chronological order."),
		.opt_code = OPT_FORWARD,
		.opt_set = optset_incr,
		.opt_ptr = &forward
	},

	{
		.opt_name = "no-header",
		.opt_doc = N_("Do not display header line."),
		.opt_set = optset_zero,
		.opt_ptr = &display_header
	},
	{
		.opt_name = "H",
		.opt_flags = OPTFLAG_ALIAS
	},

	{
		.opt_name = "count",
		.opt_argdoc = N_("NUMBER"),
		.opt_doc = N_("Show at most NUM records."),
		.opt_set = optset_ulong,
		.opt_ptr = &count
	},
	{
		.opt_name = "n",
		.opt_flags = OPTFLAG_ALIAS
	},

	{
		.opt_name = "0",
		.opt_flags = OPTFLAG_HIDDEN,
		.opt_set = optset_number,
		.opt_ptr = &count
	},
	{
		.opt_name = "1",
		.opt_flags = OPTFLAG_ALIAS|OPTFLAG_HIDDEN
	},
	{
		.opt_name = "2",
		.opt_flags = OPTFLAG_ALIAS|OPTFLAG_HIDDEN
	},
	{
		.opt_name = "3",
		.opt_flags = OPTFLAG_ALIAS|OPTFLAG_HIDDEN
	},
	{
		.opt_name = "4",
		.opt_flags = OPTFLAG_ALIAS|OPTFLAG_HIDDEN
	},
	{
		.opt_name = "5",
		.opt_flags = OPTFLAG_ALIAS|OPTFLAG_HIDDEN
	},
	{
		.opt_name = "6",
		.opt_flags = OPTFLAG_ALIAS|OPTFLAG_HIDDEN
	},
	{
		.opt_name = "7",
		.opt_flags = OPTFLAG_ALIAS|OPTFLAG_HIDDEN
	},
	{
		.opt_name = "8",
		.opt_flags = OPTFLAG_ALIAS|OPTFLAG_HIDDEN
	},
	{
		.opt_name = "9",
		.opt_flags = OPTFLAG_ALIAS|OPTFLAG_HIDDEN
	},

	{ NULL }
}, *optdef[] = { options, NULL };

extern void version_hook(WORDWRAP_FILE wf, struct parseopt *po);

static struct parseopt po = {
	.po_descr = N_("show listing of last rush logins"),
	.po_argdoc = N_("[user [user...]]"),
	.po_optdef = optdef,
	.po_package_name = PACKAGE_NAME,
	.po_package_url = PACKAGE_URL,
	.po_bugreport_address = PACKAGE_BUGREPORT,
	.po_version_hook = version_hook
};
void
xalloc_die()
{
	error(1, 0, _("not enough memory"));
	abort();
}


char *default_format =
	"(user 10 Login) "
	"(rule 8 Rule) "
	"(start-time 0 Start) "
	"(stop-time 0 Stop) "
	"(duration 7 Time) "
	"(command 32 Command)";

int
want_record(struct rush_wtmp *wtmp, int argc, char **argv)
{
	int i;
	if (argc == 0)
		return 1;
	for (i = 0; i < argc; i++)
		if (strcmp(argv[i], wtmp->user) == 0)
			return 1;
	return 0;
}

int
main(int argc, char **argv)
{
	struct rush_wtmp *wtmp = NULL;
	rushdb_format_t form;
	unsigned long i;

	rush_set_program_name(argv[0]);
	rush_i18n_init();

	format = getenv("RUSHLAST_FORMAT");
	if (!format)
		format = default_format;

	if (parseopt_getopt(&po, argc, argv) == OPT_ERR)
		error(1, errno, "%s", _("parseopt_getopt failed"));
	parseopt_argv(&po, &argc, &argv);
	parseopt_free(&po);

	if (format[0] == '@')
		format = rush_read_format(format + 1);
	form = rushdb_compile_format(format);
	if (!form)
		error(1, 0, _("invalid format: %s"), rushdb_error_string);

	switch (rushdb_open(base_name, 0)) {
	case rushdb_result_ok:
		break;

	case rushdb_result_eof:
		exit(0);

	case rushdb_result_fail:
		error(1, errno, _("cannot open database file %s"), base_name);
	}

	if (display_header)
		rushdb_print_header(form);
	if (!forward)
		rushdb_backward_direction();
	i = 0;
	while (rush_wtmp_read(&wtmp) == 0) {
		if (want_record(wtmp, argc, argv)) {
			rushdb_print(form, wtmp, 1);
			if (count && ++i == count)
				break;
		}
		free(wtmp);
	}

	rushdb_close();

	exit(0);
}
