// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
 *
 */
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/syscall.h>

#include "trace-local.h"

int silence_warnings;
int show_status;

#ifndef gettid
#define gettid() syscall(__NR_gettid)
#endif

void warning(const char *fmt, ...)
{
	va_list ap;

	if (silence_warnings)
		return;

	if (errno)
		perror("trace-cmd");
	errno = 0;

	va_start(ap, fmt);
	fprintf(stderr, "  ");
	vfprintf(stderr, fmt, ap);
	va_end(ap);

	fprintf(stderr, "\n");
}

void *malloc_or_die(unsigned int size)
{
	void *data;

	data = malloc(size);
	if (!data)
		die("malloc");
	return data;
}

/* Same as strtok_r(), but allows empty tokens */
char *strparse(char *str, char delim, char **save)
{
	char *next;

	if (!str) {
		str = *save;
		if ((*save)[0] == '\0')
			return NULL;
	}

	next = strchr(str, delim);
	if (next) {
		*next = '\0';
		*save = next + 1;
	} else {
		*save = str + strlen(str);
	}
	return str;
}

void tracecmd_debug(const char *fmt, ...)
{
	va_list ap;

	if (!tracecmd_get_debug())
		return;

	va_start(ap, fmt);
	printf("[%d] ", (int)gettid());
	vprintf(fmt, ap);
	va_end(ap);
}

static struct trace_log_severity {
	int		id;
	const char	*name;
} log_severity[] = {
	{ .id = TEP_LOG_NONE, .name = "none" },
	{ .id = TEP_LOG_CRITICAL, .name = "crit" },
	{ .id = TEP_LOG_ERROR, .name = "err" },
	{ .id = TEP_LOG_WARNING, .name = "warn" },
	{ .id = TEP_LOG_INFO, .name = "info" },
	{ .id = TEP_LOG_DEBUG, .name = "debug" },
	{ .id = TEP_LOG_ALL, .name = "all" },
};

void trace_set_loglevel(int level)
{
	tracecmd_set_loglevel(level);
	tracefs_set_loglevel(level);
	tep_set_loglevel(level);
}

int trace_set_verbose(char *level)
{
	int id;

	/* Default level is info */
	if (!level)
		level = "info";

	if (isdigit(level[0])) {
		id = atoi(level);
		if (id >= TEP_LOG_NONE) {
			if (id > TEP_LOG_ALL)
				id = TEP_LOG_ALL;
			trace_set_loglevel(id);
			return 0;
		}
	} else {
		int size = ARRAY_SIZE(log_severity);
		int i;

		for (i = 0; i < size; i++) {
			if (!strncmp(level, log_severity[i].name, strlen(log_severity[i].name))) {
				trace_set_loglevel(log_severity[i].id);
				return 0;
			}
		}
	}

	return -1;
}

/**
 * struct command
 * @name command name
 * @run function to execute on command `name`
 */
struct command {
	char *name;
	void (*run)(int argc, char **argv);
};


/**
 * Lookup table that maps command names to functions
 */
struct command commands[] = {
	{"report", trace_report},
	{"snapshot", trace_snapshot},
	{"hist", trace_hist},
	{"mem", trace_mem},
	{"listen", trace_listen},
	{"agent", trace_agent},
	{"setup-guest", trace_setup_guest},
	{"split", trace_split},
	{"restore", trace_restore},
	{"stack", trace_stack},
	{"check-events", trace_check_events},
	{"record", trace_record},
	{"start", trace_start},
	{"set", trace_set},
	{"extract", trace_extract},
	{"stop", trace_stop},
	{"stream", trace_stream},
	{"profile", trace_profile},
	{"restart", trace_restart},
	{"clear", trace_clear},
	{"reset", trace_reset},
	{"stat", trace_stat},
	{"options", trace_option},
	{"show", trace_show},
	{"list", trace_list},
	{"help", trace_usage},
	{"dump", trace_dump},
	{"attach", trace_attach},
	{"convert", trace_convert},
	{"-h", trace_usage},
};

int main (int argc, char **argv)
{
	int i;

	errno = 0;

	if (argc < 2)
		trace_usage(argc, argv);

	for (i = 0; i < ARRAY_SIZE(commands); ++i) {
		if (strcmp(argv[1], commands[i].name) == 0 ){
			commands[i].run(argc, argv);
			goto out;
		}
	}

	/* No valid command found, show help */
	trace_usage(argc, argv);
out:
	exit(0);
}
