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

typedef struct stype_s {
	char  *name;                       /* struct name                     */
	int    size;			   /* struct size 		      */
	void  (*banner)(FILE *, int);      /* struct banner function          */
	void  (*print)(kaddr_t, void *, int, FILE *); /* print routine        */
} stype_t;

stype_t Struct[] = {
	/*--------------------------------------------------------------------*/
	/*  NAME          SIZE  BANNER()               	PRINT()               */
	/*--------------------------------------------------------------------*/
	{ "mm_struct",       0, mm_struct_banner,       print_mm_struct       },
	{ "page",            0, page_banner,            print_page            },
	{ "task_struct",     0, task_struct_banner,     print_task_struct     },
	{ "vm_area_struct",  0, vm_area_struct_banner,  print_vm_area_struct  },
	{ "module",          0, module_banner,          print_module          },
	{ (char *)0,         0, 0,                     	0                     }
};

/*
 * print_banner()
 */
void
print_banner(FILE *ofp, char *bline, int flags)
{
	int i, count = 0, maxline = 0;
	char *c, *s;

	s = bline;
	while (s && (c = strchr(s, '%'))) {
		while (s < c) {
			if (flags & BANNER) {
				fputc(*s, ofp);
			}
			count++;
			s++;
		}
		c++;
		switch (*c) {
			case '>':
				if (PTRSZ64) {
					if (flags & BANNER) {
						fprintf(ofp, "        ");
					}
					count += 8;
				}
				s = (c + 1);
				break;
			case '}':
				if (*(c + 1)) {
					maxline = atoi(c + 1);
				}
				s = 0;
				break;
			default:
				s = c;
				break;
		}
	}
	if (flags & BANNER) {
		if (s) {
			while (*s) {
				if (flags & BANNER) {
					fputc(*s, ofp);
				}
				count++;
				s++;
			}
		}
		fprintf(ofp, "\n");
	}
	if (maxline && (maxline > count)) {
		count = maxline;
	}
	if (flags & SMAJOR) {
		for (i = 0; i < count; i++) {
			fputc('=', ofp);
		}
		fputc('\n', ofp);
	}
	if (flags & SMINOR) {
		for (i = 0; i < count; i++) {
			fputc('-', ofp);
		}
		fputc('\n', ofp);
	}
}

/*
 * print_value()
 */
void
print_value(FILE *ofp, char *ldstr, uint64_t value, int width, int flag)
{
	int w = 0;
	char fmtstr[12], f, s[2]="\000\000";

	if (ldstr) {
		fprintf(ofp, "%s", ldstr);
	}
	if (flag == ADDR_FLG) {
	        s[0] = '#';
		f = 'x';
		if (width) {
			if (PTRSZ64) {  
				w = 18; /* due to leading "0x" */
			} else {
			        w = 10; /* due to leading "0x" */
			}
		}
	} else {
		switch (flag) {
			case HEX_FLG:
			        s[0] = '#';
				f = 'x';
				break;
			case OCTAL_FLG:
				f = 'o';
				break;
			case UNSIGNED_FLG:
				f = 'u';
				break;
			case CHAR_FLG:
				f = 'c';
				break;
			case SIGNED_FLG:
			default:
				f = 'd';
				break;
		}
		w = width;
	}
	if (w) {
		sprintf(fmtstr, "%%%s%d"FMT64"%c", s, w, f);
	} else {
		sprintf(fmtstr, "%%%s"FMT64"%c", s, f);
	}
	fprintf(ofp, fmtstr, value);
}

/*
 * list_head_banner()
 */
void
list_head_banner(FILE *ofp, int flags)
{
	if(flags & C_LISTHEAD_N){
		print_banner(ofp, "%>STRUCT ADDR%>       PREV%>   LISTHEAD"
			     "%>       NEXT%", flags);
	} else {
		print_banner(ofp, "%>STRUCT ADDR%>       NEXT%>   LISTHEAD"
			     "%>       PREV%", flags);

	}
}

/*
 * print_list_head()
 */
