/* tools.c - core analysis suite
 *
 * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
 * Copyright (C) 2002-2017 David Anderson
 * Copyright (C) 2002-2017 Red Hat, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include "defs.h"
#include <ctype.h>

static void print_number(struct number_option *, int, int);
static long alloc_hq_entry(void);
struct hq_entry;
static void dealloc_hq_entry(struct hq_entry *);
static void show_options(void);
static void dump_struct_members(struct list_data *, int, ulong);
static void rbtree_iteration(ulong, struct tree_data *, char *);
static void dump_struct_members_for_tree(struct tree_data *, int, ulong);

struct req_entry {
	char *arg, *name, **member;
	int *is_str, *is_ptr;
	ulong *width, *offset;
	int count;
};

static void print_value(struct req_entry *, unsigned int, ulong, unsigned int);
static struct req_entry *fill_member_offsets(char *);
static void dump_struct_members_fast(struct req_entry *, int, ulong);

/*
 *  General purpose error reporting routine.  Type INFO prints the message
 *  and returns.  Type FATAL aborts the command in progress, and longjmps
 *  back to the appropriate recovery location.  If a FATAL occurs during 
 *  program initialization, exit() is called.
 *
 *  The idea is to get the message out so that it is seen by the user
 *  regardless of how the command output may be piped or redirected.
 *  Besides stderr, check whether the output is going to a file or pipe, and
 *  if so, intermingle the error message there as well.
 */
int
__error(int type, char *fmt, ...)
{
	int end_of_line, new_line;
        char buf[BUFSIZE];
	char *spacebuf;
        void *retaddr[NUMBER_STACKFRAMES] = { 0 };
	va_list ap;

	if (CRASHDEBUG(1) || (pc->flags & DROP_CORE)) {
		SAVE_RETURN_ADDRESS(retaddr);
		console("error() trace: %lx => %lx => %lx => %lx\n",
			retaddr[3], retaddr[2], retaddr[1], retaddr[0]);
	}

	va_start(ap, fmt);
	(void)vsnprintf(buf, BUFSIZE, fmt, ap);
        va_end(ap);

	if (!fmt && FATAL_ERROR(type)) {
		fprintf(stdout, "\n");
		clean_exit(1);
	}

	end_of_line = FATAL_ERROR(type) && !(pc->flags & RUNTIME);

	if ((new_line = (buf[0] == '\n')))
		shift_string_left(buf, 1);
	else if (pc->flags & PLEASE_WAIT)
		new_line = TRUE;

	if (type == CONT)
		spacebuf = space(strlen(pc->curcmd));
	else
		spacebuf = NULL;

	if (pc->stdpipe) {
		fprintf(pc->stdpipe, "%s%s%s %s%s", 
			new_line ? "\n" : "", 
			type == CONT ? spacebuf : pc->curcmd, 
			type == CONT ? " " : ":",
			type == WARNING ? "WARNING: " : 
			type == NOTE ? "NOTE: " : "", 
			buf);
		fflush(pc->stdpipe);
	} else { 
		fprintf(stdout, "%s%s%s %s%s", 
			new_line || end_of_line ? "\n" : "",
			type == WARNING ? "WARNING" : 
			type == NOTE ? "NOTE" : 
			type == CONT ? spacebuf : pc->curcmd,
			type == CONT ? " " : ":",
			buf, end_of_line ? "\n" : "");
		fflush(stdout);
	}

        if ((fp != stdout) && (fp != pc->stdpipe) && (fp != pc->tmpfile)) {
                fprintf(fp, "%s%s%s %s", new_line ? "\n" : "",
			type == WARNING ? "WARNING" : 
			type == NOTE ? "NOTE" : 
			type == CONT ? spacebuf : pc->curcmd, 
			type == CONT ? " " : ":",
			buf);
		fflush(fp);
	}

	if ((pc->flags & DROP_CORE) && (type != NOTE)) {
		dump_trace(retaddr);
		SIGACTION(SIGSEGV, SIG_DFL, &pc->sigaction, NULL);
		drop_core("DROP_CORE flag set: forcing a segmentation fault\n");
	}

        switch (type)
        {
        case FATAL:
                if (pc->flags & IN_FOREACH) 
                        RESUME_FOREACH();
		/* FALLTHROUGH */

	case FATAL_RESTART:
                if (pc->flags & RUNTIME) 
                        RESTART();
                else {
			if (REMOTE())
				remote_exit();
                        clean_exit(1);
		}

	default:
        case INFO:
        case NOTE:
	case WARNING:
                return FALSE;
        }
}

/*
 *  Parse a line into tokens, populate the passed-in argv[] array, and return
 *  the count of arguments found.  This function modifies the passed-string 
 *  by inserting a NULL character at the end of each token.  Expressions 
 *  encompassed by parentheses, and strings encompassed by apostrophes, are 
 *  collected into single tokens.
 */
int
parse_line(char *str, char *argv[])
{
	int i, j, k;
    	int string;
	int expression;

	for (i = 0; i < MAXARGS; i++)
		argv[i] = NULL;

	clean_line(str);

        if (str == NULL || strlen(str) == 0)
                return(0);

        i = j = k = 0;
        string = expression = FALSE;

	/*
	 * Special handling for when the first character is a '"'.
	 */
	if (str[0] == '"') {
next:
		do {
			i++;
		} while ((str[i] != NULLCHAR) && (str[i] != '"'));

		switch (str[i])
		{
		case NULLCHAR:
			argv[j] = &str[k];
			return j+1;
		case '"':
			argv[j++] = &str[k+1];
			str[i++] = NULLCHAR;
			if (str[i] == '"') {
				k = i;
				goto next;	
			}
			break;
		}
	} else
		argv[j++] = str;

    	while (TRUE) {
		if (j == MAXARGS)
			error(FATAL, "too many arguments in string!\n");

        	while (str[i] != ' ' && str[i] != '\t' && str[i] != NULLCHAR) {
            		i++;
        	}

	        switch (str[i])
	        {
	        case ' ':
	        case '\t':
	            str[i++] = NULLCHAR;

	            while (str[i] == ' ' || str[i] == '\t') {
	                i++;
	            }
	
	            if (str[i] == '"') {    
	                str[i] = ' ';
	                string = TRUE;
	                i++;
	            }

                    if (!string && str[i] == '(') {     
                        expression = TRUE;
                    }
	
	
	            if (str[i] != NULLCHAR && str[i] != '\n') {
	                argv[j++] = &str[i];
	                if (string) {
	                        string = FALSE;
	                        while (str[i] != '"' && str[i] != NULLCHAR)
	                                i++;
	                        if (str[i] == '"')
	                                str[i] = ' ';
	                }
                        if (expression) {
                                expression = FALSE;
                                while (str[i] != ')' && str[i] != NULLCHAR)
                                        i++;
                        }
	                break;
	            }
	                        /* else fall through */
	        case '\n':
	            str[i] = NULLCHAR;
	                        /* keep falling... */
	        case NULLCHAR:
	            argv[j] = NULLCHAR;
	            return(j);
	        }
    	}  
}

/*
 *  Defuse controversy re: extensions to ctype.h 
 */
int 
whitespace(int c)
{
	return ((c == ' ') ||(c == '\t'));
}

int
ascii(int c)
{
	return ((c >= 0) && ( c <= 0x7f));
}

/*
 *  Strip line-ending whitespace and linefeeds.
 */
char *
strip_line_end(char *line)
{
	strip_linefeeds(line);
	strip_ending_whitespace(line);
	return(line);
}

/*
 *  Strip line-beginning and line-ending whitespace and linefeeds.
 */
char *
clean_line(char *line)
{
	strip_beginning_whitespace(line);
        strip_linefeeds(line);
        strip_ending_whitespace(line);
        return(line);
}

/*
 *  Strip line-ending linefeeds in a string.
 */
char *
strip_linefeeds(char *line)
{
	char *p;

	if (line == NULL || strlen(line) == 0)
		return(line);

	p = &LASTCHAR(line);

	while (*p == '\n') {
		*p = NULLCHAR;
		if (--p < line)
			break; 
	}

	return(line);
}

/*
 *  Strip a specified line-ending character in a string.
 */
char *
strip_ending_char(char *line, char c)
{
        char *p;

        if (line == NULL || strlen(line) == 0)
                return(line);

        p = &LASTCHAR(line);

        if (*p == c)
                *p = NULLCHAR;

        return(line);
}

/*
 *  Strip a specified line-beginning character in a string.
 */
char *
strip_beginning_char(char *line, char c)
{
        if (line == NULL || strlen(line) == 0)
                return(line);

        if (FIRSTCHAR(line) == c)
                shift_string_left(line, 1);

        return(line);
}




/*
 *  Strip line-ending whitespace.
 */
char *
strip_ending_whitespace(char *line)
{
        char *p;

	if (line == NULL || strlen(line) == 0)
                return(line);

        p = &LASTCHAR(line);

        while (*p == ' ' || *p == '\t') {
                *p = NULLCHAR;
                if (p == line)
                        break;
                p--;
        }

        return(line);
}

/*
 *  Strip line-beginning whitespace.
 */
char *
strip_beginning_whitespace(char *line)
{
	char buf[BUFSIZE];
        char *p;

	if (line == NULL || strlen(line) == 0)
                return(line);

	strcpy(buf, line);
	p = &buf[0];
	while (*p == ' ' || *p == '\t')
		p++;
	strcpy(line, p);

        return(line);
}

/*
 *  End line at first comma found.
 */
char *
strip_comma(char *line)
{
	char *p;

	if ((p = strstr(line, ",")))
		*p = NULLCHAR;

	return(line);
}

/*
 *  Strip the 0x from the beginning of a hexadecimal value string.
 */
char *
strip_hex(char *line)
{
	if (STRNEQ(line, "0x")) 
		shift_string_left(line, 2);	

	return(line);
}

/*
 *  Turn a string into upper-case.
 */
char *
upper_case(const char *s, char *buf)
{
	const char *p1;
	char *p2;

	p1 = s;
	p2 = buf;

	while (*p1) {
		*p2 = toupper(*p1);
		p1++, p2++;	
	}

	*p2 = NULLCHAR;

	return(buf);
}

/*
 *  Return pointer to first non-space/tab in a string.
 */
char *
first_nonspace(char *s)
{
        return(s + strspn(s, " \t"));
}

/*
 *  Return pointer to first space/tab in a string.  If none are found,
 *  return a pointer to the string terminating NULL.
 */
char *
first_space(char *s)
{
        return(s + strcspn(s, " \t"));
}

/*
 *  Replace the first space/tab found in a string with a NULL character.
 */
char *
null_first_space(char *s)
{
	char *p1;

	p1 = first_space(s);
	if (*p1)
		*p1 = NULLCHAR;

	return s;
}

/*
 *  Replace any instances of the characters in string c that are found in
 *  string s with the character passed in r.
 */
char *
replace_string(char *s, char *c, char r)
{
	int i, j;

	for (i = 0; s[i]; i++) {
		for (j = 0; c[j]; j++) {
			if (s[i] == c[j])
				s[i] = r;
		}
	}

	return s;
}

void
string_insert(char *insert, char *where)
{
	char *p;

	p = GETBUF(strlen(insert) + strlen(where) + 1);
	sprintf(p, "%s%s", insert, where);
	strcpy(where, p);
	FREEBUF(p);
}

/*
 *  Find the rightmost instance of a substring in a string.
 */
char *
strstr_rightmost(char *s, char *lookfor)
{
	char *next, *last, *p;

	for (p = s, last = NULL; *p; p++) {
		if (!(next = strstr(p, lookfor)))
			break;
		last = p = next;
	}

	return last;
}

/*
 *  Prints a string verbatim, allowing strings with % signs to be displayed
 *  without printf conversions.
 */
void
print_verbatim(FILE *filep, char *line)
{
	int i;

        for (i = 0; i < strlen(line); i++) {
                fputc(line[i], filep);
		fflush(filep);
	}
}

char *
fixup_percent(char *s)
{
	char *p1;

	if ((p1 = strstr(s, "%")) == NULL)
		return s;

	s[strlen(s)+1] = NULLCHAR;
       	memmove(p1+1, p1, strlen(p1));
	*p1 = '%';

	return s;
}

/*
 *  Convert an indeterminate number string to either a hexadecimal or decimal 
 *  long value.  Translate with a bias towards decimal unless HEX_BIAS is set.
 */
ulong
stol(char *s, int flags, int *errptr)
{
	if ((flags & HEX_BIAS) && hexadecimal(s, 0)) 
        	return(htol(s, flags, errptr));
	else {
        	if (decimal(s, 0))
                	return(dtol(s, flags, errptr));
        	else if (hexadecimal(s, 0))
                	return(htol(s, flags, errptr));
	}

	if (!(flags & QUIET))
        	error(INFO, "not a valid number: %s\n", s);

        switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
        {
        case FAULT_ON_ERROR:
               	RESTART();

        case RETURN_ON_ERROR:
		if (errptr)
			*errptr = TRUE;
		break;
        }

	return UNUSED;
}

ulonglong
stoll(char *s, int flags, int *errptr)
{
        if ((flags & HEX_BIAS) && hexadecimal(s, 0))
                return(htoll(s, flags, errptr));
        else {
                if (decimal(s, 0))
                        return(dtoll(s, flags, errptr));
                else if (hexadecimal(s, 0))
                        return(htoll(s, flags, errptr));
        }
 
	if (!(flags & QUIET))
        	error(INFO, "not a valid number: %s\n", s);

        switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
        {
        case FAULT_ON_ERROR:
                RESTART();

        case RETURN_ON_ERROR:
                if (errptr)
                        *errptr = TRUE;
                break;
        }

        return UNUSED;
}

/*
 *  Append a two-character string to a number to make 1, 2, 3 and 4 into 
 *  1st, 2nd, 3rd, 4th, and so on...
 */
char *
ordinal(ulong val, char *buf)
{
	char *p1;
	
	sprintf(buf, "%ld", val);
	p1 = &buf[strlen(buf)-1];

	switch (*p1)
	{
	case '1':
		strcat(buf, "st");
		break;
	case '2':
		strcat(buf, "nd");
		break;
	case '3':
		strcat(buf, "rd");
		break;
	default:
		strcat(buf, "th");
		break;
	}

	return buf;
}

/*
 *  Convert a string into:
 *
 *   1.  an evaluated expression if it's enclosed within parentheses.
 *   2.  to a decimal value if the string is all decimal characters.
 *   3.  to a hexadecimal value if the string is all hexadecimal characters.
 *   4.  to a symbol value if the string is a known symbol.
 *
 *  If HEX_BIAS is set, pass the value on to htol().
 */
ulong
convert(char *s, int flags, int *errptr, ulong numflag)
{
	struct syment *sp;

	if ((numflag & NUM_EXPR) && can_eval(s))
             	return(eval(s, flags, errptr));

	if ((flags & HEX_BIAS) && (numflag & NUM_HEX) && hexadecimal(s, 0))
                return(htol(s, flags, errptr));
	else {
		if ((numflag & NUM_DEC) && decimal(s, 0))
	        	return(dtol(s, flags, errptr));
		if ((numflag & NUM_HEX) && hexadecimal(s, 0))
	        	return(htol(s, flags, errptr));
	}
	
	if ((sp = symbol_search(s)))
		return(sp->value);

        error(INFO, "cannot convert \"%s\"\n", s);

        switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
        {
        case FAULT_ON_ERROR:
                RESTART();

        case RETURN_ON_ERROR:
                if (errptr)
                	*errptr = TRUE;
		break;
        }

        return UNUSED;
}

/*
 *  Convert a string to a hexadecimal long value.
 */
ulong
htol(char *s, int flags, int *errptr)
{
    	long i, j; 
	ulong n;

    	if (s == NULL) { 
		if (!(flags & QUIET))
			error(INFO, "received NULL string\n");
		goto htol_error;
	}

    	if (STRNEQ(s, "0x") || STRNEQ(s, "0X"))
		s += 2;

    	if (strlen(s) > MAX_HEXADDR_STRLEN) { 
		if (!(flags & QUIET))
			error(INFO, 
			    "input string too large: \"%s\" (%d vs %d)\n", 
				s, strlen(s), MAX_HEXADDR_STRLEN);
		goto htol_error;
	}

    	for (n = i = 0; s[i] != 0; i++) {
	        switch (s[i]) 
	        {
	            case 'a':
	            case 'b':
	            case 'c':
	            case 'd':
	            case 'e':
	            case 'f':
	                j = (s[i] - 'a') + 10;
	                break;
	            case 'A':
	            case 'B':
	            case 'C':
	            case 'D':
	            case 'E':
	            case 'F':
	                j = (s[i] - 'A') + 10;
	                break;
	            case '1':
	            case '2':
	            case '3':
	            case '4':
	            case '5':
	            case '6':
	            case '7':
	            case '8':
	            case '9':
	            case '0':
	                j = s[i] - '0';
	                break;
		    case 'x':
		    case 'X':
		    case 'h':
			continue;
	            default:
			if (!(flags & QUIET))
				error(INFO, "invalid input: \"%s\"\n", s);
			goto htol_error;
	        }
	        n = (16 * n) + j;
    	}

    	return(n);

htol_error:
	switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
	{
	case FAULT_ON_ERROR:
		RESTART();

	case RETURN_ON_ERROR:
		if (errptr)
			*errptr = TRUE;
		break;
	}

	return BADADDR;
}

/*
 *  Convert a string to a hexadecimal unsigned long long value.
 */
ulonglong
htoll(char *s, int flags, int *errptr)
{
    	long i, j; 
	ulonglong n;

    	if (s == NULL) { 
		if (!(flags & QUIET))
			error(INFO, "received NULL string\n");
		goto htoll_error;
	}

    	if (STRNEQ(s, "0x") || STRNEQ(s, "0X"))
		s += 2;

    	if (strlen(s) > LONG_LONG_PRLEN) { 
		if (!(flags & QUIET))
			error(INFO, 
			    "input string too large: \"%s\" (%d vs %d)\n", 
				s, strlen(s), LONG_LONG_PRLEN);
		goto htoll_error;
	}

    	for (n = i = 0; s[i] != 0; i++) {
	        switch (s[i]) 
	        {
	            case 'a':
	            case 'b':
	            case 'c':
	            case 'd':
	            case 'e':
	            case 'f':
	                j = (s[i] - 'a') + 10;
	                break;
	            case 'A':
	            case 'B':
	            case 'C':
	            case 'D':
	            case 'E':
	            case 'F':
	                j = (s[i] - 'A') + 10;
	                break;
	            case '1':
	            case '2':
	            case '3':
	            case '4':
	            case '5':
	            case '6':
	            case '7':
	            case '8':
	            case '9':
	            case '0':
	                j = s[i] - '0';
	                break;
		    case 'x':
		    case 'X':
		    case 'h':
			continue;
	            default:
			if (!(flags & QUIET))
				error(INFO, "invalid input: \"%s\"\n", s);
			goto htoll_error;
	        }
	        n = (16 * n) + j;
    	}

    	return(n);

htoll_error:
	switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
	{
	case FAULT_ON_ERROR:
		RESTART();

	case RETURN_ON_ERROR:
		if (errptr)
			*errptr = TRUE;
		break;
	}

	return UNUSED;
}


/*
 *  Convert a string to a decimal long value.
 */
ulong
dtol(char *s, int flags, int *errptr)
{
        ulong retval;
        char *p, *orig;
        int j;

        if (s == NULL) {
		if (!(flags & QUIET))
                	error(INFO, "received NULL string\n");
                goto dtol_error;
        }

	if (strlen(s) == 0)
                goto dtol_error;

        p = orig = &s[0];
        while (*p++ == ' ')
                s++;

        for (j = 0; s[j] != '\0'; j++)
                if ((s[j] < '0' || s[j] > '9'))
                        break ;

	if (s[j] != '\0') {
		if (!(flags & QUIET))
                	error(INFO, "%s: \"%c\" is not a digit 0 - 9\n", 
				orig, s[j]);
                goto dtol_error;
	} else if (sscanf(s, "%lu", &retval) != 1) {
		if (!(flags & QUIET))
                	error(INFO, "invalid expression\n");
                goto dtol_error;
        }

        return(retval);

dtol_error:
        switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
        {
        case FAULT_ON_ERROR:
                RESTART();

        case RETURN_ON_ERROR:
		if (errptr)
			*errptr = TRUE;
                break;
        }

	return UNUSED;
}


/*
 *  Convert a string to a decimal long value.
 */
ulonglong
dtoll(char *s, int flags, int *errptr)
{
        ulonglong retval;
        char *p, *orig;
        int j;

        if (s == NULL) {
		if (!(flags & QUIET))
                	error(INFO, "received NULL string\n");
                goto dtoll_error;
        }

	if (strlen(s) == 0)
                goto dtoll_error;

        p = orig = &s[0];
        while (*p++ == ' ')
                s++;

        for (j = 0; s[j] != '\0'; j++)
                if ((s[j] < '0' || s[j] > '9'))
                        break ;

	if (s[j] != '\0') {
		if (!(flags & QUIET))
                	error(INFO, "%s: \"%c\" is not a digit 0 - 9\n", 
				orig, s[j]);
                goto dtoll_error;
	} else if (sscanf(s, "%llu", &retval) != 1) {
		if (!(flags & QUIET))
                	error(INFO, "invalid expression\n");
                goto dtoll_error;
        }

        return (retval);

dtoll_error:
        switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
        {
        case FAULT_ON_ERROR:
                RESTART();

        case RETURN_ON_ERROR:
		if (errptr)
			*errptr = TRUE;
                break;
        }

	return ((ulonglong)(-1));
}


/*
 *  Convert a string to a decimal integer value.
 */
