text: /*	$OpenBSD: optree.c,v 1.6 2009/01/14 21:05:53 fgsch Exp $	*/
  
  /*
   * Copyright (c) 2007 Federico G. Schwindt <fgsch@openbsd.org>
   *
   * Permission to use, copy, modify, and distribute this software for
   * any purpose with or without fee is hereby granted, provided that
   * the above copyright notice and this permission notice appear in all
   * copies.
   *
   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
   * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
   * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
   * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
   * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
   * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
   * PERFORMANCE OF THIS SOFTWARE.
   */
  
  #include <sys/ioctl.h>
  #include <err.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <stdio.h>
  #include <string.h>
  #include <unistd.h>
  
  #include <machine/openpromio.h>
  
  #include "defs.h"
  
  extern  char *path_openprom;
  
  static void
  op_print(struct opiocdesc *opio, int depth)
  {
  	char *p;
  	int i, special;
  
  	opio->op_name[opio->op_namelen] = '\0';
  	printf("%*s%s: ", depth * 4, " ", opio->op_name);
  	if (opio->op_buflen > 0) {
  		opio->op_buf[opio->op_buflen] = '\0';
  		special = 0;
  
  		/*
  		 * XXX This allows multiple NUL characters within
  		 * string-valued properties, which may not be what we
  		 * want.  But on macppc we have string-values
  		 * properties that end with multiple NUL characters,
  		 * and the serial number has them embedded within the
  		 * string.
  		 */
  		if (opio->op_buf[0] != '\0') {
  			for (i = 0; i < opio->op_buflen; i++) {
  				p = &opio->op_buf[i];
  				if (*p >= ' ' && *p <= '~')
  					continue;
  				if (*p == '\0') {
  					if (i + 1 < opio->op_buflen)
  						p++;
  					if (*p >= ' ' && *p <= '~')
  						continue;
  					if (*p == '\0')
  						continue;
  				}
  
  				special = 1;
  				break;
  			}
  		} else {
  			if (opio->op_buflen > 1)
  				special = 1;
  		}
  
  		if (special) {
  			for (i = 0; opio->op_buflen - i >= sizeof(int);
  			    i += sizeof(int)) {
  				if (i)
  					printf(".");
  				printf("%08x", *(int *)(long)&opio->op_buf[i]);
  			}
  			if (i < opio->op_buflen) {
  				if (i)
  					printf(".");
  				for (; i < opio->op_buflen; i++) {
  					printf("%02x",
  					    *(u_char *)&opio->op_buf[i]);
  				}
  			}
  		} else {
  			for (i = 0; i < opio->op_buflen;
  			    i += strlen(&opio->op_buf[i]) + 1) {
  				if (i && strlen(&opio->op_buf[i]) == 0)
  					continue;
  				if (i)
  					printf(" + ");
  				printf("'%s'", &opio->op_buf[i]);
  			}
  		}
  	} else if(opio->op_buflen < 0)
  		printf("too large");
  	printf("\n");
  }
  
  void
  op_nodes(int fd, int node, int depth)
  {
  	char op_buf[BUFSIZE * 8];
  	char op_name[BUFSIZE];
  	struct opiocdesc opio;
  
  	opio.op_nodeid = node;
  	opio.op_buf = op_buf;
  	opio.op_name = op_name;
  
  	if (!node) {
  		if (ioctl(fd, OPIOCGETNEXT, &opio) < 0)
  			err(1, "OPIOCGETNEXT");
  		node = opio.op_nodeid;
  	} else
  		printf("\n%*s", depth * 4, " ");
  
  	printf("Node 0x%x\n", node);
  
  	for (;;) {
  		opio.op_buflen = sizeof(op_buf);
  		opio.op_namelen = sizeof(op_name);
  
  		/* Get the next property. */
  		if (ioctl(fd, OPIOCNEXTPROP, &opio) < 0)
  			err(1, "OPIOCNEXTPROP");
  
  		op_buf[opio.op_buflen] = '\0';
  		(void)strlcpy(op_name, op_buf, sizeof(op_name));
  		opio.op_namelen = strlen(op_name);
  
  		/* If it's the last, punt. */
  		if (opio.op_namelen == 0)
  			break;
  
  		bzero(op_buf, sizeof(op_buf));
  		opio.op_buflen = sizeof(op_buf);
  
  		/* And its value. */
  		if (ioctl(fd, OPIOCGET, &opio) < 0) {
  			if (errno != ENOMEM)
  				err(1, "OPIOCGET");
  
  			opio.op_buflen = -1;	/* for op_print */
  		}
  
  		op_print(&opio, depth + 1);
  	}
  
  	/* Get next child. */
  	if (ioctl(fd, OPIOCGETCHILD, &opio) < 0)
  		err(1, "OPIOCGETCHILD");
  	if (opio.op_nodeid)
  		op_nodes(fd, opio.op_nodeid, depth + 1);
  
  	/* Get next node/sibling. */
  	opio.op_nodeid = node;
  	if (ioctl(fd, OPIOCGETNEXT, &opio) < 0)
  		err(1, "OPIOCGETNEXT");
  	if (opio.op_nodeid)
  		op_nodes(fd, opio.op_nodeid, depth);
  }
  
  void
  op_tree(void)
  {
  	int fd;
  
  	if ((fd = open(path_openprom, O_RDONLY, 0640)) < 0)
  		err(1, "open: %s", path_openprom);
  	op_nodes(fd, 0, 0);
  	(void)close(fd);
  }
  