Add FreeBSD support
diff --git a/strace.c b/strace.c
index f96eaa0..3e4b4a6 100644
--- a/strace.c
+++ b/strace.c
@@ -44,9 +44,12 @@
 #include <grp.h>
 #include <string.h>
 
+#ifdef USE_PROCFS
+#include <poll.h>
+#endif
+
 #ifdef SVR4
 #include <sys/stropts.h>
-#include <poll.h>
 #ifdef HAVE_MP_PROCFS
 #include <sys/uio.h>
 #endif
@@ -87,7 +90,7 @@
 #endif /* !__STDC__ */
 #endif /* !HAVE_SIG_ATOMIC_T */
 
-#ifdef SVR4
+#ifdef USE_PROCFS
 
 static struct tcb *pfd2tcb P((int pfd));
 static void reaper P((int sig));
@@ -115,7 +118,7 @@
 #else
 #define POLLWANT	POLLPRI
 #endif
-#endif /* SVR4 */
+#endif /* USE_PROCFS */
 
 static void
 usage(ofp, exitval)
@@ -334,19 +337,19 @@
 		tcp->outf = outf;
 		if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED))
 			continue;
-#ifdef SVR4
+#ifdef USE_PROCFS
 		if (proc_open(tcp, 1) < 0) {
 			fprintf(stderr, "trouble opening proc file\n");
 			droptcb(tcp);
 			continue;
 		}
-#else /* !SVR4 */
+#else /* !USE_PROCFS */
 		if (ptrace(PTRACE_ATTACH, tcp->pid, (char *) 1, 0) < 0) {
 			perror("attach: ptrace(PTRACE_ATTACH, ...)");
 			droptcb(tcp);
 			continue;
 		}
-#endif /* !SVR4 */
+#endif /* !USE_PROCFS */
 		if (!qflag)
 			fprintf(stderr,
 				"Process %u attached - interrupt to quit\n",
@@ -408,8 +411,8 @@
 			exit(1);
 			break;
 		case 0: {
-#ifdef SVR4
-			if (outf != stderr) close (fileno (outf));
+#ifdef USE_PROCFS
+		        if (outf != stderr) close (fileno (outf));
 #ifdef MIPS
 			/* Kludge for SGI, see proc_open for details. */
 			sa.sa_handler = foobar;
@@ -417,8 +420,12 @@
 			sigemptyset(&sa.sa_mask);
 			sigaction(SIGINT, &sa, NULL);
 #endif /* MIPS */
+#ifndef FREEBSD
 			pause();
-#else /* !SVR4 */
+#else /* FREEBSD */
+			kill(getpid(), SIGSTOP); /* stop HERE */
+#endif /* FREEBSD */			
+#else /* !USE_PROCFS */
 			if (outf!=stderr)	
 				close(fileno (outf));
 
@@ -459,7 +466,7 @@
 			}
 			else
 				setreuid(run_uid, run_uid);
-#endif /* !SVR4 */
+#endif /* !USE_PROCFS */
 
 			execv(pathname, &argv[optind]);
 			perror("strace: exec");
@@ -472,16 +479,16 @@
 				cleanup();
 				exit(1);
 			}
-#ifdef SVR4
+#ifdef USE_PROCFS
 			if (proc_open(tcp, 0) < 0) {
 				fprintf(stderr, "trouble opening proc file\n");
 				cleanup();
 				exit(1);
 			}
-#endif /* SVR4 */
-#ifndef SVR4
+#endif /* USE_PROCFS */
+#ifndef USE_PROCFS
 			fake_execve(tcp, pathname, &argv[optind], environ);
-#endif
+#endif /* !USE_PROCFS */
 			break;
 		}
 	}
@@ -512,10 +519,10 @@
 	sigaction(SIGQUIT, &sa, NULL);
 	sigaction(SIGPIPE, &sa, NULL);
 	sigaction(SIGTERM, &sa, NULL);
-#ifdef SVR4
+#ifdef USE_PROCFS
 	sa.sa_handler = reaper;
 	sigaction(SIGCHLD, &sa, NULL);
-#endif /* SVR4 */
+#endif /* USE_PROCFS */
 
 	if (trace() < 0)
 		exit(1);
@@ -572,7 +579,7 @@
 	return NULL;
 }
 
-#ifdef SVR4
+#ifdef USE_PROCFS
 int
 proc_open(tcp, attaching)
 struct tcb *tcp;