unsigned int
dtoi(char *s, int flags, int *errptr)
{
        unsigned int retval;
        char *p;
        int j;

        if (s == NULL) {
		if (!(flags & QUIET))
                	error(INFO, "received NULL string\n");
                goto dtoi_error;
        }

        p = &s[0];
        while (*p++ == ' ')
                s++;

        for (j = 0; s[j] != '\0'; j++)
                if ((s[j] < '0' || s[j] > '9'))
                        break ;

        if (s[j] != '\0' || (sscanf(s, "%d", (int *)&retval) != 1)) {
		if (!(flags & QUIET))
                	error(INFO, "%s: \"%c\" is not a digit 0 - 9\n", 
				s, s[j]);
                goto dtoi_error;
        }

        return(retval);

dtoi_error:
        switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
        {
        case FAULT_ON_ERROR:
                RESTART();

        case RETURN_ON_ERROR:
		if (errptr)
			*errptr = TRUE;
                break;
        }

        return((unsigned int)(-1));
}

/*
 *  Determine whether a string contains only decimal characters.
 *  If count is non-zero, limit the search to count characters.
 */
int
decimal(char *s, int count)
{
    	char *p;
	int cnt, digits;

	if (!count) {
		strip_line_end(s);
		cnt = 0;
	} else
		cnt = count;

    	for (p = &s[0], digits = 0; *p; p++) {
	        switch(*p)
	        {
	            case '0':
	            case '1':
	            case '2':
	            case '3':
	            case '4':
	            case '5':
	            case '6':
	            case '7':
	            case '8':
	            case '9':
			digits++;
	            case ' ':
	                break;
	            default:
	                return FALSE;
	        }

		if (count && (--cnt == 0))
			break;
    	}

    	return (digits ? TRUE : FALSE);
}

/*
 *  Extract a hexadecimal number from a string.  If first_instance is FALSE,
 *  and two possibilities are found, a fatal error results.
 */
int
extract_hex(char *s, ulong *result, char stripchar, ulong first_instance)
{
	int i, found;
        char *arglist[MAXARGS];
        int argc;
	ulong value;
	char *buf;

	buf = GETBUF(strlen(s));
	strcpy(buf, s);
	argc = parse_line(buf, arglist);

	for (i = found = value = 0; i < argc; i++) {
		if (stripchar) 
			strip_ending_char(arglist[i], stripchar);
		
		if (hexadecimal(arglist[i], 0)) {
			if (found) {
				FREEBUF(buf);
				error(FATAL, 
				    "two hexadecimal args in: \"%s\"\n",
					strip_linefeeds(s));
			}
			value = htol(arglist[i], FAULT_ON_ERROR, NULL);
			found = TRUE;
			if (first_instance)
				break;
		}
	}

	FREEBUF(buf);

	if (found) {
		*result = value;
		return TRUE;
	} 

	return FALSE;
}


/*
 *  Determine whether a string contains only ASCII characters.
 */
int
ascii_string(char *s)
{
        char *p;

        for (p = &s[0]; *p; p++) {
		if (!ascii(*p)) 
			return FALSE;
        }

        return TRUE;
}

/*
 *  Check whether a string contains only printable ASCII characters.
 */
int
printable_string(char *s)
{
        char *p;

        for (p = &s[0]; *p; p++) {
		if (!isprint(*p)) 
			return FALSE;
        }

        return TRUE;
}


/*
 *  Determine whether a string contains only hexadecimal characters.
 *  If count is non-zero, limit the search to count characters.
 */
int
hexadecimal(char *s, int count)
{
    	char *p;
	int cnt, digits;

	if (!count) {
		strip_line_end(s);
		cnt = 0;
	} else
		cnt = count;

	for (p = &s[0], digits = 0; *p; p++) {
        	switch(*p) 
		{
	        case 'a':
	        case 'b':
	        case 'c':
	        case 'd':
	        case 'e':
	        case 'f':
	        case 'A':
	        case 'B':
	        case 'C':
	        case 'D':
	        case 'E':
	        case 'F':
	        case '1':
	        case '2':
	        case '3':
	        case '4':
	        case '5':
	        case '6':
	        case '7':
	        case '8':
	        case '9':
	        case '0':
			digits++;
	        case 'x':
	        case 'X':
	                break;

	        case ' ':
	                if (*(p+1) == NULLCHAR)
	                    break;
	                else
	                    return FALSE;
		default:
			return FALSE;
        	}

		if (count && (--cnt == 0))
			break;
    	}

    	return (digits ? TRUE : FALSE);
}

/*
 *  Determine whether a string contains only hexadecimal characters.
 *  and cannot be construed as a decimal number.
 *  If count is non-zero, limit the search to count characters.
 */
int
hexadecimal_only(char *s, int count)
{
    	char *p;
	int cnt, only;

	if (!count) {
		strip_line_end(s);
		cnt = 0;
	} else
		cnt = count;

	only = 0;

	for (p = &s[0]; *p; p++) {
        	switch(*p) 
		{
	        case 'a':
	        case 'b':
	        case 'c':
	        case 'd':
	        case 'e':
	        case 'f':
	        case 'A':
	        case 'B':
	        case 'C':
	        case 'D':
	        case 'E':
	        case 'F':
                case 'x':
                case 'X':
			only++;
			break;
	        case '1':
	        case '2':
	        case '3':
	        case '4':
	        case '5':
	        case '6':
	        case '7':
	        case '8':
	        case '9':
	        case '0':
	                break;

	        case ' ':
	                if (*(p+1) == NULLCHAR)
	                    break;
	                else
	                    return FALSE;
		default:
			return FALSE;
        	}

		if (count && (--cnt == 0))
			break;
    	}

    	return only;
}

/*
 *  Clean a command argument that has an obvious but ignorable error.
 *  The first one is an attached comma to a number, that usually is the 
 *  result of a cut-and-paste of an address from a structure display.  
 *  The second on is an attached colon to a number, usually from a
 *  cut-and-paste of a memory dump.
 *  Add more when they become annoynance.
 *
 *  It presumes args[optind] is the argument being tinkered with, and
 *  always returns TRUE for convenience of use.
 */
int
clean_arg(void)
{
	char buf[BUFSIZE];

	if (LASTCHAR(args[optind]) == ',' || 
	    LASTCHAR(args[optind]) == ':') {
		strcpy(buf, args[optind]);
		LASTCHAR(buf) = NULLCHAR;
		if (IS_A_NUMBER(buf))
			LASTCHAR(args[optind]) = NULLCHAR;
	}

	return TRUE;
}



/*
 *  Translate a hexadecimal string into its ASCII components.
 */
void
cmd_ascii(void)
{
        int i;
        ulonglong value;
	char *s;
        int c, prlen, bytes;

	optind = 1;
	if (!args[optind]) {
		fprintf(fp, "\n");
		fprintf(fp, "      0    1   2   3   4   5   6   7\n");
		fprintf(fp, "    +-------------------------------\n");
		fprintf(fp, "  0 | NUL DLE  SP  0   @   P   '   p\n");
		fprintf(fp, "  1 | SOH DC1  !   1   A   Q   a   q\n");
		fprintf(fp, "  2 | STX DC2  %c   2   B   R   b   r\n", 0x22);
		fprintf(fp, "  3 | ETX DC3  #   3   C   S   c   s\n");
		fprintf(fp, "  4 | EOT DC4  $   4   D   T   d   t\n");
		fprintf(fp, "  5 | ENQ NAK  %c   5   E   U   e   u\n", 0x25);
		fprintf(fp, "  6 | ACK SYN  &   6   F   V   f   v\n");
		fprintf(fp, "  7 | BEL ETB  `   7   G   W   g   w\n");
		fprintf(fp, "  8 |  BS CAN  (   8   H   X   h   x\n");
		fprintf(fp, "  9 |  HT  EM  )   9   I   Y   i   y\n");
		fprintf(fp, "  A |  LF SUB  *   :   J   Z   j   z\n");
		fprintf(fp, "  B |  VT ESC  +   ;   K   [   k   {\n");
		fprintf(fp, "  C |  FF  FS  ,   <   L   %c   l   |\n", 0x5c);
		fprintf(fp, "  D |  CR  GS  _   =   M   ]   m   }\n");
		fprintf(fp, "  E |  SO  RS  .   >   N   ^   n   ~\n");
		fprintf(fp, "  F |  SI  US  /   ?   O   -   o  DEL\n");
		fprintf(fp, "\n");
		return;
	}
	
        while (args[optind]) {

		s = args[optind];
        	if (STRNEQ(s, "0x") || STRNEQ(s, "0X"))
                	s += 2;

                if (strlen(s) > LONG_PRLEN) {
			prlen = LONG_LONG_PRLEN;
			bytes = sizeof(long long);
		} else {
			prlen = LONG_PRLEN;
			bytes = sizeof(long);
		}
		
                value = htoll(s, FAULT_ON_ERROR, NULL);
                fprintf(fp, "%.*llx: ", prlen, value);
		for (i = 0; i < bytes; i++) {
			c = (value >> (8*i)) & 0xff;
			if ((c >= 0x20) && (c < 0x7f)) {
				fprintf(fp, "%c", (char)c);
				continue;
			}
			if (c > 0x7f) {
				fprintf(fp, "<%02x>", c);
				continue;
			}
			switch (c)
			{
			case 0x0: fprintf(fp, "<NUL>"); break;
			case 0x1: fprintf(fp, "<SOH>"); break;
			case 0x2: fprintf(fp, "<STX>"); break;
			case 0x3: fprintf(fp, "<ETX>"); break;
			case 0x4: fprintf(fp, "<EOT>"); break;
			case 0x5: fprintf(fp, "<ENQ>"); break;
			case 0x6: fprintf(fp, "<ACK>"); break;
			case 0x7: fprintf(fp, "<BEL>"); break;
			case 0x8: fprintf(fp, "<BS>"); break;
			case 0x9: fprintf(fp, "<HT>"); break;
			case 0xa: fprintf(fp, "<LF>"); break;
			case 0xb: fprintf(fp, "<VT>"); break;
			case 0xc: fprintf(fp, "<FF>"); break;
			case 0xd: fprintf(fp, "<CR>"); break;
			case 0xe: fprintf(fp, "<SO>"); break;
			case 0xf: fprintf(fp, "<SI>"); break;
			case 0x10: fprintf(fp, "<DLE>"); break;
			case 0x11: fprintf(fp, "<DC1>"); break;
			case 0x12: fprintf(fp, "<DC2>"); break;
			case 0x13: fprintf(fp, "<DC3>"); break;
			case 0x14: fprintf(fp, "<DC4>"); break;
			case 0x15: fprintf(fp, "<NAK>"); break;
			case 0x16: fprintf(fp, "<SYN>"); break;
			case 0x17: fprintf(fp, "<ETB>"); break;
			case 0x18: fprintf(fp, "<CAN>"); break;
			case 0x19: fprintf(fp, "<EM>"); break;
			case 0x1a: fprintf(fp, "<SUB>"); break;
			case 0x1b: fprintf(fp, "<ESC>"); break;
			case 0x1c: fprintf(fp, "<FS>"); break;
			case 0x1d: fprintf(fp, "<GS>"); break;
			case 0x1e: fprintf(fp, "<RS>"); break;
			case 0x1f: fprintf(fp, "<US>"); break;
			case 0x7f: fprintf(fp, "<DEL>"); break;
			}
		}
		fprintf(fp, "\n");

                optind++;
        }

}

/*
 *  Counts number of leading whitespace characters in a string.
 */
int
count_leading_spaces(char *s)
{
        return (strspn(s, " \t"));
}

/*
 *  Prints the requested number of spaces.
 */
void
pad_line(FILE *filep, int cnt, char c)
{
	int i;

	for (i = 0; i < cnt; i++) 
		fputc(c, filep);
}

/*
 *  Returns appropriate number of inter-field spaces in a usable string.
 *  MINSPACE is defined as -100, but implies the minimum space between two
 *  fields.  Currently this can be either one or two spaces, depending upon
 *  the architecture.  Since the mininum space must be at least 1, MINSPACE,
 *  MINSPACE-1 and MINSPACE+1 are all valid, special numbers.  Otherwise
 *  the space count must be greater than or equal to 0.
 *
 *  If the cnt request is greater than SPACES, a dynamic buffer is
 *  allocated, and normal buffer garbage collection will return it
 *  back to the pool.
 */
char *
space(int cnt)
{
#define SPACES 40
	static char spacebuf[SPACES+1] = { 0 };
	int i;
	char *bigspace;

	if (cnt > SPACES) {
		bigspace = GETBUF(cnt);
		for (i = 0; i < cnt; i++)
			bigspace[i] = ' ';
		bigspace[i] = NULLCHAR;
		return bigspace;
	}

	if (!strlen(spacebuf)) {
		for (i = 0; i < SPACES; i++)
			spacebuf[i] = ' ';
		spacebuf[i] = NULLCHAR; 
	}

	if (cnt < (MINSPACE-1))
		error(FATAL, "illegal spacing request: %d\n", cnt);
	if ((cnt > MINSPACE+1) && (cnt < 0))
		error(FATAL, "illegal spacing request\n");

	switch (cnt)
	{
	case (MINSPACE-1):
		if (VADDR_PRLEN > 8)
			return (&spacebuf[SPACES]);    /* NULL */
		else
			return (&spacebuf[SPACES-1]);  /* 1 space */

	case MINSPACE:
		if (VADDR_PRLEN > 8)
			return (&spacebuf[SPACES-1]);  /* 1 space */
		else
			return (&spacebuf[SPACES-2]);  /* 2 spaces */

	case (MINSPACE+1):
                if (VADDR_PRLEN > 8) 
                        return (&spacebuf[SPACES-2]);  /* 2 spaces */
                else    
                        return (&spacebuf[SPACES-3]);  /* 3 spaces */

	default:
		return (&spacebuf[SPACES-cnt]);        /* as requested */
	}
}

/*
 *  Determine whether substring s1, with length len, and contained within
 *  string s, is surrounded by <bracket> characters.  If len is 0, calculate
 *  it.
 */
int
bracketed(char *s, char *s1, int len)
{
	char *s2;

	if (!len) {
		if (!(s2 = strstr(s1, ">")))
			return FALSE;
		len = s2-s1;
	}

	if (((s1-s) < 1) || (*(s1-1) != '<') || 
	    ((s1+len) >= &s[strlen(s)]) || (*(s1+len) != '>'))
		return FALSE;

	return TRUE;
}

/*
 *  Counts the number of a specified character in a string.
 */
int
count_chars(char *s, char c)
{
	char *p;
	int count;

	if (!s)
		return 0;

	count = 0;

	for (p = s; *p; p++) {
		if (*p == c)
			count++;
	}

	return count;
}

/*
 *  Counts the number of a specified characters in a buffer.
 */
long count_buffer_chars(char *bufptr, char c, long len)
{
	long i, cnt;

	for (i = cnt = 0; i < len; i++, bufptr++) {
		if (*bufptr == c)
			cnt++;
	}

	return cnt;
}

/*
 *  Concatenates the tokens in the global args[] array into one string,
 *  separating each token with one space.  If the no_options flag is set,
 *  don't include any args beginning with a dash character.
 */
char *
concat_args(char *buf, int arg, int no_options)
{
	int i;

	BZERO(buf, BUFSIZE);

        for (i = arg; i < argcnt; i++) {
		if (no_options && STRNEQ(args[i], "-"))
			continue;
                strcat(buf, args[i]);
                strcat(buf, " ");
        }

	return(strip_ending_whitespace(buf));
}

/*
 *  Shifts the contents of a string to the left by cnt characters, 
 *  disposing the leftmost characters.
 */
char *
shift_string_left(char *s, int cnt)
{
	int origlen;

	if (!cnt)
		return(s);

	origlen = strlen(s);
	memmove(s, s+cnt, (origlen-cnt));
	*(s+(origlen-cnt)) = NULLCHAR;
	return(s);
}

/*
 *  Shifts the contents of a string to the right by cnt characters,
 *  inserting space characters.  (caller confirms space is available)
 */
char *
shift_string_right(char *s, int cnt)
{
        int origlen;

	if (!cnt)
		return(s);

        origlen = strlen(s);
        memmove(s+cnt, s, origlen);
        s[origlen+cnt] = NULLCHAR;
	return(memset(s, ' ', cnt));
}

/*
 *  Create a string in a buffer of a given size, centering, or justifying 
 *  left or right as requested.  If the opt argument is used, then the string
 *  is created with its string/integer value.  If opt is NULL, then the
 *  string is already in contained in string s (not justified).  Note that
 *  flag LONGLONG_HEX implies that opt is a ulonglong pointer to the 
 *  actual value.
 */
char *
mkstring(char *s, int size, ulong flags, const char *opt)
{
	int len;
	int extra;
	int left;
	int right;

	switch (flags & (LONG_DEC|LONG_HEX|INT_HEX|INT_DEC|LONGLONG_HEX|ZERO_FILL)) 
	{
	case LONG_DEC:
		sprintf(s, "%lu", (ulong)opt);
		break;
	case LONG_HEX:
		sprintf(s, "%lx", (ulong)opt);
		break;
	case (LONG_HEX|ZERO_FILL):
		if (VADDR_PRLEN == 8)
			sprintf(s, "%08lx", (ulong)opt);
		else if (VADDR_PRLEN == 16)
			sprintf(s, "%016lx", (ulong)opt);
		break;
	case INT_DEC:
		sprintf(s, "%u", (uint)((ulong)opt));
		break;
	case INT_HEX:
		sprintf(s, "%x", (uint)((ulong)opt));
		break;
	case LONGLONG_HEX:
		sprintf(s, "%llx", *((ulonglong *)opt));
		break;
	default:
		if (opt)
			strcpy(s, opt);
		break;
	}

	/*
	 *  At this point, string s has the string to be justified,
	 *  and has room to work with.  The relevant flags from this
	 *  point on are of CENTER, LJUST and RJUST.  If the length 
	 *  of string s is already larger than the requested size, 
	 *  just return it as is.
	 */
	len = strlen(s);
	if (size <= len) 
		return(s);
	extra = size - len;

	if (flags & CENTER) {
		/*
		 *  If absolute centering is not possible, justify the
		 *  string as requested -- or to the left if no justify
		 *  argument was passed in.
		 */
		if (extra % 2) {
			switch (flags & (LJUST|RJUST))
			{
			default:
			case LJUST:
				right = (extra/2) + 1;
				left = extra/2;
				break;
			case RJUST:
				right = extra/2;
				left = (extra/2) + 1;
				break;
			}
		}
		else 
			left = right = extra/2;

		shift_string_right(s, left);
		len = strlen(s);
		memset(s + len, ' ', right);
		s[len + right] = NULLCHAR;
	
		return(s);
	}

	if (flags & LJUST) {
		len = strlen(s);
		memset(s + len, ' ', extra);
		s[len + extra] = NULLCHAR;
	} else if (flags & RJUST) 
		shift_string_right(s, extra);

	return(s);
}

/*
 *  Prints the requested number of BACKSPACE characters.
 */
void
backspace(int cnt)
{
	int i;

	for (i = 0; i < cnt; i++) 
		fprintf(fp, "\b");
}

/*
 *  Set/display process context or internal variables.  Processes are set
 *  by their task or PID number, or to the panic context with the -p flag.
 *  Internal variables may be viewed or changed, depending whether an argument 
 *  follows the variable name.  If no arguments are entered, the current
 *  process context is dumped.  The current set of variables and their
 *  acceptable settings are:
 *
 *        debug  "on", "off", or any number.  "on" sets it to a value of 1.
 *         hash  "on", "off", or any number.  Non-zero numbers are converted 
 *               to "on", zero is converted to "off".
 *       scroll  "on", "off", or any number.  Non-zero numbers are converted 
 *               to "on", zero is converted to "off".
 *       silent  "on", "off", or any number.  Non-zero numbers are converted
 *               to "on", zero is converted to "off".
 *      refresh  "on", "off", or any number.  Non-zero numbers are converted
 *               to "on", zero is converted to "off".
 *          sym  regular filename
 *      console  device filename
 *        radix  10 or 16
 *         core  (no arg) drop core when error() is called.
 *           vi  (no arg) set editing mode to vi (from .rc file only).
 *        emacs  (no arg) set editing mode to emacs (from .rc file only).
 *     namelist  kernel name (from .rc file only).
 *     dumpfile  dumpfile name (from .rc file only).
 *
 *  gdb variable settings not changeable by gdb's "set" command:
 *
 *    print_max  value (default is 200).
 */
