#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include "ptrace.h"
#include <asm/unistd.h>

#include "ltrace.h"
#include "options.h"

/* Returns 1 if the sysnum may make a new child to be created
 * (ie, with fork() or clone())
 * Returns 0 otherwise.
 */
int
fork_p(int sysnum) {
	return 0
#if defined(__NR_fork)
		|| (sysnum == __NR_fork)
#endif
#if defined(__NR_clone)
		|| (sysnum == __NR_clone)
#endif
#if defined(__NR_vfork)
		|| (sysnum == __NR_vfork)
#endif
		;
}

/* Returns 1 if the sysnum may make the process exec other program
 */
int
exec_p(int sysnum) {
	return (sysnum == __NR_execve);
}

void
trace_me(void) {
	if (ptrace(PTRACE_TRACEME, 0, 1, 0)<0) {
		perror("PTRACE_TRACEME");
		exit(1);
	}
}

int
trace_pid(pid_t pid) {
	if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) {
		return -1;
	}
	return 0;
}

void
untrace_pid(pid_t pid) {
	ptrace(PTRACE_DETACH, pid, 1, 0);
}

void
continue_after_signal(pid_t pid, int signum) {
	/* We should always trace syscalls to be able to control fork(), clone(), execve()... */
	ptrace(PTRACE_SYSCALL, pid, 0, signum);
}

void
continue_process(pid_t pid) {
	continue_after_signal(pid, 0);
}

void
continue_enabling_breakpoint(pid_t pid, struct breakpoint * sbp) {
	enable_breakpoint(pid, sbp);
	continue_process(pid);
}

void
continue_after_breakpoint(struct process *proc, struct breakpoint * sbp) {
	if (sbp->enabled) disable_breakpoint(proc->pid, sbp);
	set_instruction_pointer(proc, sbp->addr);
	if (sbp->enabled == 0) {
		continue_process(proc->pid);
	} else {
		proc->breakpoint_being_enabled = sbp;
#ifdef __sparc__
		continue_process(proc->pid);
#else
		ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0);
#endif
	}
}

int
umovestr(struct process * proc, void * addr, int len, void * laddr) {
	union { long a; char c[sizeof(long)]; } a;
	int i;
	int offset=0;

	while(offset<len) {
		a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr+offset, 0);
		for(i=0; i<sizeof(long); i++) {
			if (a.c[i] && offset+i < len) {
				*(char *)(laddr+offset+i) = a.c[i];
			} else {
				*(char *)(laddr+offset+i) = '\0';
				return 0;
			}
		}
		offset += sizeof(long);
	}
	*(char *)(laddr+offset) = '\0';
	return 0;
}
