#! /usr/bin/env pike
// -*- Pike -*-

/* UNBUG - Pike debugger
 * Written by Fredrik Hbinette
 *
 * GDB frontend for debugging pike code.
 * This program was written using the 'al dente' method.
 * I basically threw a lot of code at the screen to see what stuck.
 *  -Hubbe
 *
 * TODO:
 *   integrate with Emacs GUD mode
 *   lots and lots of testing
 *   fix attach
 *   fix all bugs
 *   Support Pike 7.2, 7.0? 0.6???
 *   Allow it to run on Win32
 *   Trap errors and exceptions
 *   Use hardware watchpoints when possible
 *   Breakpoint conditions
 *   (Add your own wishes here)
 */

#include <profiling.h>

#define MASTER_programs 29 /* The id of the programs variable in master() */

#ifndef DEBUG
#define DEBUG 0
#endif
int failsafe;

mapping revsyms=([]);
mapping syms=
([ /* 437 elements */
          "ARRAY_CYCLIC":2,
          "ARRAY_LVALUE":4,
          "ARRAY_WEAK_FLAG":1,
          "ARRAY_WEAK_SHRINK":8,
          "AUTO_BIGNUM":1,
          "AVERAGE_HASH_LENGTH":16,
          "BIT_MIXED":32767,
          "BIT_NOTHING":0,
          "BMLEN":768,
          "BUFFER_BEGIN_SIZE":4080,
	  "CALLABLE_DYNAMIC":1,
          "CASE_INFO_SHIFT0_HIGH":15,
          "CHARS":256,
	  "COMBINE_PATH_DEBUG":0,
          "COMPILER_IN_CATCH":1,
          "DEBUG_SIGNALS":1,
          "DECLARE_ENVIRON":1,
          "DMALLOC_TRACELOGSIZE":131072,
          "DO_INDIRECT":8,
          "DO_LVALUE":1,
          "DO_LVALUE_IF_POSSIBLE":16,
          "DO_NOT_COPY":2,
          "DO_NOT_COPY_TOPLEVEL":32,
          "DO_POP":4,
          "DOUBLE_IS_IEEE_LITTLE":1,
	  "EFUN_CONST":1,
	  "EFUN_GLOBAL_SIDE_EFFECT":4,
	  "EFUN_LOCAL_SIDE_EFFECT":2,
	  "EFUN_OTHER_SIDE_EFFECT":8,
	  "ENCAPSULATE_MALLOC":1,
	  "ENTRY_PROLOGUE_SIZE":0,
	  "ERRORCHECK_MUTEXES":16,
          "EXTRACT_CHAR_BY_CAST":1,
          "EXTRACT_UCHAR_BY_CAST":1,
          "fd_APPEND":4,
          "fd_BIDIRECTIONAL":16,
          "fd_BINARY":0,
          "fd_BUFFERED":8,
          "fd_CAN_NONBLOCK":2,
          "fd_CAN_SHUTDOWN":4,
          "fd_CREAT":8,
          "fd_EXCL":32,
          "fd_INTERPROCESSABLE":1,
	  "fd_LARGEFILE":0,
          "fd_LOCK_EX":2,
          "fd_LOCK_NB":8,
          "fd_LOCK_SH":1,
          "fd_LOCK_UN":4,
          "fd_RDONLY":1,
          "fd_RDWR":3,
          "fd_shutdown_both":2,
          "fd_shutdown_read":0,
          "fd_shutdown_write":1,
          "fd_TRUNC":16,
          "fd_WRONLY":2,
          "_FILE_OFFSET_BITS":64,
          "FLOAT_IS_IEEE_LITTLE":1,
          "GAUGE_RUSAGE_INDEX":0,
	  "GC_MAX_ALLOC_THRESHOLD":2000000000,
	  "GC_MIN_ALLOC_THRESHOLD":1000,
	  "GC_RESET_DMALLOC":8,
          "GETTIMEOFDAY_TAKES_TWO_ARGS":1,
          "HAVE_ALARM":1,
          "HAVE_ALLOCA":1,
          "HAVE_ALLOCA_H":1,
          "HAVE_AND_USE_POLL":1,
          "HAVE_ANSI_CONCAT":1,
          "HAVE_BCOPY":1,
          "HAVE_BROKEN_LINUX_THREAD_EUID":1,
          "HAVE_BZERO":1,
          "HAVE_CLOCK":1,
          "HAVE_CRYPT":1,
          "HAVE_CRYPT_H":1,
          "HAVE_DLFCN_H":1,
          "HAVE_DLOPEN":1,
          "HAVE_ERRNO_H":1,
          "HAVE_EXTERNAL_TIMEZONE":1,
          "HAVE_FCHMOD":1,
          "HAVE_FCNTL_H":1,
          "HAVE_FINITE":1,
          "HAVE_FLOCK":1,
          "HAVE_FORK":1,
          "HAVE_FREXP":1,
          "HAVE_FUNCTION_ATTRIBUTES":1,
          "HAVE_GETEGID":1,
          "HAVE_GETENV":1,
          "HAVE_GETEUID":1,
          "HAVE_GETGID":1,
          "HAVE_GETGRENT":1,
          "HAVE_GETGRNAM":1,
          "HAVE_GETHOSTNAME":1,
          "HAVE_GETHRTIME":1,
          "HAVE_GETPAGESIZE":1,
          "HAVE_GETPGID":1,
          "HAVE_GETPGRP":1,
          "HAVE_GETPWENT":1,
          "HAVE_GETPWNAM":1,
          "HAVE_GETPWUID":1,
          "HAVE_GETRLIMIT":1,
          "HAVE_GETRUSAGE":1,
          "HAVE_GETTIMEOFDAY":1,
          "HAVE_GETUID":1,
          "HAVE_GMTIME":1,
          "HAVE_GRP_H":1,
          "HAVE_INDEX":1,
          "HAVE_INITGROUPS":1,
          "HAVE_ISGRAPH":1,
          "HAVE_ISINF":1,
          "HAVE_ISNAN":1,
          "HAVE_ISSPACE":1,
          "HAVE_KILL":1,
          "HAVE_LDEXP":1,
          "HAVE_LIBDL":1,
          "HAVE_LIBM":1,
          "HAVE_LIBNSL":1,
          "HAVE_LIBRT":1,
          "HAVE_LIMITS_H":1,
          "HAVE_LOCALE_H":1,
          "HAVE_LOCALTIME":1,
          "HAVE_LOCKF":1,
          "HAVE_MALLOC_H":1,
          "HAVE_MEMCHR":1,
          "HAVE_MEMCMP":1,
          "HAVE_MEMCPY":1,
          "HAVE_MEMMOVE":1,
          "HAVE_MEMORY_H":1,
          "HAVE_MEMSET":1,
          "HAVE_MKTIME":1,
          "HAVE_MMAP":1,
          "HAVE_MMX_H":1,
          "HAVE_MUNMAP":1,
          "HAVE_NANOSLEEP":1,
          "HAVE_NETINET_IN_H":1,
          "HAVE_NICE":1,
          "HAVE_PERROR":1,
          "HAVE_PIPE":1,
          "HAVE_POLL":1,
          "HAVE_POLL_H":1,
	  "HAVE_PRCTL":1,
          "HAVE_PTHREAD_ATFORK":1,
          "HAVE_PTHREAD_ATTR_SETSTACKSIZE":1,
          "HAVE_PTHREAD_COND_INIT":1,
          "HAVE_PTHREAD_H":1,
          "HAVE_PTHREAD_KILL":1,
          "HAVE_PTHREAD_MUTEXATTR_INIT":1,
          "HAVE_PTHREAD_MUTEX_RECURSIVE_NP":1,
	  "HAVE_PTHREAD_YIELD":1,
          "HAVE_PWD_H":1,
          "HAVE_RINDEX":1,
          "HAVE_RINT":1,
          "HAVE_SCHED_H":1,
          "HAVE_SCHED_SETSCHEDULER":1,
          "HAVE_SETBUF":1,
          "HAVE_SETEGID":1,
          "HAVE_SETEUID":1,
          "HAVE_SETGID":1,
          "HAVE_SETGROUPS":1,
          "HAVE_SETITIMER":1,
          "HAVE_SETJMP_H":1,
          "HAVE_SETLOCALE":1,
          "HAVE_SETPGID":1,
          "HAVE_SETPGRP":1,
          "HAVE_SETPRIORITY":1,
          "HAVE_SETRESUID":1,
          "HAVE_SETRLIMIT":1,
          "HAVE_SETSID":1,
          "HAVE_SETUID":1,
          "HAVE_SETVBUF":1,
          "HAVE_SIGACTION":1,
          "HAVE_SIGBLOCK":1,
          "HAVE_SIGNAL_H":1,
          "HAVE_SIGPROCMASK":1,
          "HAVE_SIGVEC":1,
          "HAVE_SOCKETPAIR":1,
          "HAVE_STDDEF_H":1,
          "HAVE_STDLIB_H":1,
          "HAVE_STRCASECMP":1,
          "HAVE_STRCHR":1,
          "HAVE_STRCOLL":1,
          "HAVE_STRCSPN":1,
          "HAVE_STRDUP":1,
          "HAVE_STRERROR":1,
          "HAVE_STRING_H":1,
          "HAVE_STRINGS_H":1,
          "HAVE_STRNCMP":1,
          "HAVE_STRNLEN":1,
          "HAVE_STRRCHR":1,
          "HAVE_STRSTR":1,
          "HAVE_STRTOD":1,
          "HAVE_STRTOK":1,
          "HAVE_STRTOL":1,
	  "HAVE_STRUCT_SOCKADDR_IN6":1,
          "HAVE_STRUCT_TIMEVAL":1,
          "HAVE_SYS_ERRNO_H":1,
          "HAVE_SYS_FILE_H":1,
	  "HAVE_SYS_IOCTL_H":1,
          "HAVE_SYS_MMAN_H":1,
          "HAVE_SYS_PARAM_H":1,
          "HAVE_SYS_POLL_H":1,
	  "HAVE_SYS_PRCTL_H":1,
          "HAVE_SYS_PROCFS_H":1,
	  "HAVE_SYS_PTRACE_H":1,
          "HAVE_SYS_RESOURCE_H":1,
          "HAVE_SYS_SELECT_H":1,
          "HAVE_SYS_SOCKET_H":1,
          "HAVE_SYS_STAT_H":1,
	  "HAVE_SYS_TERMIOS_H":1,
          "HAVE_SYS_TIME_H":1,
          "HAVE_SYS_TIMES_H":1,
          "HAVE_SYS_TYPES_H":1,
	  "HAVE_SYS_USER_H":1,
          "HAVE_SYS_WAIT_H":1,
          "HAVE_TIME":1,
          "HAVE_TIME_H":1,
          "HAVE_TIMES":1,
	  "HAVE_TZSET":1,
          "HAVE_UALARM":1,
	  "HAVE_UNION_INIT":1,
          "HAVE_UNISTD_H":1,
          "HAVE_USLEEP":1,
	  "HAVE_VALGRIND_H":1,
	  "HAVE_VALGRIND_MEMCHECK_H":1,
          "HAVE_VALUES_H":1,
          "HAVE_VFPRINTF":1,
          "HAVE_VSNPRINTF":1,
          "HAVE_VSPRINTF":1,
          "HAVE_WAIT3":1,
          "HAVE_WAIT4":1,
          "HAVE_WAITPID":1,
          "HAVE_WORKING___FUNC__":1,
          "HAVE_WORKING___FUNCTION__":1,
          "I_DATA":9,
	  "IDENTIFIER_ALIAS":128,
          "IDENTIFIER_C_FUNCTION":2,
          "IDENTIFIER_CONSTANT":4,
          "IDENTIFIER_FUNCTION":3,
	  "IDENTIFIER_HAS_BODY":16,
          "IDENTIFIER_MASK":255,
	  "IDENTIFIER_NO_THIS_REF":8,
          "IDENTIFIER_PIKE_FUNCTION":1,
          "IDENTIFIER_PROTOTYPED":16,
          "IDENTIFIER_SCOPED":32,
          "IDENTIFIER_SCOPE_USED":64,
	  "IDENTIFIER_TYPE_MASK":7,
          "IDENTIFIER_VARARGS":8,
	  "IDENTIFIER_VARIABLE":0,
	  "ID_DONT_SAVE_PARENT":0x20000,
          "ID_EXTERN":512,
          "ID_HIDDEN":64,
          "ID_INHERITED":128,
          "ID_INLINE":32,
          "ID_MODIFIER_MASK":2047,
          "ID_NOMASK":4,
          "ID_OPTIONAL":256,
          "ID_PRIVATE":2,
          "ID_PROTECTED":16,
          "ID_PUBLIC":8,
	  "ID_SAVE_PARENT":0x10000,
          "ID_STATIC":1,
          "ID_STRICT_TYPES":32768,
          "I_HASARG":1,
          "I_HASARG2":16,
          "I_ISJUMP":7,
          "I_ISPOINTER":3,
          "I_JUMP":4,
          "I_POINTER":2,
          "I_TWO_ARGS":17,
          "_LARGEFILE64_SOURCE":1,
          "LFUN_ADD":3,
          "LFUN_ADD_EQ":38,
          "LFUN_AND":5,
          "LFUN_ARROW":22,
          "LFUN_ASSIGN_ARROW":23,
          "LFUN_ASSIGN_INDEX":21,
          "LFUN_CALL":27,
          "LFUN_CAST":18,
          "LFUN_COMPL":13,
          "LFUN_CREATE":1,
          "LFUN_DESTROY":2,
          "LFUN_DIVIDE":11,
          "LFUN_EQ":14,
          "LFUN__EQUAL":41,
          "LFUN__GET_ITERATOR":43,
          "LFUN_GT":16,
          "LFUN___HASH":17,
          "LFUN_INDEX":20,
          "LFUN__INDICES":25,
          "LFUN___INIT":0,
          "LFUN__IS_TYPE":39,
          "LFUN_LSH":8,
          "LFUN_LT":15,
          "LFUN__M_DELETE":42,
          "LFUN_MOD":12,
          "LFUN_MULTIPLY":10,
          "LFUN_NOT":19,
          "LFUN_OR":6,
          "LFUN_RADD":28,
          "LFUN_RAND":30,
          "LFUN_RDIVIDE":36,
          "LFUN_RLSH":33,
          "LFUN_RMOD":37,
          "LFUN_RMULTIPLY":35,
          "LFUN_ROR":31,
          "LFUN_RRSH":34,
          "LFUN_RSH":9,
          "LFUN_RSUBTRACT":29,
          "LFUN_RXOR":32,
	  "LFUN__SEARCH":44,
          "LFUN__SIZEOF":24,
          "LFUN__SPRINTF":40,
          "LFUN_SUBTRACT":4,
          "LFUN__VALUES":26,
          "LFUN_XOR":7,
          "MAPPING_FLAG_WEAK":1,
	  "MAX_EMPTY_BLOCKS":4,
          "MAX_GLOBAL_VARIABLES":1000,
          "MAX_INT32":2147483647,
	  "MAX_INT64":0x7fffffffffffffff,
          "MAX_OPEN_FILEDESCRIPTORS":1024,
          "MEMSEARCH_LINKS":512,
          "_MIT_POSIX_THREADS":1,
          "NEW_HASHTABLE_SIZE":4,
	  "NO_PEEP_OPTIMIZING":4,
          "NO_TAILRECURSION":2,
          "NUMBER_DESTRUCTED":2,
          "NUMBER_NUMBER":0,
          "NUMBER_UNDEFINED":1,
          "NUM_LFUNS":44,
	  "NUM_PROG_EVENTS":4,
          "OPT_APPLY":4096,
          "OPT_ASSIGNMENT":8,
          "OPT_BREAK":256,
          "OPT_CASE":64,
          "OPT_CONTINUE":128,
          "OPT_CUSTOM_LABELS":65536,
          "OPT_EXTERNAL_DEPEND":32,
          "OPT_NOT_CONST":2,
          "OPT_OPTIMIZED":1,
          "OPT_RETURN":512,
          "OPT_SIDE_EFFECT":4,
          "OPT_TRY_OPTIMIZE":16,
          "OPT_TYPE_NOT_FIXED":1024,
          "OWN_GETHRTIME":1,
          "OWN_GETHRTIME_RDTSC":1,
          "PIKE_ARRAY_OP_A":1,
          "PIKE_ARRAY_OP_B":4,
          "PIKE_ARRAY_OP_SKIP_A":2,
          "PIKE_ARRAY_OP_SKIP_B":8,
          "PIKE_ARRAY_OP_TAKE_A":3,
          "PIKE_ARRAY_OP_TAKE_B":12,
          "PIKE_BUILD_VERSION":15,
	  "PIKE_BYTECODE_PPC32":4,
          "PIKE_BYTEORDER":1234,
          "PIKE_DEBUG":1,
	  "PIKE_FRAME_MALLOCED_LOCALS":0x8000,
	  "PIKE_FRAME_RETURN_INTERNAL":1,
	  "PIKE_FRAME_RETURN_POP":2,
          "PIKE_INT32_ALIGNMENT":4,
          "PIKE_MAJOR_VERSION":7,
          "PIKE_MINOR_VERSION":5,
	  "PIKE_NEW_MULTISETS":1,
          "PIKE_OOB_WORKS":3,
	  "PIKE_POINTER_ALIGNMENT":4,
          "PIKE_T_ARRAY":0,
          "PIKE_T_FLOAT":9,
          "PIKE_T_FUNCTION":4,
          "PIKE_THREADS":1,
          "PIKE_T_INT":8,
          "PIKE_T_MAPPING":1,
          "PIKE_T_MIXED":251,
          "PIKE_T_MULTISET":2,
          "PIKE_T_NAME":241,
          "PIKE_T_OBJECT":3,
          "PIKE_T_PROGRAM":5,
          "PIKE_T_RING":240,
          "PIKE_T_SCOPE":243,
          "PIKE_T_STRING":6,
          "PIKE_T_TUPLE":244,
          "PIKE_T_TYPE":7,
          "PIKE_T_UNKNOWN":247,
          "PIKE_TYPE_STACK_SIZE":100000,
          "PIKE_T_ZERO":14,
	  "PIKE_USE_MACHINE_CODE":1,
	  "PIKE_WEAK_BOTH":6,
	  "PIKE_WEAK_INDICES":2,
	  "PIKE_WEAK_VALUES":4,
          "PROG___BUILTIN_ID":9,
	  "PROG_EVENT_EXIT":1,
	  "PROG_EVENT_GC_CHECK":3,
	  "PROG_EVENT_GC_RECURSE":2,
	  "PROG_EVENT_INIT":0,
          "PROG_GMP_MPZ_ID":32,
          "PROG_IMAGE_CLASS_START":100,
          "PROG_IMAGE_COLOR_COLOR_ID":200,
          "PROG_IMAGE_COLORTABLE_ID":101,
          "PROG_IMAGE_FONT_ID":103,
          "PROG_IMAGE_IMAGE_ID":100,
          "PROG_IMAGE_LAYER_ID":102,
          "PROG_IMAGE_POLY_ID":104,
          "PROG_IMAGE_SUBMAGIC_START":160,
          "PROG_IMAGE_SUBMODULE_START":120,
          "PROG_PARSER_HTML_ID":8,
          "PROGRAM_AVOID_CHECK":1024,
          "PROGRAM_CONSTANT":64,
          "PROGRAM_DESTRUCT_IMMEDIATE":16,
          "PROGRAM_FINISHED":4,
          "PROGRAM_FIXED":2,
          "PROGRAM_HAS_C_METHODS":32,
	  "PROGRAM_NEEDS_PARENT":0x1000,
          "PROGRAM_NO_EXPLICIT_DESTRUCT":512,
          "PROGRAM_NO_WEAK_FREE":256,
          "PROGRAM_OPTIMIZED":1,
          "PROGRAM_PASS_1_DONE":8,
          "PROGRAM_USES_PARENT":128,
          "PROGRAM_VIRGIN":2048,
          "PROG_STDIO_FD_ID":1,
	  "PROG_STDIO_FD_REF_ID":026,
	  "PROG_STDIO_FILE_LOCK_ID":027,
	  "PROG_STDIO_IPPROTO_ID":034,
	  "PROG_STDIO_PORT_ID":030,
	  "PROG_STDIO_SENDFILE_ID":031,
	  "PROG_STDIO_SOCK_ID":033,
          "PROG_STDIO_STAT_ID":10,
	  "PROG_STDIO_UDP_ID":032,
          "PROG_THREAD_CONDITION_ID":5,
          "PROG_THREAD_DISABLE_THREADS_ID":7,
          "PROG_THREAD_ID_ID":2,
          "PROG_THREAD_LOCAL_ID":6,
          "PROG_THREAD_MUTEX_ID":4,
          "PROG_THREAD_MUTEX_KEY_ID":3,
	  "PTRACE_ADDR_TYPE_IS_POINTER":1,
	  "PTRACE_TAKES_FOUR_ARGS":1,
          "_REENTRANT":1,
          "RTLD_GLOBAL":1,
          "RTLD_LAZY":0,
          "RTLD_NOW":0,
          "RUNTIME_CHECK_TYPES":1,
          "RUNTIME_STRICT_TYPES":2,
          "SCOPE_LOCAL":1,
          "SCOPE_SCOPED":2,
          "SCOPE_SCOPE_USED":4,
          "SECURITY_BIT_CALL":4,
          "SECURITY_BIT_CONDITIONAL_IO":32,
          "SECURITY_BIT_DESTRUCT":64,
          "SECURITY_BIT_INDEX":1,
          "SECURITY_BIT_NOT_SETUID":16,
          "SECURITY_BIT_SECURITY":8,
          "SECURITY_BIT_SET_INDEX":2,
          "SEEK_CUR":1,
          "SEEK_END":2,
          "SEEK_SET":0,
          "SEE_PRIVATE":2,
          "SEE_STATIC":1,
          "SHARED_NODES":1,
          "S_IFIFO":4096,
          "S_IFSOCK":49152,
	  "SIGNAL_ONESHOT":1,
          "SIZEOF_CHAR_P":4,
          "SIZEOF_DOUBLE":8,
          "SIZEOF_FLOAT":4,
          "SIZEOF_INT":4,
          "SIZEOF___INT64":0,
	  "SIZEOF_INT64":8,
          "SIZEOF_LONG":4,
	  "SIZEOF_LONG_DOUBLE":12,
          "SIZEOF_LONG_LONG":8,
	  "SIZEOF_OFF_T":0,
          "SIZEOF_SHORT":2,
	  "STACK_SLICE_SIZE":20,
          "STDC_HEADERS":1,
	  "STEP_BREAK_LINE":109,
	  "STRUCT_TM_HAS_GMTOFF":1,
          "T_AND":254,
          "T_ARRAY_LVALUE":250,
          "T_ASSIGN":245,
          "T_DELETED":246,
	  "THREAD_DEBUG_LOOSE":1,
          "THREAD_EXITED":1,
          "THREAD_RUNNING":0,
          "_THREAD_SAFE":1,
	  "THREAD_TABLE_SIZE":127,
          "THREAD_TRACE":1,
          "THROW_ERROR":10,
          "THROW_EXIT":40,
          "THROW_MAX_SEVERITY":100,
          "THROW_THREAD_EXIT":20,
          "THROW_THREAD_KILLED":30,
          "TIME_WITH_SYS_TIME":1,
          "T_LVALUE":249,
          "T_MANY":17,
          "T_MAPPING_DATA":10001,
	  "T_MULTISET_DATA":10003,
          "T_NOT":253,
	  "T_OBJ_INDEX":248,
          "T_OR":255,
	  "T_PIKE_FRAME":10002,
          "T_SHORT_LVALUE":248,
	  "T_STORAGE":10000,
	  "T_STRUCT_CALLABLE":10004,
	  "T_SVALUE_PTR":249,
          "T_UNFINISHED":15,
          "T_VOID":16,
	  "TOK_ADD_EQ":280,
	  "TOK_AND_EQ":281,
	  "TOK_ARRAY_ID":282,
	  "TOK_ARROW":258,
	  "TOK_BREAK":283,
	  "TOK_CASE":284,
	  "TOK_CATCH":277,
	  "TOK_CLASS":285,
	  "TOK_COLON_COLON":286,
	  "TOK_CONSTANT":259,
	  "TOK_CONTINUE":287,
	  "TOK_DEC":264,
	  "TOK_DEFAULT":288,
	  "TOK_DIV_EQ":289,
	  "TOK_DO":290,
	  "TOK_DOT_DOT":291,
	  "TOK_DOT_DOT_DOT":292,
	  "TOK_ELSE":293,
	  "TOK_ENUM":294,
	  "TOK_EQ":266,
	  "TOK_EXTERN":295,
	  "TOK_FINAL_ID":307,
	  "TOK_FLOAT":260,
	  "TOK_FLOAT_ID":296,
	  "TOK_FOR":297,
	  "TOK_FOREACH":278,
	  "TOK_FUNCTION_ID":298,
	  "TOK_GAUGE":299,
	  "TOK_GE":267,
	  "TOK_GLOBAL":300,
	  "TOK_IDENTIFIER":301,
	  "TOK_IF":302,
	  "TOK_IMPORT":303,
	  "TOK_INC":263,
	  "TOK_INHERIT":304,
	  "TOK_INLINE":305,
	  "TOK_INT_ID":308,
	  "TOK_LAMBDA":309,
	  "TOK_LAND":273,
	  "TOK_LE":268,
	  "TOK_LEX_EOF":279,
	  "TOK_LOCAL_ID":306,
	  "TOK_LOR":274,
	  "TOK_LSH":271,
	  "TOK_LSH_EQ":313,
	  "TOK_MAPPING_ID":314,
	  "TOK_MIXED_ID":315,
	  "TOK_MOD_EQ":316,
	  "TOK_MULT_EQ":317,
	  "TOK_MULTISET_END":311,
	  "TOK_MULTISET_ID":310,
	  "TOK_MULTISET_START":312,
	  "TOK_NE":269,
	  "TOK_NO_MASK":318,
	  "TOK_NOT":270,
	  "TOK_NUMBER":262,
	  "TOK_OBJECT_ID":319,
	  "TOK_OPTIONAL":336,
	  "TOK_OR_EQ":320,
	  "TOK_PREDEF":324,
	  "TOK_PRIVATE":321,
	  "TOK_PROGRAM_ID":322,
	  "TOK_PROTECTED":323,
	  "TOK_PUBLIC":325,
	  "TOK_RETURN":265,
	  "TOK_RSH":272,
	  "TOK_RSH_EQ":326,
	  "TOK_SSCANF":276,
	  "TOK_STATIC":327,
	  "TOK_STRING":261,
	  "TOK_STRING_ID":328,
	  "TOK_SUB_EQ":329,
	  "TOK_SWITCH":275,
	  "TOK_TYPEDEF":330,
	  "TOK_TYPEOF":331,
	  "TOK_VARIANT":332,
	  "TOK_VOID_ID":333,
	  "TOK_WHILE":334,
	  "TOK_XOR_EQ":335,
          "UALARM_TAKES_TWO_ARGS":1,
	  "USE_ERRORCHECK_MUTEX":1,
	  "USE_FCNTL_0_NONBLOCK":1,
          "USE_FCNTL_FNDELAY":1,
          "USE_PIKE_TYPE":1,
          "USE_SIGCHILD":1,
          "USE_Wl":1,
	  "WITH_LONG_INT":1,
          "WITH_OOB":1,
  ]);