void
cmd_set(void)
{
	int i, c;
	ulong value;
	int cpu, runtime, from_rc_file;
	char buf[BUFSIZE];
	char *extra_message;
	struct task_context *tc;
	struct syment *sp;

#define defer()  do { } while (0)
#define already_done()  do { } while (0)
#define ignore()  do { } while (0)

	extra_message = NULL;
	runtime = pc->flags & RUNTIME ? TRUE : FALSE;
	from_rc_file = pc->curcmd_flags & FROM_RCFILE ? TRUE : FALSE;

        while ((c = getopt(argcnt, args, "pvc:a:")) != EOF) {
                switch(c)
		{
		case 'c':
			if (XEN_HYPER_MODE() || (pc->flags & MINIMAL_MODE))
				option_not_supported(c);

			if (!runtime)
				return;

		        if (ACTIVE()) {
                		error(INFO, "not allowed on a live system\n");
				argerrs++;
				break;
			}
			cpu = dtoi(optarg, FAULT_ON_ERROR, NULL);
			set_cpu(cpu);
			return;

		case 'p':
			if (XEN_HYPER_MODE() || (pc->flags & MINIMAL_MODE))
				option_not_supported(c);

			if (!runtime)
				return;

			if (ACTIVE()) {
				set_context(tt->this_task, NO_PID);
				show_context(CURRENT_CONTEXT());
				return;
			}

			if (!tt->panic_task) {
                		error(INFO, "no panic task found!\n");
				return;
			}
        		set_context(tt->panic_task, NO_PID);
			show_context(CURRENT_CONTEXT());
			return;

		case 'v':
			if (!runtime)
				return;

			show_options();
			return;

		case 'a':
			if (XEN_HYPER_MODE() || (pc->flags & MINIMAL_MODE))
				option_not_supported(c);

			if (!runtime)
				return;

			if (ACTIVE())
				error(FATAL, 
				    "-a option not allowed on live systems\n");

	                switch (str_to_context(optarg, &value, &tc))
	                {
	                case STR_PID:
				if ((i = TASKS_PER_PID(value)) > 1)
					error(FATAL, 
					    "pid %d has %d tasks: "
					    "use a task address\n",
						value, i);
	                        break;
	
	                case STR_TASK:
	                        break;
	
	                case STR_INVALID:
	                        error(FATAL, "invalid task or pid value: %s\n",
	                                optarg);
	                }
		
			cpu = tc->processor;
			tt->active_set[cpu] = tc->task;
			if (tt->panic_threads[cpu])
				tt->panic_threads[cpu] = tc->task;
			fprintf(fp, 
			    "\"%s\" task %lx has been marked as the active task on cpu %d\n",
				tc->comm, tc->task, cpu);
			return;

		default:
			argerrs++;
			break;
		}
	}

	if (argerrs) {
		if (runtime)
			cmd_usage(pc->curcmd, SYNOPSIS);
		return;
	}

	if (!args[optind]) {
		if (XEN_HYPER_MODE())
			error(INFO, 
			    "requires an option with the Xen hypervisor\n");
		else if (pc->flags & MINIMAL_MODE)
			show_options();
		else if (runtime)
			show_context(CURRENT_CONTEXT());
		return;
	}

	while (args[optind]) {
		if (STREQ(args[optind], "debug")) {
                        if (args[optind+1]) {
                                optind++;
				if (!runtime)
					defer();
                                else if (STREQ(args[optind], "on"))
                                        pc->debug = 1;
                                else if (STREQ(args[optind], "off"))
                                        pc->debug = 0;
				else if (IS_A_NUMBER(args[optind])) 
					pc->debug = stol(args[optind], 
						FAULT_ON_ERROR, NULL);
				else
					goto invalid_set_command;
                        }
			if (runtime)
                        	fprintf(fp, "debug: %ld\n", pc->debug);

			set_lkcd_debug(pc->debug);
			set_vas_debug(pc->debug);
			return;

                } else if (STREQ(args[optind], "hash")) {
                        if (args[optind+1]) {
                                optind++;
				if (!runtime)
					defer();
                                else if (STREQ(args[optind], "on"))
                                        pc->flags |= HASH;
                                else if (STREQ(args[optind], "off"))
                                        pc->flags &= ~HASH;
				else if (IS_A_NUMBER(args[optind])) {
					value = stol(args[optind],
                                    		FAULT_ON_ERROR, NULL);
					if (value)
						pc->flags |= HASH;
					else
						pc->flags &= ~HASH;
				} else
					goto invalid_set_command;
                        }

			if (runtime)
                        	fprintf(fp, "hash: %s\n",
                                	pc->flags & HASH ? "on" : "off");
			return;

                } else if (STREQ(args[optind], "unwind")) {
                        if (args[optind+1]) {
                                optind++;
				if (!runtime)
					defer();
                                else if (STREQ(args[optind], "on")) {
				    	if ((kt->flags & DWARF_UNWIND_CAPABLE) ||
					    !runtime) {
                                        	kt->flags |= DWARF_UNWIND;
						kt->flags &= ~NO_DWARF_UNWIND;
					}
                                } else if (STREQ(args[optind], "off")) {
                                        kt->flags &= ~DWARF_UNWIND;
					if (!runtime)
						kt->flags |= NO_DWARF_UNWIND;
				} else if (IS_A_NUMBER(args[optind])) {
					value = stol(args[optind],
                                    		FAULT_ON_ERROR, NULL);
					if (value) {
				    		if ((kt->flags & DWARF_UNWIND_CAPABLE) ||
						    !runtime) {
							kt->flags |= DWARF_UNWIND;
							kt->flags &= ~NO_DWARF_UNWIND;
						}
					} else {
						kt->flags &= ~DWARF_UNWIND;
						if (!runtime)
							kt->flags |= NO_DWARF_UNWIND;
					}
				} else
					goto invalid_set_command;
                        }

			if (runtime)
                        	fprintf(fp, "unwind: %s\n",
                                	kt->flags & DWARF_UNWIND ? "on" : "off");
			return;

               } else if (STREQ(args[optind], "refresh")) {
                        if (args[optind+1]) {
                                optind++;
				if (!runtime)
					defer();
                                else if (STREQ(args[optind], "on"))
                                        tt->flags |= TASK_REFRESH;
                                else if (STREQ(args[optind], "off")) {
                                        tt->flags &= ~TASK_REFRESH;
					if (!runtime)
						tt->flags |= TASK_REFRESH_OFF;
                                } else if (IS_A_NUMBER(args[optind])) {
                                        value = stol(args[optind],
                                                FAULT_ON_ERROR, NULL);
                                        if (value)
                                                tt->flags |= TASK_REFRESH;
                                        else {
                                                tt->flags &= ~TASK_REFRESH;
						if (!runtime)
							tt->flags |= 
							    TASK_REFRESH_OFF;
					}
                                } else
					goto invalid_set_command;
                        }

                        if (runtime)
                                fprintf(fp, "refresh: %s\n",
                               	    tt->flags & TASK_REFRESH ?  "on" : "off");
			return;

               } else if (STREQ(args[optind], "gdb")) {
                        if (args[optind+1]) {
                                optind++;
				if (!runtime)
					defer();
                                else if (STREQ(args[optind], "on")) {
					if (pc->flags & MINIMAL_MODE)
						goto invalid_set_command;
					else
                                        	pc->flags2 |= GDB_CMD_MODE;
                                } else if (STREQ(args[optind], "off"))
                                        pc->flags2 &= ~GDB_CMD_MODE;
                                else if (IS_A_NUMBER(args[optind])) {
                                        value = stol(args[optind],
                                                FAULT_ON_ERROR, NULL);
                                        if (value) {
						if (pc->flags & MINIMAL_MODE)
							goto invalid_set_command;
						else
                                                	pc->flags2 |= GDB_CMD_MODE;
                                        } else
                                                pc->flags2 &= ~GDB_CMD_MODE;
                                } else
					goto invalid_set_command;

				set_command_prompt(pc->flags2 & GDB_CMD_MODE ?
					"gdb> " : NULL);
                        }

                        if (runtime)
                                fprintf(fp, "gdb: %s\n",
                               	    pc->flags2 & GDB_CMD_MODE ?  "on" : "off");
			return;

               } else if (STREQ(args[optind], "scroll")) {
                        if (args[optind+1] && pc->scroll_command) {
                                optind++;
				if (from_rc_file)
					already_done();
                                else if (STREQ(args[optind], "on"))
                                        pc->flags |= SCROLL;
                                else if (STREQ(args[optind], "off"))
                                        pc->flags &= ~SCROLL;
				else if (STREQ(args[optind], "more"))
					pc->scroll_command = SCROLL_MORE;
				else if (STREQ(args[optind], "less"))
					pc->scroll_command = SCROLL_LESS;
				else if (STREQ(args[optind], "CRASHPAGER")) {
					if (CRASHPAGER_valid())
						pc->scroll_command = SCROLL_CRASHPAGER;
				} else if (IS_A_NUMBER(args[optind])) {
                                        value = stol(args[optind],
                                                FAULT_ON_ERROR, NULL);
                                        if (value)
                                                pc->flags |= SCROLL;
                                        else
                                                pc->flags &= ~SCROLL;
                                } else
					goto invalid_set_command;
                        }

			if (runtime) {
				fprintf(fp, "scroll: %s ",
					pc->flags & SCROLL ? "on" : "off");
				switch (pc->scroll_command)
				{
				case SCROLL_LESS:
					fprintf(fp, "(/usr/bin/less)\n");
					break;
				case SCROLL_MORE:
					fprintf(fp, "(/bin/more)\n");
					break;
				case SCROLL_NONE:
					fprintf(fp, "(none)\n");
					break;
				case SCROLL_CRASHPAGER:
					fprintf(fp, "(CRASHPAGER: %s)\n", getenv("CRASHPAGER"));
					break;
				}
			}

			return;

               } else if (STREQ(args[optind], "silent")) {
                        if (args[optind+1]) {
                                optind++;
                                if (STREQ(args[optind], "on")) {
                                        pc->flags |= SILENT;
					pc->flags &= ~SCROLL;
				}
                                else if (STREQ(args[optind], "off"))
                                        pc->flags &= ~SILENT;
                                else if (IS_A_NUMBER(args[optind])) {
                                        value = stol(args[optind],
                                                FAULT_ON_ERROR, NULL);
                                        if (value) {
                                                pc->flags |= SILENT;
						pc->flags &= ~SCROLL;
					}
                                        else
                                                pc->flags &= ~SILENT;
                                } else
					goto invalid_set_command;

				if (!(pc->flags & SILENT))
                                	fprintf(fp, "silent: off\n");

                        } else if (runtime && !(pc->flags & SILENT))
                               	fprintf(fp, "silent: off\n");
			return;

                } else if (STREQ(args[optind], "console")) {
			int assignment;

                        if (args[optind+1]) {
                                create_console_device(args[optind+1]);
				optind++;
				assignment = optind;
			} else
				assignment = 0;

			if (runtime) {
				fprintf(fp, "console: ");
				if (pc->console)
					fprintf(fp, "%s\n", pc->console);
				else {
					if (assignment)
						fprintf(fp, 
					            "assignment to %s failed\n",
						    	args[assignment]);
					else
						fprintf(fp, "not set\n");
				}		
			}
			return;

		} else if (STREQ(args[optind], "core")) {
                        if (args[optind+1]) {
                                optind++;
                                if (STREQ(args[optind], "on"))
                                        pc->flags |= DROP_CORE;
                                else if (STREQ(args[optind], "off"))
                                        pc->flags &= ~DROP_CORE;
                                else if (IS_A_NUMBER(args[optind])) {
                                        value = stol(args[optind],
                                                FAULT_ON_ERROR, NULL);
                                        if (value)
                                                pc->flags |= DROP_CORE;
                                        else
                                                pc->flags &= ~DROP_CORE;
                                } else
                                        goto invalid_set_command;
                        }
		
			if (runtime) {
				fprintf(fp, "core: %s on error message)\n",
					pc->flags & DROP_CORE ? 
					"on (drop core" : 
					"off (do NOT drop core");
			}
			return;

                } else if (STREQ(args[optind], "radix")) {
                       if (args[optind+1]) {
                                optind++;
				if (!runtime)
					defer();
				else if (from_rc_file && 
				    (pc->flags2 & RADIX_OVERRIDE))
					ignore();
                                else if (STREQ(args[optind], "10") ||
				    STRNEQ(args[optind], "dec") ||
				    STRNEQ(args[optind], "ten")) 
					pc->output_radix = 10;
                                else if (STREQ(args[optind], "16") ||
			            STRNEQ(args[optind], "hex") ||
				    STRNEQ(args[optind], "six")) 
					pc->output_radix = 16;
				else 
					goto invalid_set_command;
			} 

                        if (runtime) {
				sprintf(buf, "set output-radix %d",
					pc->output_radix);
                                gdb_pass_through(buf, NULL, GNU_FROM_TTY_OFF);
                        	fprintf(fp, "output radix: %d (%s)\n",
					pc->output_radix, 
					pc->output_radix == 10 ? 
					"decimal" : "hex");
			}
			return;

                } else if (STREQ(args[optind], "hex")) {
			if (from_rc_file && (pc->flags2 & RADIX_OVERRIDE))
				ignore();
			else if (runtime) {
				pc->output_radix = 16;
				gdb_pass_through("set output-radix 16", 
					NULL, GNU_FROM_TTY_OFF);
				fprintf(fp, "output radix: 16 (hex)\n");
			}
			return;

                } else if (STREQ(args[optind], "dec")) {
			if (from_rc_file && (pc->flags2 & RADIX_OVERRIDE))
				ignore();
			else if (runtime) {
				pc->output_radix = 10;
                                gdb_pass_through("set output-radix 10", 
                                        NULL, GNU_FROM_TTY_OFF);
				fprintf(fp, "output radix: 10 (decimal)\n");
			}
			return;

               } else if (STREQ(args[optind], "edit")) {
                        if (args[optind+1]) {
				if (runtime && !from_rc_file)
					error(FATAL, 
		                "cannot change editing mode during runtime\n");
                                optind++;
				if (from_rc_file)
					already_done();
                                else if (STREQ(args[optind], "vi"))
                                        pc->editing_mode = "vi";
                                else if (STREQ(args[optind], "emacs"))
                                        pc->editing_mode = "emacs";
				else
                                        goto invalid_set_command;
                        }

                        if (runtime)
                                fprintf(fp, "edit: %s\n", pc->editing_mode);
                        return;

                } else if (STREQ(args[optind], "vi")) {
			if (runtime) {
				if (!from_rc_file)
					error(FATAL, 
		               "cannot change editing mode during runtime\n"); 
				fprintf(fp, "edit: %s\n", pc->editing_mode);
			} else
				pc->editing_mode = "vi";
			return;

                } else if (STREQ(args[optind], "emacs")) {
			if (runtime) {
				if (!from_rc_file)
					error(FATAL, 
		               "cannot change %s editing mode during runtime\n",
						pc->editing_mode);
				fprintf(fp, "edit: %s\n", pc->editing_mode);
			} else
				pc->editing_mode = "emacs";
			return;

                } else if (STREQ(args[optind], "print_max")) {
			optind++;
			if (args[optind]) {
				if (!runtime)
					defer();
				else if (decimal(args[optind], 0))
					*gdb_print_max = atoi(args[optind]);
				else if (hexadecimal(args[optind], 0))
					*gdb_print_max = (unsigned int)
					    htol(args[optind], 
						FAULT_ON_ERROR, NULL);
				else
					goto invalid_set_command;

			}
			if (runtime)
				fprintf(fp, "print_max: %d\n", *gdb_print_max);
			return;

                } else if (STREQ(args[optind], "scope")) {
			optind++;
			if (args[optind]) {
				if (!runtime)
					defer();
				else if (can_eval(args[optind])) 
					value = eval(args[optind], FAULT_ON_ERROR, NULL);
				else if (hexadecimal(args[optind], 0))
					value = htol(args[optind], FAULT_ON_ERROR, NULL);
				else if ((sp = symbol_search(args[optind])))
					value = sp->value;
				else
					goto invalid_set_command;

				if (runtime) {
					if (gdb_set_crash_scope(value, args[optind]))
						pc->scope = value;
					else
						return;
				}
			}
			if (runtime) {
				fprintf(fp, "scope: %lx ", pc->scope);
				if (pc->scope)
					fprintf(fp, "(%s)\n", 
						value_to_symstr(pc->scope, buf, 0));
				else
					fprintf(fp, "(not set)\n");
			}
			return;

                } else if (STREQ(args[optind], "null-stop")) {
			optind++;
			if (args[optind]) {
				if (!runtime)
					defer();
				else if (STREQ(args[optind], "on"))
					*gdb_stop_print_at_null = 1;
				else if (STREQ(args[optind], "off"))
					*gdb_stop_print_at_null = 0;
				else if (IS_A_NUMBER(args[optind])) {
					value = stol(args[optind],
						FAULT_ON_ERROR, NULL);
					if (value)
						*gdb_stop_print_at_null = 1;
					else
						*gdb_stop_print_at_null = 0;
					} else
						goto invalid_set_command;
			}
			if (runtime)
				fprintf(fp, "null-stop: %s\n", 
					*gdb_stop_print_at_null ? "on" : "off");
			return;

                } else if (STREQ(args[optind], "print_array")) {
			optind++;
			if (args[optind]) {
				if (!runtime)
					defer();
				else if (STREQ(args[optind], "on"))
					*gdb_prettyprint_arrays = 1;
				else if (STREQ(args[optind], "off"))
					*gdb_prettyprint_arrays = 0;
				else if (IS_A_NUMBER(args[optind])) {
					value = stol(args[optind],
						FAULT_ON_ERROR, NULL);
					if (value)
						*gdb_prettyprint_arrays = 1;
					else
						*gdb_prettyprint_arrays = 0;
					} else
						goto invalid_set_command;
			}
			if (runtime)
				fprintf(fp, "print_array: %s\n", 
					*gdb_prettyprint_arrays ? "on" : "off");
			return;

                } else if (STREQ(args[optind], "namelist")) {
			optind++;
                        if (!runtime && args[optind]) {
                		if (!is_elf_file(args[optind])) 
                                	error(FATAL, 
			       "%s: not a kernel namelist (from .%src file)\n",
                                        	args[optind],
						pc->program_name);
                                if ((pc->namelist = (char *)
                                    malloc(strlen(args[optind])+1)) == NULL) {
                                        error(INFO,
                                  "cannot malloc memory for namelist: %s: %s\n",
                                                args[optind], strerror(errno));
                                } else
                                        strcpy(pc->namelist, args[optind]);
			}
			if (runtime)
				fprintf(fp, "namelist: %s\n", pc->namelist);
			return;

                } else if (STREQ(args[optind], "free")) {
			if (!runtime)
				defer();
			else
				fprintf(fp, "%d pages freed\n",
					dumpfile_memory(DUMPFILE_FREE_MEM));
			return;

                } else if (STREQ(args[optind], "data_debug")) {

			pc->flags |= DATADEBUG;
			return;

                } else if (STREQ(args[optind], "zero_excluded")) {

                        if (args[optind+1]) {
                                optind++;
				if (from_rc_file)
					already_done();
                                else if (STREQ(args[optind], "on")) {
                                        *diskdump_flags |= ZERO_EXCLUDED;
					sadump_set_zero_excluded();
                                } else if (STREQ(args[optind], "off")) {
                                        *diskdump_flags &= ~ZERO_EXCLUDED;
					sadump_unset_zero_excluded();
				} else if (IS_A_NUMBER(args[optind])) {
					value = stol(args[optind],
                                    		FAULT_ON_ERROR, NULL);
					if (value) {
                                        	*diskdump_flags |= ZERO_EXCLUDED;
						sadump_set_zero_excluded();
					} else {
                                        	*diskdump_flags &= ~ZERO_EXCLUDED;
						sadump_unset_zero_excluded();
					}
				} else
					goto invalid_set_command;
                        }

			if (runtime)
                        	fprintf(fp, "zero_excluded: %s\n",
					(*diskdump_flags & ZERO_EXCLUDED) ||
					sadump_is_zero_excluded() ?
					"on" : "off");
			return;

                } else if (STREQ(args[optind], "offline")) {

                        if (args[optind+1]) {
                                optind++;
				if (from_rc_file)
					already_done();
                                else if (STREQ(args[optind], "show"))
                                        pc->flags2 &= ~OFFLINE_HIDE;
                                else if(STREQ(args[optind], "hide"))
                                        pc->flags2 |= OFFLINE_HIDE;
                                else
                                        goto invalid_set_command;
                        }

			if (runtime)
				fprintf(fp, "      offline: %s\n",
					pc->flags2 & OFFLINE_HIDE ? "hide" : "show");

			return;

		} else if (STREQ(args[optind], "redzone")) {
                        if (args[optind+1]) {
                                optind++;
                                if (STREQ(args[optind], "on"))
                                        pc->flags2 |= REDZONE;
                                else if (STREQ(args[optind], "off"))
                                        pc->flags2 &= ~REDZONE;
                                else if (IS_A_NUMBER(args[optind])) {
                                        value = stol(args[optind],
                                                FAULT_ON_ERROR, NULL);
                                        if (value)
                                                pc->flags2 |= REDZONE;
                                        else
                                                pc->flags2 &= ~REDZONE;
                                } else
                                        goto invalid_set_command;
                        }
		
			if (runtime) {
				fprintf(fp, "redzone: %s\n",
					pc->flags2 & REDZONE ? 
					"on" : "off");
			}
			return;

		} else if (XEN_HYPER_MODE()) {
			error(FATAL, "invalid argument for the Xen hypervisor\n");
		} else if (pc->flags & MINIMAL_MODE) {
			error(FATAL, "invalid argument in minimal mode\n");
		} else if (runtime) {
			ulong pid, task;

	                switch (str_to_context(args[optind], &value, &tc))
	                {
	                case STR_PID:
                                pid = value;
                                task = NO_TASK;
                        	if (set_context(task, pid))
                                	show_context(CURRENT_CONTEXT());
	                        break;
	
	                case STR_TASK:
                                task = value;
                                pid = NO_PID;
                                if (set_context(task, pid))
                                        show_context(CURRENT_CONTEXT()); 
	                        break;
	
	                case STR_INVALID:
	                        error(INFO, "invalid task or pid value: %s\n",
	                                args[optind]);
	                        break;
	                }
		} else
			console("set: ignoring \"%s\"\n", args[optind]);

		optind++;
	}

	return;

invalid_set_command:

	sprintf(buf, "invalid command");
	if (!runtime)
		sprintf(&buf[strlen(buf)], " in .%src file", pc->program_name);
	strcat(buf, ": ");
	for (i = 0; i < argcnt; i++)
		sprintf(&buf[strlen(buf)], "%s ", args[i]);
	strcat(buf, "\n");
	if (extra_message)
		strcat(buf, extra_message);
	error(runtime ? FATAL : INFO, buf);

#undef defer
#undef already_done
#undef ignore
}

/*
 *  Display the set of settable internal variables.
 */
