Index: ps.1
===================================================================
RCS file: /cvs/src/bin/ps/ps.1,v
retrieving revision 1.65
diff -u -r1.65 ps.1
--- ps.1	31 May 2007 19:19:15 -0000	1.65
+++ ps.1	13 Nov 2007 20:45:09 -0000
@@ -88,6 +88,8 @@
 .Dq sh .
 .It Fl e
 Display the environment as well.
+.It Fl H
+Display the processes in a tree hierarchy.
 .It Fl h
 Repeat the information header as often as necessary to guarantee one
 header per page of information.
Index: ps.c
===================================================================
RCS file: /cvs/src/bin/ps/ps.c,v
retrieving revision 1.43
diff -u -r1.43 ps.c
--- ps.c	1 Sep 2007 19:32:19 -0000	1.43
+++ ps.c	13 Nov 2007 20:45:10 -0000
@@ -81,13 +81,15 @@
 int	totwidth;		/* calculated width of requested variables */

 int	ncpu = 1;
+int	xflg = 0;

 int	needcomm, needenv, commandonly;

-enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
+enum sort { DEFAULT, SORTMEM, SORTCPU, SORTFOREST } sortby = DEFAULT;

 static char	*kludge_oldps_options(char *);
 static int	 pscomp(const void *, const void *);
+static void	 printforest(struct kinfo_proc2 **, const int, struct varent *);
 static void	 scanvars(void);
 static void	 usage(void);

@@ -113,7 +115,7 @@
 	pid_t pid;
 	uid_t uid;
 	int all, ch, flag, i, fmt, lineno, nentries, mib[6];
-	int prtheader, wflag, kflag, what, Uflag, xflg;
+	int prtheader, wflag, kflag, what, Uflag;
 	char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX];
 	size_t size;

@@ -128,13 +130,13 @@
 	if (argc > 1)
 		argv[1] = kludge_oldps_options(argv[1]);

-	all = fmt = prtheader = wflag = kflag = Uflag = xflg = 0;
+	all = fmt = prtheader = wflag = kflag = Uflag = 0;
 	pid = -1;
 	uid = 0;
 	ttydev = NODEV;
 	memf = nlistf = swapf = NULL;
 	while ((ch = getopt(argc, argv,
-	    "acCeghjkLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1)
+	    "acCegHhjkLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1)
 		switch((char)ch) {
 		case 'a':
 			all = 1;
@@ -150,6 +152,9 @@
 			break;
 		case 'g':
 			break;			/* no-op */
+		case 'H':
+			sortby = SORTFOREST;
+			break;
 		case 'h':
 			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
 			break;
@@ -366,6 +371,12 @@
 	for (i = 0; i < nentries; i++)
 		kinfo[i] = &kp[i];
 	qsort(kinfo, nentries, sizeof(*kinfo), pscomp);
+
+	if (sortby == SORTFOREST) {
+		printforest(kinfo, nentries, vent);
+		exit(eval);
+	}
+
 	/*
 	 * for each proc, call each variable output function.
 	 */
@@ -388,6 +399,99 @@
 	exit(eval);
 }

+/*
+ * Print out processes in tree format. Begins with kinfo[0] and
+ * searches on p_ppid to find children.
+ */
+static void
+printforest(struct kinfo_proc2 **kinfo, const int nentries,
+	    struct varent *vent)
+{
+	int *parent, *hasfollower;
+	int i, j, pid, gotchild, width, depth = 0;
+
+	if ((parent = calloc(nentries, sizeof(int))) == NULL)
+		err(1, "failed to allocate memory for parent pointers");
+	if ((hasfollower = calloc(nentries, sizeof(int))) == NULL)
+		err(1, "failed to allocate memory for follower pointers");
+
+	for (i = 0; i < nentries; i++) {
+		if (xflg == 0 && ((int)kinfo[i]->p_tdev == NODEV ||
+		    (kinfo[i]->p_flag & P_CONTROLT) == 0))
+			continue;
+
+		if ((pid = kinfo[i]->p_pid) == 0)
+			continue;
+
+		/* Print process. */
+		for (vent = vhead; vent; vent = vent->next) {
+			width = 0;
+			/* Intercept COMMAND column for our ASCII art. */
+			if (depth > 0 && vent->var->oproc == command) {
+				for (j = 1; j < depth; j++) {
+					putchar(' ');
+					putchar(hasfollower[j] ? '|' : ' ');
+					putchar(' ');
+					putchar(' ');
+				}
+				putchar(' ');
+				putchar('\\');
+				putchar('_');
+				putchar(' ');
+				if (vent->var->width > 4 * depth) {
+					width = vent->var->width;
+					vent->var->width -= 4 * depth;
+				}
+			}
+			(vent->var->oproc)(kinfo[i], vent);
+			if (width != 0)
+				vent->var->width = width;
+			if (vent->next != NULL)
+				putchar(' ');
+		}
+		putchar('\n');
+		kinfo[i]->p_pid = 0;
+		if (i + 1 >= nentries ||
+		    kinfo[i]->p_ppid != kinfo[i + 1]->p_ppid)
+			hasfollower[depth] = 0;
+
+		if (pid == 1)
+			continue; /* Don't let init be the root node. */
+
+		/* Look for a child. */
+		gotchild = 0;
+		for (j = 0; j < nentries; j++) {
+			if (kinfo[j]->p_ppid == pid) { /* Found a child. */
+				depth++;
+				parent[depth] = i;
+				hasfollower[depth] = 0;
+				if (j + 1 < nentries &&
+				    kinfo[j + 1]->p_ppid == pid)
+					hasfollower[depth] = 1;
+				i = j - 1;
+				gotchild = 1;
+				break;
+			}
+			if (kinfo[j]->p_ppid > pid)
+				break;
+		}
+		if (gotchild)
+			continue;
+
+		/* No more siblings, walk up to parent. */
+		while (depth > 0) {
+			if (i + 1 < nentries &&
+			    kinfo[i]->p_ppid == kinfo[i + 1]->p_ppid)
+				break;
+			i = parent[depth];
+			depth--;
+		}
+	}
+
+	free(parent);
+	free(hasfollower);
+}
+
 static void
 scanvars(void)
 {
@@ -415,6 +519,8 @@
 	int i;
 #define VSIZE(k) ((k)->p_vm_dsize + (k)->p_vm_ssize + (k)->p_vm_tsize)

+	if (sortby == SORTFOREST && (i = kp1->p_ppid - kp2->p_ppid) != 0)
+		return (i);
 	if (sortby == SORTCPU && (i = getpcpu(kp2) - getpcpu(kp1)) != 0)
 		return (i);
 	if (sortby == SORTMEM && (i = VSIZE(kp2) - VSIZE(kp1)) != 0)
@@ -487,10 +593,10 @@
 }

 static void
-usage(void)
+	usage(void)
 {
 	(void)fprintf(stderr,
-	    "usage: %s [-aCcehjkLlmrSTuvwx] [-M core] [-N system] [-O fmt]
[-o fmt] [-p pid]\n",
+	    "usage: %s [-aCceHhjkLlmrSTuvwx] [-M core] [-N system] [-O fmt]
[-o fmt] [-p pid]\n",
 	    __progname);	
 	(void)fprintf(stderr,
 	    "%-*s[-t tty] [-U username] [-W swap]\n", strlen(__progname) + 8, "");