void
print_list_head(FILE *ofp, kaddr_t saddr, kaddr_t lhaddr, 
		kaddr_t prev, kaddr_t next, int flags)
{
	if (flags & C_TABLE){
		print_value(ofp, " ", ADDR64(saddr), 8, ADDR_FLG);
		if(flags & C_LISTHEAD_N){
			print_value(ofp, " ", ADDR64(prev), 8, ADDR_FLG);
			print_value(ofp, " ", ADDR64(lhaddr), 8, ADDR_FLG);
			print_value(ofp, " ", ADDR64(next), 8, ADDR_FLG);
		} else {
			print_value(ofp, " ", ADDR64(next), 8, ADDR_FLG);
			print_value(ofp, " ", ADDR64(lhaddr), 8, ADDR_FLG);
			print_value(ofp, " ", ADDR64(prev), 8, ADDR_FLG);
		}
		fprintf(ofp, "\n");
	} else if (flags & C_STRUCT) {
		print_value(ofp, "STRUCT ADDR: ", ADDR64(saddr), 8, ADDR_FLG);
		fprintf(ofp, "\n");
	} 
}

/*
 * check_prev_ptr()
 */
void
check_prev_ptr(FILE *ofp, kaddr_t ptr, kaddr_t prev)
{
	if(ptr != prev) {
		fprintf(ofp, "\nWARNING: Pointer broken. %#lx,"
			" SHOULD BE: %#lx\n", (unsigned long) prev,
			(unsigned long) ptr);
	}
}

/*
 * mm_struct_banner()
 */
void
mm_struct_banner(FILE *ofp, int flags)
{
	print_banner(ofp, "%>      ADDR%  "
                        "MM_COUNT  MAP_COUNT  %>      MMAP%", flags);
}

/*
 * print_mm_struct()
 */
void
print_mm_struct(kaddr_t addr, void *p, int flags, FILE *ofp)
{
	kaddr_t vmap;
	void *vmapp;

	print_value(ofp, "", ADDR64(addr), 8, ADDR_FLG);
	if (LINUX_2_2_X(KL_LINUX_RELEASE)) {
		print_value(ofp, "  ", 
			UINT64(KL_UINT(K_PTR(p, "mm_struct", "count"),
			"atomic_t", "counter")), 8, UNSIGNED_FLG);
	} else {
		print_value(ofp, "  ", 
			UINT64(KL_UINT(K_PTR(p, "mm_struct", "mm_count"),
			"atomic_t", "counter")), 8, UNSIGNED_FLG);
	}
	print_value(ofp, "  ", 
		UINT64(KL_UINT(p, "mm_struct", "map_count")), 9, UNSIGNED_FLG);
	print_value(ofp, "  ", 
		ADDR64(kl_kaddr(p, "mm_struct", "mmap")), 8, ADDR_FLG);
	fprintf(ofp, "\n");

	if (flags & C_FULL) {
		fprintf(ofp, "\n");
		print_value(ofp, "  START_CODE:", 
			ADDR64(kl_kaddr(p, "mm_struct", "start_code")), 
			0, ADDR_FLG);
		print_value(ofp, ", END_CODE:", 
			ADDR64(kl_kaddr(p, "mm_struct", "end_code")), 
			0, ADDR_FLG);
		fprintf(ofp, "\n");

		print_value(ofp, "  START_DATA:", 
			ADDR64(kl_kaddr(p, "mm_struct", "start_data")), 
			0, ADDR_FLG);
		print_value(ofp, ", END_DATA:", 
			ADDR64(kl_kaddr(p, "mm_struct", "end_data")), 
			0, ADDR_FLG);
		fprintf(ofp, "\n");

		print_value(ofp, "  START_BRK:", 
			ADDR64(kl_kaddr(p, "mm_struct", "start_brk")), 
			0, ADDR_FLG);
		print_value(ofp, ", START_STACK:", 
			ADDR64(kl_kaddr(p, "mm_struct", "start_stack")), 
			0, ADDR_FLG);
		fprintf(ofp, "\n");

		print_value(ofp, "  ARG_START:", 
			ADDR64(kl_kaddr(p, "mm_struct", "arg_start")), 
			0, ADDR_FLG);
		print_value(ofp, ", ARG_END:", 
			ADDR64(kl_kaddr(p, "mm_struct", "arg_end")), 
			0, ADDR_FLG);
		fprintf(ofp, "\n");

		print_value(ofp, "  TOTAL_VM:", 
			UINT64(KL_UINT(p, "mm_struct", "total_vm")),
			0, HEX_FLG);
		fprintf(ofp, "\n\n");
	}
	if (flags & C_NEXT) {
		vmapp = kl_alloc_block(kl_struct_len("vm_area_struct"), K_TEMP);
		if (klib_error) {
			fprintf(ofp, "could not allocate %d bytes for buffer "
				"to hold vm_area_struct\n", 
				kl_struct_len("vm_area_struct"));
			return;
		}
		vmap = kl_kaddr(p, "mm_struct", "mmap");
		fprintf(ofp, "\n");
		vm_area_struct_banner(ofp, BANNER|SMINOR);
		while(vmap) {
			GET_BLOCK(vmap, kl_struct_len("vm_area_struct"), vmapp);
			if (klib_error) {
				fprintf(ofp, "error reading vmap_struct at ");
				print_value(ofp, "", UINT64(vmap), 
					0, ADDR_FLG);
				break;
			}
			print_vm_area_struct(vmap, vmapp, 0, ofp);
			vmap = kl_kaddr(vmapp, "vm_area_struct", "vm_next");
		}
		vm_area_struct_banner(ofp, SMINOR);
		fprintf(ofp, "\n");
		kl_free_block(vmapp);
	}
}