static void
show_options(void)
{
	char buf[BUFSIZE];

	fprintf(fp, "        scroll: %s ",
		pc->flags & SCROLL ? "on" : "off");
	switch (pc->scroll_command)
	{
	case SCROLL_LESS:
		fprintf(fp, "(/usr/bin/less)\n");
		break;
	case SCROLL_MORE:
		fprintf(fp, "(/bin/more)\n");
		break;
	case SCROLL_NONE:
		fprintf(fp, "(none)\n");
		break;
	case SCROLL_CRASHPAGER:
		fprintf(fp, "(CRASHPAGER: %s)\n", getenv("CRASHPAGER"));
		break;
	}
        fprintf(fp, "         radix: %d (%s)\n", pc->output_radix,
                pc->output_radix == 10 ? "decimal" :
                pc->output_radix == 16 ? "hexadecimal" : "unknown");
	fprintf(fp, "       refresh: %s\n", tt->flags & TASK_REFRESH ? "on" : "off");
	fprintf(fp, "     print_max: %d\n", *gdb_print_max);
	fprintf(fp, "   print_array: %s\n", *gdb_prettyprint_arrays ? "on" : "off");
	fprintf(fp, "       console: %s\n", pc->console ? 
		pc->console : "(not assigned)");
	fprintf(fp, "         debug: %ld\n", pc->debug);
	fprintf(fp, "          core: %s\n", pc->flags & DROP_CORE ? "on" : "off");
	fprintf(fp, "          hash: %s\n", pc->flags & HASH ? "on" : "off");
	fprintf(fp, "        silent: %s\n", pc->flags & SILENT ? "on" : "off"); 
	fprintf(fp, "          edit: %s\n", pc->editing_mode);
	fprintf(fp, "      namelist: %s\n", pc->namelist);
	fprintf(fp, "      dumpfile: %s\n", pc->dumpfile);
	fprintf(fp, "        unwind: %s\n", kt->flags & DWARF_UNWIND ? "on" : "off");
	fprintf(fp, " zero_excluded: %s\n",
		(*diskdump_flags & ZERO_EXCLUDED) || sadump_is_zero_excluded() ?
		"on" : "off");
	fprintf(fp, "     null-stop: %s\n", *gdb_stop_print_at_null ? "on" : "off");
	fprintf(fp, "           gdb: %s\n", pc->flags2 & GDB_CMD_MODE ? "on" : "off");
	fprintf(fp, "         scope: %lx ", pc->scope);
	if (pc->scope)
		fprintf(fp, "(%s)\n", value_to_symstr(pc->scope, buf, 0));
	else
		fprintf(fp, "(not set)\n");
	fprintf(fp, "       offline: %s\n", pc->flags2 & OFFLINE_HIDE ? "hide" : "show");
	fprintf(fp, "       redzone: %s\n", pc->flags2 & REDZONE ? "on" : "off");
}




/*
 *  Evaluate an expression, which can consist of a single symbol, single value,
 *  or an expression consisting of two values and an operator.  If the 
 *  expression contains redirection characters, the whole expression must
 *  be enclosed with parentheses.  The result is printed in decimal, hex,
 *  octal and binary.  Input number values can only be hex or decimal, with
 *  a bias towards decimal (use 0x when necessary).
 */
void 
cmd_eval(void)
{
	int flags;
	int bitflag, longlongflag, longlongflagforce;
	struct number_option nopt;
	char buf1[BUFSIZE];

	/*
	 *  getopt() is not used to avoid confusion with minus sign.
	 */
	optind = 1;
	bitflag = 0;
	longlongflag = longlongflagforce = 0;
	BZERO(&nopt, sizeof(struct number_option));

	if (STREQ(args[optind], "-lb") || STREQ(args[optind], "-bl")) {
		longlongflagforce++;
		bitflag++;
		optind++;
	} else if (STREQ(args[optind], "-l")) {
		longlongflagforce++;
		optind++;
		if (STREQ(args[optind], "-b") && args[optind+1]) { 
			optind++;
			bitflag++;
		}
	} else if (STREQ(args[optind], "-b")) { 
		if (STREQ(args[optind+1], "-l")) { 
			if (args[optind+2]) {
				bitflag++;
				longlongflagforce++;
				optind += 2;
			} else
                		cmd_usage(pc->curcmd, SYNOPSIS);
		} else if (args[optind+1]) {
			bitflag++;
			optind++;
		}
	}

        if (!args[optind])
                cmd_usage(pc->curcmd, SYNOPSIS);

	longlongflag = BITS32() ? TRUE : FALSE;
	flags = longlongflag ? (LONG_LONG|RETURN_ON_ERROR) : FAULT_ON_ERROR;

	if(!BITS32())
		longlongflagforce = 0;

	BZERO(buf1, BUFSIZE);
	buf1[0] = '(';

        while (args[optind]) {
                if (*args[optind] == '(') {
			if (eval_common(args[optind], flags, NULL, &nopt))
				print_number(&nopt, bitflag, longlongflagforce);
			else
				error(FATAL, "invalid expression: %s\n", 
					args[optind]);
			return;
                }
		else {
			strcat(buf1, args[optind]);
			strcat(buf1, " ");
		}
		optind++;
        }
	clean_line(buf1);
	strcat(buf1, ")");

	if (eval_common(buf1, flags, NULL, &nopt))
        	print_number(&nopt, bitflag, longlongflagforce);
	else
		error(FATAL, "invalid expression: %s\n", buf1);
}

/*
 *  Pre-check a string for eval-worthiness.  This allows callers to avoid
 *  having to encompass a non-whitespace expression with parentheses.
 *  Note that the data being evaluated is not error-checked here, but
 *  rather that it exists in the proper format.
 */
int
can_eval(char *s)
{
	char *op;
	char *element1, *element2;
	char work[BUFSIZE];

	/*
	 *  If we've got a () pair containing any sort of stuff in between,
	 *  then presume it's eval-able.  It might contain crap, but it 
	 *  should be sent to eval() regardless.
	 */
	if ((FIRSTCHAR(s) == '(') &&
	    (count_chars(s, '(') == 1) &&
	    (count_chars(s, ')') == 1) &&
	    (strlen(s) > 2) &&
	    (LASTCHAR(s) == ')'))
		return TRUE;

	/*
	 *  If the string contains any of the operators except the shifters,
         *  and has any kind of data on either side, it's also eval-able.
	 */
	strcpy(work, s);

        if (!(op = strpbrk(work, "><+-&|*/%^")))
		return FALSE; 

        element1 = &work[0];
        *op = NULLCHAR;
	element2 = op+1;

	if (!strlen(element1) || !strlen(element2))
		return FALSE;

	return TRUE;
}

/*
 *  Evaluate an expression involving two values and an operator.  
 */
#define OP_ADD   (1)
#define OP_SUB   (2)
#define OP_AND   (3)
#define OP_OR    (4)
#define OP_MUL   (5)
#define OP_DIV   (6)
#define OP_MOD   (7)
#define OP_SL    (8)
#define OP_SR    (9)
#define OP_EXOR  (10)
#define OP_POWER (11)

ulong
eval(char *s, int flags, int *errptr)
{
	struct number_option nopt;

	if (eval_common(s, flags, errptr, &nopt)) {
		return(nopt.num);
	} else {
	        switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
	        {
	        case FAULT_ON_ERROR:
	                error(FATAL, "invalid expression: %s\n", s);
	
	        case RETURN_ON_ERROR:
	                error(INFO, "invalid expression: %s\n", s);
	                if (errptr)
	                        *errptr = TRUE;
	                break;
	        }
        	return UNUSED;
	}
}

ulonglong
evall(char *s, int flags, int *errptr)
{
        struct number_option nopt;

	if (BITS32())
		flags |= LONG_LONG;

        if (eval_common(s, flags, errptr, &nopt)) {
                return(nopt.ll_num);
        } else {
                switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
                {
                case FAULT_ON_ERROR:
                        error(FATAL, "invalid expression: %s\n", s);

                case RETURN_ON_ERROR:
                        error(INFO, "invalid expression: %s\n", s);
                        if (errptr)
                                *errptr = TRUE;
                        break;
                }
                return UNUSED;
        }
}


int
eval_common(char *s, int flags, int *errptr, struct number_option *np)
{
	char *p1, *p2;
        char *op, opcode;
	ulong value1;
	ulong value2;
	ulonglong ll_value1;
	ulonglong ll_value2;
	char work[BUFSIZE];
	char *element1;
	char *element2;
	struct syment *sp;

	opcode = 0;
	value1 = value2 = 0;
	ll_value1 = ll_value2 = 0;

	if (strstr(s, "(") || strstr(s, ")")) {
		p1 = s;
		if (*p1 != '(')
			goto malformed;
		if (LASTCHAR(s) != ')')
			goto malformed;
		p2 = &LASTCHAR(s);
		if (strstr(s, ")") != p2)
			goto malformed;
	
		strcpy(work, p1+1);
		LASTCHAR(work) = NULLCHAR;
	
		if (strstr(work, "(") || strstr(work, ")")) 
			goto malformed;
	} else
		strcpy(work, s);

        if (work[0] == '-') {
                shift_string_right(work, 1);
                work[0] = '0';
        }

        if (!(op = strpbrk(work, "#><+-&|*/%^"))) {
		if (calculate(work, &value1, &ll_value1, 
		    flags & (HEX_BIAS|LONG_LONG))) { 
			if (flags & LONG_LONG) {
				np->ll_num = ll_value1;
				if (BITS32() && (ll_value1 > 0xffffffff)) 
					np->retflags |= LONG_LONG;
				return TRUE;
			} else {
				np->num = value1;
				return TRUE;
			}
		}
               	goto malformed;
        }

	switch (*op)
        {
        case '+': 
		opcode = OP_ADD; 
		break;

        case '-': 
		opcode = OP_SUB; 
		break;

        case '&': 
		opcode = OP_AND; 
		break;

        case '|': 
		opcode = OP_OR; 
		break;

        case '*': 
		opcode = OP_MUL; 
		break;

        case '%': 
		opcode = OP_MOD; 
		break;

        case '/': 
		opcode = OP_DIV; 
		break;

	case '<': 
		if (*(op+1) != '<')
			goto malformed;
		opcode = OP_SL;
	        break;

	case '>': 
                if (*(op+1) != '>')
                        goto malformed;
                opcode = OP_SR;
	        break;

	case '^':
		opcode = OP_EXOR;
		break;

	case '#':
		opcode = OP_POWER;
		break;
	}

        element1 = &work[0];
	*op = NULLCHAR;
	if ((opcode == OP_SL) || (opcode == OP_SR)) {
		*(op+1) = NULLCHAR;
		element2 = op+2;
	} else 
		element2 = op+1;

        if (strlen(clean_line(element1)) == 0)
                goto malformed;

        if (strlen(clean_line(element2)) == 0)
                goto malformed;

	if ((sp = symbol_search(element1)))
                value1 = ll_value1 = sp->value;
	else {
		if (!calculate(element1, &value1, &ll_value1, 
		    flags & (HEX_BIAS|LONG_LONG)))
			goto malformed;
                if (BITS32() && (ll_value1 > 0xffffffff)) 
                	np->retflags |= LONG_LONG;
	}

        if ((sp = symbol_search(element2)))
                value2 = ll_value2 = sp->value;
        else if (!calculate(element2, &value2, &ll_value2, 
	    	flags & (HEX_BIAS|LONG_LONG)))
		goto malformed;

	if (flags & LONG_LONG) {
		if (BITS32() && (ll_value2 > 0xffffffff)) 
			np->retflags |= LONG_LONG;

                switch (opcode)
                {
                case OP_ADD:
                        np->ll_num = (ll_value1 + ll_value2);
			break;           
                case OP_SUB:
                        np->ll_num = (ll_value1 - ll_value2);
			break;           
                case OP_AND:
                        np->ll_num = (ll_value1 & ll_value2);
			break;           
                case OP_OR:
                        np->ll_num = (ll_value1 | ll_value2);
			break;           
                case OP_MUL:
                        np->ll_num = (ll_value1 * ll_value2);
			break;           
                case OP_DIV:
                        np->ll_num = (ll_value1 / ll_value2);
			break;           
                case OP_MOD:
                        np->ll_num = (ll_value1 % ll_value2);
			break;           
                case OP_SL:
                        np->ll_num = (ll_value1 << ll_value2);
			break;           
                case OP_SR:
                        np->ll_num = (ll_value1 >> ll_value2);
			break;           
                case OP_EXOR:
                        np->ll_num = (ll_value1 ^ ll_value2);
			break;
		case OP_POWER:
			np->ll_num = ll_power(ll_value1, ll_value2);
			break;
                }
	} else {
		switch (opcode)
		{
		case OP_ADD: 
			np->num = (value1 + value2);
			break;
		case OP_SUB:
			np->num = (value1 - value2);
			break;
		case OP_AND: 
			np->num = (value1 & value2);
			break;
		case OP_OR:  
			np->num = (value1 | value2);
			break;
		case OP_MUL: 
			np->num = (value1 * value2);
			break;
		case OP_DIV: 
			np->num = (value1 / value2);
			break;
		case OP_MOD: 
			np->num = (value1 % value2);
			break;
		case OP_SL:  
			np->num = (value1 << value2);
			break;
		case OP_SR:  
			np->num = (value1 >> value2);
			break;
		case OP_EXOR:
			np->num = (value1 ^ value2);
			break;
		case OP_POWER:
			np->num = power(value1, value2);
			break;
		}
	}

	return TRUE;

malformed:
	return FALSE;
}


/*
 *  Take string containing a number, and possibly a multiplier, and calculate
 *  its real value.  The allowable multipliers are k, K, m, M, g and G, for
 *  kilobytes, megabytes and gigabytes.
 */
int
calculate(char *s, ulong *value, ulonglong *llvalue, ulong flags)
{
	ulong factor, bias;
	int errflag;
	int ones_complement;
	ulong localval;
	ulonglong ll_localval;
	struct syment *sp;

	bias = flags & HEX_BIAS;

	if (*s == '~') {
		ones_complement = TRUE;
		s++;
	} else
		ones_complement = FALSE;

        if ((sp = symbol_search(s))) {
		if (flags & LONG_LONG) {
			*llvalue = (ulonglong)sp->value;
			if (ones_complement)
                		*llvalue = ~(*llvalue);
		} else 
                	*value = ones_complement ? ~(sp->value) : sp->value;
		return TRUE;
	}

	factor = 1;
	errflag = 0;

        switch (LASTCHAR(s))
        {
        case 'k':
        case 'K':
                LASTCHAR(s) = NULLCHAR;
                if (IS_A_NUMBER(s))
                        factor = 1024;
		else
			return FALSE;
                break;

        case 'm':
        case 'M':
                LASTCHAR(s) = NULLCHAR;
                if (IS_A_NUMBER(s))
                        factor = (1024*1024);
		else 
			return FALSE;
                break;

        case 'g':
        case 'G':
                LASTCHAR(s) = NULLCHAR;
                if (IS_A_NUMBER(s))
                        factor = (1024*1024*1024);
		else
			return FALSE;
                break;

        default:
		if (!IS_A_NUMBER(s))
			return FALSE;
		break;
        }

	if (flags & LONG_LONG) {
                ll_localval = stoll(s, RETURN_ON_ERROR|bias, &errflag);
                if (errflag)
                        return FALSE;

                if (ones_complement)
                        *llvalue = ~(ll_localval * factor);
                else
                        *llvalue = ll_localval * factor;
	} else {
		localval = stol(s, RETURN_ON_ERROR|bias, &errflag);
		if (errflag)
			return FALSE;

		if (ones_complement)
			*value = ~(localval * factor);
		else
			*value = localval * factor;
	}

	return TRUE;
}


/*
 *  Print a 32-bit or 64-bit number in hexadecimal, decimal, octal and binary,
 *  also showing the bits set if appropriate.
 *  
 */
static void
print_number(struct number_option *np, int bitflag, int longlongflagforce)
{
	int i;
	ulong hibit;
	ulonglong ll_hibit;
        int ccnt;
        ulong mask;
	ulonglong ll_mask;
        char *hdr = "   bits set: ";
        char buf[BUFSIZE];
        int hdrlen;
	int longlongformat;

	longlongformat = longlongflagforce;

	if (!longlongflagforce) {
		if (BITS32()) {
			if (np->retflags & LONG_LONG)
				longlongformat = TRUE;
			if (np->ll_num > 0xffffffff) 
				longlongformat = TRUE;
			else
				np->num = (ulong)np->ll_num;
		} 
	}

	if (longlongformat) {
                ll_hibit = (ulonglong)(1) << ((sizeof(long long)*8)-1);
                
                fprintf(fp, "hexadecimal: %llx  ", np->ll_num);
                if (np->ll_num >= KILOBYTES(1)) {
                        if ((np->ll_num % GIGABYTES(1)) == 0)
                                fprintf(fp, "(%lldGB)", 
					np->ll_num / GIGABYTES(1));
                        else if ((np->ll_num % MEGABYTES(1)) == 0)
                                fprintf(fp, "(%lldMB)", 
					np->ll_num / MEGABYTES(1));
                        else if ((np->ll_num % KILOBYTES(1)) == 0)
                                fprintf(fp, "(%lldKB)",
					 np->ll_num / KILOBYTES(1));
                }
                fprintf(fp, "\n");

                fprintf(fp, "    decimal: %llu  ", np->ll_num);
                if ((long long)np->ll_num < 0)
                        fprintf(fp, "(%lld)\n", (long long)np->ll_num);
                else
                        fprintf(fp, "\n");
                fprintf(fp, "      octal: %llo\n", np->ll_num);
                fprintf(fp, "     binary: ");
                for(i = 0, ll_mask = np->ll_num; i < (sizeof(long long)*8); 
		    i++, ll_mask <<= 1)
                        if (ll_mask & ll_hibit)
                                fprintf(fp, "1");
                        else
                                fprintf(fp, "0");
                fprintf(fp,"\n");
	} else {
		hibit = (ulong)(1) << ((sizeof(long)*8)-1);
	
	        fprintf(fp, "hexadecimal: %lx  ", np->num);
	        if (np->num >= KILOBYTES(1)) {
	                if ((np->num % GIGABYTES(1)) == 0)
	                        fprintf(fp, "(%ldGB)", np->num / GIGABYTES(1));
	                else if ((np->num % MEGABYTES(1)) == 0)
	                        fprintf(fp, "(%ldMB)", np->num / MEGABYTES(1));
	                else if ((np->num % KILOBYTES(1)) == 0)
	                        fprintf(fp, "(%ldKB)", np->num / KILOBYTES(1));
	        }
	        fprintf(fp, "\n");
	
	        fprintf(fp, "    decimal: %lu  ", np->num);
		if ((long)np->num < 0)
	                fprintf(fp, "(%ld)\n", (long)np->num);
	        else
	                fprintf(fp, "\n");
	        fprintf(fp, "      octal: %lo\n", np->num);
	        fprintf(fp, "     binary: ");
	        for(i = 0, mask = np->num; i < (sizeof(long)*8); 
		    i++, mask <<= 1)
	                if (mask & hibit)
	                        fprintf(fp, "1");
	                else
	                        fprintf(fp, "0");
	        fprintf(fp,"\n");
	}

	if (!bitflag)
		return;

	hdrlen = strlen(hdr);
	ccnt = hdrlen;
	fprintf(fp, "%s", hdr);

	if (longlongformat) {
	        for (i = 63; i >= 0; i--) {
	                ll_mask = (ulonglong)(1) << i;
	                if (np->ll_num & ll_mask) {
	                        sprintf(buf, "%d ", i);
	                        fprintf(fp, "%s", buf);
	                        ccnt += strlen(buf);
	                        if (ccnt >= 77) {
	                                fprintf(fp, "\n");
	                                INDENT(strlen(hdr));
	                                ccnt = hdrlen;
	                        }
	                }
	        }
	} else {
	        for (i = BITS()-1; i >= 0; i--) {
	                mask = (ulong)(1) << i;
	                if (np->num & mask) {
	                        sprintf(buf, "%d ", i);
	                        fprintf(fp, "%s", buf);
	                        ccnt += strlen(buf);
	                        if (ccnt >= 77) {
	                                fprintf(fp, "\n");
	                                INDENT(strlen(hdr));
	                                ccnt = hdrlen;
	                        }
	                }
	        }
	}
        fprintf(fp, "\n");
}


/*
 *  Display the contents of a linked list.  Minimum requirements are a starting
 *  address, typically of a structure which contains the "next" list entry at 
 *  some offset into the structure.  The default offset is zero bytes, and need
 *  not be entered if that's the case.  Otherwise a number argument that's not 
 *  a kernel *  virtual address will be understood to be the offset.  
 *  Alternatively the offset may be entered in "struct.member" format.  Each 
 *  item in the list is dumped, and the list will be considered terminated upon
 *  encountering a "next" value that is:
 *
 *     a NULL pointer. 
 *     a pointer to the starting address. 
 *     a pointer to the entry pointed to by the starting address. 
 *     a pointer to the structure itself.
 *     a pointer to the value specified with the "-e ending_addr" option.
 *
 *  If the structures are linked using list_head structures, the -h or -H 
 *  options must be used.  In that case, the "start" address is:
 *  a pointer to the structure that contains the list_head structure (-h),
 *  or a pointer to a LIST_HEAD() structure (-H).
 *
 *  Given that the contents of the structures containing the next pointers
 *  often contain useful data, the "-s structname" also prints each structure
 *  in the list. 
 *
 *  By default, the list members are hashed to guard against duplicate entries
 *  causing the list to wrap back upon itself.
 *
 *  WARNING: There's an inordinate amount of work parsing arguments below
 *  in order to maintain backwards compatibility re: not having to use -o,
 *  which gets sticky with zero-based kernel virtual address space.
 */