@@ -580,9 +587,11 @@
 {
 	char proc[32];
 	long arg;
+#ifdef SVR4
 	sysset_t sc_enter, sc_exit;
 	sigset_t signals;
 	fltset_t faults;
+#endif
 #ifndef HAVE_POLLABLE_PROCFS
 	static int last_pfd;
 #endif
@@ -630,8 +639,13 @@
 	}
 #else
 	/* Open the process pseudo-file in /proc. */
+#ifndef FREEBSD
 	sprintf(proc, "/proc/%d", tcp->pid);
 	if ((tcp->pfd = open(proc, O_RDWR|O_EXCL)) < 0) {
+#else /* FREEBSD */
+	sprintf(proc, "/proc/%d/mem", tcp->pid);
+	if ((tcp->pfd = open(proc, O_RDWR)) < 0) {
+#endif /* FREEBSD */
 		perror("strace: open(\"/proc/...\", ...)");
 		return -1;
 	}
@@ -644,6 +658,21 @@
 		return -1;
 	}
 #endif
+#ifdef FREEBSD
+	sprintf(proc, "/proc/%d/regs", tcp->pid);
+	if ((tcp->pfd_reg = open(proc, O_RDONLY)) < 0) {
+		perror("strace: open(\"/proc/.../regs\", ...)");
+		return -1;
+	}
+	if (cflag) {
+		sprintf(proc, "/proc/%d/status", tcp->pid);
+		if ((tcp->pfd_status = open(proc, O_RDONLY)) < 0) {
+			perror("strace: open(\"/proc/.../status\", ...)");
+			return -1;
+		}
+	} else
+		tcp->pfd_status = -1;
+#endif /* FREEBSD */
 	rebuild_pollv();
 	if (!attaching) {
 		/*
@@ -655,15 +684,21 @@
 				perror("strace: PIOCSTATUS");
 				return -1;
 			}
+#ifndef FREEBSD			
 			if (tcp->status.PR_FLAGS & PR_ASLEEP)
-				break;
+#else
+			if (tcp->status.state == 1)
+#endif
+			    break;
 		}
 	}
+#ifndef FREEBSD
 	/* Stop the process so that we own the stop. */
 	if (IOCTL(tcp->pfd, PIOCSTOP, (char *)NULL) < 0) {
 		perror("strace: PIOCSTOP");
 		return -1;
 	}
+#endif	
 #ifdef PIOCSET
 	/* Set Run-on-Last-Close. */
 	arg = PR_RLC;
@@ -678,6 +713,7 @@
 		return -1;
 	}
 #else  /* !PIOCSET */
+#ifndef FREEBSD	
 	if (ioctl(tcp->pfd, PIOCSRLC) < 0) {
 		perror("PIOCSRLC");
 		return -1;
@@ -686,7 +722,20 @@
 		perror("PIOC{S,R}FORK");
 		return -1;
 	}
+#else /* FREEBSD */
+	/* just unset the PF_LINGER flag for the Run-on-Last-Close. */
+	if (ioctl(tcp->pfd, PIOCGFL, &arg) < 0) {
+	        perror("PIOCGFL");
+	        return -1;
+	}
+	arg &= ~PF_LINGER;
+	if (ioctl(tcp->pfd, PIOCSFL, arg) < 0) {
+	        perror("PIOCSFL");
+	        return -1;
+	}
+#endif /* FREEBSD */
 #endif /* !PIOCSET */
+#ifndef FREEBSD
 	/* Enable all syscall entries. */
 	prfillset(&sc_enter);
 	if (IOCTL(tcp->pfd, PIOCSENTRY, &sc_enter) < 0) {
@@ -711,6 +760,14 @@
 		perror("PIOCSFAULT");
 		return -1;
 	}