class Gdb
{
  Process.Process pid;
  Stdio.File in;
  Stdio.File out;
  string prompt="[[`$$Ziuqakdfa972093874$q$w$09280928341kjhsdf===$$']]";

  string buffer="";
  int spos=0;

  string read_result()
    {
      int pos;
      string ret;
      do {
	string tmp=out->read(10000,1);
	if(!tmp)
	{
	  werror("Failed to read from GDB:\n");
	  sleep(8699999);
//	  exit(0);
	}
#if DEBUG > 9
	werror("GOT: "+tmp+".\n");
#endif
	buffer+=tmp;
	if(spos<0) spos=0;
	pos=search(buffer,prompt,spos);
	spos=sizeof(buffer)-sizeof(prompt);
      }while(pos == -1);
      ret=buffer[..pos-1];
      pos+=sizeof(prompt);
      buffer=buffer[pos..];
      spos=0;
      return ret;
    }

  string cmd(string s)
    {
#if DEBUG
      if(has_value(s,"\n"))
      {
	error("NEWLINE IN COMMAND\n");

	if(has_value(buffer,prompt))
	  error("PROMPT PRESENT IN BUFFER!!!!\n");
      }
#endif

#if DEBUG > 9      
      werror("SENT: "+s+".\n");
#endif
      in->write(s+"\n");
      return read_result();
    }

