/*
 * Copyright (c) 1991, 1992 Paul Kranenburg <pk@cs.few.eur.nl>
 * Copyright (c) 1993 Branko Lankester <branko@hacktic.nl>
 * Copyright (c) 1993-1996 Rick Sladkey <jrs@world.std.com>
 * Copyright (c) 1996-1999 Wichert Akkerman <wichert@cistron.nl>
 * Copyright (c) 2007 Roland McGrath <roland@redhat.com>
 * Copyright (c) 2011-2012 Denys Vlasenko <vda.linux@googlemail.com>
 * Copyright (c) 2010-2015 Dmitry V. Levin <ldv@strace.io>
 * Copyright (c) 2014-2021 The strace developers.
 * All rights reserved.
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

#include "defs.h"

static void
printargv(struct tcb *const tcp, kernel_ulong_t addr)
{
	if (!addr || !verbose(tcp)) {
		printaddr(addr);
		return;
	}

	const unsigned int wordsize = current_wordsize;
	kernel_ulong_t prev_addr = 0;
	unsigned int n = 0;

	for (;; prev_addr = addr, addr += wordsize, ++n) {
		union {
			unsigned int w32;
			kernel_ulong_t wl;
			char data[sizeof(kernel_ulong_t)];
		} cp;

		if (addr < prev_addr || umoven(tcp, addr, wordsize, cp.data)) {
			if (n == 0) {
				printaddr(addr);
				return;
			}
			tprint_array_next();
			tprint_more_data_follows();
			printaddr_comment(addr);
			break;
		}

		const kernel_ulong_t word = (wordsize == sizeof(cp.w32))
					    ? (kernel_ulong_t) cp.w32 : cp.wl;
		if (n == 0)
			tprint_array_begin();
		if (word == 0)
			break;
		if (n != 0)
			tprint_array_next();

		if (abbrev(tcp) && n >= max_strlen) {
			tprint_more_data_follows();
			break;
		}

		printstr(tcp, word);
	}

	tprint_array_end();
}

static void
printargc(struct tcb *const tcp, kernel_ulong_t addr)
{
	printaddr(addr);

	if (!addr || !verbose(tcp))
		return;

	const unsigned int wordsize = current_wordsize;
	kernel_ulong_t prev_addr = 0;
	unsigned int n;

	for (n = 0; addr > prev_addr; prev_addr = addr, addr += wordsize, ++n) {
		kernel_ulong_t word = 0;
		if (umoven(tcp, addr, wordsize, &word)) {
			if (n == 0)
				return;

			addr = 0;
			break;
		}
		if (word == 0)
			break;
	}
	tprintf_comment("%u var%s%s",
			n, n == 1 ? "" : "s",
			addr < prev_addr ? ", unterminated" : "");
}

static void
decode_execve(struct tcb *tcp, const unsigned int index)
{
	/* pathname */
	printpath(tcp, tcp->u_arg[index + 0]);
	tprint_arg_next();

	/* argv */
	printargv(tcp, tcp->u_arg[index + 1]);
	tprint_arg_next();

	/* envp */
	(abbrev(tcp) ? printargc : printargv) (tcp, tcp->u_arg[index + 2]);
}

SYS_FUNC(execve)
{
	decode_execve(tcp, 0);

	return RVAL_DECODED;
}

SYS_FUNC(execveat)
{
	/* dirfd */
	print_dirfd(tcp, tcp->u_arg[0]);
	tprint_arg_next();

	/* pathname, argv, envp */
	decode_execve(tcp, 1);
	tprint_arg_next();

	/* flags */
	printflags(at_flags, tcp->u_arg[4], "AT_???");

	return RVAL_DECODED;
}

#if defined(SPARC) || defined(SPARC64)
SYS_FUNC(execv)
{
	/* pathname */
	printpath(tcp, tcp->u_arg[0]);
	tprint_arg_next();

	/* argv */
	printargv(tcp, tcp->u_arg[1]);

	return RVAL_DECODED;
}
#endif /* SPARC || SPARC64 */