/*
 * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
 */
#include <lcrash.h>
#include <lc_eval.h>

/*
 * single_type()
 */
int
single_type(command_t *cmd)
{
	int type_cnt = 0, hit_count, i;
	kltype_t *kltp;
	char *cp, buffer[256], *type_name;
	syment_t *sp;
	static char *types[] = {"struct ", "union ", "enum ", NULL};

	type_name = buffer;
	if (cmd->args[0][0] == '\"') {
		strcpy(type_name, &cmd->args[0][1]);
		if ((cp = strchr(type_name, '\"'))) {
			*cp = 0;
		}
	} else {
		strcpy(type_name, cmd->args[0]);
	}
	/* strip leading syntatic sugar */
	for (i = 0; types[i]; i++) {
		if (!strncmp(type_name, types[i], strlen(types[i]))) {
			type_name += strlen(types[i]);
			break;
		}
	}

	hit_count = 0;
	if ((kltp = (kltype_t*)kl_find_sym(type_name, KLT_TYPE))) {
		hit_count++;

		if (cmd->flags & C_LIST) {
			if (cmd->flags & C_NEXT) {
				walk_ktype(kltp, cmd->flags, cmd->ofp);
			} else {
				ktype_banner(cmd->ofp, (BANNER|SMAJOR));
				print_ktype(kltp, cmd->flags, cmd->ofp);
			}
		} else {
			kl_print_type((void *)NULL, kltp, 
				0, cmd->flags, cmd->ofp);
		}
	}
	if ((kltp = (kltype_t *)kl_find_sym(type_name, KLT_TYPEDEF))) {
		if (hit_count) {
			fprintf(cmd->ofp, "\n");
		}
		hit_count++;
		if (cmd->flags & C_LIST) {
			if (cmd->flags & C_NEXT) {
				walk_ktype(kltp, cmd->flags, cmd->ofp);
			} else {
				ktype_banner(cmd->ofp, (BANNER|SMAJOR));
				print_ktype(kltp, cmd->flags, cmd->ofp);
			}
		} else {
			kltype_t *rkltp;

			kl_print_type((void *)NULL, kltp, 
				0, cmd->flags, cmd->ofp);
			rkltp = kltp->kl_realtype;
			while (rkltp && (rkltp->kl_type == KLT_POINTER)) {
				rkltp = rkltp->kl_realtype;
			}
			while (rkltp && (rkltp->kl_realtype)
			       && (rkltp != rkltp->kl_realtype)) {
				rkltp = rkltp->kl_realtype;
			}

			if (rkltp && ((rkltp->kl_type == KLT_STRUCT) ||
				    (rkltp->kl_type == KLT_UNION) ||
				    (rkltp->kl_type == KLT_ENUMERATION))) { 
					
				kl_print_type((void *)NULL, rkltp, 0, 
					cmd->flags, cmd->ofp);
			} else if (!rkltp) {
				fprintf(cmd->efp, 
					"Type information not available\n");
			}
		}
	}
	if ((sp = kl_lkup_symname(type_name))) {
		if (hit_count) {
			fprintf(cmd->ofp, "\n");
		}
		hit_count++;
		symbol_banner(cmd->ofp, BANNER|SMAJOR);
		print_symbol(0, sp, cmd->flags, cmd->ofp);
	}

	if (hit_count) {
		type_cnt += hit_count;
	} else {
		fprintf(KL_ERRORFP, "could not find type information for %s\n",
		type_name);
	} 
	return(type_cnt);
}

/*
 * whatis_cmd() -- Run the 'whatis' command.
 */