  void create()
  {
    out=Stdio.File();
    object tmp2=out->pipe(Stdio.PROP_IPC);
    object tmp1=Stdio.File();
    in=tmp1->pipe(Stdio.PROP_IPC);
    pid=Process.Process( ({"gdb"}),
			 (["setsid":1,
			   "stdin":tmp1,
			   "stdout":tmp2]));
    cmd("set prompt "+prompt);
    cmd("set width 0");
    cmd("set print elements 0");
    cmd("set print repeats 0");
    cmd("set height 0");
  }

  void destroy()
    {
      in->write("quit\n");
    }

  array(string) tokens=({"(",")","*",",",";","!","%","^","&"});
  array(string) token_dividers;

  void init_tokenize()
    {
      if(!token_dividers)
      {
	token_dividers=Array.map(tokens, lambda(string x) {
					   return " "+x+" "; });
	tokens+=({"\n","\t","\r"});
	token_dividers+=({" "," "," "});
      }
    }

  array(string) tokenize(string x)
    {
      return replace(x,tokens,token_dividers)/" "-({""});
    }

  string find_function_name(string x)
    {
      if(sscanf(x,"%*s:")) return 0;
      int parlevel=1;
      int done=0;
      foreach(reverse(tokenize(x))[2..], string token)
	{
	  switch(token)
	  {
	    case ")":
	      parlevel++;
	      break;

	    case "(":
	      if(!--parlevel) done=1;
	      break;

	    default:
	      if(parlevel>done) break;
	      return token;
	  }
	}
      return 0;
    }

  int do_break(string where)
    {
#if DEBUG
      werror("BREAK: %O\n",where);
#endif
      if(sscanf(cmd("break "+where),"Breakpoint %d at",int num)) return num;
      return -1;
    }

  void flush()
    {
      handlecache=([]);
    }


  void stop_execution()
    {
      pid->kill(signum("SIGINT"));
    }

  mapping(int:array) breakpoint_callbacks=([]);

  void register_breakpoint_callback(int bp, function f, mixed ... args)
    {
      breakpoint_callbacks[bp]=({f})+args;
    }

  class Run
  {
    string ret;
    string why;
    mixed data;
    string desc;
    mixed other;

    void create(string command)
      {
	flush();
	ret=cmd(command);
	if(sscanf(ret,"%*sBreakpoint %d,",data))
	{
	  why="breakpoint";
	  if(breakpoint_callbacks[data])
	    other=breakpoint_callbacks[data][0](
	      this_object(),
	      @ breakpoint_callbacks[data][1..]);
	  return;
	}
	if(sscanf(ret,"%*sreceived signal %s, %s.", data, desc))
	{
	  why="signal";
	  return;
	}
	if(sscanf(ret,"%*sProgram exited normally."))
	{
	  data=0;
	  why="exit";
	  return;
	}
	if(sscanf(ret,"%*sProgram exited with code %i.",data)==2)
	{
	  why="exit";
	  return;
	}
	why="???";
      }
  };

  int evaluated_handles;

  class Handle
  {
    string __expr;
    int __varno=-1;
    mixed __value;
    string __type;

    string _sprintf(int op)
      {
	if(op == 'O')
	  return sprintf("Handle(%O)",__expr);
      }

    static string decode_string(string s)
      {
	s=s[1..];
//	werror("decode_string(%O)\n",s);
	String.Buffer ret = String.Buffer(sizeof(s));
	while(sscanf(s,"%[^\"\\]%c%s",string safe, int c, s)==3)
	{
	  ret->add(safe);
	  switch(c)
	  {
	    case '\\':
	      switch(s[0])
	      {
		case '0' .. '9':
		  sscanf(s,"%3o%s",c,s);
		  break;

		case 'a': c=7; s=s[1..]; break;
		case 'b': c=8; s=s[1..]; break;
	        case 'v': c=11; s=s[1..]; break;
		case 'f': c=12; s=s[1..]; break;
		case 'n': c='\n'; s=s[1..]; break;
		case 't': c='\t'; s=s[1..]; break;
		case 'r': c='\r'; s=s[1..]; break;
		case 'e': c='\e'; s=s[1..]; break;
		case '"': c='"';  s=s[1..]; break;
		case '\\': c='\\'; s=s[1..]; break;
		default:
		  werror("Unknown string coding: %d (%c)\n",s[0],s[0]);
	      }
	      ret->putchar(c);
	      break;
		  
	    case '"': /* end of string */
	      return (string)ret;
	  }
	}

	werror("Failed to decode string!\n");
      }

    mixed parse_return_value(array data)
      {
	mixed v;
#if DEBUG > 3
	werror("parse_return_value(%O)\n",data);
#endif

	if(arrayp(data[0]) && "{" == (string) data[0][0])
	{
	  v=([]);
	  data=data[0];
	  data=data[1..sizeof(data)-2];
	  foreach(data/({","}), array tmp)
	    v[ (string) (tmp[0]) ] = parse_return_value(tmp[2..]);
	  
	  return v;
	}
	
	switch( ((string) (data[-1]) )[0] )
	{
	  case '\'':
	    return parse_return_value(data[..sizeof(data)-2]);

	  case '"': /* string */
	    PROF_BEGIN("decode_string");
	    string strret = decode_string( (string) (data[-1]) );
	    PROF_END("decode_string");
	    return strret;
	    
	  case '0': /* Hex */
	  case '1' .. '9':
	    sscanf( (string) (data[-1]) ,"%i",v);
	    if(sizeof(data) > 1 && data[-2]=="-") v=-v;
	    return v;
	}
      }

    void _evaluate()
      {
	evaluated_handles++;
	if(__varno==-1)
	{
#if DEBUG > 3
	  werror("evaluate(%O)\n",__expr);
#endif
	  string ret=cmd("print "+__expr);

#if DEBUG > 3
	  werror("Got: %O\n",ret);
#endif

	  if(sscanf(ret,"$%d = %s\n",__varno,ret)!=2)
	  {
	    error(sprintf("Failed to evaluate: %s\n",__expr));
	  }

	  mixed data=Parser.C.split(ret);
	  data=Parser.C.tokenize(data);
	  data=Parser.C.hide_whitespaces(data);
	  data=Parser.C.group(data);

	  if(arrayp(data[0]) && "(" == (string) data[0][0])
	  {
	    __type=Parser.C.simple_reconstitute(data[0][1..sizeof(data[0])-2]);
	    data=data[1..];
	  }

	  __value=parse_return_value(data);
#if DEBUG > 3
	  werror("  -> %O\n",__value);
#endif
	}
      }

    int _varno() { _evaluate(); return __varno; }
    string _type() { _evaluate(); return __type; }
    mixed _value() { _evaluate(); return __value; }

    mixed `-(mixed ... args)
      {
	if(sizeof(args))
	{
	  return mkhandle(this_object(),"-",@args);
	}else{
	  return mkhandle("-",this_object());
	}
      }

    mixed `+(mixed ... args)
      {
	return mkhandle(this_object(),"+", @(args/1*({"+"})));
      }

    mixed ``+(mixed ... args)
      {
	return mkhandle(@(args/1*({"+"})),"+",this_object());
      }

    mixed `*(mixed ... args)
      {
	return mkhandle(this_object(),"*",@(args/1*({"*"})));
      }

    mixed ``*(mixed ... args)
      {
	return mkhandle(@(args/1*({"*"})),"*",this_object());
      }

    mixed `[](int ind)
      {
	return mkhandle(this_object(),"[",ind,"]");
      }