/*
 * page_banner()
 */
void
page_banner(FILE *ofp, int flags)
{
	print_banner(ofp, "%>      ADDR%    PGNO  COUNT  "
		"%>     FLAGS%  %>   VIRTUAL%", flags);
}

/*
 * print_page()
 */
void
print_page(kaddr_t addr, void *pp, int flags, FILE *ofp)
{
	int pgno;

	pgno = (addr - MEM_MAP) / PAGE_SZ;

	print_value(ofp, "", ADDR64(addr), 8, ADDR_FLG);
	print_value(ofp, "  ", UINT64(pgno), 6, UNSIGNED_FLG);
	print_value(ofp, "  ", UINT64(KL_UINT(K_PTR(pp, "page", "count"),
		"atomic_t", "counter")), 5, UNSIGNED_FLG);
	print_value(ofp, "  ", ADDR64(KL_UINT(pp, "page", "flags")),
		8, ADDR_FLG);
	print_value(ofp, "  ", ADDR64(KL_UINT(pp, "page", "virtual")),
		8, ADDR_FLG);
	fprintf(ofp, "\n");

	if (flags & C_FULL) {
		fprintf(ofp, "\n");
		if (LINUX_2_2_X(KL_LINUX_RELEASE)) {
			print_value(ofp, "  NEXT:", 
				ADDR64(KL_UINT(pp, "page", "next")),
				0, ADDR_FLG);
			print_value(ofp, ", PREV:", 
				ADDR64(KL_UINT(pp, "page", "prev")),
				0, ADDR_FLG);
			fprintf(ofp, "\n");
		} else {
			print_value(ofp, "  NEXT:", 
				ADDR64(KL_UINT(K_PTR(pp, "page", "list"),
				"list_head", "next")), 0, ADDR_FLG);
			print_value(ofp, ", PREV:", 
				ADDR64(KL_UINT(K_PTR(pp, "page", "list"),
				"list_head", "prev")), 0, ADDR_FLG);
			fprintf(ofp, "\n");
		}
		print_value(ofp, "  NEXT_HASH:", 
			ADDR64(KL_UINT(pp, "page", "next_hash")),
			0, ADDR_FLG);
		fprintf(ofp, "\n");

		print_value(ofp, "  MAPPING:", 
			ADDR64(KL_UINT(pp, "page", "mapping")),
			0, ADDR_FLG);
		print_value(ofp, ", INDEX:", 
			ADDR64(KL_UINT(pp, "page", "index")),
			0, ADDR_FLG);
		fprintf(ofp, "\n\n");
	}
}

/*
 * task_struct_banner()
 */
void
task_struct_banner(FILE *ofp, int flags)
{
	print_banner(ofp, "%>      ADDR%    UID    PID   PPID  STATE  "
				"   FLAGS CPU  NAME%}79", flags);
}

/*
 * print_task_struct()
 */