+#else /* FREEBSD */
+	/* set events flags. */
+	arg = S_SIG | S_SCE | S_SCX ;
+	if(ioctl(tcp->pfd, PIOCBIS, arg) < 0) {
+		perror("PIOCBIS");
+		return -1;
+	}
+#endif /* FREEBSD */
 	if (!attaching) {
 #ifdef MIPS
 		/*
@@ -719,13 +776,19 @@
 		 */
 		kill(tcp->pid, SIGINT);
 #else /* !MIPS */
+#ifdef PRSABORT	
 		/* The child is in a pause(), abort it. */
 		arg = PRSABORT;
 		if (IOCTL (tcp->pfd, PIOCRUN, &arg) < 0) {
 			perror("PIOCRUN");
 			return -1;
 		}
-#endif /* !MIPS */
+#endif		
+#endif /* !MIPS*/
+#ifdef FREEBSD
+		/* wake up the child if it received the SIGSTOP */
+		kill(tcp->pid, SIGCONT);
+#endif		
 		for (;;) {
 			/* Wait for the child to do something. */
 			if (IOCTL_WSTOP (tcp) < 0) {
@@ -733,22 +796,40 @@
 				return -1;
 			}
 			if (tcp->status.PR_WHY == PR_SYSENTRY) {
-#ifdef HAVE_PR_SYSCALL
-				int scno = tcp->status.pr_syscall;
-#else /* !HAVE_PR_SYSCALL */
-				int scno = tcp->status.PR_WHAT;
-#endif /* !HAVE_PR_SYSCALL */
-				if (scno == SYS_execve)
+				tcp->flags &= ~TCB_INSYSCALL;
+				get_scno(tcp);
+				if (tcp->scno == SYS_execve)
 					break;
 			}
 			/* Set it running: maybe execve will be next. */
+#ifndef FREEBSD
 			arg = 0;
 			if (IOCTL(tcp->pfd, PIOCRUN, &arg) < 0) {
+#else /* FREEBSD */
+			if (IOCTL(tcp->pfd, PIOCRUN, 0) < 0) {
+#endif /* FREEBSD */			  
 				perror("PIOCRUN");
 				return -1;
 			}
+#ifdef FREEBSD
+			/* handle the case where we "opened" the child before
+			   it did the kill -STOP */
+			if (tcp->status.PR_WHY == PR_SIGNALLED &&
+			    tcp->status.PR_WHAT == SIGSTOP)
+			        kill(tcp->pid, SIGCONT);
+#endif			
 		}
+#ifndef FREEBSD
 	}
+#else /* FREEBSD */
+	} else {
+	       /* little hack to show the current syscall */
+	       IOCTL_STATUS(tcp);
+	       tcp->flags &= ~TCB_INSYSCALL;
+	       tcp->status.why = PR_SYSENTRY;
+	       trace_syscall(tcp);
+	}
+#endif /* FREEBSD */
 #ifndef HAVE_POLLABLE_PROCFS
 	if (proc_poll_pipe[0] != -1)
 		proc_poller(tcp->pfd);
@@ -762,7 +843,7 @@
 	return 0;
 }
 
-#endif /* SVR4 */
+#endif /* USE_PROCFS */
 
 static struct tcb *
 pid2tcb(pid)
@@ -780,7 +861,7 @@
 	return NULL;
 }
 
-#ifdef SVR4
+#ifdef USE_PROCFS
 
 static struct tcb *
 pfd2tcb(pfd)
@@ -798,7 +879,7 @@
 	return NULL;
 }
 
-#endif /* SVR4 */
+#endif /* USE_PROCFS */
 
 void
 droptcb(tcp)
@@ -812,7 +893,17 @@
 	if (tcp->pfd != -1) {
 		close(tcp->pfd);
 		tcp->pfd = -1;
-#ifdef SVR4
+#ifdef FREEBSD
+		if (tcp->pfd_reg != -1) {
+		        close(tcp->pfd_reg);
+		        tcp->pfd_reg = -1;
+		}
+		if (tcp->pfd_status != -1) {
+			close(tcp->pfd_status);
+			tcp->pfd_status = -1;
+		}
+#endif /* !FREEBSD */		
+#ifdef USE_PROCFS
 		rebuild_pollv();
 #endif
 	}
@@ -827,7 +918,7 @@
 	tcp->outf = 0;
 }
 
-#ifndef SVR4
+#ifndef USE_PROCFS
 
 static int
 resume(tcp)
@@ -852,7 +943,7 @@
 	return 0;
 }
 
-#endif /* !SVR4 */
+#endif /* !USE_PROCFS */
 
 /* detach traced process; continue with sig */
 
@@ -934,10 +1025,10 @@
 		perror("detach: ptrace(PTRACE_DETACH, ...)");
 #endif /* SUNOS4 */
 
-#ifndef SVR4
+#ifndef USE_PROCFS
 	if (waiting_parent(tcp))
 		error = resume(tcp->parent);
-#endif /* !SVR4 */
+#endif /* !USE_PROCFS */
 
 	if (!qflag)
 		fprintf(stderr, "Process %u detached\n", tcp->pid);
@@ -946,7 +1037,7 @@
 	return error;
 }
 
-#ifdef SVR4
+#ifdef USE_PROCFS
 
 static void
 reaper(sig)
@@ -966,7 +1057,7 @@
 	}
 }
 
-#endif /* SVR4 */
+#endif /* USE_PROCFS */
 
 static void
 cleanup()
@@ -1054,7 +1145,7 @@
 
 #endif /* HAVE_STRSIGNAL */
 