int
whatis_cmd(command_t *cmd)
{
	int i, j = 0, ptr_cnt = 0, type_cnt = 0, flags;
	kltype_t *kltp, *rkltp;
	char *buf;
	node_t *np;
	type_t *tp;

	if (cmd->nargs == 0) {
		if (cmd->flags & C_ALL) {
			type_cnt = list_ktypes(cmd->flags, cmd->ofp);
		} else {
			return(1);
		}
	} else if ((cmd->nargs == 1) && !strpbrk(cmd->args[0], 
		"\'.-()*&0123456789")) {
		type_cnt = single_type(cmd);
	} else {
		/* Count the number of bytes necessary to hold the 
		 * entire expression string.
		 */
		for (i = 0; i < cmd->nargs; i++) {
			j += (strlen(cmd->args[i]) + 1);
		}

		/* Allocate space for the expression string and copy 
		 * the individual arguments into it.
		 */
		buf = (char *)kl_alloc_block(j, K_TEMP);
		for (i = 0; i < cmd->nargs; i++) {
			strcat(buf, cmd->args[i]);
			if ((i + 1) < cmd->nargs) {
				strcat(buf, " ");
			}
		}

		/* Evaluate the expression
		 */
		np = eval(&buf, C_WHATIS|C_NOVARS);
		if (!np || eval_error) {
			print_eval_error(cmd->command, buf,
				(error_token ? error_token : (char*)NULL), 
				eval_error, CMD_NAME);
			kl_free_block((void *)buf);
			free_nodes(np);
			free_eval_memory();
			return(1);
		}

		/* Print the results
		 */
		if ((tp = np->type)) {
			while (tp && (tp->flag == POINTER_FLAG)) {
				ptr_cnt++;
				tp = tp->t_next;
			}
		}
		if (!tp || (tp->flag != KLTYPE_FLAG)) {
			if (!(kltp = number_to_type(np))) {
				fprintf(cmd->efp, 
					"Type information not available\n");
				free_eval_memory();
				free_nodes(np);
				return(1);
			}
		} else {
			kltp = tp->t_kltp;
		}
		if (cmd->flags & C_LIST) {
			rkltp = kl_realtype(kltp, 0);
			if (!rkltp) {
				rkltp = kltp;
			}
			if (cmd->flags & C_NEXT) {
				walk_ktype(rkltp, cmd->flags, cmd->ofp);
			} else {
				ktype_banner(cmd->ofp, (BANNER|SMAJOR));
				print_ktype(rkltp, cmd->flags, cmd->ofp);
			}
		} else {
			if (!(rkltp = kl_realtype(kltp, KLT_FUNCTION))) {
				fprintf(cmd->efp, "Could not find real "
					"type information\n");
				free_eval_memory();
				free_nodes(np);
				return(1);
			} 
			if (rkltp->kl_type == KLT_FUNCTION) {
				kl_print_type((void *)NULL, kltp, 0, 
					flags|SUPPRESS_NAME, cmd->ofp);
			} else if (ptr_cnt) {
				switch(rkltp->kl_type) {
					case KLT_STRUCT:
						fprintf(cmd->ofp, "struct ");
						break;

					case KLT_UNION:
						fprintf(cmd->ofp, "union ");
						break;

					case KLT_ENUMERATION:
						break;
				}
				fprintf(cmd->ofp, "%s ", rkltp->kl_name); 
				while(ptr_cnt) {
					fprintf(cmd->ofp, "*"); 
					ptr_cnt--;
				}
				fprintf(cmd->ofp, "\n");
			} else if ((rkltp->kl_type == KLT_STRUCT) ||
					(rkltp->kl_type == KLT_UNION) ||
					(rkltp->kl_type == KLT_ENUMERATION)) {
				kl_print_type((void *)NULL, rkltp, 0, 
					cmd->flags, cmd->ofp);
			} else {
				kl_print_type((void *)NULL, kltp, 0, 
					cmd->flags|SUPPRESS_NAME, cmd->ofp);
			}
#ifdef NOT
			} else if (kltp->kl_realtype && 
				(kltp->kl_realtype->kl_type == KLT_TYPEDEF)) { 

				kl_print_type((void *)NULL, kltp, 0, 
					cmd->flags|SUPPRESS_NAME, cmd->ofp);
			} else {
				kl_print_type((void *)NULL, rkltp, 0, 
					flags, cmd->ofp);
			} 
#endif
		}
		type_cnt++;
		if (buf) {
			kl_free_block(buf);
		}
		free_nodes(np);
	}

	if (cmd->flags & C_LIST) {
		ktype_banner(cmd->ofp, SMAJOR);
		PLURAL("type", type_cnt, cmd->ofp);
	}
	free_eval_memory();
	return(0);
}

#define _WHATIS_USAGE "[-a] [-f] [-l] [-n] [-w outfile] expression"

/*
 * whatis_usage() -- Print the usage string for the 'whatis' command.
 */
void
whatis_usage(command_t *cmd)
{
	CMD_USAGE(cmd, _WHATIS_USAGE);
}

/*
 * whatis_help() -- Print the help information for the 'whatis' command.
 */
void
whatis_help(command_t *cmd)
{
	CMD_HELP(cmd, _WHATIS_USAGE,
	"Display, in C-like fashion, detailed information about kernel "
	"types (structs, unions, typedefs, base types, etc.) If the "
	"-a option is specified, display a list of all types. If the -l " 
	"option is specified, display type information in tabular form. "
	"When the -f option is specified, along with the -l option, "
	"display additional information about the type. If the -n option "
	"is specified for a struct or union, along with the -l option, "
	"display information about each member.");
}

/*
 * whatis_parse() -- Parse the command line arguments for 'whatis'.
 */
int
whatis_parse(command_t *cmd)
{
	if (set_cmd_flags(cmd, (C_ALL|C_LIST|C_FULL|C_NEXT|
				C_WRITE|C_NO_OPCHECK), 0)) {
		return(1);
	}
	return(0);
}

/*
 * whatis_complete() -- Complete arguments of 'whatis' command.
 */
char *
whatis_complete(command_t *cmd)
{
	char *ret;

	/* complete standard options (for example, -w option) arguments
	 */
	if ((ret = complete_standard_options(cmd)) != NOT_COMPLETED) {
		return(ret);
	}
	fprintf(cmd->ofp, "\n");
	whatis_usage(cmd);
	return(DRAW_NEW_ENTIRE_LINE);
}