void
print_task_struct(kaddr_t addr, void *tsp, int flags, FILE *ofp)
{
	kaddr_t mmap_addr;
	void *mmp;

	print_value(ofp, "", ADDR64(addr), 8, ADDR_FLG);
	print_value(ofp, "  ", KL_UINT(tsp, "task_struct", "uid"), 
		5, UNSIGNED_FLG);
	print_value(ofp, "  ", KL_UINT(tsp, "task_struct", "pid"), 
		5, UNSIGNED_FLG);
	print_value(ofp, "  ", UINT64(kl_parent_pid(tsp)), 5, UNSIGNED_FLG);
	print_value(ofp, "  ", KL_UINT(tsp, "task_struct", "state"), 
		5, UNSIGNED_FLG);
	print_value(ofp, "  ", KL_UINT(tsp, "task_struct", "flags"), 
		8, HEX_FLG);

	if(task_has_cpu(tsp))
		print_value(ofp,"  ",KL_INT(tsp,"task_struct","processor"),2,SIGNED_FLG);
	else
		fprintf(ofp,"   -");
	fprintf(ofp, "  %s\n", (char *)K_PTR(tsp, "task_struct", "comm"));

	if (flags & C_FULL) {
		fprintf(ofp, "\n");

		print_value(ofp, "  MM:", 
			KL_UINT(tsp, "task_struct", "mm"), 0, ADDR_FLG);
		fprintf(ofp, "\n\n");

		if (LINUX_2_2_X(KL_LINUX_RELEASE)) {
			fprintf(ofp, "THREAD:\n");
			print_value(ofp, "  ESP0:", 
				KL_UINT(K_PTR(tsp, "task_struct", "tss"),
				"thread_struct", "esp0"), 0, ADDR_FLG);
			print_value(ofp, ", ESP:", 
				KL_UINT(K_PTR(tsp, "task_struct", "tss"),
				"thread_struct", "esp"), 0, ADDR_FLG);
			print_value(ofp, ", EIP:", 
				KL_UINT(K_PTR(tsp, "task_struct", "tss"),
				"thread_struct", "eip"), 0, ADDR_FLG);
			fprintf(ofp, "\n");

			print_value(ofp, "  FS:", 
				KL_UINT(K_PTR(tsp, "task_struct", "tss"),
				"thread_struct", "fs"), 0, ADDR_FLG);
			print_value(ofp, ", GS:", 
				KL_UINT(K_PTR(tsp, "task_struct", "tss"),
				"thread_struct", "gs"), 0, ADDR_FLG);
			fprintf(ofp, "\n");
		} else if (KL_ARCH == ARCH_I386) {
			fprintf(ofp, "THREAD:\n");
			print_value(ofp, "  ESP0:", 
				KL_UINT(K_PTR(tsp, "task_struct", "thread"),
				"thread_struct", "esp0"), 0, ADDR_FLG);
			print_value(ofp, ", ESP:", 
				KL_UINT(K_PTR(tsp, "task_struct", "thread"),
				"thread_struct", "esp"), 0, ADDR_FLG);
			print_value(ofp, ", EIP:", 
				KL_UINT(K_PTR(tsp, "task_struct", "thread"),
				"thread_struct", "eip"), 0, ADDR_FLG);
			fprintf(ofp, "\n");

			print_value(ofp, "  FS:", 
				KL_UINT(K_PTR(tsp, "task_struct", "thread"),
				"thread_struct", "fs"), 0, ADDR_FLG);
			print_value(ofp, ", GS:", 
				KL_UINT(K_PTR(tsp, "task_struct", "thread"),
				"thread_struct", "gs"), 0, ADDR_FLG);
			fprintf(ofp, "\n");
		}
		fprintf(ofp, "\n");
	}
	if (flags & C_NEXT) {
		fprintf(ofp, "\n");
		if (!(mmp = kl_alloc_block(MM_STRUCT_SZ, K_TEMP))) {
			return;
		}
		mmap_addr = kl_kaddr(tsp, "task_struct", "mm");
		GET_BLOCK(mmap_addr, MM_STRUCT_SZ, mmp);
		if (klib_error) {
			kl_free_block(mmp);
			return;	
		}
		mm_struct_banner(ofp, (BANNER|SMINOR));
		print_mm_struct(mmap_addr, mmp, flags&(~C_FULL), ofp);
		kl_free_block(mmp);
		fprintf(ofp, "\n");
	}
}