void
cmd_list(void)
{
	int c;
	struct list_data list_data, *ld;
	struct datatype_member struct_member, *sm;
	struct syment *sp;
	ulong value, struct_list_offset; 

	sm = &struct_member;
	ld = &list_data;
	BZERO(ld, sizeof(struct list_data));
	struct_list_offset = 0;

	while ((c = getopt(argcnt, args, "Hhrs:S:e:o:xdl:")) != EOF) {
                switch(c)
		{
		case 'H':
			ld->flags |= LIST_HEAD_FORMAT;
			ld->flags |= LIST_HEAD_POINTER;
			break;

		case 'h':
			ld->flags |= LIST_HEAD_FORMAT;
			break;

		case 'r':
			ld->flags |= LIST_HEAD_REVERSE;
			break;

		case 's':
		case 'S':
			if (ld->structname_args++ == 0)
				hq_open();
			hq_enter((ulong)optarg);
			ld->flags |= (c == 's') ? LIST_PARSE_MEMBER : LIST_READ_MEMBER;
			if (count_bits_long(ld->flags & (LIST_PARSE_MEMBER|LIST_READ_MEMBER)) > 1)
				error(FATAL, "-S and -s options are mutually exclusive\n");
			break;

		case 'l':
                        if (IS_A_NUMBER(optarg))
                                struct_list_offset = stol(optarg,
                                        FAULT_ON_ERROR, NULL);
                        else if (arg_to_datatype(optarg,
                                sm, RETURN_ON_ERROR) > 1)
                                struct_list_offset = sm->member_offset;
			else
				error(FATAL, "invalid -l option: %s\n", 
					optarg);
			break;

		case 'o':
			if (ld->flags & LIST_OFFSET_ENTERED) 
                               error(FATAL,
                                "offset value %d (0x%lx) already entered\n",
                                        ld->member_offset, ld->member_offset);
			else if (IS_A_NUMBER(optarg)) 
				ld->member_offset = stol(optarg, 
					FAULT_ON_ERROR, NULL);
			else if (arg_to_datatype(optarg, 
				sm, RETURN_ON_ERROR) > 1) 
				ld->member_offset = sm->member_offset;
			else
				error(FATAL, "invalid -o argument: %s\n",
					optarg);

			ld->flags |= LIST_OFFSET_ENTERED; 
			break;

		case 'e':
			ld->end = htol(optarg, FAULT_ON_ERROR, NULL);
			break;

		case 'x':
			if (ld->flags & LIST_STRUCT_RADIX_10)
				error(FATAL,
					"-d and -x are mutually exclusive\n");
			ld->flags |= LIST_STRUCT_RADIX_16;
			break;

		case 'd':
			if (ld->flags & LIST_STRUCT_RADIX_16)
				error(FATAL,
					"-d and -x are mutually exclusive\n");
			ld->flags |= LIST_STRUCT_RADIX_10;
			break;

		default:
			argerrs++;
			break;
		}
	}

	if (argerrs)
		cmd_usage(pc->curcmd, SYNOPSIS);

	if (args[optind] && args[optind+1] && args[optind+2]) {
		error(INFO, "too many arguments\n");
		cmd_usage(pc->curcmd, SYNOPSIS);
	}

	if (ld->structname_args) {
		ld->structname = (char **)GETBUF(sizeof(char *) * ld->structname_args);
		retrieve_list((ulong *)ld->structname, ld->structname_args); 
		hq_close(); 
		ld->struct_list_offset = struct_list_offset;
	} else if (struct_list_offset) {
		error(INFO, "-l option can only be used with -s or -S option\n");
		cmd_usage(pc->curcmd, SYNOPSIS);
	}

	while (args[optind]) {
		if (strstr(args[optind], ".") &&
		    arg_to_datatype(args[optind], sm, RETURN_ON_ERROR) > 1) {
			if (ld->flags & LIST_OFFSET_ENTERED)
				error(FATAL, 
			           "offset value %ld (0x%lx) already entered\n",
					ld->member_offset, ld->member_offset);
			ld->member_offset = sm->member_offset;
			ld->flags |= LIST_OFFSET_ENTERED;
		} else {
			/* 
			 *  Do an inordinate amount of work to avoid -o...
			 *
			 *  OK, if it's a symbol, then it has to be a start.
			 */
			if ((sp = symbol_search(args[optind]))) {
				if (ld->flags & LIST_START_ENTERED) 
                                        error(FATAL,
                                            "list start already entered\n");
                                ld->start = sp->value;
                                ld->flags |= LIST_START_ENTERED;
				goto next_arg;
			}

			/*
			 *  If it's not a symbol nor a number, bail out if it
			 *  cannot be evaluated as a start address.
			 */
			if (!IS_A_NUMBER(args[optind])) {	
				if (can_eval(args[optind])) {
                        		value = eval(args[optind], FAULT_ON_ERROR, NULL);
					if (IS_KVADDR(value)) {
                               			if (ld->flags & LIST_START_ENTERED)
                                        		error(FATAL,
                                            		    "list start already entered\n");
                                		ld->start = value;
                                		ld->flags |= LIST_START_ENTERED;
						goto next_arg;
					}
				}
				
				error(FATAL, "invalid argument: %s\n",
                                	args[optind]);
			}

			/*
			 *  If the start is known, it's got to be an offset.
			 */
                        if (ld->flags & LIST_START_ENTERED) {
                                value = stol(args[optind], FAULT_ON_ERROR,
                                        NULL);
                                ld->member_offset = value;
                                ld->flags |= LIST_OFFSET_ENTERED;
                                break;
                        }

			/*
			 *  If the offset is known, or there's no subsequent
                         *  argument, then it's got to be a start.
			 */
			if ((ld->flags & LIST_OFFSET_ENTERED) ||
			    !args[optind+1]) {
				value = htol(args[optind], FAULT_ON_ERROR, 
					NULL);
				if (!IS_KVADDR(value))
					error(FATAL, 
				        "invalid kernel virtual address: %s\n",
						args[optind]);
                                ld->start = value;
                                ld->flags |= LIST_START_ENTERED;
				break;
			}

			/*
			 *  Neither start nor offset has been entered, and
			 *  it's a number.  Look ahead to the next argument.
			 *  If it's a symbol, then this must be an offset.
			 */
			if ((sp = symbol_search(args[optind+1]))) {
                                value = stol(args[optind], FAULT_ON_ERROR,
                                        NULL);
                                ld->member_offset = value;
                                ld->flags |= LIST_OFFSET_ENTERED;
                                goto next_arg;
			} else if ((!IS_A_NUMBER(args[optind+1]) &&
				!can_eval(args[optind+1])) &&
				!strstr(args[optind+1], "."))
				error(FATAL, "symbol not found: %s\n",
                                        args[optind+1]);
			/*
			 *  Crunch time.  We've got two numbers.  If they're
			 *  both ambigous we must have zero-based kernel 
			 *  virtual address space.
			 */
			if (COMMON_VADDR_SPACE() &&
			    AMBIGUOUS_NUMBER(args[optind]) &&
			    AMBIGUOUS_NUMBER(args[optind+1])) {
				error(INFO, 
                     "ambiguous arguments: \"%s\" and \"%s\": -o is required\n",
					args[optind], args[optind+1]);
				cmd_usage(pc->curcmd, SYNOPSIS);
			}

			if (hexadecimal_only(args[optind], 0)) {
				value = htol(args[optind], FAULT_ON_ERROR, 
					NULL);
                                if (IS_KVADDR(value)) {
                                	ld->start = value;
                                	ld->flags |= LIST_START_ENTERED;
					goto next_arg;
				}
			} 
			value = stol(args[optind], FAULT_ON_ERROR, NULL);
                        ld->member_offset = value;
                        ld->flags |= LIST_OFFSET_ENTERED;
		}
next_arg:
		optind++;
	}

	if (!(ld->flags & LIST_START_ENTERED)) {
		error(INFO, "starting address required\n");
		cmd_usage(pc->curcmd, SYNOPSIS);
	}

	if ((ld->flags & LIST_OFFSET_ENTERED) && ld->struct_list_offset) {
		error(INFO, "-l and -o are mutually exclusive\n");
                cmd_usage(pc->curcmd, SYNOPSIS);
	}

	if (ld->flags & LIST_HEAD_FORMAT) {
		ld->list_head_offset = ld->member_offset;
		if (ld->flags & LIST_HEAD_REVERSE)
			ld->member_offset = sizeof(void *);
		else
			ld->member_offset = 0;
		if (ld->flags & LIST_HEAD_POINTER) {
			if (!ld->end)
				ld->end = ld->start;
			readmem(ld->start + ld->member_offset, KVADDR, &ld->start,
				sizeof(void *), "LIST_HEAD contents", FAULT_ON_ERROR);
			if (ld->start == ld->end) {
				fprintf(fp, "(empty)\n");
				return;
			}
		} else
			ld->start += ld->list_head_offset;
	}

	ld->flags &= ~(LIST_OFFSET_ENTERED|LIST_START_ENTERED);
	ld->flags |= VERBOSE;

	hq_open();
	c = do_list(ld);
	hq_close();

        if (ld->structname_args)
		FREEBUF(ld->structname);
}

void
dump_struct_members_fast(struct req_entry *e, int radix, ulong p)
{
	unsigned int i;
	char b[BUFSIZE];

	if (!(e && IS_KVADDR(p)))
		return;

	if (!radix)
		radix = *gdb_output_radix;

	for (i = 0; i < e->count; i++) {
		if (0 < e->width[i] && (e->width[i] <= 8 || e->is_str[i])) {
			print_value(e, i, p, e->is_ptr[i] ? 16 : radix);
		} else if (e->width[i] == 0 || e->width[i] > 8) {
			snprintf(b, BUFSIZE, "%s.%s", e->name, e->member[i]);
			dump_struct_member(b, p, radix);
		}
	}
}

static struct req_entry *
fill_member_offsets(char *arg)
{
	int j;
	char *p, m;
	struct req_entry *e;
	char buf[BUFSIZE];

	if (!(arg && *arg))
		return NULL;

	j = count_chars(arg, ',') + 1;
	e = (struct req_entry *)GETBUF(sizeof(*e));

	e->arg = GETBUF(strlen(arg + 1));
	strcpy(e->arg, arg);

	m = ((p = strchr(e->arg, '.')) != NULL);
	if (!p++)
		p = e->arg + strlen(e->arg) + 1;

	e->name = GETBUF(p - e->arg);
	strncpy(e->name, e->arg, p - e->arg - 1);

	if (!m)
		return e;

	e->count  = count_chars(p, ',') + 1;
	e->width  = (ulong *)GETBUF(e->count * sizeof(ulong));
	e->is_ptr = (int *)GETBUF(e->count * sizeof(int));
	e->is_str = (int *)GETBUF(e->count * sizeof(int));
	e->member = (char **)GETBUF(e->count * sizeof(char *));
	e->offset = (ulong *)GETBUF(e->count * sizeof(ulong));

	replace_string(p, ",", ' ');
	parse_line(p, e->member);

	for (j = 0; j < e->count; j++) {
		e->offset[j] = MEMBER_OFFSET(e->name, e->member[j]);
		if (e->offset[j] == INVALID_OFFSET)
			e->offset[j] = ANON_MEMBER_OFFSET(e->name, e->member[j]);
		if (e->offset[j] == INVALID_OFFSET)
			error(FATAL, "Can't get offset of '%s.%s'\n",
				e->name, e->member[j]);

		e->is_ptr[j] = MEMBER_TYPE(e->name, e->member[j]) == TYPE_CODE_PTR;
		e->is_str[j] = is_string(e->name, e->member[j]);

		/* Dirty hack for obtaining size of particular field */
		snprintf(buf, BUFSIZE, "%s + 1", e->member[j]);
		e->width[j] = ANON_MEMBER_OFFSET(e->name, buf) - e->offset[j];
	}

	return e;
}

static void
print_value(struct req_entry *e, unsigned int i, ulong addr, unsigned int radix)
{
	union { uint64_t v64; uint32_t v32;
		uint16_t v16; uint8_t v8;
	} v;
	char buf[BUFSIZE];
	struct syment *sym;

	addr += e->offset[i];

	/* Read up to 8 bytes, counters, pointers, etc. */
	if (e->width[i] <= 8 && !readmem(addr, KVADDR, &v, e->width[i],
	    "structure value", RETURN_ON_ERROR | QUIET)) {
		error(INFO, "cannot access member: %s at %lx\n", e->member[i], addr);
		return;
	}
	snprintf(buf, BUFSIZE, "  %%s = %s%%%s%s",
		 (radix == 16 ? "0x" : ""),
		 (e->width[i] == 8 ? "l" : ""),
		 (radix == 16 ? "x" : "u" )
		);

	switch (e->width[i]) {
		case 1: fprintf(fp, buf, e->member[i], v.v8); break;
		case 2: fprintf(fp, buf, e->member[i], v.v16); break;
		case 4: fprintf(fp, buf, e->member[i], v.v32); break;
		case 8: fprintf(fp, buf, e->member[i], v.v64); break;
	}


	if (e->is_str[i]) {
		if (e->is_ptr[i]) {
			read_string(v.v64, buf, BUFSIZE);
			fprintf(fp, "  \"%s\"", buf);
		} else {
			read_string(addr, buf, e->width[i]);
			fprintf(fp, "  %s = \"%s\"", e->member[i], buf);
		}
	} else if ((sym = value_search(v.v64, 0)) && is_symbol_text(sym))
		fprintf(fp, " <%s>", sym->name);

	fprintf(fp, "\n");
}

/*
 *  Does the work for cmd_list() and any other function that requires the
 *  contents of a linked list.  See cmd_list description above for details.
 */
int
do_list(struct list_data *ld)
{
	ulong next, last, first, offset;
	ulong searchfor, readflag;
	int i, count, others, close_hq_on_return;
	unsigned int radix;
	struct req_entry **e = NULL;

	if (CRASHDEBUG(1)) {
		others = 0;
		console("             flags: %lx (", ld->flags);
		if (ld->flags & VERBOSE)
			console("%sVERBOSE", others++ ? "|" : "");
		if (ld->flags & LIST_OFFSET_ENTERED)
			console("%sLIST_OFFSET_ENTERED", others++ ? "|" : "");
		if (ld->flags & LIST_START_ENTERED)
			console("%sLIST_START_ENTERED", others++ ? "|" : "");
		if (ld->flags & LIST_HEAD_FORMAT)
			console("%sLIST_HEAD_FORMAT", others++ ? "|" : "");
		if (ld->flags & LIST_HEAD_POINTER)
			console("%sLIST_HEAD_POINTER", others++ ? "|" : "");
		if (ld->flags & RETURN_ON_DUPLICATE)
			console("%sRETURN_ON_DUPLICATE", others++ ? "|" : "");
		if (ld->flags & RETURN_ON_LIST_ERROR)
			console("%sRETURN_ON_LIST_ERROR", others++ ? "|" : "");
		if (ld->flags & RETURN_ON_LIST_ERROR)
			console("%sRETURN_ON_LIST_ERROR", others++ ? "|" : "");
		if (ld->flags & LIST_STRUCT_RADIX_10)
			console("%sLIST_STRUCT_RADIX_10", others++ ? "|" : "");
		if (ld->flags & LIST_STRUCT_RADIX_16)
			console("%sLIST_STRUCT_RADIX_16", others++ ? "|" : "");
		if (ld->flags & LIST_ALLOCATE)
			console("%sLIST_ALLOCATE", others++ ? "|" : "");
		if (ld->flags & LIST_CALLBACK)
			console("%sLIST_CALLBACK", others++ ? "|" : "");
		if (ld->flags & CALLBACK_RETURN)
			console("%sCALLBACK_RETURN", others++ ? "|" : "");
		console(")\n");
		console("             start: %lx\n", ld->start);
		console("     member_offset: %ld\n", ld->member_offset);
		console("  list_head_offset: %ld\n", ld->list_head_offset);
		console("               end: %lx\n", ld->end);
		console("         searchfor: %lx\n", ld->searchfor);
		console("   structname_args: %lx\n", ld->structname_args);
		if (!ld->structname_args)
			console("        structname: (unused)\n");
		for (i = 0; i < ld->structname_args; i++)	
			console("     structname[%d]: %s\n", i, ld->structname[i]);
		console("            header: %s\n", ld->header);
		console("          list_ptr: %lx\n", (ulong)ld->list_ptr);
		console("     callback_func: %lx\n", (ulong)ld->callback_func);
		console("     callback_data: %lx\n", (ulong)ld->callback_data);
		console("struct_list_offset: %lx\n", ld->struct_list_offset);
	}

	count = 0;
	searchfor = ld->searchfor;
	ld->searchfor = 0;
	if (ld->flags & LIST_STRUCT_RADIX_10)
		radix = 10;
	else if (ld->flags & LIST_STRUCT_RADIX_16)
		radix = 16;
	else	
		radix = 0;
	next = ld->start;

	close_hq_on_return = FALSE;
	if (ld->flags & LIST_ALLOCATE) {
		if (!hq_is_open()) {
			hq_open();
			close_hq_on_return = TRUE;
		} else if (hq_is_inuse()) {
			error(ld->flags & RETURN_ON_LIST_ERROR ? INFO : FATAL,
				"\ndo_list: hash queue is in use?\n");
			return -1;
		}
	}

	readflag = ld->flags & RETURN_ON_LIST_ERROR ? 
		(RETURN_ON_ERROR|QUIET) : FAULT_ON_ERROR;

	if (!readmem(next + ld->member_offset, KVADDR, &first, sizeof(void *),
            "first list entry", readflag)) {
                error(INFO, "\ninvalid list entry: %lx\n", next);
		if (close_hq_on_return)
			hq_close();
		return -1;
	}

	if (ld->header)
		fprintf(fp, "%s", ld->header);

	offset = ld->list_head_offset + ld->struct_list_offset;

	if (ld->structname && (ld->flags & LIST_READ_MEMBER)) {
		e = (struct req_entry **)GETBUF(sizeof(*e) * ld->structname_args);
		for (i = 0; i < ld->structname_args; i++)
			e[i] = fill_member_offsets(ld->structname[i]);
	}

	while (1) {
		if (ld->flags & VERBOSE) {
			fprintf(fp, "%lx\n", next - ld->list_head_offset);

			if (ld->structname) {
				for (i = 0; i < ld->structname_args; i++) {
					switch (count_chars(ld->structname[i], '.'))
					{
					case 0:
						dump_struct(ld->structname[i],
							next - offset, radix);
						break;
					default:
						if (ld->flags & LIST_PARSE_MEMBER)
							dump_struct_members(ld, i, next);
						else if (ld->flags & LIST_READ_MEMBER)
							dump_struct_members_fast(e[i],
								radix, next - offset);
						break;
					}
				}
			}
		}

                if (next && !hq_enter(next - ld->list_head_offset)) {
			if (ld->flags & 
			    (RETURN_ON_DUPLICATE|RETURN_ON_LIST_ERROR)) {
                        	error(INFO, "\nduplicate list entry: %lx\n", 
					next);
				if (close_hq_on_return)
					hq_close();
				return -1;
			}
                        error(FATAL, "\nduplicate list entry: %lx\n", next);
		}

		if ((searchfor == next) || 
		    (searchfor == (next - ld->list_head_offset)))
			ld->searchfor = searchfor;

		count++;
                last = next;

		if ((ld->flags & LIST_CALLBACK) &&
		    ld->callback_func((void *)(next - ld->list_head_offset),
		    ld->callback_data) && (ld->flags & CALLBACK_RETURN))
			break;

                if (!readmem(next + ld->member_offset, KVADDR, &next, 
		    sizeof(void *), "list entry", readflag)) {
			error(INFO, "\ninvalid list entry: %lx\n", next);
			if (close_hq_on_return)
				hq_close();
			return -1;
		}

		if (next == 0) {
			if (ld->flags & LIST_HEAD_FORMAT) {
				error(INFO, "\ninvalid list entry: 0\n");
				if (close_hq_on_return)
					hq_close();
				return -1;
			}
			if (CRASHDEBUG(1))
				console("do_list end: next:%lx\n", next);
			break;
		}

		if (next == ld->end) {
			if (CRASHDEBUG(1))
				console("do_list end: next:%lx == end:%lx\n", 
					next, ld->end);
			break;
		}

		if (next == ld->start) {
			if (CRASHDEBUG(1))
				console("do_list end: next:%lx == start:%lx\n", 
					next, ld->start);
			break;
		}

		if (next == last) {
			if (CRASHDEBUG(1))
				console("do_list end: next:%lx == last:%lx\n", 
					next, last);
			break;
		}

		if ((next == first) && (count != 1)) {
			if (CRASHDEBUG(1))
		      console("do_list end: next:%lx == first:%lx (count %d)\n",
				next, last, count);
			break;
		}
	}

	if (CRASHDEBUG(1))
		console("do_list count: %d\n", count);

	if (ld->flags & LIST_ALLOCATE) {
		ld->list_ptr = (ulong *)GETBUF(count * sizeof(void *));
		count = retrieve_list(ld->list_ptr, count);
		if (close_hq_on_return)
			hq_close();
	}

	return count;
}

/*
 *  Issue a dump_struct_member() call for one or more structure
 *  members.  Multiple members are passed in a comma-separated
 *  list using the the format:  
 *
 *            struct.member1,member2,member3
 */
void
dump_struct_members(struct list_data *ld, int idx, ulong next)
{
	int i, argc;
	char *p1, *p2;
	char *structname, *members;
	char *arglist[MAXARGS];
	unsigned int radix;

	if (ld->flags & LIST_STRUCT_RADIX_10)
		radix = 10;
	else if (ld->flags & LIST_STRUCT_RADIX_16)
		radix = 16;
	else
		radix = 0;

	structname = GETBUF(strlen(ld->structname[idx])+1);
	members = GETBUF(strlen(ld->structname[idx])+1);

	strcpy(structname, ld->structname[idx]);
	p1 = strstr(structname, ".") + 1;

	p2 = strstr(ld->structname[idx], ".") + 1;
	strcpy(members, p2);
	replace_string(members, ",", ' ');
	argc = parse_line(members, arglist);

	for (i = 0; i < argc; i++) {
		*p1 = NULLCHAR;
		strcat(structname, arglist[i]);
 		dump_struct_member(structname, 
			next - ld->list_head_offset - ld->struct_list_offset, radix);
	}

	FREEBUF(structname);
	FREEBUF(members);
}

#define RADIXTREE_REQUEST (0x1)
#define RBTREE_REQUEST    (0x2)