    mixed `->(string val)
      {
	if(val[0]=='_' && ::`->(val)) return ::`->(val);
	return mkhandle(this_object(),"->"+val);
      }

    void create(string e)
      {
	__expr=e;
	if(sizeof(__expr) > 200) _evaluate();
      }
  }

  mapping(string:Handle) handlecache=([]);

  Handle mkhandle(mixed ... parts)
    {
      string|String.Buffer expr = String.Buffer();
      foreach(parts, mixed part)
	{
	  switch(sprintf("%t",part))
	  {
	    case "string": expr->add(part); break;
	    case "int":    expr->add(" ",(string)part," "); break;
	    case "float":  expr->add(" ",(string)part," "); break;
	    case "object":
	      if(part->__varno<0)
		expr->add("(", part->__expr, ")");
	      else
		expr->add("$", (string)(part->__varno), " ");
	  }
	}
      expr = (string)expr;
#if DEBUG>5
      werror("mkhandle(%O)\n",expr);
#endif
      /* This prevents the expression to become any longer */

      if(handlecache[expr]) return handlecache[expr];
      return handlecache[expr]=Handle(expr);
    }

  array(string) get_all_function_names()
    {
      init_tokenize();
      string tmp=cmd("info functions");
      array(string) ret=({});
      sscanf(tmp,"%sNon-debugging symbols:",tmp);
      return Array.map(tmp/"\n",find_function_name)-({0});
    }

  array(string) get_all_source_files()
    {
      string tmp=cmd("info sources");
      tmp=replace(tmp,({","," ","\t","\r"}),({"\n","\n","\n","\r"}));
      return tmp/"\n"-({""});
    }
}

int counter;

class Debug
{
  inherit Gdb;
  object terminal;

  int got_unbug_error;
  void unbug_error(string fmt, mixed ... args)
    {
      got_unbug_error++;
      error(sprintf(fmt,@args));
    }

  /* Line number handling */

  mapping(int:string) line_number_info_cache=([]);

  string get_line_number_info(Handle prog)
    {
      string ret;

      int progid=prog->id->_value();
      if(ret=line_number_info_cache[progid]) return ret;

#if DEBUG>1
      werror("Get line numbers for %O\n",prog);
#endif

      ret = mkhandle(prog, "->linenumbers[0]@ ", prog,
		     "->num_linenumbers")->_value();
      if(sizeof(ret) != prog->num_linenumbers->_value())
      {
	werror("Failed to get line number info!\n");
	werror("sizeof(%O) != %O (it is %d)\n", ret,
	       prog->num_linenumbers->_value(), sizeof(ret));
      }
      return line_number_info_cache[progid]=ret;
    }

  string find_line_number(Handle prog, Handle PC)
    {
      string lineinfo=get_line_number_info(prog);
      int offset=(PC - prog->program)->_value();
      if(!offset) return "";

      int cnt=0;
      int off=0;
      int line=0;
      string file="Line not found";

      int get_small_number()
	{
	  switch(int ret=lineinfo[cnt++])
	  {
	    case 256-127:
	      if(syms->PIKE_BYTEORDER==1234)
	      {
		sscanf(lineinfo[cnt..cnt+1],"%-2c",ret);
	      }else{
		sscanf(lineinfo[cnt..cnt+1],"%2c",ret);
	      }
	      cnt+=2;
	      if(ret>0x7fff) ret-=0x10000;
	      return ret;

	    case 256-128:
	      if(syms->PIKE_BYTEORDER==1234)
	      {
		sscanf(lineinfo[cnt..cnt+3],"%-4c",ret);
	      }else{
		sscanf(lineinfo[cnt..cnt+3],"%4c",ret);
	      }
	      cnt+=4;
	      if(ret>0x7fffffff) ret-=0x100000000;
	      return ret;
	      
	    case 256-126 .. 256-1:
	      return ret-256;

	    default:
	      return ret;
	  }
	};
      
      while(cnt < sizeof(lineinfo))
      {
	if(lineinfo[cnt] == 127)
	{
	  cnt++;
	  int len=get_small_number();
	  int shift = lineinfo[cnt++];
	  file=lineinfo[cnt..cnt+len-1]; // FIXME: Only works if shift==0?
	  cnt += len<<shift;
	}
	off+=get_small_number();

	if(off > offset) break;
	line+=get_small_number();
      }
      return file+":"+line;
    }


  class SvalueHandle
  {
    string _type()
      {
	unbug_error("Unbug cannot get the value of this value.\n");
      }

    mixed _value()
      {
	unbug_error("Unbug cannot get the value of this type.\n");
      }

    string _sprintf(int oper)
      {
	if(oper == 't') return _type();
	unbug_error("Unbug cannot describe this type yet.\n");
      }

    int low_hash()
      {
	unbug_error("Unbug cannot hash this type yet.\n");
      }
    
    int clamp(int i)
      {
	i&=0xffffffff;
	if(i>0x7fffffff) i-=0x100000000;
	return i;
      }

    int _hash()
      {
	int h=low_hash();
	h=clamp(h+h % 997);
	h=clamp(h+clamp(clamp(h + syms[ "PIKE_T_"+upper_case(_type()) ])
			* 9248339));
	return h;
      }

    mixed `+ (mixed ... args){return FakeHandle(predef::`+(_value(),@args)); }
    mixed ``+(mixed ... args){return FakeHandle(predef::`+(@args,_value())); }
    mixed `- (mixed ... args){return FakeHandle(predef::`-(_value(),@args)); }
    mixed ``-(mixed ... args){return FakeHandle(predef::`-(@args,_value())); }
    mixed `* (mixed ... args){return FakeHandle(predef::`*(_value(),@args)); }
    mixed ``*(mixed ... args){return FakeHandle(predef::`*(@args,_value())); }
    mixed `/ (mixed ... args){return FakeHandle(predef::`/(_value(),@args)); }
    mixed ``/(mixed ... args){return FakeHandle(predef::`/(@args,_value())); }
    mixed `% (mixed ... args){return FakeHandle(predef::`%(_value(),@args)); }
    mixed ``%(mixed ... args){return FakeHandle(predef::`%(@args,_value())); }

    mixed `& (mixed ... args){return FakeHandle(predef::`&(_value(),@args)); }
    mixed ``&(mixed ... args){return FakeHandle(predef::`&(@args,_value())); }
    mixed `| (mixed ... args){return FakeHandle(predef::`|(_value(),@args)); }
    mixed ``|(mixed ... args){return FakeHandle(predef::`|(@args,_value())); }
    mixed `^ (mixed ... args){return FakeHandle(predef::`^(_value(),@args)); }
    mixed ``^(mixed ... args){return FakeHandle(predef::`^(@args,_value())); }

    mixed `<< (mixed arg) { return FakeHandle(predef::`<<(_value(),arg)); }
    mixed ``<<(mixed arg) { return FakeHandle(predef::`<<(arg,_value())); }
    mixed `>> (mixed arg) { return FakeHandle(predef::`>>(_value(),arg)); }
    mixed ``>>(mixed arg) { return FakeHandle(predef::`>>(arg,_value())); }

    mixed `~ () { return FakeHandle(~(_value())); }

    mixed `< (mixed arg) { return FakeHandle(predef::`<(_value(),arg)); }
    mixed `> (mixed arg) { return FakeHandle(predef::`>(_value(),arg)); }

    mixed `[](mixed ... args) {return FakeHandle(predef::`[](_value(),@args));}
  }

  class SvalueError (mixed u, int type) {
    inherit SvalueHandle;
    string _type() { return "error"; }
    mixed _value() {
      return class {
	string _sprintf(int t) {
	  return t=='O' && sprintf("$ERRROR(type:%d)$",type);
	}
      }();
    }
    string _sprintf(int t) {
      if(t=='t') return "error";
      if(t!='O') return UNDEFINED;
      return sprintf("$ERRROR(type:%d)$",type);
    }
  }

  class PtrSvalueHandle
  {
    inherit SvalueHandle;
    Handle __ptr;

    void create(Handle h)
      {
	__ptr=h;
      }

    int `==(mixed obj)
      {
	return
	  object_program(this_object()) == object_program(obj) &&
	  obj->__ptr->_value() == __ptr->_value();
      }
    int low_hash() { return __ptr->_value() >> 2; }
  }

  class StringHandle
  {
    inherit PtrSvalueHandle;
    string value;
    int len=-1;
    int shift=-1;

    string _type() { return "string"; }

    int _sizeof() {
      if(len == -1)
	len=__ptr->len->_value();
      return len;
    }

    int _width() {
      if(shift == -1)
	shift=__ptr->size_shift->_value();
      return shift;
    }

    string _sprintf(int op)
      {
	if(op == 't') return _type();

	int trunc;

	if(!value)
	{
	  if(_width() == 0)
	  {
	    if(_sizeof()<1000)
	    {
	      value=mkhandle("(char *)",__ptr->str)->_value();
	    }else{
	      value=mkhandle("((char *)",__ptr->str,")[0]@1000")->_value();
	    }
	  }else{
	    return sprintf("string { len = %d, width=%d }",_sizeof(),_width());
	  }
	}
	

	string ret;
	switch(op)
	{
	  case 'O': ret=sprintf("%O",value); break;
	  case 's': ret=value;
	}
	if(value && _sizeof() > sizeof(value))
	  ret+="..."+(sizeof(value) - len);
	return ret;
      }

    string _value(void|int all)
      {
	return _sprintf('s');
      }

    object `[](int x)
      {
	if( x<0 ) x+=_sizeof();
	if(x < 0 || x >= _sizeof())
	  unbug_error("Index %d is out of range (0..%d)\n",x,_sizeof()-1);

	return FakeHandle( mkhandle("((p_wchar" + _width() + " *)(",
				    __ptr, "->str))[", x, "]")->_value());
      }
  }

  class ArrayHandle
  {
    inherit PtrSvalueHandle;
    int __size=-1;
    string odesc;

    string _type() { return "array"; }
    int _sizeof()
      {
	if(__size==-1)
	  __size=__ptr->size->_value();
	return __size;
      }

    string _sprintf(int op)
      {
	switch(op)
	{
	  case 't': return _type();
	  case 'O':
	    if(odesc) return odesc;

	    // FIXME: Needs clipping
	    odesc="({";
	    for(int e=0;e<_sizeof();e++)
	    {
	      if(sizeof(odesc)>300)
	      {
		odesc+="...."+(_sizeof()-e);
		break;
	      }
	      if(e) odesc+=",";
	      odesc+=sprintf("%O",`[](e));
	    }
	    odesc+="})"; 
	    return odesc;
	}
      }

    mixed `[](int x)
      {
	if(x<0 ) x+=_sizeof();
	if(x < 0 || x >= _sizeof())
	  unbug_error("Index %d is out of range (0..%d)\n",x,_sizeof()-1);

	return Svalue( __ptr->item + x );
      }
  }

  class MappingHandle
  {
    inherit PtrSvalueHandle;
    array cache;
    string odesc;

    string _type() { return "mapping"; }

    int _sizeof()
      {
	_walk();
	return sizeof(cache);
      }

    class KeyPair
    {
      static Handle _pair;
      static SvalueHandle ind, val;
      static int hval;
      
      void create(Handle pair) {
	_pair = pair;
      }

      SvalueHandle get_ind() {
	return ind || ( ind=Svalue(_pair->ind) );
      }

      SvalueHandle get_val() {
	return val || ( val=Svalue(_pair->val) );
      }

      int get_hval() {
	return hval || ( hval=_pair->hval->_value() );
      }
    }

    void _walk()
      {
	if(!cache)
	{
#if DEBUG >1
	  werror("Walking mapping %O\n",__ptr);
#endif
	  cache=({});
	  Handle data=__ptr->data;
	  int hsize=data->hashsize->_value();
	  for(int h=0;h<hsize;h++)
	  {
	    for(Handle pair=data->hash[h];pair->_value();pair=pair->next)
	      cache+=({ KeyPair(pair) });
	  }
	}
      }

    string _sprintf(int op)
      {
	if(op == 't') return _type();
	if(failsafe) return "MAPPING";
	_walk();
	/* FIXME: truncating ! */
	if(!odesc)
	{
	  string new;
	  odesc="([";
	  int done;
	  foreach(cache, KeyPair k)
	    {
	      if(sizeof(odesc)>2) odesc+=",";
	      done++;
	      new = sprintf("%O:%O",k->get_ind(), k->get_val());
	      if(sizeof(odesc+new)>300)
	      {
		odesc+="...."+(_sizeof()-done);
		break;
	      }
	      odesc += new;
	    }
	  odesc+="])";
	}
	return odesc;
      }

    /* This uses linear lookups right now, and can be optimized
     * a lot. However, optimizing this only helps if we can reduce
     * the overhead to only walking one hash bucket.
     */
    mixed `[](mixed key)
      {
	_walk();
	foreach(cache, KeyPair k)
	  if(k->get_ind() == key)
	    return k->get_val();
      }

    mixed _search(mixed val)
      {
	_walk();
	foreach(cache, KeyPair k)
	  if(k->get_val() == val)
	    return k->get_ind();
      }

    mixed `->(string val)
      {
	if(val[0]=='_' && ::`->(val)) return ::`->(val);
	return `[](val);
      }

    mixed _indices() {
      return cache->get_ind();
    }

    mixed _values() {
      return cache->get_val();
    }
  }

  class MultisetHandle
  {
    inherit PtrSvalueHandle;
    Handle msd;

    string _type() { return "multiset"; }
    int _sizeof() { return msd->size->_value(); }
    string _sprintf(int op)
      {
	if(op == 't') return _type();
	if(failsafe) return "MULTISET";
	string desc="(< ";
	Handle h = msd->nodes->i;
	for(int e=0;e<_sizeof();e++) {
	  desc += sprintf("%O", Svalue(h->ind, 1)->_value());
	  if(sizeof(desc)>300) {
	    desc += " ..."+(_sizeof()-e);
	    break;
	  }
	  if(e<_sizeof()-1) desc += ", ";
	  h=h->next;
	}
	desc += ">)";

	return desc;
      }

    /* SLOW */
    mixed `[](mixed key)
      {
	Handle h = msd->nodes->i;
	for(int e=0;e<_sizeof();e++) {
	  mixed i = Svalue(h->ind, 1)->_value();
	  if(i == key)
	    return 1;
	  h=h->next;
	}
	return 0;
      }

    void create(Handle h)
      {
	::create(h);
	msd = h->msd;
      }

    mixed `->(string val)
      {
	if(val[0]=='_' && ::`->(val)) return ::`->(val);
	return `[](val);
      }
  }

  class ProgramHandle 
  {
    inherit PtrSvalueHandle;
    int __id=-1;
    string odesc;

    string _type() { return "program"; }

    int _id()
      {
	if(__id == -1)
	  __id = __ptr->id->_value();
	return __id;
      }

    class Identifier
    {
      static Handle identifier;
      static Handle _so;
      static int _storage_offset=-1;
      static int _num;
      static string _name;
      static string _flags;
      static string _run_time_type;
      static int _offset=-10;

      void create(int e, Handle in, Handle id)
	{
	  _num=e;
	  identifier=id;
	  _so = in->storage_offset;
//	  werror("Identifier { %d, name = %s }\n",_num, _name);
	}

      string run_time_type()
	{
	  if(!_run_time_type)
	    _run_time_type = 
	      revsyms->PIKE_T[identifier->run_time_type->_value()];
	  return _run_time_type;
	}

      int offset()
	{
	  if(_offset == -10)
	    _offset = identifier->func->offset->_value();
	  return _offset;
	}

      int storage_offset()
	{
	  if(_storage_offset==-1)
	    _storage_offset = _so->_value();
	  return _storage_offset;
	}

      string name()
	{
	  if(!_name)
	    _name=mkhandle("(char *)",identifier->name->str)->_value();
	  return _name;
	}

      int num()
	{
	  return _num;
	}

      string flags()
	{
	  if(!_flags)
	  {
	    int flags=identifier->identifier_flags->_value();
	    _flags="";
	    if(syms->IDENTIFIER_PIKE_FUNCTION & flags) _flags+="PF";
	    if(syms->IDENTIFIER_C_FUNCTION & flags) _flags+="CF";
	    if(syms->IDENTIFIER_CONSTANT & flags) _flags+="CONST";
	  }
#if DEBUG >3
	  werror("FLAGS=%O\n",_flags);
#endif
	  return _flags;
	}
    };

    mapping __name_to_id;
    array __id_to_name;

    void _walk(void|int no_name)
      {
	if(!__id_to_name)
	{
#if DEBUG >1
	  werror("Walking program %O\n",__ptr);
#endif
	  __id_to_name=({});

	  int num_ids=__ptr->num_identifier_references->_value();
	  Handle idrefs=__ptr->identifier_references;
	  idrefs->_evaluate();
	  Handle inherits=__ptr->inherits;
	  
//	  werror("num_ids: %O\n",num_ids);
	  for(int e=0;e<num_ids;e++)
	  {
	    Handle idref = idrefs + e;
	    Handle in=inherits + idref->inherit_offset;
	    Handle id =in->prog->identifiers + idref->identifier_offset;
	    object i=Identifier(e, in, id);
	    __id_to_name+=({i});
	  }
	}
	if(!__name_to_id && !no_name) {
	  __name_to_id=([]);
	  foreach(__id_to_name, Identifier i)
	    __name_to_id[i->name()] = i;
	}
      }

    array(string) _globals()
      {
	_walk(1);
	array(string) ret=({});
	foreach(__id_to_name, Identifier i)
	  if(i->flags() == "")
	    ret+=({ i->name() });
	return ret;
      }

    object _constant(int c)
      {
	return Svalue( mkhandle( "&(", (__ptr -> constants + c) -> sval,")"));
      }

    ProgramHandle _parent()
      {
	Handle p=__ptr->parent;
	if(p->_value()) return MKSH(ProgramHandle,p);
	return 0;
      }

    Identifier _low_search(mixed value)
      {
	_walk(1);
	foreach(__id_to_name, Identifier i)
	  if(i->flags() == "CONST")
	    if(_constant( i->offset()) == value)
	      return i;
      }

    string _search(mixed value)
      {
//	failsafe=1;
//	trace(9);
	Identifier i=_low_search(value);
//	trace(0);
//	failsafe=0;
	if(i) return i->name();
      }

    Identifier _id_to_name(int i)
      {
	_walk(1);
	if(i<0 || i>=sizeof(__id_to_name)) return 0;
	return __id_to_name[i];
      }


    Identifier _name_to_id(string i)
      {
	_walk();
	return __name_to_id[i];
      }

    string _sprintf(int op)
      {
	if(op == 't') return _type();
	if(failsafe) return odesc || "PROGRAM";
	if(odesc) return odesc;
	odesc="PROGRAM"; /* Recursion protection */
#if 1
	if(_parent())
	{
//	  werror("LOOKING UP PARENT!\n");
	  return odesc=sprintf("%O->%s",
			       _parent(),
			       _parent()->_search(this_object()) || "???");
	}
#endif

	if(odesc=revsyms->PROG_ID[_id()]) return odesc;

	
#if 1
	if(mixed name=ObjectHandle(Handle("master_object"))->
	   _get_id_or_name(MASTER_programs, "programs")->
	   _search(this_object()))
	{
	  return odesc=name->_value();
	}
#endif

	return odesc="PROGRAM";
      }

    /* Change when `[]() is done */
#if 0
    mixed `->(string val)
      {
	if(val[0]=='_' && ::`->(val)) return ::`->(val);
	return `[](val);
      }
#endif
  }

  class ObjectHandle
  {
    inherit PtrSvalueHandle;
    string _type() { return "object"; }
    ProgramHandle __prog;

    ProgramHandle _prog()
      {
	if(!__prog)
	{
	  if(!__ptr->prog->_value()) return 0;
	  __prog=MKSH(ProgramHandle,__ptr->prog);
	}

	return __prog;
      }

    mixed _object_program()
      {
	if(!_prog()) return 0;
	if(_prog()->__ptr->flags->_value() & syms->PROGRAM_USES_PARENT)
	{
	  Handle pi=mkhandle("(struct parent_info *)(",__ptr->storage,")");
	  if(pi->parent->_value())
	    return MKFSH(FunctionHandle,
			 pi->parent,
			 pi->parent_identifier->_value());
	}
#if 0
	catch {
	  /* OLD (<=7.2) STYLE?? */
	  if(__ptr->parent->_value())
	    return MKFSH(FunctionHandle,
			 __ptr->parent,
			 __ptr->parent_identifier->_value());
	};
#endif
	return _prog();
      }

    string _sprintf(int op)
      {
	if(op == 't') return _type();
	if(failsafe) return "OBJECT";
	if(__ptr->_value() == mkhandle("master_object")->_value())
	  return "master()";

	if(mixed prog=_object_program())
	{
	  string desc=sprintf("%O",prog);
	  if(desc == "PROGRAM") return "OBJECT";
	  return sprintf("%s()",desc);
	}

	return "OBJECT";
      }

    mixed _make_ret_value(object i, object p) {
	mixed ret;
	if(!i) return ([])[0];
	switch(i->flags())
	{
	  case "CONST":
	    ret=p->_constant(i->offset());
	    /* FIXME: Check for 'PROGRAM_USES_PARENT' ? */
	    if(ret->_type() != "program")
	      return ret;
	  case "CF":
	  case "PF":
	    return MKFSH(FunctionHandle, __ptr, i->num());

	  case "": /* variable */
	    if(i->run_time_type() == "PIKE_T_MIXED")
	    {
	      return Svalue(mkhandle("(struct svalue *)(",
				     __ptr -> storage + i->storage_offset() +
				     i->offset(), ")"));
	    }else{
	      return ShortSvalue(mkhandle("(union anything *)(",
					  __ptr -> storage +
					  i->storage_offset() +
					  i->offset(), ")"),
				 i->run_time_type());
	    }

	  default:
	    unbug_error("Failed to index object (flags=%O)\n",i->flags());
	}
      }

    mixed `[](string key)
      {
	ProgramHandle p=_prog();
	if(!p) return ([])[0]; /* Cast error ?? */
	object i=p->_name_to_id(key);
	return _make_ret_value(i,p);
      }

    mixed _get_id_or_name(int id, string key) {
      ProgramHandle p=_prog();
      if(!p) return ([])[0]; /* Cast error ?? */
      object i=p->_id_to_name(id);
      if(i && i->name()==key)
	return _make_ret_value(i,p);
      else
	return `[](key);
    }

    array(string) _globals()
      {
	return _prog()->_globals();
      }

    mixed `->(string val)
      {
	if(val[0]=='_' && ::`->(val)) return ::`->(val);
	return `[](val);
      }
  }

  class FunctionHandle
  {
    inherit PtrSvalueHandle;
    int __func;
    string odesc;
    ProgramHandle __prog;

    ProgramHandle _prog()
      {
	if(!__prog)
	{
	  if(!__ptr->prog->_value()) return 0;
	  __prog=MKSH(ProgramHandle,__ptr->prog);
	}
	return __prog;
      }
    
    string _type() { return "function"; }

    string _sprintf(int op)
      {
	if(op == 't') return _type();
	if(failsafe)
	  return sprintf("FUNCTION(%d)",__func);

	if(!odesc)
	{
	  Handle prog=__ptr->prog;
	  if(!prog->_value() ||
	     __func < 0 ||
	     __func >= prog->num_identifier_references->_value())
	    return odesc="function";

	  Handle idref=prog->identifier_references + __func;
	  Handle identifier=(prog->inherits +
			     idref->inherit_offset)->prog->identifiers +
	    idref->identifier_offset;
	  odesc=sprintf("%O->%s",
			MKSH(ObjectHandle,__ptr),
			mkhandle("(char *)",identifier->name->str)->_value());
#if 0
	  werror("FOOBAR1: %s\n", MKSH(ObjectHandle,__ptr)->_sprintf('O'));
	  werror("FOOBAR2: %O\n", mkhandle("(char *)",
					   identifier->name->str)->_value());
#endif
	}
	return odesc;
      }

    void create(Handle h, int f)
      {
	::create(h);
	__func=f;
      }

    int `==(mixed obj)
      {
	return object_program(this_object()) == object_program(obj) &&
	  obj->__ptr == __ptr &&
	  obj->__func == __func;
      }

    Breakpoint _break()
      {
	ProgramHandle p=_prog();
	object ID=p->_id_to_name(__func);

	switch(ID->flags())
	{
	  case "PF":
	    Handle prog=__ptr->prog;
	    Handle idref=prog->identifier_references + __func;
	    Handle identifier=(prog->inherits +
			       idref->inherit_offset)->prog->identifiers +
	      idref->identifier_offset;
	    return Breakpoint(mkhandle("(long)(", prog->program +
				       identifier->func->offset + 2,
				       ")")->_value(), _sprintf('O'));
	    break;

	  case "CF":
	    return CFunctionBreakpoint(mkhandle("(long)(", ID->offset(),
						")")->_value(), _sprintf('O'));
	}
      }
    
  }

  class CallableHandle
  {
    inherit PtrSvalueHandle;
    string odesc;
    
    string _type() { return "function"; }

    string _sprintf(int op)
      {
	if(op == 't') return _type();
	if(odesc) return odesc;
	if(failsafe)
	  return sprintf("BUILTIN FUNCTION");

	return odesc=mkhandle("(char *)(",__ptr->name->str,")")->_value();
      }


    Breakpoint _break()
    {
      return CFunctionBreakpoint(mkhandle("(long)(", __ptr->function,
					  ")")->_value(), _sprintf('O'));
    }

  }

  class FakeHandle
  {
    inherit SvalueHandle;
    mixed value;
    int _sizeof() { return sizeof(value); }
    string _type() { return sprintf("%t",value); }
    mixed _value() { return value; }

    int _hash() {
      catch { return value->_hash(); } ;
      return hash(value);
    }
    mixed `[](mixed ind) { return value[ind]; }
    string _sprintf(int op)
      {
	if(op=='t') return _type();
	return sprintf(sprintf("%%%c",op),value);
      }

    mixed `! () { return !value; }

    void create(mixed v)
      {
	value=v;
      }

    mixed `->(string val)
      {
	if(val[0]=='_' && ::`->(val)) return ::`->(val);
	return `[](val);
      }
  }


  mapping(int|string:object) ptrhandlecache=([]);
  mapping(int|string:object) proghandlecache=([]);

  object MKSH(program p, Handle h)
    {
      int v=h->_value();
      mixed ret;
      if(!zero_type(ret=ptrhandlecache[v])) return ret;
      if(p == ProgramHandle)
      {
// werror("LOOKING UP HANDLE %d (%O) => %O\n",v,h,zero_type(proghandlecache[v]));
	if(!zero_type(ret=proghandlecache[v]))
	{
//	  werror("Found it, checking ids (%O , %O)\n",
//		 ret->_id(),
//		 h->id->_value());
	  if(ret->_id() == h->id->_value())
	  {
//	    werror("IDS MATCH\n");
	    return ptrhandlecache[v]=ret;
	  }
	}
	ret=p(h);
	ret->_id();
	return ptrhandlecache[v]=proghandlecache[v]=ret;
      }
      return ptrhandlecache[v]=p(h);
    }

  object MKFSH(program p, Handle h, int f)
    {
      if(f<0) unbug_error("Undefined function.\n");
      string v=h->_value() +":"+f;
      if(!zero_type(ptrhandlecache[v])) return ptrhandlecache[v];
      return ptrhandlecache[v]=p(h,f);
    }

  object ShortSvalue(Handle u, string type)
    {
      if(!u->refs->_value()) return FakeHandle(0);
      switch(type)
      {
	case "PIKE_T_ARRAY": return MKSH(ArrayHandle,u->array);
	case "PIKE_T_MAPPING":return MKSH(MappingHandle,u->mapping);
	case "PIKE_T_MULTISET":return MKSH(MultisetHandle,u->multiset);

	case "PIKE_T_INT":   return FakeHandle(u->integer->_value());
	case "PIKE_T_FLOAT": return FakeHandle(u->float_number->_value());
	case "PIKE_T_STRING":return MKSH(StringHandle,u->string);

	case "PIKE_T_OBJECT": return MKSH(ObjectHandle,u->object);
	case "PIKE_T_PROGRAM": return MKSH(ProgramHandle,u->program);

#if 0
	case "PIKE_T_TYPE": return TypeSval(s);
#endif

	default:
	  unbug_error("Unknown type (type=%O)!\n",type);
      }
    }

  object Svalue(Handle s, void|int(0..1) is_multiset)
    {
      int itype = s->type->_value();
      if(is_multiset)
	itype &= 0b0000111111111111;
      switch(string type=revsyms->PIKE_T[itype])
      {
	case 0:
	  return SvalueError(s->u, s->type->_value());
	  unbug_error("Unknown type (type=%O)!\n",s->type->_value());
	  
	case "PIKE_T_FUNCTION":
	  int subtype=s->subtype->_value();
	  if(subtype != 0xffff)
	    return MKFSH(FunctionHandle,s->u->object, subtype);

	  return MKSH(CallableHandle,s->u->efun);

	default:
	  return ShortSvalue(s->u, type);
      }
    }

  class FrameHandle
  {
    Handle fp;
    FrameHandle previous;
    int num=-1;

    string location()
      {
	return find_line_number(fp->current_object->prog,
				fp->pc);
      }

    string get_source_line()
      {
	sscanf(location(),"%s:%d",string file, int line);
	return getline(file, line);
      }

    int pc()
      {
	return mkhandle("(long)(", fp->pc, ")")->_value();
      }

    string funcdesc()
      {
	Handle obj=fp->current_object;
	return sprintf("%O",MKFSH(FunctionHandle,obj,fp->fun->_value()));;
      }

    string _sprintf(int op)
      {
	string ret="";
	if(fp->_value())
	{
	  int args=fp->args->_value();
	  for(int e=0;e<args;e++)
	  {
	    if(e) ret+=",";
	    ret+=sprintf("%O",Svalue(fp->locals + e));
	  }
	  
	  Handle obj=fp->current_object;
	  if(obj->_value())
	  {
	    Handle prog=obj->prog;
	    ret=sprintf("%O(%s)",
			MKFSH(FunctionHandle,obj,fp->fun->_value()),
			ret);
	    if(prog->_value())
	      ret+=sprintf(" at %s",find_line_number(prog, fp->pc));
	  }else{
	    ret=sprintf("unknown function(%s)",ret);
	  }
	}else{
	  ret="NULL";
	}
	if(op == 'O')
	  ret=sprintf("#%d %s",num,ret);
	return ret;
      }
    
    void create(void|Handle f)
      {
	if(f)
	{
	  fp=f;
	}else{
	  fp=mkhandle("Pike_interpreter->frame_pointer");
	  ptrhandlecache[fp->_value()]=this_object();
	  num=0;
	}
      }

    FrameHandle up()
      {
	Handle n=fp->next;
	if(n->_value())
	{
	  FrameHandle ret=MKSH(FrameHandle,n);
	  ret->previous=predef::this_object();
	  if(num>=0) ret->num=num+1;
	  return ret;
	}
      }

    FrameHandle down()
      {
	return previous;
      }

    ObjectHandle this_object()
      {
	return MKSH(ObjectHandle, fp->current_object);
      }


    int num_locals()
      {
	return fp->num_locals->_value();
      }

    object get_local(int l)
      {
	l--;
	if(l < 0 ||  l >= num_locals())
	  unbug_error("Local variable out of range.\n");

	return Svalue(fp->locals + l);
      }
  }



  /* For the print command */

  array history = ({});

  mixed get_history(int num)
    {
      num--;
      if(num < 0 || num>=sizeof(history))
	unbug_error("History value out of range.\n");
      return history[num];
    }

  class EvalHandler
  {
    mixed get_default_module()
      { 
	return all_constants() +
	  ([
	    "__THIS_OBJECT__":(current_frame &&
			       current_frame->this_object()) || ([]),
	    "__GET_HISTORY__":get_history,
	    "this_object":lambda()
	  {
	    if(!current_frame)
	      unbug_error("No current frame\n");
	    return current_frame->this_object();
	  },
	    "__GET_LOCAL__":lambda(int num)
	    {
	      if(!current_frame)
		unbug_error("No current frame\n");
	      return current_frame->get_local(num);
	    },
	    "master":lambda()
	  {
	    return MKSH(ObjectHandle, Handle("master_object"));
	  }
	    ]);
      }

    void compile_warning(string current_file, int current_line, string warning)
      {
      }

    void compile_error(string current_file, int current_line, string warning)
      {
	master()->compile_error(current_file, current_line, warning);
      }
    
  }

  mixed eval(string expression)
    {
      expression=replace(expression,"$","________DOLLAR_________");
      array(string) data=Parser.C.split(expression);
      for(int e=0;e<sizeof(data);e++)
      {
	switch(data[e][0])
	{
	  case '_':
	    string tmp=replace(data[e],"________DOLLAR_________","$");
	    if(tmp[0]=='$')
	    {
	      if(tmp=="$$")
		data[e]=sprintf("__GET_HISTORY__(%d)",sizeof(history));
	      else
		data[e]=sprintf("__GET_HISTORY__(%s)",tmp[1..]);
	    }
	    break;
	  case '@':
	    if(sscanf(data[e+1],"%*[0-9]%*c")==1)
	    {
	      data[e]=sprintf("__GET_LOCAL__(%s)",data[e+1]);
	      data[e+1]=" ";
	    }
	    break;
	}
      }

      expression=data*"";
      expression=replace(expression,"________DOLLAR_________","$");

      string expr=sprintf(
	"import __THIS_OBJECT__;\n"
	"mixed func() { return (mixed) ( %s ); }\n",
	expression);
      return compile_string(expr, "-", EvalHandler())()->func();
    }


  /* */

  FrameHandle current_frame;
  string current_file;
  int current_line;
  string last_cmd;


#define RUNINFO array(RunInfo)
  class RunInfo
  {
    int pri(); /* lower priority should mean more verbose */
    string short(); /* one-word description */
    string long(); /* Frame/locals description */
  }

  /*
   * This flushes all caches when we run code
   */
  void flush()
    {
      ::flush();
      current_frame=0;
      current_file=0;
      ptrhandlecache=([]);
    }

  void fix_current_frame()
    {
      if(!current_frame && started)
	current_frame=FrameHandle();
    }

  int started=0;

  string go()
    {
      while(1)
      {
	flush();
	Run r=Run(started?"cont":"run");
//	werror("WHY=%O (%O)\n",r->why,r->other);
	started=1;
	if(r->why == "exit") started=0;
	
	if(r->other)
	{
	  RUNINFO a=r->other;
	  if(!sizeof(a)) continue;
	  sort(a->pri(), a);
	  return a->short()*""+a[0]->long();
	}

	/* FIXME: It would be nicer to create a RunInfo
	 * struct for this data
	 */
	   
	fix_current_frame();

	if(!current_frame) return r->ret;

	return sprintf("%s\n%s\n%s",
		       r->ret,
		       current_frame,
	               current_frame->get_source_line());
      }
    }

  array(int) step_breakpoints;
  array watchpoint_callbacks=({});

  RUNINFO call_watchpoint_callbacks()
    {
      RUNINFO ret=({});
      foreach(watchpoint_callbacks, mixed cb)
      {
	if(cb)
	  ret+=`()(@cb);
      }
      return ret;
    }

  mixed register_internal_watchpoint(function cb, mixed ... args)
    {
      if(!sizeof(watchpoint_callbacks))
      {
	if(!step_breakpoints)
	{
	  int tmp;
	  step_breakpoints=({});
	  tmp=do_break("interpreter.h:"+syms->STEP_BREAK_LINE);
	  if(tmp != -1) 
	  {
	    step_breakpoints+=({ tmp });
	    register_breakpoint_callback(tmp, call_watchpoint_callbacks);
	  }
	  
	  tmp=do_break("interpreter_debug.h:"+syms->STEP_BREAK_LINE);
	  if(tmp != -1)
	  {
	    step_breakpoints+=({ tmp });
	    register_breakpoint_callback(tmp, call_watchpoint_callbacks);
	  }
	}else{
	  cmd(sprintf("enable %{%d %}",step_breakpoints));
	}
	if(!sizeof(step_breakpoints))
	{
	  unbug_error("This pike does not support stepping!\n");
	}
      }
      array handle=({cb})+args;
      watchpoint_callbacks+=({handle});
//      werror("WPCB REG : %O\n",watchpoint_callbacks);
      return handle;
    }

  void unregister_internal_watchpoint(mixed handle)
    {
      watchpoint_callbacks -= ({ handle });
//      werror("WPCB UNREG: %O\n",watchpoint_callbacks);
      if(!sizeof(watchpoint_callbacks))
	cmd(sprintf("disable %{%d %}",step_breakpoints));
    }

  class stepi_wp
  {
    string desc;
    mixed handle;

    void create()
      {
	if(current_frame)
	  desc=current_frame->funcdesc();
	handle=register_internal_watchpoint(this_object());
      }

    RUNINFO `() () {
      unregister_internal_watchpoint(handle);
      return ({ this_object() });
    }

    /* implements RunInfo */
    int pri() { return  100; }
    string short()
      {
	/* FIXME: Return current address here? */
	return "";
      }
	  
    string long()
      {
	fix_current_frame();
	string ret="";
	if(current_frame)
	{
	  if(current_frame->funcdesc() != desc)
	    ret+=sprintf("%O\n",current_frame);
//	  werror("LONG\n");
	  ret+=current_frame->get_source_line();
	}else{
	  return "Program is not running\n";
	}
	return ret;
      }

    string _sprintf() { return "nexti_wp"; }

  }

  class next_line_wp
  {
    inherit stepi_wp;
    string loc;

    string _sprintf() { return "next_line_wp"; }

    void create()
      {
	if(current_frame)
	  loc=current_frame->location();
	::create();
      }

    RUNINFO `() () {
      fix_current_frame();
      if(current_frame &&
	 (!loc || loc != current_frame->location()))
      {
	unregister_internal_watchpoint(handle);
	return ({ this_object() });
      }
      return ({});
    }

    string short() { return ""; }
  }

  class WatchPoint
  {
    int num;
    string condition;
    mixed val, oldval;
    mixed handle;

    RUNINFO `() () {
      mixed tmp;
      fix_current_frame();
      if(catch {
	tmp=eval(condition) -> _value();
      }) {
	write("Failed to evaluate watchpoint %d, disabling.\n",num);
	disable();
	return ({});
      }
      if(tmp != val)
      {
	oldval=val;
	val=tmp;
	return ({this_object()});
      }

      return ({});
    }

    void enable()
      {
	if(!handle)
	  handle=register_internal_watchpoint(this_object());
      }

    void disable()
      {
	if(handle)
	{
	  unregister_internal_watchpoint(handle);
	  handle=0;
	}
      }

    string _sprintf(int op)
      {
	switch(op)
	{
	  case 'O':
	    return sprintf("%3d watchpoint    %c           %s",
			   num,
			   handle?'y':'n',
			   condition);
	  case 's':
	    return sprintf("Watchpoint %d: %s",num,condition);
	}
      }

    void create(string c)
      {
	val=eval(condition=c)->_value();
	number_to_breakpoint+=({this_object()});
	num=sizeof(number_to_breakpoint);

	enable();
      }

    /* Implements RunInfo */
    int pri() { return 5; }

    string short()
      {
	return sprintf("Watchpoint %d, ",num);
      }

    string long()
      {
	fix_current_frame();
	return sprintf("%s\nOld value: %O\n"
		       "New value: %O\n"
		       "%s",
		       current_frame,
		       oldval, val,
	               current_frame->get_source_line());
      }
  }



  /* breakpoint handling */

  int breakpoint_breakpoint;
  int F_BREAKPOINT;

  class Breakpoint
  {
    /*
     * FIXME
     * We should remember what program this breakpoint is in and then
     * check that the program is still there before disabling/enabling
     * the breakpoint. -Hubbe
     */
    
    int num;
    int location; /* address */
    int oldval=-10000;
    int on;
    string cond;
    string desc;
    int count;
    int ignore;

    class step_over_wp
    {
      mixed handle;
      
      void create()
	{
	  handle=register_internal_watchpoint(this_object());
	}

      RUNINFO `() ()
	{
	  unregister_internal_watchpoint(handle);
	  if(on)
	    cmd(sprintf("print *(unsigned char *)(%d)=%d",
			location, F_BREAKPOINT));
	  return ({});
	}
    }

    
    int enable()
      {
	if(!on)
	{
	  if(oldval == -10000)
	  {
	    oldval=Handle(sprintf("*(unsigned char *)(%d)",
				  location))->_value();
	  }
	  cmd(sprintf("print *(unsigned char *)(%d)=%d",
		      location, F_BREAKPOINT));
	  on=1;
	}
      }
    int disable()
      {
	if(on)
	{
	  cmd(sprintf("print *(unsigned char *)(%d)=%d",location,oldval));
	  on=0;
	}
      }

    RUNINFO callback()
      {
	if(on)
	{
	  count++;
	  step_over_wp();
	  cmd(sprintf("print *(unsigned char *)(%d)=%d",location,oldval));
	  return ({ this_object() });
	}
	return ({});
      }

    void create(int l, void|string d)
      {
	desc=d;
	number_to_breakpoint+=({this_object()});
	num=sizeof(number_to_breakpoint);
	location=l;
	location_to_breakpoint[l]=this_object();
      }

    string _sprintf(int op)
      {
	switch(op)
	{
	  case 'O':
	    return sprintf("%3d breakpoint    %c  0x%08x  %s",
			   num,
			   on?'y':'n',
			   location,
			   desc);
	  case 's':
	    return sprintf("Breakpoint %d at %s",num,desc);
	}
      }

    /* implements RunInfo */
    int pri() { return  10; }
    string short()
      {
	return sprintf("Breakpoint %d, ",num);
      }

    string long()
      {
	fix_current_frame();
	return sprintf("%s\n%s",
		       current_frame,
	               current_frame->get_source_line());
      }

  };


  class CFunctionBreakpoint
  {
    int __breakpoint;
    inherit Breakpoint;

    void enable()
      {
	if(!on)
	{
	  cmd("enable "+__breakpoint);
	  on=1;
	}
      }

    void disable()
      {
	if(on)
	{
	  cmd("disable "+__breakpoint);
	  on=0;
	}
      }


    void create(int l, void|string desc)
      {
	::create(l,desc);
	__breakpoint=do_break("*" + l);
	on=1;
	register_breakpoint_callback(__breakpoint, breakpoint_callback);
      }
  }


  mapping (int:Breakpoint) location_to_breakpoint=([]);
  array(Breakpoint) number_to_breakpoint = ({});


  mixed breakpoint_callback()
    {
      fix_current_frame();

      int pc=current_frame->pc();
//      werror("PC=%x\n",pc);
      return location_to_breakpoint[pc]->callback();
    }

  Breakpoint set_breakpoint(string at)
    {
      int location;
      if(!breakpoint_breakpoint)
      {
	breakpoint_breakpoint=do_break("o_breakpoint");
	if(breakpoint_breakpoint == -1)
	{
	  breakpoint_breakpoint=0;
	  unbug_error("Breakpoints are not supported in this Pike.\n");
	}
	register_breakpoint_callback(breakpoint_breakpoint,
				     breakpoint_callback);
      }

      if(!F_BREAKPOINT)
      {
	F_BREAKPOINT=Handle("F_BREAKPOINT-F_OFFSET")->_value();
      }

      Breakpoint bp;
      if(sscanf(at,"%d%*c",int line)==1)
      {
	/* lookup line in current file/program */
	unbug_error("Cannot put breakpoints on line numbers yet, sorry.\n");
      }else{
	/* Let's hope it's a function */
	mixed func=eval(at);
	bp=func->_break();
	if(!bp)
	  unbug_error("Failed to set breakpoint.\n");
      }
      bp->enable();
      return bp;
    }

  /* Source code handling */
  mapping(string:array(string)) source_cache=([]);

  string getline(string file, int line)
    {
//      werror("showline(%O:%O)\n",file,line);
      if(line < 1) return sprintf("negative line: %s:%d\n",file,line);
      line--;
      array(string) source;
      if(!(source=source_cache[file])) {
	if(!file_stat(file))
	  return sprintf("Could not load source file %O\n", file);
	source=source_cache[file]=Stdio.read_file(file)/"\n";
      }

      if(line >= sizeof(source))
	return sprintf("line out of range: %s:%d\n",file,line);

      return sprintf("%7d %s\n",line+1,source[line]);
    }


  /* Interactive stuff */
  string command(string s)
  {
    array foo=s/" ";
    switch(foo[0])
    {
      case "gdb":
      {
	flush(); /* flush caches, needed? */
	return cmd(foo[1..]*" ");
      }
      break;

      case "backtrace":
      case "bt":
      {
	for(FrameHandle fp=FrameHandle();fp;fp=fp->up())
	  write("%O\n",fp);
      }
      return "";

      case "quit":
      case "q":
	destruct(this_object());
	exit(0);

      case "run":/* FIXME: This should restart program */
	started=0;
      case "cont":
      case "go":
	return go();

      case "up":
      case "down":
      {
	FrameHandle n=current_frame[foo[0]]();
	if(!n) return sprintf("You cannot go %s.\n",foo[0]);
	current_frame=n;
	current_file=0;
	return sprintf("%O\n",current_frame);
      }

      case "list":
      {
	int numlines=7;
	string loc;
	if(sizeof(foo)==1)
	  if(!current_file)
	    if(current_frame)
	      foo+=({current_frame->location()});
	    else
	      return "No current frame to read file location from.\n";

	if(sizeof(foo)>1)
	{
	  current_line=1;
	  current_file=foo[1];
	  sscanf(foo[1],"%s:%d",current_file,current_line);
	  current_line-=numlines;
	}

	for(int e=-numlines;e<=numlines;e++)
	  write(getline(current_file, current_line++));
	return "";
      }
      break;
	
	
      case "p":
      case "print":
      {
	mixed bar;
	got_unbug_error=0;
	if(mixed err=catch {
	  bar=eval(foo[1..]*" ");
	}) {
//	  werror("got_unbug_eror=%d\n%O\n",got_unbug_error,err);
	  if(got_unbug_error)
	  {
	    return err[0];
	  }else{
	    return master()->describe_backtrace(err);
	  }
	}else{
	  history+=({bar});
	  return sprintf("$%d = %O\n",sizeof(history),bar);
	}
      }

      case "locals":
	if(!current_frame)
	  return "No current frame to read locals from.\n";

	sscanf(current_frame->location(),"%s:%d",current_file,current_line);
        array locals = find_local_names(current_file, current_line);

        if(sizeof(locals) != current_frame->num_locals())
          write("WARNING! calculated local names not equal to the number of actual locals.\n\n");
      
	for(int e=1;e<=current_frame->num_locals();e++)
        {
          if(locals[e-1]) 
   	    write("@%d line %d %s %s = %O\n",e, locals[e-1][2], locals[e-1][0], locals[e-1][1], current_frame->get_local(e));
          else
 	    write("@%d  = %O\n",e, current_frame->get_local(e));
         }
	return "";

      case "globals":
	object to;
	PROF_BEGIN("cmd globals");
	if(sizeof(foo)>1)
	{
	  to=eval(foo[1..]*" ");
	}else{
	  if(!current_frame)
	    return "No current frame to find global variables from.\n";
	  to=current_frame->this_object();
	}

	foreach(to->_globals(), string name)
	  write("%s = %O\n",name, to[name]);
	PROF_END("cmd globals");
	return "";


      case "breakpoint":
      case "break":
      {
	if(String.trim_all_whites(foo[1..]*"")=="")
	  return "No breakpoint given.\n";
	Breakpoint bp=set_breakpoint(foo[1..]*" ");
	return sprintf("%s\n",bp);
      }

      case "info":
      {
	if(sizeof(foo)>1)
	  switch(foo[1])
	  {
	  case "breakpoints":
	    foreach(number_to_breakpoint, Breakpoint bp)
	      write("%O\n",bp);
	    return "";
	    break;
	  case "profiling":
#define werror write
	    PROF_RESULT();
	    return "";
#undef werror
	  case "evals":
	    write("%d Handles evaluated\n", evaluated_handles);
	    return "";
	  break;
	  }
	write("info breakpoints  : show breakpoints\n");
	return "";
      }
      case "en":
      case "enable":
	foreach(foo[1..], string s)
	  number_to_breakpoint[((int)s)-1]->enable();
	return "";

      case "disable":
      case "d":
      case "delete":
	foreach(foo[1..], string s)
	  number_to_breakpoint[((int)s)-1]->disable();
	return "";

      case "step":
	next_line_wp();
	return go();

      case "stepi":
	stepi_wp();
	return go();

      case "watch":
      {
	WatchPoint wp=WatchPoint(foo[1..]*" ");
	return sprintf("%s\n",wp);
      }

      case "display":
      case "disp":


      case "attach":
      case "atta":

      case "handle": /* for exceptions */
	return "Not implemented yet.\n";

      case "help":
      case "h":
      case "?":
	write("Commands currently understood by UNBUG:\n");
	write("go/run/cont       : let the program run\n");
	write("step              : step one line\n");
	write("stepi             : step one instruction\n");
	write("backtrace/bt      : Show backtrace\n");
	write("up                : go up to higher stack frame\n");
	write("down              : go down to lower stack frame\n");
	write("list              : show source\n");
	write("print/p E         : evaluate an expression\n");
	write("locals            : show local variables\n");
	write("globals           : show global variables\n");
	write("breakpoint/break  : set a breakpoint\n");
	write("watch E           : Watch an expression.\n");
	write("info breakpoints  : show breakpoints\n");
	write("enable B          : enable breakpoint B\n");
	write("disable B         : disable breakpoint B\n");
	write("quit/q            : exit unbug\n");
	write("gdb <gdb command> : run a GDB command\n");
	write("help/h/?          : show this text\n");
	return "";

      default:
	return sprintf("Unknown command: %s\n",s);
    }
  }

  void interact()
    {
      object rl=Stdio.Readline();
      rl->set_prompt("(unbug) ");
      rl->enable_history(512);
      while(string s=rl->read("(unbug) "))
      {
	if(s=="") s=last_cmd;

	if(mixed err=catch 
	{
	  write(command(s));
	}){
	  werror("Describing error:\n");
	  failsafe=1;
	  write(master()->describe_backtrace(err));
	  failsafe=0;
	}
	last_cmd=s;
      }
    }
  
  void destroy()
    {
      ::destroy();
      terminal->kill(signum("SIGINT"));
    }

  void create(array(string) argv)
    {
      ::create();

      string tmpfile=sprintf("pike-debugger-%d-%d",getpid(),counter++);
      string c="stty sane ;tty >" + tmpfile + ".tmp ; mv " + tmpfile +
	".tmp " + tmpfile + " ; while kill -0 " + getpid() +
	"; do sleep 2; done";
      array(string) fnord;
      if(getenv("STY")) fnord=({/*"screen",*/"/bin/sh","-c",c });
      else if(getenv("DISPLAY")) fnord=({"xterm","-e","/bin/sh","-c",c });
      object error_redirect = Stdio.File()->pipe(Stdio.PROP_IPC);
      terminal=Process.create_process(fnord, (["setsid":1,
					       "stderr":error_redirect]) );

      while(!file_stat(tmpfile))
	sleep(0.2);
      string tmp=Stdio.FILE(tmpfile,"r")->gets();
      sscanf(tmp,"%s\n",tmp);
      rm(tmpfile);

      cmd("file "+argv[0]);
      cmd("tty "+tmp);
      cmd("cd "+getcwd());

      /* FIXME: Args will probably need quoting */

      cmd("set args "+argv[1..]*" ");
    }

}