/*
 * print_active_tasks()
 */
int
print_active_tasks(int flags, FILE *ofp)
{
	int first_time = 1, task_cnt = 0;
	kaddr_t addr, first_task;
#if (ARCH == ia64) /*  XXX temporary HACK for snia64 */
	kaddr_t p1, p2;
#endif
	syment_t *sp;
	void *tsp;

	if (LINUX_2_2_X(KL_LINUX_RELEASE)) {
		if (!(sp = kl_lkup_symname("init_task_union"))) {
			return(0);
		}
		first_task = sp->s_addr;
	} else {
		if (!(sp = kl_lkup_symname("init_tasks"))) {
			return(0);
		}
		GET_BLOCK(sp->s_addr, sizeof(kaddr_t), &first_task);
	}

	if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) {
		return(0);
	}
	addr = first_task;
	task_struct_banner(ofp, (BANNER|SMAJOR));
	do {
		unsigned int state;

		if (kl_get_task_struct(addr, 2, tsp)) {
			break;
		}
		state = KL_UINT(tsp, "task_struct", "state");
		if (((flags & C_RUNABLE) && (state != 0)) ||
			((flags & C_UNRUNABLE) && (state >= 0)) ||
			((flags & C_STOPPED) && (state <= 0))) {
				goto next;
		}
		if (first_time) {
			first_time = 0;
		} else if (flags & (C_FULL|C_NEXT)) {
			task_struct_banner(ofp, (BANNER|SMAJOR));
		}
		print_task_struct(addr, tsp, flags, ofp);
		task_cnt++;
next:
		addr = kl_kaddr(tsp, "task_struct", "next_task");
#if (ARCH == ia64) /* XXX temporary HACK for snia64 */
		if (kl_virtop(addr, NULL, &p1)) {
			break;
		}
		if (kl_virtop(first_task, NULL, &p2)) {
			break;
		}
	} while (p1 != p2);
#else 
	} while (addr != first_task);
#endif
	kl_free_block(tsp);
	return(task_cnt);
}

/*
 * vm_area_struct_banner()
 */
void
vm_area_struct_banner(FILE *ofp, int flags)
{
	print_banner(ofp, "%>      ADDR%  %>  VM_START%  "
                        "%>    VM_END%  VM_PGOFF %>  VM_FLAGS%", flags);
}

/*
 * print_vm_area_struct()
 */
void
print_vm_area_struct(kaddr_t addr, void *vmap, int flags, FILE *ofp)
{
	print_value(ofp, "", ADDR64(addr), 8, ADDR_FLG);
	print_value(ofp, "  ", 
		ADDR64(KL_UINT(vmap, "vm_area_struct", "vm_start")), 
		8, ADDR_FLG);
	print_value(ofp, "  ", 
		ADDR64(KL_UINT(vmap, "vm_area_struct", "vm_end")), 
		8, ADDR_FLG);
	if (LINUX_2_2_X(KL_LINUX_RELEASE)) {
		print_value(ofp, "  ", 
			INT64(KL_INT(vmap, "vm_area_struct", "vm_offset")), 
			8, SIGNED_FLG);
	} else {
		print_value(ofp, "  ", 
			INT64(KL_INT(vmap, "vm_area_struct", "vm_pgoff")), 
			8, SIGNED_FLG);
	}
	print_value(ofp, " ", 
		ADDR64(KL_UINT(vmap, "vm_area_struct", "vm_flags")), 
		8, ADDR_FLG);
	fprintf(ofp, "\n");

	if (flags & C_FULL) {
		fprintf(ofp, "\n");
		print_value(ofp, "  VM_MM:", 
			ADDR64(KL_UINT(vmap, "vm_area_struct", "vm_mm")), 
			8, ADDR_FLG);
		print_value(ofp, ", VM_NEXT:", 
			ADDR64(KL_UINT(vmap, "vm_area_struct", "vm_next")), 
			0, ADDR_FLG);
		fprintf(ofp, "\n");
		print_value(ofp, "  VM_FILE:", 
			ADDR64(KL_UINT(vmap, "vm_area_struct", "vm_file")), 
			0, ADDR_FLG);
		print_value(ofp, ", VM_PDATA:", 
			ADDR64(KL_UINT(vmap, "vm_area_struct", 
			"vm_private_data")), 0, ADDR_FLG);
		fprintf(ofp, "\n\n");
	}
}