void
cmd_tree()
{
	int c, type_flag, others;
	long root_offset;
	struct tree_data tree_data, *td;
	struct datatype_member struct_member, *sm;
	struct syment *sp;
	ulong value;

	type_flag = 0;
	root_offset = 0;
	sm = &struct_member;
	td = &tree_data;
	BZERO(td, sizeof(struct tree_data));

	while ((c = getopt(argcnt, args, "xdt:r:o:s:S:pN")) != EOF) {
		switch (c)
		{
		case 't':
			if (type_flag & (RADIXTREE_REQUEST|RBTREE_REQUEST)) {
				error(INFO, "multiple tree types may not be entered\n");
				cmd_usage(pc->curcmd, SYNOPSIS);
			}

			if (STRNEQ(optarg, "ra"))
				type_flag = RADIXTREE_REQUEST;
			else if (STRNEQ(optarg, "rb"))
				type_flag = RBTREE_REQUEST;
			else {
				error(INFO, "invalid tree type: %s\n", optarg);
				cmd_usage(pc->curcmd, SYNOPSIS);
			}
				
			break;

		case 'r':
			if (td->flags & TREE_ROOT_OFFSET_ENTERED) 
				error(FATAL,
					"root offset value %d (0x%lx) already entered\n",
						root_offset, root_offset);
			else if (IS_A_NUMBER(optarg)) 
				root_offset = stol(optarg, FAULT_ON_ERROR, NULL);
			else if (arg_to_datatype(optarg, sm, RETURN_ON_ERROR) > 1) 
				root_offset = sm->member_offset;
			else
				error(FATAL, "invalid -r argument: %s\n",
					optarg);

			td->flags |= TREE_ROOT_OFFSET_ENTERED; 
			break;

		case 'o':
			if (td->flags & TREE_NODE_OFFSET_ENTERED) 
				error(FATAL,
					"node offset value %d (0x%lx) already entered\n",
						td->node_member_offset, td->node_member_offset);
			else if (IS_A_NUMBER(optarg)) 
				td->node_member_offset = stol(optarg, 
					FAULT_ON_ERROR, NULL);
			else if (arg_to_datatype(optarg, sm, RETURN_ON_ERROR) > 1) 
				td->node_member_offset = sm->member_offset;
			else
				error(FATAL, "invalid -o argument: %s\n",
					optarg);

			td->flags |= TREE_NODE_OFFSET_ENTERED; 
			break;

		case 's':
		case 'S':
			if (td->structname_args++ == 0) 
				hq_open();
			hq_enter((ulong)optarg);
			td->flags |= (c == 's') ? TREE_PARSE_MEMBER : TREE_READ_MEMBER;
			if (count_bits_long(td->flags & (TREE_PARSE_MEMBER|TREE_READ_MEMBER)) > 1)
				error(FATAL, "-S and -s options are mutually exclusive\n");
			break;

		case 'p':
			td->flags |= TREE_POSITION_DISPLAY;
			break;

		case 'N':
			td->flags |= TREE_NODE_POINTER;
			break;

		case 'x':
			if (td->flags & TREE_STRUCT_RADIX_10)
				error(FATAL,
					"-d and -x are mutually exclusive\n");
			td->flags |= TREE_STRUCT_RADIX_16;
			break;

		case 'd':
			if (td->flags & TREE_STRUCT_RADIX_16)
				error(FATAL,
					"-d and -x are mutually exclusive\n");
			td->flags |= TREE_STRUCT_RADIX_10;
			break;
		default:
			argerrs++;
			break;
		}
	}

	if (argerrs)
		cmd_usage(pc->curcmd, SYNOPSIS);

	if ((type_flag & RADIXTREE_REQUEST) && (td->flags & TREE_NODE_OFFSET_ENTERED))
		error(FATAL, "-o option is not applicable to radix trees\n");

	if ((td->flags & TREE_ROOT_OFFSET_ENTERED) && 
	    (td->flags & TREE_NODE_POINTER))
		error(INFO, "-r and -N options are mutually exclusive\n");

	if (!args[optind]) {
		error(INFO, "a starting address is required\n");
		cmd_usage(pc->curcmd, SYNOPSIS);
	}

	if ((sp = symbol_search(args[optind]))) {
		td->start = sp->value;
		goto next_arg;
	}

	if (!IS_A_NUMBER(args[optind])) {	
		if (can_eval(args[optind])) {
			value = eval(args[optind], FAULT_ON_ERROR, NULL);
			if (IS_KVADDR(value)) {
				td->start = value;
				goto next_arg;
			}
		}
		error(FATAL, "invalid start argument: %s\n", args[optind]);
	} 

	if (hexadecimal_only(args[optind], 0)) {
		value = htol(args[optind], FAULT_ON_ERROR, NULL);
		if (IS_KVADDR(value)) {
			td->start = value;
			goto next_arg;
		}
	}
	 
	error(FATAL, "invalid start argument: %s\n", args[optind]);

next_arg:
	if (args[optind+1]) {
		error(INFO, "too many arguments entered\n");
		cmd_usage(pc->curcmd, SYNOPSIS);
	}

	if (td->structname_args) {
		td->structname = (char **)GETBUF(sizeof(char *) *
				td->structname_args);
		retrieve_list((ulong *)td->structname, td->structname_args); 
		hq_close();
	}

	if (!(td->flags & TREE_NODE_POINTER))
		td->start = td->start + root_offset;

	if (CRASHDEBUG(1)) {
		others = 0;
		fprintf(fp, "             flags: %lx (", td->flags);
		if (td->flags & TREE_ROOT_OFFSET_ENTERED)
			fprintf(fp, "%sTREE_ROOT_OFFSET_ENTERED",
				others++ ? "|" : "");
		if (td->flags & TREE_NODE_OFFSET_ENTERED)
			fprintf(fp, "%sTREE_NODE_OFFSET_ENTERED",
				others++ ? "|" : "");
		if (td->flags & TREE_NODE_POINTER)
			fprintf(fp, "%sTREE_NODE_POINTER",
				others++ ? "|" : "");
		if (td->flags & TREE_POSITION_DISPLAY)
			fprintf(fp, "%sTREE_POSITION_DISPLAY",
				others++ ? "|" : "");
		if (td->flags & TREE_STRUCT_RADIX_10)
			fprintf(fp, "%sTREE_STRUCT_RADIX_10",
				others++ ? "|" : "");
		if (td->flags & TREE_STRUCT_RADIX_16)
			fprintf(fp, "%sTREE_STRUCT_RADIX_16",
				others++ ? "|" : "");
		fprintf(fp, ")\n");
		fprintf(fp, "              type: %s\n",
			type_flag & RADIXTREE_REQUEST ? "radix" : "red-black");
		fprintf(fp, "      node pointer: %s\n",
			td->flags & TREE_NODE_POINTER ? "yes" : "no");
		fprintf(fp, "             start: %lx\n", td->start);
		fprintf(fp, "node_member_offset: %ld\n", td->node_member_offset);
		fprintf(fp, "   structname_args: %d\n", td->structname_args);
		fprintf(fp, "             count: %d\n", td->count);
	}

	td->flags &= ~TREE_NODE_OFFSET_ENTERED;
	td->flags |= VERBOSE;

	hq_open();
	if (type_flag & RADIXTREE_REQUEST)
		do_rdtree(td);
	else
		do_rbtree(td);
	hq_close();

        if (td->structname_args)
		FREEBUF(td->structname);
}

static ulong RADIX_TREE_MAP_SHIFT = UNINITIALIZED;
static ulong RADIX_TREE_MAP_SIZE = UNINITIALIZED;
static ulong RADIX_TREE_MAP_MASK = UNINITIALIZED;

#define RADIX_TREE_ENTRY_MASK		3UL
#define RADIX_TREE_INTERNAL_NODE	1UL

static void do_radix_tree_iter(ulong node, uint height, char *path,
			       ulong index, struct radix_tree_ops *ops)
{
	uint off;

	if (!hq_enter(node))
		error(FATAL,
			"\nduplicate tree node: %lx\n", node);

	for (off = 0; off < RADIX_TREE_MAP_SIZE; off++) {
		ulong slot;
		ulong shift = (height - 1) * RADIX_TREE_MAP_SHIFT;

		readmem(node + OFFSET(radix_tree_node_slots) +
			sizeof(void *) * off, KVADDR, &slot, sizeof(void *),
			"radix_tree_node.slot[off]", FAULT_ON_ERROR);
		if (!slot)
			continue;

		if (slot & RADIX_TREE_INTERNAL_NODE)
			slot &= ~RADIX_TREE_INTERNAL_NODE;

		if (height == 1)
			ops->entry(node, slot, path, index | off, ops->private);
		else {
			ulong child_index = index | (off << shift);
			char child_path[BUFSIZE];
			sprintf(child_path, "%s/%d", path, off);
			do_radix_tree_iter(slot, height - 1,
					   child_path, child_index, ops);
		}
	}
}

int do_radix_tree_traverse(ulong ptr, int is_root, struct radix_tree_ops *ops)
{
	static ulong max_height = UNINITIALIZED;
	ulong node_p;
	long nlen;
	uint height, is_internal;
	unsigned char shift;
	char path[BUFSIZE];

	if (!VALID_STRUCT(radix_tree_root) || !VALID_STRUCT(radix_tree_node) ||
	    ((!VALID_MEMBER(radix_tree_root_height) ||
	      !VALID_MEMBER(radix_tree_root_rnode) ||
	      !VALID_MEMBER(radix_tree_node_slots) ||
	      !ARRAY_LENGTH(height_to_maxindex)) &&
	     (!VALID_MEMBER(radix_tree_root_rnode) ||
	      !VALID_MEMBER(radix_tree_node_shift) ||
	      !VALID_MEMBER(radix_tree_node_slots) ||
	      !ARRAY_LENGTH(height_to_maxnodes))))
		error(FATAL, "radix trees do not exist or have changed "
			"their format\n");

	if (RADIX_TREE_MAP_SHIFT == UNINITIALIZED) {
		if (!(nlen = MEMBER_SIZE("radix_tree_node", "slots")))
			error(FATAL, "cannot determine length of "
				     "radix_tree_node.slots[] array\n");
		nlen /= sizeof(void *);
		RADIX_TREE_MAP_SHIFT = ffsl(nlen) - 1;
		RADIX_TREE_MAP_SIZE = (1UL << RADIX_TREE_MAP_SHIFT);
		RADIX_TREE_MAP_MASK = (RADIX_TREE_MAP_SIZE-1);

		if (ARRAY_LENGTH(height_to_maxindex))
			max_height = ARRAY_LENGTH(height_to_maxindex);
		else
			max_height = ARRAY_LENGTH(height_to_maxnodes);
	}

	height = 0;
	if (!is_root) {
		node_p = ptr;

		if (node_p & RADIX_TREE_INTERNAL_NODE)
			node_p &= ~RADIX_TREE_INTERNAL_NODE;

		if (VALID_MEMBER(radix_tree_node_height)) {
			readmem(node_p + OFFSET(radix_tree_node_height), KVADDR,
				&height, sizeof(uint), "radix_tree_node height",
				FAULT_ON_ERROR);
		} else if (VALID_MEMBER(radix_tree_node_shift)) {
			readmem(node_p + OFFSET(radix_tree_node_shift), KVADDR,
				&shift, sizeof(shift), "radix_tree_node shift",
				FAULT_ON_ERROR);
			height = (shift / RADIX_TREE_MAP_SHIFT) + 1;
		} else
			error(FATAL, "-N option is not supported or applicable"
				" for radix trees on this architecture or kernel\n");
		if (height > max_height)
			goto error_height;
	} else {
		if (VALID_MEMBER(radix_tree_root_height)) {
			readmem(ptr + OFFSET(radix_tree_root_height), KVADDR, &height,
				sizeof(uint), "radix_tree_root height", FAULT_ON_ERROR);
		}

		readmem(ptr + OFFSET(radix_tree_root_rnode), KVADDR, &node_p,
			sizeof(void *), "radix_tree_root rnode", FAULT_ON_ERROR);
		is_internal = (node_p & RADIX_TREE_INTERNAL_NODE);
		if (node_p & RADIX_TREE_INTERNAL_NODE)
			node_p &= ~RADIX_TREE_INTERNAL_NODE;

		if (is_internal && VALID_MEMBER(radix_tree_node_shift)) {
			readmem(node_p + OFFSET(radix_tree_node_shift), KVADDR, &shift,
				sizeof(shift), "radix_tree_node shift", FAULT_ON_ERROR);
			height = (shift / RADIX_TREE_MAP_SHIFT) + 1;
		}

		if (height > max_height) {
			node_p = ptr;
			goto error_height;
		}
	}

	if (CRASHDEBUG(1)) {
		fprintf(fp, "radix_tree_node.slots[%ld]\n",
			RADIX_TREE_MAP_SIZE);
		fprintf(fp, "max_height %ld: ", max_height);
		fprintf(fp, "\n");
		fprintf(fp, "pointer at %lx (is_root? %s):\n",
			node_p, is_root ? "yes" : "no");
		if (is_root)
			dump_struct("radix_tree_root", ptr, RADIX(ops->radix));
		else
			dump_struct("radix_tree_node", node_p, RADIX(ops->radix));
	}

	if (height == 0) {
		strcpy(path, "direct");
		ops->entry(node_p, node_p, path, 0, ops->private);
	} else {
		strcpy(path, "root");
		do_radix_tree_iter(node_p, height, path, 0, ops);
	}

	return 0;

error_height:
	fprintf(fp, "radix_tree_node at %lx\n", node_p);
	dump_struct("radix_tree_node", node_p, RADIX(ops->radix));
	error(FATAL, "height %d is greater than "
	      "maximum radix tree height index %ld\n",
	      height, max_height);
	return -1;
}

static void do_rdtree_entry(ulong node, ulong slot, const char *path,
			    ulong index, void *private)
{
	struct tree_data *td = private;
	static struct req_entry **e = NULL;
	uint print_radix;
	int i;

	if (!td->count && td->structname_args) {
		/*
		 * Retrieve all members' info only once (count == 0)
		 * After last iteration all memory will be freed up
		 */
		e = (struct req_entry **)GETBUF(sizeof(*e) * td->structname_args);
		for (i = 0; i < td->structname_args; i++)
			e[i] = fill_member_offsets(td->structname[i]);
	}

	td->count++;

	if (td->flags & VERBOSE)
		fprintf(fp, "%lx\n", slot);

	if (td->flags & TREE_POSITION_DISPLAY) {
		fprintf(fp, "  position: %s/%ld\n",
			path, index & RADIX_TREE_MAP_MASK);
	}

	if (td->structname) {
		if (td->flags & TREE_STRUCT_RADIX_10)
			print_radix = 10;
		else if (td->flags & TREE_STRUCT_RADIX_16)
			print_radix = 16;
		else
			print_radix = 0;

		for (i = 0; i < td->structname_args; i++) {
			switch (count_chars(td->structname[i], '.')) {
			case 0:
				dump_struct(td->structname[i], slot, print_radix);
				break;
			default:
				if (td->flags & TREE_PARSE_MEMBER)
					dump_struct_members_for_tree(td, i, slot);
				else if (td->flags & TREE_READ_MEMBER)
					dump_struct_members_fast(e[i], print_radix, slot);
				break;
			}
		}
	}
}

int do_rdtree(struct tree_data *td)
{
	struct radix_tree_ops ops = {
		.entry		= do_rdtree_entry,
		.private	= td,
	};
	int is_root = !(td->flags & TREE_NODE_POINTER);

	if (td->flags & TREE_STRUCT_RADIX_10)
		ops.radix = 10;
	else if (td->flags & TREE_STRUCT_RADIX_16)
		ops.radix = 16;
	else
		ops.radix = 0;

	do_radix_tree_traverse(td->start, is_root, &ops);

	return 0;
}

int
do_rbtree(struct tree_data *td)
{
	ulong start;
	char pos[BUFSIZE];

	if (!VALID_MEMBER(rb_root_rb_node) || !VALID_MEMBER(rb_node_rb_left) ||
	    !VALID_MEMBER(rb_node_rb_right))
		error(FATAL, "red-black trees do not exist or have changed "
			"their format\n");

	sprintf(pos, "root");

	if (td->flags & TREE_NODE_POINTER)
		start = td->start;
	else
		readmem(td->start + OFFSET(rb_root_rb_node), KVADDR,
			&start, sizeof(void *), "rb_root rb_node", FAULT_ON_ERROR);

	rbtree_iteration(start, td, pos);

	return td->count;
}

void
rbtree_iteration(ulong node_p, struct tree_data *td, char *pos)
{
	int i;
	uint print_radix;
	ulong struct_p, left_p, right_p;
	char left_pos[BUFSIZE], right_pos[BUFSIZE];
	static struct req_entry **e;

	if (!node_p)
		return;

	if (!td->count && td->structname_args) {
		/*
		 * Retrieve all members' info only once (count == 0)
		 * After last iteration all memory will be freed up
		 */
		e = (struct req_entry **)GETBUF(sizeof(*e) *
			td->structname_args);
		for (i = 0; i < td->structname_args; i++)
			e[i] = fill_member_offsets(td->structname[i]);
	}

	if (hq_enter(node_p))
		td->count++;
	else
		error(FATAL, "\nduplicate tree entry: %lx\n", node_p);

	struct_p = node_p - td->node_member_offset;

	if (td->flags & VERBOSE)
		fprintf(fp, "%lx\n", struct_p);
	
	if (td->flags & TREE_POSITION_DISPLAY)
		fprintf(fp, "  position: %s\n", pos);

	if (td->structname) {
		if (td->flags & TREE_STRUCT_RADIX_10)
			print_radix = 10;
		else if (td->flags & TREE_STRUCT_RADIX_16)
			print_radix = 16;
		else
			print_radix = 0;

		for (i = 0; i < td->structname_args; i++) {
			switch(count_chars(td->structname[i], '.'))
			{
			case 0:
				dump_struct(td->structname[i], struct_p, print_radix);
				break;
			default:
				if (td->flags & TREE_PARSE_MEMBER)
					dump_struct_members_for_tree(td, i, struct_p);
				else if (td->flags & TREE_READ_MEMBER)
					dump_struct_members_fast(e[i], print_radix,
						struct_p);
				break;
			}
		}
	}

	readmem(node_p+OFFSET(rb_node_rb_left), KVADDR, &left_p,
		sizeof(void *), "rb_node rb_left", FAULT_ON_ERROR);
	readmem(node_p+OFFSET(rb_node_rb_right), KVADDR, &right_p,
		sizeof(void *), "rb_node rb_right", FAULT_ON_ERROR);

	sprintf(left_pos, "%s/l", pos);
	sprintf(right_pos, "%s/r", pos);

	rbtree_iteration(left_p, td, left_pos);
	rbtree_iteration(right_p, td, right_pos);		
}

void
dump_struct_members_for_tree(struct tree_data *td, int idx, ulong struct_p)
{
	int i, argc;
	uint print_radix;
	char *p1;
	char *structname, *members;
	char *arglist[MAXARGS];

	if (td->flags & TREE_STRUCT_RADIX_10)
		print_radix = 10;
	else if (td->flags & TREE_STRUCT_RADIX_16)
		print_radix = 16;
	else
		print_radix = 0;

	structname = GETBUF(strlen(td->structname[idx])+1);
	members = GETBUF(strlen(td->structname[idx])+1);

	strcpy(structname, td->structname[idx]);
	p1 = strstr(structname, ".") + 1;

	strcpy(members, p1);
	replace_string(members, ",", ' ');
	argc = parse_line(members, arglist);

	for (i = 0; i <argc; i++) {
		*p1 = NULLCHAR;
		strcat(structname, arglist[i]);
		dump_struct_member(structname, struct_p, print_radix);
	}

	FREEBUF(structname);
	FREEBUF(members);
}

/*
 *  The next set of functions are a general purpose hashing tool used to
 *  identify duplicate entries in a set of passed-in data, and if found, 
 *  to fail the entry attempt.  When a command wishes to verify a list
 *  of contains unique values, the hash functions should be used in the
 *  following order:
 *
 *      hq_open()
 *      hq_enter(value_1)
 *      hq_enter(value_2)
 *      ...
 *      hq_enter(value_n)
 *      hq_close()
 *
 *  If a duplicate entry is passed in between the hq_open()/hq_close() pair,
 *  hq_enter() will return FALSE;
 */

#define HASH_QUEUE_NONE       (0x1)
#define HASH_QUEUE_FULL       (0x2)
#define HASH_QUEUE_OPEN       (0x4)
#define HASH_QUEUE_CLOSED     (0x8)

#define HQ_ENTRY_CHUNK   (1024)
#define NR_HASH_QUEUES_DEFAULT   (32768UL)
#define HQ_SHIFT         (machdep->pageshift)
#define HQ_INDEX(X)      (((X) >> HQ_SHIFT) % pc->nr_hash_queues)

struct hq_entry {
        int next;
	int order;
        ulong value;
};

struct hq_head {
	int next;
	int qcnt;
};

struct hash_table {
	ulong flags;
	struct hq_head *queue_heads;
	struct hq_entry *memptr;
	long count;
	long index;
	int reallocs;
} hash_table = { 0 };

/*
 *  For starters, allocate a hash table containing HQ_ENTRY_CHUNK entries.
 *  If necessary during runtime, it will be increased in size.
 */
void
hq_init(void)
{
	struct hash_table *ht;

	ht = &hash_table;

	if (pc->nr_hash_queues == 0)
		pc->nr_hash_queues = NR_HASH_QUEUES_DEFAULT;

        if ((ht->queue_heads = (struct hq_head *)malloc(pc->nr_hash_queues *
	    sizeof(struct hq_head))) == NULL) {
		error(INFO, "cannot malloc memory for hash queue heads: %s\n",
			strerror(errno));
		ht->flags = HASH_QUEUE_NONE;
		pc->flags &= ~HASH;
		return;
	}

        if ((ht->memptr = (struct hq_entry *)malloc(HQ_ENTRY_CHUNK * 
	    sizeof(struct hq_entry))) == NULL) {
		error(INFO, "cannot malloc memory for hash queues: %s\n",
			strerror(errno));
		ht->flags = HASH_QUEUE_NONE;
		pc->flags &= ~HASH;
		return;
	}
        
	BZERO(ht->memptr, HQ_ENTRY_CHUNK * sizeof(struct hq_entry));
	ht->count = HQ_ENTRY_CHUNK;
	ht->index = 0;
}

/*
 *  Get a free hash queue entry.  If there's no more available, realloc()
 *  a new chunk of memory with another HQ_ENTRY_CHUNK entries stuck on the end.
 */
static long
alloc_hq_entry(void)
{
	struct hash_table *ht;
	struct hq_entry *new, *end_of_old;

	ht = &hash_table;

	if (++ht->index == ht->count) {
                if (!(new = (void *)realloc((void *)ht->memptr,
		    (ht->count+HQ_ENTRY_CHUNK) * sizeof(struct hq_entry)))) {
			error(INFO, 
			    "cannot realloc memory for hash queues: %s\n",
				strerror(errno));
			ht->flags |= HASH_QUEUE_FULL;
			return(-1);
		}
		ht->reallocs++;
		ht->memptr = new;
		end_of_old = ht->memptr + ht->count;
		BZERO(end_of_old, HQ_ENTRY_CHUNK * sizeof(struct hq_entry));
		ht->count += HQ_ENTRY_CHUNK;
	}

	return(ht->index);
}