string find_in_path(string cmd)
{
  /* GDB looks in $PATH for executable, and so must we */
  if(!has_value(cmd,"/"))
  {
    foreach(getenv("PATH")/":", string p)
    {
      p=combine_path(getcwd(),p,cmd);
      if(Stdio.file_size(p)>0) return p;
    }
  }
  return cmd;
}

int main(int argc, array(string) argv)
{
  write(#"UNBUG $Id: c59d038a54c85fe40e2d545230253c23246e431e $
Copyright 2001-2002, Roxen Internet Software AB.
Copyright 2002-2012, Department of Computer and Information Science,
                     Linkping University
UNBUG is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
");

  if(argc < 2)
  {
    write("\nUsage: unbug <pike> <pike script>\n");
    exit(1);
  }

  if(!getenv("STY") && !getenv("DISPLAY"))
  {
    werror("You must run X-windows or Screen to use UNBUG.\n");
    exit(1);
  }

  string pike;
  int is_script=0;
  do {
    is_script=0;
    pike=argv[1];
    pike=find_in_path(pike);
    Stdio.File f=Stdio.File();
    if(f->open(pike,"r"))
    {
      /* This is a script, find interpreter */
      if(f->read(2) == "#!")
      {
	is_script=1;
	argv=argv[..0] + ( ((f->read(256)/"\n")[0]/" ") - ({""}) ) + argv[1..];
      }
      f->close();
    }
  }while(is_script);


  string realpike=pike;
#if constant(readlink)
  catch {
    while(1)
      realpike=combine_path(dirname(realpike), readlink(realpike));
  };
#endif

  if(string symbols=Stdio.read_file(realpike+".syms"))
  {
    syms=(mapping)(array_sscanf(symbols,
				"%{%*sdefine%*[ \t]%s%*[ \t]%i%*s\n%}")[0]);
  }else{
    write("Warning, no symbol file found, using builtin symbols!\n");
  }

  /* Make it possible to map numbers to symbol names */
  revsyms->PROG_ID=([]);
  foreach(indices(syms), string symname)
    {
      mapping rev;
      int val=syms[symname];
      array parts=symname/"_";
      string key=parts[0];
      for(int e=1;e<sizeof(parts);e++)
      {
	if(!(rev=revsyms[key])) revsyms[key]=rev=([]);
	rev[val]=symname;
	key+="_"+parts[e];
      }

      if(sscanf(symname, "PROG_%s_ID",string progname))
	revsyms->PROG_ID[progname]=val;
    }

  Debug debug=Debug(argv[1..]);
  signal(signum("SIGINT"),lambda() { debug->stop_execution(); });
  debug->interact();
  return 0;
}

ADT.Stack st;

array find_local_names(string file, int line)
{
  st = ADT.Stack();

  string f = Stdio.read_file(file);
  
  int current_block = 0;
  int stop_line = line;
  int func_block = 0;
  mapping current_locals;

  array t = Parser.Pike.tokenize(Parser.Pike.split(f), f);

  mapping locals = (["locals" : ({}) ]);

  st->push(locals);
  current_locals = locals;

  for(int i=0; i<sizeof(t); i++)
  {
    int xt = 0;
    Parser.Pike.Token token = t[i];

    if(token->text == "{" && t[i-1]->text != "(")
    {
     current_block ++;
//     write(("  "*current_block) + "in\n");
     locals = (["locals" : ({}) ]);
     st->push(locals);
     current_locals = locals;
     continue;
    }
    else if (token->text =="}" && t[i+1]->text !=")")
    {
//     write(("  "*current_block) + "out\n");
     current_block --;
     if(token->line > stop_line) break;

     st->pop();
     current_locals = st->top();
     continue;
    }

   array x;

   switch(token->text)
   {   
     case "int":
     case "float":
     case "string":
     case "array":
     case "mapping":
     case "multiset":
     case "function":
     case "object":
       x = get_var(t,i+1);
       if(sizeof(x)) 
       {
//         werror("%s: %s\n", token->text, x[1]*", ");
         i = x[0];     
         foreach(x[1], string vn)
           current_locals->locals+=({ ({token->text, vn, token->line}) });
       }
     xt=1;
     continue;
     break;
     
   }

   string tx="";

   if(t[i+1]->text ==".")
   {
//     write("got a .\n");
     int z=i;

     do {

     tx+=t[z]->text;
     if(t[z+1] == ".") tx +=".";
     else break;
     z=z+2;  
     }
     while(1);
     i = z;
   }
   else tx = token->text;

//   write("candidate token: %s\n", tx);

   if(!xt && is_object_identifier(tx))
   {
       x = get_var(t,i+1, 1);
       if(sizeof(x))
       {
         i = x[0];     
//          werror("%s: %s\n", tx, x[1]*", ");
         foreach(x[1], string vn)
           current_locals->locals+=({ ({token->text, vn, token->line}) });
       }
   }

  }
  mapping block_locals;
  array outdata=({});
// werror("sizeof blocks: %d\n", sizeof(st));
  array vals = ({});
  if(sizeof(st) > 1) vals = values(st)[1..];
//vals=values(st);
  foreach(vals, block_locals)
  {
     foreach(block_locals->locals, array cv)
     {
       outdata+=({cv});
     }
  }
//  write("current_block: %d\n", current_block);
  return outdata;
}

#define FOUND_VAR 1
#define LOOKING_VAR 2
#define IN_LIMITATION 3
#define FOUND_VAR 4
#define COMPLETE 5
#define IDLE 6
#define HAD_LIMITATION 1

array get_var(array tokens, int loc, void|int obj)
{
  array vn=({});
  int state, state2;
  int parens = 0;
  string lt;

  state = LOOKING_VAR;

  do
  {
    if(tokens[loc]->text ==";" || (tokens[loc]->text =="{" && state != IN_LIMITATION))
    {
//      write("end of the line!\n");
      if(lt != 0) { 
//         werror("adding %s to varnames\n", lt);
         vn+=({ lt });
      }
      if(sizeof(vn))
        return ({loc, vn});
      else return ({});
    }
 
    else if(state2 != HAD_LIMITATION
       && tokens[loc]->text == "(") // we're starting a variable limitation
    {
      state = IN_LIMITATION;
      parens++;
      loc++;
      continue;
    }

    else if(state == IN_LIMITATION && tokens[loc] == ")")
    {
      parens --;
      if(!parens) // we are back out of the limitation
      {
        state = LOOKING_VAR;
        state2 = HAD_LIMITATION;
      }
      loc++;
      continue;
    }

    else if(state == IN_LIMITATION)
    {
      loc++;
      continue;
    }

    else if(is_whitespace(tokens[loc]->text))
    {
       loc ++;
       continue;
    }

    else if (state2 == HAD_LIMITATION && tokens[loc]->text =="(" && state != IDLE) // started a function, probably.
    {
//      werror("parsing function, %s.\n", lt);
      state = IDLE;
      lt = 0;
      loc++;
      continue;
    }

    else if(state == FOUND_VAR && tokens[loc]->text !=",")
    {
      state = IDLE;
    }

    else if((state == FOUND_VAR) && 
          tokens[loc]->text ==",") // we're looking for another var name.
    {
      state = LOOKING_VAR;
//      write ("adding %s to varnames\n", lt);
      vn+=({lt});
      lt=0;
      loc++;
      continue;
    }

    else if((state == LOOKING_VAR) && 
      is_identifier(tokens[loc]->text))
    { 
      state = FOUND_VAR;
      state2 = HAD_LIMITATION;
  //    write("%s is an identifier!\n", tokens[loc]->text);
      lt = tokens[loc]->text;
    }

    else
    {
       state = IDLE;
       loc ++;
       continue;
    }

    loc ++;

  } while(loc < sizeof(tokens));

}

int is_identifier(string t)
{
  switch(t)
  {
    case "if":
    case "else":
    case "return":
    case "while":
    case "do":
    case "catch":
    case "gauge":
    case "for":
    case "int":
    case "float":
    case "array":
    case "string":
    case "mapping":
    case "multiset":
    case "object":
    case "function":
    case "void":
    case "mixed":
      return 0;
    break;
  }
  object r = Regexp.SimpleRegexp("^[a-zA-Z_][a-zA-Z0-9_]*$");

  return r->match(t);
}

int is_object_identifier(string t)
{
  foreach(t/".", string s)
    if(!is_identifier(s)) return 0;

  return 1;
}

int is_whitespace(string t)
{
  if(t == " " || t == "\n" || t == "\t")
    return 1;
  return 0;
}