/*
 * struct_index()
 */
int
struct_index(char *s)
{
	int i = 0;

	while(Struct[i].name) {
		if (!strcmp(s, Struct[i].name)) {
			return(i);
		}
		i++;
	}
	return(-1);
}

/*
 * structlist_banner()
 */
void
structlist_banner(FILE *ofp, int flags)
{
	if (flags & BANNER) {
		fprintf(ofp, "NUM  NAME                      SIZE\n");
	}

	if (flags & SMAJOR) {
		fprintf(ofp, "===================================\n");
	}
}

/*
 * structlist()
 */
void
structlist(FILE *ofp)
{
	int i = 0;

	structlist_banner(ofp, BANNER|SMAJOR);
	while(Struct[i].name) {
		if (Struct[i].size == 0) {
			Struct[i].size = kl_struct_len(Struct[i].name);
		}
		fprintf(ofp, "%3d  %-16s         %5d\n", 
			i, Struct[i].name, Struct[i].size);
		i++;
	}
	structlist_banner(ofp, SMAJOR);
}

/*
 * walk_structs() -- walk linked lists of kernel data structures
 */
int
walk_structs(char *s, char *f, int offset, kaddr_t addr, int flags, FILE *ofp)
{
	int sid, size, firsttime = 1;
	kaddr_t last = 0, next;
	void *ptr = NULL;
	kltype_t *klt = (kltype_t *)NULL;

	void *lhead_ptr = NULL;
	int lhead_size, counter = 0;
	kaddr_t head, head_next, head_prev, entry, entry_next, entry_prev;

	/* Make sure we have the right number of paramaters.
	 */
	if (!s || (!f && (offset == -1)) || !addr) {
		KL_ERROR = KLE_BAD_STRUCT;
		return(1);
	}

	/* Check for mutual exclusive flags: */
	/* C_STRUCT NOR C_TABLE, C_LISTHEAD_N NOR C_LISTHEAD_P. */
	/* If C_TABLE is set, C_LISTHEAD must also be set. */
	if (((flags & C_STRUCT) && (flags & C_TABLE)) ||
	    ((flags & C_LISTHEAD_P) && (flags & C_LISTHEAD_N))||
	    ((flags & C_TABLE) && !(flags & C_LISTHEAD))) {
		fprintf(KL_ERRORFP, "ERROR: Internal error. Wrong flags "
			"specified in function %s().\n", __FUNCTION__);
		return(1);
	}

	/* Get the index for struct name, set flag for "predefined" struct
	 */
	if ((sid = struct_index(s)) > -1){
		flags |= C_PDSTRUCT;
	}

	/* One of C_STRUCT, C_PDSTRUCT or C_TABLE has to be set. */
	if (!(flags & (C_STRUCT | C_PDSTRUCT | C_TABLE))) {
		fprintf(KL_ERRORFP, "Neither a \"predefined\""
			"structure, nor options '-s' or '-t' "
			"specified. Can't print structure.\n");
		return(1);
	}

	/* If field name was passed, determine its offset in the struct.
	 */
	if (offset == -1) {
		if ((offset = kl_member_offset(s, f)) == -1) {
			fprintf(KL_ERRORFP, "Could not determine "
				"offset for member %s of %s.\n", f, s);
			KL_ERROR = KLE_BAD_FIELD;
			return(1);
		}
	}

	/* Get the type */
	if (!(klt = kl_find_type(s))) {
		KL_ERROR = KLE_BAD_STRUCT;
		return(1);
	}
	/* Get the struct size */
	if (flags & C_PDSTRUCT) {
		if ((size = Struct[sid].size) == 0) {
			if ((size = kl_struct_len(s)) == 0) {
				KL_ERROR = KLE_BAD_STRUCT;
				return(1);
			}
			/* Set the size value for future reference. */
			Struct[sid].size = size;
		}
	} else {
		if ((size = kl_struct_len(s)) == 0) {
			KL_ERROR = KLE_BAD_STRUCT;
			return(1);
		}
	}

	if ((next = addr)) {
		/* get head of list (anchor) when struct list_head is used */
		if(flags & C_LISTHEAD) {
			if ((lhead_size = kl_struct_len("list_head")) == 0){
				KL_ERROR = KLE_BAD_STRUCT;
				return(1);
			}
			head = next;
			lhead_ptr = kl_alloc_block(lhead_size, K_TEMP);
			kl_get_struct(head, lhead_size, lhead_ptr, 0);
			/* get next field of anchor */
			head_next = kl_kaddr(lhead_ptr, "list_head", "next");
			/* get prev field of anchor */
			head_prev = kl_kaddr(lhead_ptr, "list_head", "prev");
			entry = 0;
		}

		if (!(flags & C_STRUCT)) {
			/* print headline for output */
			if (flags & C_TABLE) {
				list_head_banner(ofp, BANNER|SMAJOR|flags);
			} else { /* if (flags & C_PDSTRUCT) */
				(*Struct[sid].banner)(ofp, BANNER|SMAJOR|flags);
			}
		}
	}

	while(next && CHECK_ITER_THRESHOLD(counter++)) {
		if(flags & C_LISTHEAD) {
			if(!(entry)){
				if(flags & C_TABLE) {
					print_list_head(ofp, 0, head, head_prev,
							head_next, flags);
				}
				if(flags & C_LISTHEAD_N){
					entry = head_next;
				} else {
					entry = head_prev;
				}
				last = head;
			}

			if(head == entry) {
				if(flags & C_LISTHEAD_N){
					check_prev_ptr(ofp, last, head_prev);
				} else {
					check_prev_ptr(ofp, last, head_next);
				}
				if(flags & C_TABLE) {
					list_head_banner(ofp, SMAJOR|flags);
				}
				break;
			}

			next = entry - offset;
			kl_get_struct(entry, lhead_size, lhead_ptr, 0);
			entry_next = kl_kaddr(lhead_ptr, "list_head", "next");
			entry_prev = kl_kaddr(lhead_ptr, "list_head", "prev");
			if(flags & C_LISTHEAD_N){
				check_prev_ptr(ofp, last, entry_prev);
			} else {
				check_prev_ptr(ofp, last, entry_next);
			}
			print_list_head(ofp, next, entry, entry_prev,
					entry_next, flags);
			last = entry;
			if(flags & C_LISTHEAD_N){
				entry = entry_next;
			} else {
				entry = entry_prev;
			}
		}
		
		if (!(flags & C_TABLE)) {
			ptr = kl_alloc_block(size, K_TEMP);
			kl_get_struct(next, size, ptr, s);
			if (KL_ERROR) {
				KL_ERROR |= KLE_BAD_STRUCT;
				return(1);
			}
			if (flags & C_STRUCT) {
				/* If C_STRUCT flag is set, print structure
				 *  in C-like struct format.
				 */
				if (flags & C_OCTAL) {
					kl_print_type(ptr, klt, 0, 
						      K_OCTAL, ofp);
				} else if (flags & C_HEX) {
					kl_print_type(ptr, klt, 0, K_HEX, ofp);
				} else {
					kl_print_type(ptr, klt, 0, 0, ofp);
				}
			} else { /* if (flags & C_PDSTRUCT) */
				if (flags & C_FULL) {
					if (!firsttime) {
						(*Struct[sid].banner)
							(ofp, BANNER|
							 SMAJOR|flags);
					} else {
						firsttime = 0;
					}
				}
				if (Struct[sid].print) {
					(*Struct[sid].print)(next, ptr, 
							     flags, ofp);
				} else {
					fprintf(ofp, 
						"%s: can't print struct\n", 
						Struct[sid].name);
				}
			}
			if(!(flags & C_LISTHEAD)) {
				last = next;
				kl_get_kaddr(next+offset, &next);
				if (KL_ERROR || (next == addr) || 
				    (next == last)) {
					break;
				}
			}
		}
	}
	ITER_THRESHOLD_MSG(ofp, counter-1);

	if (!(flags & (C_TABLE | C_STRUCT))) { /* && (flags & C_PDSTRUCT) */

		(*Struct[sid].banner)(ofp, SMAJOR|flags);
	}				

	kl_free_block(lhead_ptr);
	kl_free_block(ptr);
	return(0);
}