/*
 *  Restore the hash queue to its state before the duplicate entry 
 *  was attempted.
 */ 
static void
dealloc_hq_entry(struct hq_entry *entry)
{
        struct hash_table *ht;
        long hqi;

        ht = &hash_table;
	hqi = HQ_INDEX(entry->value);

	ht->index--;

	BZERO(entry, sizeof(struct hq_entry));
	ht->queue_heads[hqi].qcnt--;
}

/*
 *  Initialize the hash table for a hashing session.
 */
int
hq_open(void)
{
	struct hash_table *ht;

	if (!(pc->flags & HASH))
		return FALSE;

	ht = &hash_table;
	if (ht->flags & (HASH_QUEUE_NONE|HASH_QUEUE_OPEN))
		return FALSE;

	ht->flags &= ~(HASH_QUEUE_FULL|HASH_QUEUE_CLOSED);
	BZERO(ht->queue_heads, sizeof(struct hq_head) * pc->nr_hash_queues);
	BZERO(ht->memptr, ht->count * sizeof(struct hq_entry));
	ht->index = 0;

	ht->flags |= HASH_QUEUE_OPEN;

	return TRUE;
}

int
hq_is_open(void)
{
	struct hash_table *ht;

	ht = &hash_table;
	return (ht->flags & HASH_QUEUE_OPEN ? TRUE : FALSE);
}

int
hq_is_inuse(void)
{
	struct hash_table *ht;

	if (!hq_is_open())
		return FALSE;

	ht = &hash_table;
	return (ht->index ? TRUE : FALSE);
}


/*
 *  Close the hash table, returning the number of items hashed in this session.
 */
int
hq_close(void)
{
	struct hash_table *ht;

	ht = &hash_table;

	ht->flags &= ~(HASH_QUEUE_OPEN);
	ht->flags |= HASH_QUEUE_CLOSED;

	if (!(pc->flags & HASH))
		return(0);

	if (ht->flags & HASH_QUEUE_NONE)
		return(0);

	ht->flags &= ~HASH_QUEUE_FULL;

	return(ht->index);
}

char *corrupt_hq = "corrupt hash queue entry: value: %lx next: %d order: %d\n";

/*
 *  For a given value, allocate a hash queue entry and hash it into the 
 *  open hash table.  If a duplicate entry is found, return FALSE; for all 
 *  other possibilities return TRUE.  Note that it's up to the user to deal 
 *  with failure.
 */
int
hq_enter(ulong value)
{
	struct hash_table *ht;
	struct hq_entry *entry;
	struct hq_entry *list_entry;
	long hqi;
	long index;

	if (!(pc->flags & HASH))
		return TRUE;

	ht = &hash_table;

	if (ht->flags & (HASH_QUEUE_NONE|HASH_QUEUE_FULL))
		return TRUE;

	if (!(ht->flags & HASH_QUEUE_OPEN))
		return TRUE;

	if ((index = alloc_hq_entry()) < 0) 
		return TRUE;

	entry = ht->memptr + index;
	if (entry->next || entry->value || entry->order) {
		error(INFO, corrupt_hq,
			entry->value, entry->next, entry->order);
		ht->flags |= HASH_QUEUE_NONE;
		return TRUE;
	}

	entry->next = 0;
	entry->value = value;
	entry->order = index;

	hqi = HQ_INDEX(value);

	if (ht->queue_heads[hqi].next == 0) {
		ht->queue_heads[hqi].next = index;
		ht->queue_heads[hqi].qcnt = 1;
		return TRUE;
	} else
		ht->queue_heads[hqi].qcnt++;

	list_entry = ht->memptr + ht->queue_heads[hqi].next;

	while (TRUE) {
	        if (list_entry->value == entry->value) {
			dealloc_hq_entry(entry);
                	return FALSE;
		}

		if (list_entry->next >= ht->count) {
			error(INFO, corrupt_hq,
			    	list_entry->value, 
				list_entry->next,
				list_entry->order);
			ht->flags |= HASH_QUEUE_NONE;
			return TRUE;
		}

		if (list_entry->next == 0)
			break;

        	list_entry = ht->memptr + list_entry->next;
	}

	list_entry->next = index;

	return TRUE;
}

/*
 *  "hash -d" output
 */
void
dump_hash_table(int verbose)
{
	int i;
	struct hash_table *ht;
	struct hq_entry *list_entry;
	long elements;
	long queues_in_use;
	int others;
	uint minq, maxq; 

	ht = &hash_table;
	others = 0;

	fprintf(fp, "              flags: %lx (", ht->flags);
        if (ht->flags & HASH_QUEUE_NONE)
                fprintf(fp, "%sHASH_QUEUE_NONE", others++ ? "|" : "");
        if (ht->flags & HASH_QUEUE_OPEN)
                fprintf(fp, "%sHASH_QUEUE_OPEN", others++ ? "|" : "");
        if (ht->flags & HASH_QUEUE_CLOSED)
                fprintf(fp, "%sHASH_QUEUE_CLOSED", others++ ? "|" : "");
        if (ht->flags & HASH_QUEUE_FULL)
                fprintf(fp, "%sHASH_QUEUE_FULL", others++ ? "|" : "");
	fprintf(fp, ")\n");
	fprintf(fp, "  queue_heads[%ld]: %lx\n", pc->nr_hash_queues, 
		(ulong)ht->queue_heads);
	fprintf(fp, "             memptr: %lx\n", (ulong)ht->memptr);
	fprintf(fp, "              count: %ld  ", ht->count);
	if (ht->reallocs)
		fprintf(fp, "  (%d reallocs)", ht->reallocs);
	fprintf(fp, "\n");
	fprintf(fp, "              index: %ld\n", ht->index);

	queues_in_use = 0;
	minq = ~(0);
	maxq = 0;

	for (i = 0; i < pc->nr_hash_queues; i++) {
               	if (ht->queue_heads[i].next == 0) {
			minq = 0;
                       	continue;
		}

		if (ht->queue_heads[i].qcnt < minq)
			minq = ht->queue_heads[i].qcnt;
		if (ht->queue_heads[i].qcnt > maxq)
			maxq = ht->queue_heads[i].qcnt;

               	queues_in_use++;
	}

	elements = 0;
	list_entry = ht->memptr;
        for (i = 0; i < ht->count; i++, list_entry++) {
	         if (!list_entry->order) {
	                if (list_entry->value || list_entry->next)
				goto corrupt_list_entry;
	                continue;
	         }
	
	         if (list_entry->next >= ht->count)
	                        goto corrupt_list_entry;

	         ++elements;
       	}

	if (elements != ht->index)
        	fprintf(fp, "     elements found: %ld (expected %ld)\n", 
			elements, ht->index);
        fprintf(fp, "      queues in use: %ld of %ld\n", queues_in_use, 
		pc->nr_hash_queues);
	fprintf(fp, " queue length range: %d to %d\n", minq, maxq);

	if (verbose) {
		if (!elements) {
        		fprintf(fp, "            entries: (none)\n");
			return;
		}

        	fprintf(fp, "            entries: ");

        	list_entry = ht->memptr;
	        for (i = 0; i < ht->count; i++, list_entry++) {
	                 if (list_entry->order)
	                        fprintf(fp, "%s%lx (%d)\n", 
					list_entry->order == 1 ?
					"" : "                     ",
	                                list_entry->value, list_entry->order);
	        }
	}
	return;

corrupt_list_entry:

        error(INFO, corrupt_hq,
        	list_entry->value, list_entry->next, list_entry->order);
        ht->flags |= HASH_QUEUE_NONE;
}

/*
 *  Retrieve the count of, and optionally stuff a pre-allocated array with,
 *  the current hash table entries.  The entries will be sorted according
 *  to the order in which they were entered, so from this point on, no
 *  further hq_enter() operations on this list will be allowed.  However, 
 *  multiple calls to retrieve_list are allowed because the second and 
 *  subsequent ones will go directly to where the non-zero (valid) entries 
 *  start in the potentially very large list_entry memory chunk.
 */
int
retrieve_list(ulong array[], int count)
{
        int i; 
        struct hash_table *ht;
        struct hq_entry *list_entry;
        int elements;

	if (!(pc->flags & HASH))
		error(FATAL, 
		    "cannot perform this command with hash turned off\n");

        ht = &hash_table;

	list_entry = ht->memptr;
	for (i = elements = 0; i < ht->count; i++, list_entry++) {
		if (!list_entry->order) {
			if (list_entry->value || list_entry->next)
				goto corrupt_list_entry;
			continue;
		}

                if (list_entry->next >= ht->count) 
			goto corrupt_list_entry;

		if (array) 
			array[elements] = list_entry->value; 

                if (++elements == count)
                       	break;
	}

	return elements;

corrupt_list_entry:

        error(INFO, corrupt_hq,
               list_entry->value, list_entry->next, list_entry->order);
        ht->flags |= HASH_QUEUE_NONE;
        return(-1);
}

/*
 *  For a given value, check to see if a hash queue entry exists.  If an
 *  entry is found, return TRUE; for all other possibilities return FALSE.
 */
int
hq_entry_exists(ulong value)
{
	struct hash_table *ht;
	struct hq_entry *list_entry;
	long hqi;

	if (!(pc->flags & HASH))
		return FALSE;

	ht = &hash_table;

	if (ht->flags & (HASH_QUEUE_NONE))
		return FALSE;

	if (!(ht->flags & HASH_QUEUE_OPEN))
		return FALSE;

	hqi = HQ_INDEX(value);
	list_entry = ht->memptr + ht->queue_heads[hqi].next;

	while (TRUE) {
		if (list_entry->value == value)
			return TRUE;

		if (list_entry->next >= ht->count) {
			error(INFO, corrupt_hq,
				list_entry->value, 
				list_entry->next,
 				list_entry->order);
			ht->flags |= HASH_QUEUE_NONE;
			return FALSE;
		}

		if (list_entry->next == 0)
			break;

		list_entry = ht->memptr + list_entry->next;
	}

	return FALSE;
}

/*
 *  K&R power function for integers
 */
long
power(long base, int exp)
{
	int i;
	long p;

	p = 1;
	for (i = 1; i <= exp; i++)
		p = p * base;

	return p;
}

long long 
ll_power(long long base, long long exp)
{
        long long i;
        long long p;

        p = 1;
        for (i = 1; i <= exp; i++)
                p = p * base;

        return p;
}

/*
 *  Internal buffer allocation scheme to avoid inline malloc() calls and 
 *  resultant memory leaks due to aborted commands.  These buffers are
 *  for TEMPORARY use on a per-command basis.  They are allocated by calls
 *  to GETBUF(size).  They can explicitly freed by FREEBUF(address), but
 *  they are all freed by free_all_bufs() which is called in a number of
 *  places, most not
 */

#define NUMBER_1K_BUFS  (10)
#define NUMBER_2K_BUFS  (10)
#define NUMBER_4K_BUFS   (5)
#define NUMBER_8K_BUFS   (5)
#define NUMBER_32K_BUFS  (1)

#define SHARED_1K_BUF_FULL   (0x003ff)
#define SHARED_2K_BUF_FULL   (0x003ff)
#define SHARED_4K_BUF_FULL   (0x0001f)
#define SHARED_8K_BUF_FULL   (0x0001f)
#define SHARED_32K_BUF_FULL  (0x00001)

#define SHARED_1K_BUF_AVAIL(X) \
  (NUMBER_1K_BUFS && !(((X) & SHARED_1K_BUF_FULL) == SHARED_1K_BUF_FULL))
#define SHARED_2K_BUF_AVAIL(X) \
  (NUMBER_2K_BUFS && !(((X) & SHARED_2K_BUF_FULL) == SHARED_2K_BUF_FULL))
#define SHARED_4K_BUF_AVAIL(X) \
  (NUMBER_4K_BUFS && !(((X) & SHARED_4K_BUF_FULL) == SHARED_4K_BUF_FULL))
#define SHARED_8K_BUF_AVAIL(X) \
  (NUMBER_8K_BUFS && !(((X) & SHARED_8K_BUF_FULL) == SHARED_8K_BUF_FULL))
#define SHARED_32K_BUF_AVAIL(X) \
  (NUMBER_32K_BUFS && !(((X) & SHARED_32K_BUF_FULL) == SHARED_32K_BUF_FULL))

#define B1K  (0)
#define B2K  (1)
#define B4K  (2)
#define B8K  (3)
#define B32K (4)

#define SHARED_BUF_SIZES  (B32K+1)
#define MAX_MALLOC_BUFS   (2000)
#define MAX_CACHE_SIZE    (KILOBYTES(32))

struct shared_bufs {
	char buf_1K[NUMBER_1K_BUFS][1024];
	char buf_2K[NUMBER_2K_BUFS][2048];
	char buf_4K[NUMBER_4K_BUFS][4096];
	char buf_8K[NUMBER_8K_BUFS][8192];
	char buf_32K[NUMBER_32K_BUFS][32768];
	long buf_1K_used;
	long buf_2K_used;
	long buf_4K_used;
	long buf_8K_used;
	long buf_32K_used;
        long buf_1K_maxuse;
        long buf_2K_maxuse;
        long buf_4K_maxuse;
        long buf_8K_maxuse;
        long buf_32K_maxuse;
        long buf_1K_ovf;
        long buf_2K_ovf;
        long buf_4K_ovf;
        long buf_8K_ovf;
        long buf_32K_ovf;
	int buf_inuse[SHARED_BUF_SIZES];
	char *malloc_bp[MAX_MALLOC_BUFS];
	long smallest;
	long largest;
	long embedded;
	long max_embedded;
	long mallocs;
	long frees;
	double total;
	ulong reqs;
} shared_bufs;

void
buf_init(void)
{
	struct shared_bufs *bp;

	bp = &shared_bufs;
	BZERO(bp, sizeof(struct shared_bufs));

	bp->smallest = 0x7fffffff; 
	bp->total = 0.0;
}


/*
 *  Free up all buffers used by the last command.
 */
void free_all_bufs(void)
{
	int i;
	struct shared_bufs *bp;

	bp = &shared_bufs;
	bp->embedded = 0;

        for (i = 0; i < SHARED_BUF_SIZES; i++)
                bp->buf_inuse[i] = 0;

	for (i = 0; i < MAX_MALLOC_BUFS; i++) {
		if (bp->malloc_bp[i]) {
			free(bp->malloc_bp[i]);
			bp->malloc_bp[i] = NULL;
			bp->frees++;
		}
	}

	if (bp->mallocs != bp->frees)
		error(WARNING, "malloc/free mismatch (%ld/%ld)\n",
			bp->mallocs, bp->frees);
}

/*
 *  Free a specific buffer that may have been returned by malloc().
 *  If the address is one of the static buffers, look for it and
 *  clear its inuse bit.
 */
void 
freebuf(char *addr)
{
        int i;
        struct shared_bufs *bp;

        bp = &shared_bufs;
	bp->embedded--;

        if (CRASHDEBUG(8)) {
		INDENT(bp->embedded*2);
                fprintf(fp, "FREEBUF(%ld)\n", bp->embedded);
        }

	for (i = 0; i < NUMBER_1K_BUFS; i++) {
		if (addr == (char *)&bp->buf_1K[i]) {
			bp->buf_inuse[B1K] &= ~(1 << i);
			return;
		}
	}

	for (i = 0; i < NUMBER_2K_BUFS; i++) {
		if (addr == (char *)&bp->buf_2K[i]) {
			bp->buf_inuse[B2K] &= ~(1 << i);
			return;
		}
	}

	for (i = 0; i < NUMBER_4K_BUFS; i++) {
		if (addr == (char *)&bp->buf_4K[i]) {
			bp->buf_inuse[B4K] &= ~(1 << i);
			return;
		}
	}

	for (i = 0; i < NUMBER_8K_BUFS; i++) {
		if (addr == (char *)&bp->buf_8K[i]) {
			bp->buf_inuse[B8K] &= ~(1 << i);
			return;
		}
	}

        for (i = 0; i < NUMBER_32K_BUFS; i++) {
                if (addr == (char *)&bp->buf_32K[i]) {
                        bp->buf_inuse[B32K] &= ~(1 << i);
                        return;
                }
        }

        for (i = 0; i < MAX_MALLOC_BUFS; i++) {
                if (bp->malloc_bp[i] == addr) {
                        free(bp->malloc_bp[i]);
                        bp->malloc_bp[i] = NULL;
                        bp->frees++;
                        return;
                }
        }

	error(FATAL, 
	    "freeing an unknown buffer -- shared buffer inconsistency!\n");
}

/* DEBUG */
void
dump_embedded(char *s)
{
        struct shared_bufs *bp;
	char *p1;

	p1 = s ? s : "";

        bp = &shared_bufs;
        console("%s: embedded: %ld  mallocs: %ld  frees: %ld\n", 
		p1, bp->embedded, bp->mallocs, bp->frees);
}
/* DEBUG */
long
get_embedded(void)
{
	struct shared_bufs *bp;

        bp = &shared_bufs;
	return(bp->embedded);
}

/*
 *  "help -b" output
 */
void
dump_shared_bufs(void)
{
        int i;
        struct shared_bufs *bp;

        bp = &shared_bufs;

        fprintf(fp, "   buf_1K_used: %ld\n", bp->buf_1K_used);
        fprintf(fp, "   buf_2K_used: %ld\n", bp->buf_2K_used);
        fprintf(fp, "   buf_4K_used: %ld\n", bp->buf_4K_used);
        fprintf(fp, "   buf_8K_used: %ld\n", bp->buf_8K_used);
        fprintf(fp, "  buf_32K_used: %ld\n", bp->buf_32K_used);

        fprintf(fp, "    buf_1K_ovf: %ld\n", bp->buf_1K_ovf);
        fprintf(fp, "    buf_2K_ovf: %ld\n", bp->buf_2K_ovf);
        fprintf(fp, "    buf_4K_ovf: %ld\n", bp->buf_4K_ovf);
        fprintf(fp, "    buf_8K_ovf: %ld\n", bp->buf_8K_ovf);
        fprintf(fp, "   buf_32K_ovf: %ld\n", bp->buf_32K_ovf);

        fprintf(fp, " buf_1K_maxuse: %2ld of %d\n", bp->buf_1K_maxuse, 
		NUMBER_1K_BUFS);
        fprintf(fp, " buf_2K_maxuse: %2ld of %d\n", bp->buf_2K_maxuse, 
		NUMBER_2K_BUFS);
        fprintf(fp, " buf_4K_maxuse: %2ld of %d\n", bp->buf_4K_maxuse, 
		NUMBER_4K_BUFS);
        fprintf(fp, " buf_8K_maxuse: %2ld of %d\n", bp->buf_8K_maxuse, 
		NUMBER_8K_BUFS);
        fprintf(fp, "buf_32K_maxuse: %2ld of %d\n", bp->buf_32K_maxuse, 
		NUMBER_32K_BUFS);

	fprintf(fp, "  buf_inuse[%d]: ", SHARED_BUF_SIZES);
	for (i = 0; i < SHARED_BUF_SIZES; i++)
		fprintf(fp, "[%lx]", (ulong)bp->buf_inuse[i]);
	fprintf(fp, "\n");

        for (i = 0; i < MAX_MALLOC_BUFS; i++) 
		if (bp->malloc_bp[i])
			fprintf(fp, "  malloc_bp[%d]: %lx\n", 
				i, (ulong)bp->malloc_bp[i]);

	if (bp->smallest == 0x7fffffff)
        	fprintf(fp, "      smallest: 0\n");
	else 
        	fprintf(fp, "      smallest: %ld\n", bp->smallest);
        fprintf(fp, "       largest: %ld\n", bp->largest);

	fprintf(fp, "      embedded: %ld\n", bp->embedded);
	fprintf(fp, "  max_embedded: %ld\n", bp->max_embedded);
	fprintf(fp, "       mallocs: %ld\n", bp->mallocs);
	fprintf(fp, "         frees: %ld\n", bp->frees);
	fprintf(fp, "    reqs/total: %ld/%.0f\n", bp->reqs, bp->total);
	fprintf(fp, "  average size: %.0f\n", bp->total/bp->reqs);
}

/*
 *  Try to get one of the static buffers first.  If not available, fall
 *  through and get it from malloc(), keeping trace of the returned address.
 */

#define SHARED_BUFSIZE(size) \
                ((size <= 1024) ? 1024 >> 7 : \
                    ((size <= 2048) ? 2048 >> 7 : \
                        ((size <= 4096) ? 4096 >> 7 : \
                            ((size <= 8192) ? 8192 >> 7 : \
                                ((size <= 32768) ? 32768 >> 7 : -1)))))