-#ifdef SVR4
+#ifdef USE_PROCFS
 
 static void
 rebuild_pollv()
@@ -1141,6 +1232,9 @@
 	int i;
 	int n;
 	struct rlimit rl;
+#ifdef FREEBSD
+	struct procfs_status pfs;
+#endif /* FREEBSD */
 
 	switch (fork()) {
 	case -1:
@@ -1180,7 +1274,11 @@
 	pollinfo.fd = pfd;
 	pollinfo.pid = getpid();
 	for (;;) {
-		if (ioctl(pfd, PIOCWSTOP, NULL) < 0)
+#ifndef FREEBSD
+	        if (ioctl(pfd, PIOCWSTOP, NULL) < 0)
+#else /* FREEBSD */
+	        if (ioctl(pfd, PIOCWSTOP, &pfs) < 0)
+#endif /* FREEBSD */
 		{
 			switch (errno) {
 			case EINTR:
@@ -1331,7 +1429,17 @@
 	FOUND:
 		/* Get the status of the process. */
 		if (!interrupted) {
+#ifndef FREEBSD
 			ioctl_result = IOCTL_WSTOP (tcp);
+#else /* FREEBSD */
+			/* Thanks to some scheduling mystery, the first poller
+			   sometimes waits for the already processed end of fork
+			   event. Doing a non blocking poll here solves the problem. */
+			if (proc_poll_pipe[0] != -1)
+				ioctl_result = IOCTL_STATUS (tcp);
+			else
+			  	ioctl_result = IOCTL_WSTOP (tcp);
+#endif /* FREEBSD */			  
 			ioctl_errno = errno;
 #ifndef HAVE_POLLABLE_PROCFS
 			if (proc_poll_pipe[0] != -1) {
@@ -1354,6 +1462,9 @@
 			case EINTR:
 			case EBADF:
 				continue;
+#ifdef FREEBSD
+			case ENOTTY:
+#endif			  
 			case ENOENT:
 				droptcb(tcp);
 				continue;
@@ -1371,15 +1482,27 @@
 
 		if (cflag) {
 			struct timeval stime;
+#ifdef FREEBSD
+			char buf[1024];
+			int len;
 
+			if ((len = pread(tcp->pfd_status, buf, sizeof(buf) - 1, 0)) > 0) {
+				buf[len] = '\0';
+				sscanf(buf,
+				       "%*s %*d %*d %*d %*d %*d,%*d %*s %*d,%*d %*d,%*d %ld,%ld",
+				       &stime.tv_sec, &stime.tv_usec);
+			} else
+				stime.tv_sec = stime.tv_usec = 0;
+#else /* !FREEBSD */			
 			stime.tv_sec = tcp->status.pr_stime.tv_sec;
 			stime.tv_usec = tcp->status.pr_stime.tv_nsec/1000;
+#endif /* !FREEBSD */
 			tv_sub(&tcp->dtime, &stime, &tcp->stime);
 			tcp->stime = stime;
 		}
-
 		what = tcp->status.PR_WHAT;
 		switch (tcp->status.PR_WHY) {
+#ifndef FREEBSD
 		case PR_REQUESTED:
 			if (tcp->status.PR_FLAGS & PR_ASLEEP) {
 				tcp->status.PR_WHY = PR_SYSENTRY;
@@ -1389,6 +1512,7 @@
 				}
 			}
 			break;
+#endif /* !FREEBSD */
 		case PR_SYSENTRY:
 #ifdef POLL_HACK
 		        in_syscall = tcp;
@@ -1414,13 +1538,21 @@
 				printtrailer(tcp);
 			}
 			break;
+#ifdef FREEBSD
+		case 0: /* handle case we polled for nothing */
+		  	continue;
+#endif			
 		default:
 			fprintf(stderr, "odd stop %d\n", tcp->status.PR_WHY);
 			exit(1);
 			break;
 		}
 		arg = 0;
+#ifndef FREEBSD		
 		if (IOCTL (tcp->pfd, PIOCRUN, &arg) < 0) {
+#else		  
+		if (IOCTL (tcp->pfd, PIOCRUN, 0) < 0) {
+#endif		  
 			perror("PIOCRUN");
 			exit(1);
 		}
@@ -1428,7 +1560,7 @@
 	return 0;
 }
 
-#else /* !SVR4 */
+#else /* !USE_PROCFS */
 
 static int
 trace()
@@ -1667,7 +1799,7 @@
 	return 0;
 }
 
-#endif /* !SVR4 */
+#endif /* !USE_PROCFS */
 
 static int curcol;