char *
getbuf(long reqsize)
{
	int i;
	int index;
	int bdx;
	int mask;
	struct shared_bufs *bp;
	char *bufp;

	if (!reqsize) { 
                ulong retaddr = (ulong)__builtin_return_address(0);
                error(FATAL, "zero-size memory allocation! (called from %lx)\n",
                        retaddr);
        }

	bp = &shared_bufs;

	index = SHARED_BUFSIZE(reqsize);

	if (CRASHDEBUG(7) && (reqsize > MAX_CACHE_SIZE))
		error(NOTE, "GETBUF request > MAX_CACHE_SIZE: %ld\n", 
			reqsize);

	if (CRASHDEBUG(8)) {
		INDENT(bp->embedded*2);
		fprintf(fp, "GETBUF(%ld -> %ld)\n", reqsize, bp->embedded);
	}

	bp->embedded++;
	if (bp->embedded > bp->max_embedded)
		bp->max_embedded = bp->embedded;

	if (reqsize < bp->smallest)
		bp->smallest = reqsize;
	if (reqsize > bp->largest)
		bp->largest = reqsize;

	bp->total += reqsize;
	bp->reqs++;

	switch (index)
	{
	case -1:
		break;

	case 8:
                if (SHARED_1K_BUF_AVAIL(bp->buf_inuse[B1K])) {
                        mask = ~(bp->buf_inuse[B1K]);
                        bdx = ffs(mask) - 1;
                        bufp = bp->buf_1K[bdx];
                        bp->buf_1K_used++;
                        bp->buf_inuse[B1K] |= (1 << bdx);
			bp->buf_1K_maxuse = MAX(bp->buf_1K_maxuse, 
				count_bits_int(bp->buf_inuse[B1K]));
                        BZERO(bufp, 1024);
                        return(bufp);
                }
		bp->buf_1K_ovf++;  /* FALLTHROUGH */

	case 16:
                if (SHARED_2K_BUF_AVAIL(bp->buf_inuse[B2K])) {
                        mask = ~(bp->buf_inuse[B2K]);
                        bdx = ffs(mask) - 1;
                        bufp = bp->buf_2K[bdx];
                        bp->buf_2K_used++;
                        bp->buf_inuse[B2K] |= (1 << bdx);
                        bp->buf_2K_maxuse = MAX(bp->buf_2K_maxuse,
                                count_bits_int(bp->buf_inuse[B2K]));
                        BZERO(bufp, 2048);
                        return(bufp);
                }
		bp->buf_2K_ovf++;  /* FALLTHROUGH */

	case 32:
                if (SHARED_4K_BUF_AVAIL(bp->buf_inuse[B4K])) {
                        mask = ~(bp->buf_inuse[B4K]);
                        bdx = ffs(mask) - 1;
                        bufp = bp->buf_4K[bdx];
                        bp->buf_4K_used++;
                        bp->buf_inuse[B4K] |= (1 << bdx);
                        bp->buf_4K_maxuse = MAX(bp->buf_4K_maxuse,
                                count_bits_int(bp->buf_inuse[B4K]));
                        BZERO(bufp, 4096);
                        return(bufp);
                }
		bp->buf_4K_ovf++;  /* FALLTHROUGH */

        case 64:
                if (SHARED_8K_BUF_AVAIL(bp->buf_inuse[B8K])) {
                        mask = ~(bp->buf_inuse[B8K]);
                        bdx = ffs(mask) - 1;
                        bufp = bp->buf_8K[bdx];
                        bp->buf_8K_used++;
                        bp->buf_inuse[B8K] |= (1 << bdx);
                        bp->buf_8K_maxuse = MAX(bp->buf_8K_maxuse,
                                count_bits_int(bp->buf_inuse[B8K]));
                        BZERO(bufp, 8192);
                        return(bufp);
                }
		bp->buf_8K_ovf++;  /* FALLTHROUGH */

	case 256:
               if (SHARED_32K_BUF_AVAIL(bp->buf_inuse[B32K])) {
                        mask = ~(bp->buf_inuse[B32K]);
                        bdx = ffs(mask) - 1;
                        bufp = bp->buf_32K[bdx];
                        bp->buf_32K_used++;
                        bp->buf_inuse[B32K] |= (1 << bdx);
                        bp->buf_32K_maxuse = MAX(bp->buf_32K_maxuse,
                                count_bits_int(bp->buf_inuse[B32K]));
                        BZERO(bufp, 32768);
                        return(bufp);
                }
                bp->buf_32K_ovf++;
		break;
	}

	for (i = 0; i < MAX_MALLOC_BUFS; i++) {
		if (bp->malloc_bp[i])
			continue;

		if ((bp->malloc_bp[i] = (char *)calloc(reqsize, 1))) {
			bp->mallocs++;
			return(bp->malloc_bp[i]);
		}

		break;
	}

	dump_shared_bufs();
	
	return ((char *)(long)
		error(FATAL, "cannot allocate any more memory!\n"));
}

/*
 *  Change the size of the previously-allocated memory block 
 *  pointed to by oldbuf to newsize bytes.  Copy the minimum
 *  of oldsize and newsize bytes from the oldbuf to the newbuf,
 *  and return the address of the new buffer, which will have
 *  a different address than oldbuf.
 */
char *
resizebuf(char *oldbuf, long oldsize, long newsize)
{
	char *newbuf;

	newbuf = GETBUF(newsize);
	BCOPY(oldbuf, newbuf, MIN(oldsize, newsize));
	FREEBUF(oldbuf);

	return newbuf;
}

/*
 *  Duplicate a string into a buffer allocated with GETBUF().
 */
char *
strdupbuf(char *oldstring)
{
	char *newstring;

	newstring = GETBUF(strlen(oldstring)+1);
	strcpy(newstring, oldstring);
	return newstring;
}

/*
 *  Return the number of bits set in an int or long.
 */

int
count_bits_int(int val)
{
	int i, cnt;
	int total;

	cnt = sizeof(int) * 8;

	for (i = total = 0; i < cnt; i++) {
		if (val & 1)
			total++;
		val >>= 1;
	}

	return total;
}

int
count_bits_long(ulong val)
{
        int i, cnt;
        int total;

        cnt = sizeof(long) * 8;

        for (i = total = 0; i < cnt; i++) {
                if (val & 1)
                        total++;
                val >>= 1;
        }

        return total;
}

int
highest_bit_long(ulong val)
{
        int i, cnt;
        int total;
	int highest;

	highest = -1;
        cnt = sizeof(long) * 8;

        for (i = total = 0; i < cnt; i++) {
                if (val & 1)
                        highest = i;
                val >>= 1;
        }

        return highest;
}

int
lowest_bit_long(ulong val)
{
        int i, cnt;
	int lowest;

	lowest = -1;
	cnt = sizeof(long) * 8;

        for (i = 0; i < cnt; i++) {
                if (val & 1) {
                        lowest = i;
			break;
		}
                val >>= 1;
        }

	return lowest;
}

/*
 *  Debug routine to stop whatever's going on in its tracks.
 */
void
drop_core(char *s)
{
	volatile int *nullptr;
	int i ATTRIBUTE_UNUSED;

	if (s && ascii_string(s))
		fprintf(stderr, "%s", s);

	kill((pid_t)pc->program_pid, 3);

	nullptr = NULL;
	while (TRUE)
		i = *nullptr;
}


/*
 *  For debug output to a device other than the current terminal.
 *  pc->console must have been preset by:
 *
 *   1. by an .rc file setting:    "set console /dev/whatever"
 *   2. by a runtime command:      "set console /dev/whatever"
 *   3. during program invocation:  "-c /dev/whatever"
 *
 *  The first time it's called, the device will be opened.
 */
int
console(char *fmt, ...)
{
        char output[BUFSIZE*2];
	va_list ap;

        if (!pc->console || !strlen(pc->console) || 
            (pc->flags & NO_CONSOLE) || (pc->confd == -1))
                return 0;

        if (!fmt || !strlen(fmt))
                return 0;

        va_start(ap, fmt);
        (void)vsnprintf(output, BUFSIZE*2, fmt, ap);
        va_end(ap);

        if (pc->confd == -2) {
                if ((pc->confd = open(pc->console, O_WRONLY|O_NDELAY)) < 0) {
                        error(INFO, "console device %s: %s\n",
                                pc->console, strerror(errno), 0, 0);
                        return 0;
                }
        }

        return(write(pc->confd, output, strlen(output)));
}

/*
 *  Allocate space to store the designated console device name.
 *  If a console device pre-exists, free its name space and close the device.
 */
void
create_console_device(char *dev)
{
        if (pc->console) {
                if (pc->confd != -1)
                        close(pc->confd);
                free(pc->console);
        }

        pc->confd = -2;

        if ((pc->console = (char *)malloc(strlen(dev)+1)) == NULL)
                fprintf(stderr, "console name malloc: %s\n", strerror(errno));
        else {
                strcpy(pc->console, dev);
                if (console("debug console [%ld]: %s\n", 
		    pc->program_pid, (ulong)pc->console) < 0) {
			close(pc->confd);
                	free(pc->console);
			pc->console = NULL;
			pc->confd = -1;
			if (!(pc->flags & RUNTIME))
				error(INFO, "cannot set console to %s\n", dev);
				
		}
        }
}

/*
 *  Disable console output without closing the device.  
 *  Typically used with CONSOLE_OFF() macro.
 */
int
console_off(void)
{
        int orig_no_console;

        orig_no_console = pc->flags & NO_CONSOLE;
        pc->flags |= NO_CONSOLE;

        return orig_no_console;
}

/*
 *  Re-enable console output.  Typically used with CONSOLE_ON() macro.
 */
int
console_on(int orig_no_console)
{
        if (!orig_no_console)
                pc->flags &= ~NO_CONSOLE;

        return(pc->flags & NO_CONSOLE);
}

/*
 *  Print a string to the console device with no formatting, useful for
 *  sending strings containing % signs.
 */
int
console_verbatim(char *s)
{
        char *p;
	int cnt;

        if (!pc->console || !strlen(pc->console) || 
	    (pc->flags & NO_CONSOLE) || (pc->confd == -1))
                return 0;

        if (!s || !strlen(s))
                return 0;

        if (pc->confd == -2) {
                if ((pc->confd = open(pc->console, O_WRONLY|O_NDELAY)) < 0) {
                        fprintf(stderr, "%s: %s\n",
                                pc->console, strerror(errno));
                        return 0;
                }
        }

        for (cnt = 0, p = s; *p; p++) {
                if (write(pc->confd, p, 1) != 1) 
			break;
		cnt++;
        }

	return cnt;
}

/*
 *  Set up a signal handler.
 */
void
sigsetup(int sig, void *handler, struct sigaction *act,struct sigaction *oldact)
{
	BZERO(act, sizeof(struct sigaction));
        act->sa_handler = handler;
        act->sa_flags = SA_NOMASK;
        sigaction(sig, act, oldact);
}

/*
 *  Convert a jiffies-based time value into a string showing the
 *  the number of days, hours:minutes:seconds.
 */
#define SEC_MINUTES  (60)
#define SEC_HOURS    (60 * SEC_MINUTES)
#define SEC_DAYS     (24 * SEC_HOURS)

char *
convert_time(ulonglong count, char *buf)
{
	ulonglong total, days, hours, minutes, seconds;

	if (CRASHDEBUG(2))
		error(INFO, "convert_time: %lld (%llx)\n", count, count);

	if (!machdep->hz) {
		sprintf(buf, "(cannot calculate: unknown HZ value)");
		return buf;
	}

        total = (count)/(ulonglong)machdep->hz;

        days = total / SEC_DAYS;
        total %= SEC_DAYS;
        hours = total / SEC_HOURS;
        total %= SEC_HOURS;
        minutes = total / SEC_MINUTES;
        seconds = total % SEC_MINUTES;

	buf[0] = NULLCHAR;

        if (days)
        	sprintf(buf, "%llu days, ", days);
        sprintf(&buf[strlen(buf)], "%02llu:%02llu:%02llu", 
		hours, minutes, seconds);

	return buf;
}

/*
 *  Stall for a number of microseconds.
 */
void
stall(ulong microseconds)
{
        struct timeval delay;

        delay.tv_sec = 0;
        delay.tv_usec = (__time_t)microseconds;

        (void) select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &delay);
}


/*
 *  Fill a buffer with a page count translated to a GB/MB/KB value.
 */ 
char *
pages_to_size(ulong pages, char *buf)
{
	double total;
	char *p1, *p2;

	if (pages == 0) {
		sprintf(buf, "0");
		return buf;
	}

	total = (double)pages * (double)PAGESIZE();

    	if (total >= GIGABYTES(1))
        	sprintf(buf, "%.1f GB", total/(double)GIGABYTES(1));
    	else if (total >= MEGABYTES(1))
        	sprintf(buf, "%.1f MB", total/(double)MEGABYTES(1));
        else
        	sprintf(buf, "%ld KB", (ulong)(total/(double)KILOBYTES(1)));

	if ((p1 = strstr(buf, ".0 "))) {
		p2 = p1 + 3;
		*p1++ = ' ';
		strcpy(p1, p2);
	}

	return buf;
}

/*
 *  If the list_head.next value points to itself, it's an emtpy list.
 */
int
empty_list(ulong list_head_addr)
{
	ulong next;
 
	if (!readmem(list_head_addr, KVADDR, &next, sizeof(void *),
            "list_head next contents", RETURN_ON_ERROR))
		return TRUE;

	return (next == list_head_addr);
}

int
machine_type(char *type)
{
	return STREQ(MACHINE_TYPE, type);
}

int 
machine_type_mismatch(char *file, char *e_machine, char *alt, ulong query)
{
	if (machine_type(e_machine) || machine_type(alt))
		return FALSE;

	if (query == KDUMP_LOCAL)  /* already printed by NETDUMP_LOCAL */
		return TRUE;

	error(WARNING, "machine type mismatch:\n");

	fprintf(fp, "         crash utility: %s\n", MACHINE_TYPE);
	fprintf(fp, "         %s: %s%s%s\n\n", file, e_machine,
		alt ? " or " : "", alt ? alt : "");
		
	return TRUE;
}
void
command_not_supported()
{
	error(FATAL, 
	    "command not supported or applicable on this architecture or kernel\n");
}

void
option_not_supported(int c)
{
	error(FATAL, 
	    "-%c option not supported or applicable on this architecture or kernel\n", 
		(char)c);
}

static int please_wait_len = 0;

void
please_wait(char *s)
{
	int fd;
	char buf[BUFSIZE];

	if ((pc->flags & SILENT) || !DUMPFILE() || (pc->flags & RUNTIME))
		return;

	if (!(pc->flags & TTY) && KVMDUMP_DUMPFILE()) {
		if (!isatty(fileno(stdin)) ||
		    ((fd = open("/dev/tty", O_RDONLY)) < 0))
			return;
		close(fd);
	}

	pc->flags |= PLEASE_WAIT;

        please_wait_len = sprintf(buf, "\rplease wait... (%s)", s);
	fprintf(fp, "%s", buf);
        fflush(fp);
}

void
please_wait_done(void)
{
	if (!(pc->flags & PLEASE_WAIT))
		return;

	pc->flags &= ~PLEASE_WAIT;

	fprintf(fp, "\r");
	pad_line(fp, please_wait_len, ' ');
	fprintf(fp, "\r");
	fflush(fp);
}

/*
 *  Compare two pathnames.
 */
int
pathcmp(char *p1, char *p2)
{
        char c1, c2;

        do {
                if ((c1 = *p1++) == '/')
                        while (*p1 == '/') { p1++; }
                if ((c2 = *p2++) == '/')
                        while (*p2 == '/') { p2++; }
                if (c1 == '\0')
                        return ((c2 == '/') && (*p2 == '\0')) ? 0 : c1 - c2;
        } while (c1 == c2);

        return ((c2 == '\0') && (c1 == '/') && (*p1 == '\0')) ? 0 : c1 - c2;
}

#include <elf.h>

/*
 *  Check the byte-order of an ELF file vs. the host byte order.
 */
int
endian_mismatch(char *file, char dumpfile_endian, ulong query)
{
	char *endian;

	switch (dumpfile_endian)
	{
	case ELFDATA2LSB:
		if (__BYTE_ORDER == __LITTLE_ENDIAN)
			return FALSE;
		endian = "little-endian";
		break;
	case ELFDATA2MSB:
		if (__BYTE_ORDER == __BIG_ENDIAN)	
			return FALSE;
		endian = "big-endian";
		break;
	default:
		endian = "unknown";	
		break;
	}

	if (query == KDUMP_LOCAL)  /* already printed by NETDUMP_LOCAL */
		return TRUE;

        error(WARNING, "endian mismatch:\n");

        fprintf(fp, "         crash utility: %s\n", 
		(__BYTE_ORDER == __LITTLE_ENDIAN) ?
		"little-endian" : "big-endian");
        fprintf(fp, "         %s: %s\n\n", file, endian);

	return TRUE;	
}

uint16_t
swap16(uint16_t val, int swap)
{
	if (swap) 
        	return (((val & 0x00ff) << 8) |
                	((val & 0xff00) >> 8));
	else
		return val;
}

uint32_t
swap32(uint32_t val, int swap)
{
	if (swap)
        	return (((val & 0x000000ffU) << 24) |
                	((val & 0x0000ff00U) <<  8) |
                	((val & 0x00ff0000U) >>  8) |
                	((val & 0xff000000U) >> 24));
	else
		return val;
}

uint64_t
swap64(uint64_t val, int swap)
{
	if (swap)
		return (((val & 0x00000000000000ffULL) << 56) |
			((val & 0x000000000000ff00ULL) << 40) |
			((val & 0x0000000000ff0000ULL) << 24) |
			((val & 0x00000000ff000000ULL) <<  8) |
			((val & 0x000000ff00000000ULL) >>  8) |
			((val & 0x0000ff0000000000ULL) >> 24) |
			((val & 0x00ff000000000000ULL) >> 40) |
			((val & 0xff00000000000000ULL) >> 56));
	else
		return val;
}

/*
 *  Get a sufficiently large buffer for cpumask.
 *  You should call FREEBUF() on the result when you no longer need it.
 */
ulong *
get_cpumask_buf(void)
{
	int cpulen;
	if ((cpulen = STRUCT_SIZE("cpumask_t")) < 0)
		cpulen = DIV_ROUND_UP(kt->cpus, BITS_PER_LONG) * sizeof(ulong);
	return (ulong *)GETBUF(cpulen);
}

int
make_cpumask(char *s, ulong *mask, int flags, int *errptr)
{
	char *p, *q, *orig;
	int start, end;
	int i;

	if (s == NULL) {
		if (!(flags & QUIET))
			error(INFO, "make_cpumask: received NULL string\n");
		orig = NULL;
		goto make_cpumask_error;
	}

	orig = strdup(s);

	p = strtok(s, ",");
	while (p) {
		s = strtok(NULL, "");

		if (STREQ(p, "a") || STREQ(p, "all")) {
			start = 0;
			end = kt->cpus - 1;
		} else {
			start = end = -1;
			q = strtok(p, "-");
			start = dtoi(q, flags, errptr);
			if ((q = strtok(NULL, "-")))
				end = dtoi(q, flags, errptr);

			if (end == -1)
				end = start;
		}
		if ((start < 0) || (start >= kt->cpus) || 
		    (end < 0) || (end >= kt->cpus)) {
			error(INFO, "invalid cpu specification: %s\n", orig);
			goto make_cpumask_error;
		}

		for (i = start; i <= end; i++)
			SET_BIT(mask, i);

		p = strtok(s, ",");
	}

	free(orig);

	return TRUE;

make_cpumask_error:
	free(orig);

	switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
	{
	case FAULT_ON_ERROR:
		RESTART();

	case RETURN_ON_ERROR:
		if (errptr)
			*errptr = TRUE;
		break;
	}

	return UNUSED;
}

/*
 * Copy a string into a sized buffer.  If necessary, truncate 
 * the resultant string in the sized buffer so that it will 
 * always be NULL-terminated.
 */
size_t 
strlcpy(char *dest, char *src, size_t size)
{
	size_t ret = strlen(src);

	if (size) {
		size_t len = (ret >= size) ? size - 1 : ret;
		memcpy(dest, src, len);
		dest[len] = '\0';
	}
	return ret;
}

struct rb_node *
rb_first(struct rb_root *root)
{
        struct rb_root rloc;
        struct rb_node *n;
	struct rb_node nloc;

	readmem((ulong)root, KVADDR, &rloc, sizeof(struct rb_root), 
		"rb_root", FAULT_ON_ERROR);

        n = rloc.rb_node;
        if (!n)
                return NULL;
        while (rb_left(n, &nloc))
		n = nloc.rb_left;

        return n;
}

struct rb_node *
rb_parent(struct rb_node *node, struct rb_node *nloc)
{
	readmem((ulong)node, KVADDR, nloc, sizeof(struct rb_node), 
		"rb_node", FAULT_ON_ERROR);

	return (struct rb_node *)(nloc->rb_parent_color & ~3);
}

struct rb_node *
rb_right(struct rb_node *node, struct rb_node *nloc)
{
	readmem((ulong)node, KVADDR, nloc, sizeof(struct rb_node), 
		"rb_node", FAULT_ON_ERROR);

	return nloc->rb_right;
}

struct rb_node *
rb_left(struct rb_node *node, struct rb_node *nloc)
{
	readmem((ulong)node, KVADDR, nloc, sizeof(struct rb_node), 
		"rb_node", FAULT_ON_ERROR);

	return nloc->rb_left;
}

struct rb_node *
rb_next(struct rb_node *node)
{
	struct rb_node nloc;
        struct rb_node *parent;

	/* node is destroyed */
	if (!accessible((ulong)node))
		return NULL;

	parent = rb_parent(node, &nloc);

	if (parent == node)
		return NULL;

        if (nloc.rb_right) {
		/* rb_right is destroyed */
		if (!accessible((ulong)nloc.rb_right))
			return NULL;

		node = nloc.rb_right;
		while (rb_left(node, &nloc)) {
			/* rb_left is destroyed */
			if (!accessible((ulong)nloc.rb_left))
				return NULL;
			node = nloc.rb_left;
		}
		return node;
	}

	while ((parent = rb_parent(node, &nloc))) {
		/* parent is destroyed */
                if (!accessible((ulong)parent))
                        return NULL;


		if (node != rb_right(parent, &nloc))
			break;

		node = parent;
	}

        return parent;
}

struct rb_node *
rb_last(struct rb_root *root)
{
	struct rb_node *node;
	struct rb_node nloc;

	/* meet destroyed data */
	if (!accessible((ulong)(root + OFFSET(rb_root_rb_node))))
		return NULL;

	readmem((ulong)(root + OFFSET(rb_root_rb_node)), KVADDR, &node,
		sizeof(node), "rb_root node", FAULT_ON_ERROR);

	while (1) {
		if (!node)
			break;

		/* meet destroyed data */
		if (!accessible((ulong)node))
			return NULL;

		readmem((ulong)node, KVADDR, &nloc, sizeof(struct rb_node),
		"rb_node last", FAULT_ON_ERROR);

		/*  meet the last one  */
		if (!nloc.rb_right)
			break;

		/* meet destroyed data */
		if (!!accessible((ulong)nloc.rb_right))
			break;

		node = nloc.rb_right;
	}

	return node;
}
