Index: expect-5.21/CHANGES.NT
diff -c expect-5.21/CHANGES.NT:1.17 expect-5.21/CHANGES.NT:1.19
*** expect-5.21/CHANGES.NT:1.17	Sat Nov 15 00:51:26 1997
--- expect-5.21/CHANGES.NT	Thu Dec 18 06:46:43 1997
***************
*** 1,3 ****
--- 1,17 ----
+ * Fix multiple close problem on process handles.
+ 
+ * Add support for creating tracebacks when a subprocess dies.  This
+   is only enabled when in debugging mode (exp_nt_debug is on).
+   Tracebacks written to the application event log as well as to
+   the console where the error occurred.
+ 
+ * Fix to buffer handling problems in expectlib (C library).
+ 
+ * Only do console mode setting if the console was properly initialized.
+ 
+ * Fixed pipe/console input handling to only start threads if someone
+   shows an interest.  This is needed by expectlib.
+ 
  ----------------- Expect-5.21 r1b1 Release --------------------------
  
  * Fixed premature EOF for serial communications.
Index: expect-5.21/generic/exp_clib.c
diff -c expect-5.21/generic/exp_clib.c:1.4 expect-5.21/generic/exp_clib.c:1.5
*** expect-5.21/generic/exp_clib.c:1.4	Sat Aug 23 03:26:20 1997
--- expect-5.21/generic/exp_clib.c	Sat Dec  6 01:18:29 1997
***************
*** 66,71 ****
--- 66,72 ----
  int getptymaster();
  int getptyslave();
  int Exp_StringMatch();
+ int pid;
  
  static Tcl_Interp *InterpPtr = NULL;
  #define sysreturn(x)	return(errno = x, -1)
***************
*** 75,80 ****
--- 76,113 ----
  /*
   *----------------------------------------------------------------------
   *
+  * expectlib_init --
+  *
+  *	Make sure an interpreter exists.
+  *
+  * Results:
+  *	FALSE on failure, TRUE on success.
+  *
+  *----------------------------------------------------------------------
+  */
+ static int
+ expectlib_init()
+ {
+ #ifdef __WIN32__
+     pid = GetCurrentProcessId();
+ #else
+     pid = getpid();
+ #endif
+     if (InterpPtr == NULL) {
+ 	InterpPtr = Tcl_CreateInterp();
+ 	if (Expect_Init(InterpPtr) == TCL_ERROR) {
+ 	    /* XXX: This should really return some error value */
+ 	    fprintf(stderr, "Expect_Init failed: %s\n", InterpPtr->result);
+ 	    return FALSE;
+ 	}
+     }
+     return TRUE;
+ }
+ 
+ 
+ /*
+  *----------------------------------------------------------------------
+  *
   * exp_strerror --
   *
   *	Returns a string with an error message in it.
***************
*** 146,158 ****
      int need;
      struct exp_f *f;
  
!     if (InterpPtr == NULL) {
! 	InterpPtr = Tcl_CreateInterp();
! 	if (Expect_Init(InterpPtr) == TCL_ERROR) {
! 	    /* XXX: This should really return some error value */
! 	    fprintf(stderr, "Expect_Init failed: %s\n", InterpPtr->result);
! 	    return NULL;
! 	}
      }
  
  #ifdef __WIN32__
--- 179,186 ----
      int need;
      struct exp_f *f;
  
!     if (!expectlib_init()) {
! 	return NULL;
      }
  
  #ifdef __WIN32__
***************
*** 166,173 ****
      }
  
      if (!argv[0] || strcmp(file,argv[0])) {
! 	debuglog("expect: warning: file (%s) != argv[0] (%s)\n",
! 		 file, argv[0]?argv[0]:"");
      }
      
      need = sizeof("spawn") + sizeof(char *);
--- 194,201 ----
      }
  
      if (!argv[0] || strcmp(file,argv[0])) {
! 	debuglog("expectlib(%d): warning: file (%s) != argv[0] (%s)\n",
! 		 pid, file, argv[0]?argv[0]:"");
      }
      
      need = sizeof("spawn") + sizeof(char *);
***************
*** 177,183 ****
  	cc++;
      }
  
!     nargv = (char **) malloc(need);
      p = (char *) &nargv[cc+2];
      nargv[0] = p;
      memcpy(p, "spawn", sizeof("spawn"));
--- 205,211 ----
  	cc++;
      }
  
!     nargv = (char **) ckalloc(need);
      p = (char *) &nargv[cc+2];
      nargv[0] = p;
      memcpy(p, "spawn", sizeof("spawn"));
***************
*** 214,220 ****
  	Tcl_GetChannelType(channel)->blockModeProc(
  	    Tcl_GetChannelInstanceData(channel), TCL_MODE_BLOCKING);
      }
!     free(nargv);
      return (ExpHandle) channel;
  }
  
--- 242,248 ----
  	Tcl_GetChannelType(channel)->blockModeProc(
  	    Tcl_GetChannelInstanceData(channel), TCL_MODE_BLOCKING);
      }
!     ckfree((char *) nargv);
      return (ExpHandle) channel;
  }
  
***************
*** 251,257 ****
  	errno = EINVAL;
  	return NULL;
      }
!     if (!(argv = (char **)malloc((i+1)*sizeof(char *)))) {
  	errno = ENOMEM;
  	return NULL;
      }
--- 279,285 ----
  	errno = EINVAL;
  	return NULL;
      }
!     if (!(argv = (char **)ckalloc((i+1)*sizeof(char *)))) {
  	errno = ENOMEM;
  	return NULL;
      }
***************
*** 261,267 ****
  	if (!argv[i]) break;
      }
      handle = exp_spawnv(argv[0],argv+1);
!     free((char *)argv);
      return(handle);
  }
  
--- 289,295 ----
  	if (!argv[i]) break;
      }
      handle = exp_spawnv(argv[0],argv+1);
!     ckfree((char *)argv);
      return(handle);
  }
  
***************
*** 381,387 ****
  {
      int cc = 0;			/* number of chars returned in a single read */
      int buf_length;		/* numbers of chars in exp_buffer */
-     int old_length;		/* old buf_length */
      int first_time = TRUE;	/* force old buffer to be tested before */
  				/* additional reads */
      int polled = 0;		/* true if poll has caused read() to occur */
--- 409,414 ----
***************
*** 403,408 ****
--- 430,436 ----
  
      f = exp_chan2f(InterpPtr, Tcl_GetChannelName(channel),0,1,"");
      if (!f) return_errno(ENOMEM);
+     f->rm_nulls = exp_remove_nulls;
  
      if (!ecases) return_errno(EINVAL);
  
***************
*** 457,463 ****
  	    f->printed = bufsiz-1;
  	    f->buffer[f->size] = '\0';
  	}
! 	exp_buffer = realloc(exp_buffer,bufsiz);
  	if (!exp_buffer) return_errno(ENOMEM);
  	exp_buffer[buf_length] = '\0';
  	exp_buffer_end = exp_buffer + buf_length;
--- 485,491 ----
  	    f->printed = bufsiz-1;
  	    f->buffer[f->size] = '\0';
  	}
! 	exp_buffer = ckrealloc(exp_buffer,bufsiz);
  	if (!exp_buffer) return_errno(ENOMEM);
  	exp_buffer[buf_length] = '\0';
  	exp_buffer_end = exp_buffer + buf_length;
***************
*** 476,486 ****
      for (;;) {
  	/* when buffer fills, copy second half over first and */
  	/* continue, so we can do matches over multiple buffers */
! 	if (buf_length == bufsiz) {
  	    int first_half, second_half;
  
  	    if (exp_full_buffer) {
! 		debuglog("expect: full buffer\r\n");
  		exp_match = exp_buffer;
  		exp_match_end = exp_buffer + buf_length;
  		exp_buffer_end = exp_match_end;
--- 504,514 ----
      for (;;) {
  	/* when buffer fills, copy second half over first and */
  	/* continue, so we can do matches over multiple buffers */
! 	if (buf_length == bufsiz-1) {
  	    int first_half, second_half;
  
  	    if (exp_full_buffer) {
! 		debuglog("expectlib(%d): full buffer\r\n", pid);
  		exp_match = exp_buffer;
  		exp_match_end = exp_buffer + buf_length;
  		exp_buffer_end = exp_match_end;
***************
*** 490,496 ****
  	    second_half = bufsiz - first_half;
  
  	    memmove(exp_buffer,exp_buffer+first_half,second_half);
! 	    buf_length = second_half;
  	    exp_buffer_end = exp_buffer + second_half;
  	    f->size = buf_length;
  	    f->printed = buf_length;
--- 518,524 ----
  	    second_half = bufsiz - first_half;
  
  	    memmove(exp_buffer,exp_buffer+first_half,second_half);
! 	    buf_length = second_half-1;
  	    exp_buffer_end = exp_buffer + second_half;
  	    f->size = buf_length;
  	    f->printed = buf_length;
***************
*** 509,515 ****
  	 * check for timeout
  	 */
  	if ((exp_timeout >= 0) && ((remtime < 0) || polled)) {
! 	    debuglog("expect: timeout\r\n");
  	    exp_match_end = exp_buffer;
  	    return_normally(EXP_TIMEOUT);
  	}
--- 537,543 ----
  	 * check for timeout
  	 */
  	if ((exp_timeout >= 0) && ((remtime < 0) || polled)) {
! 	    debuglog("expectlib(%d): timeout\r\n", pid);
  	    exp_match_end = exp_buffer;
  	    return_normally(EXP_TIMEOUT);
  	}
***************
*** 525,550 ****
  
  	cc = expect_read(InterpPtr,&f,1,&ignore,remtime,key);
  	if (cc == EXP_EOF) {
  	    /* do nothing */
  	} else if (cc == EXP_TIMEOUT) {
! 	    debuglog("expect: timed out\r\n");
  	} else if (cc == EXP_RECONFIGURE) {
  	    goto restart;
  	}
  	if (cc < 0) {
  	    return cc;
  	}
  
- 	old_length = buf_length;
  	buf_length += cc;
  	exp_buffer_end += buf_length;
  
- 	/* remove nulls from input, so we can use C-style strings */
- 	/* doing it here lets them be sent to the screen, just */
- 	/*  in case they are involved in formatting operations */
- 	if (exp_remove_nulls) {
- 	    buf_length -= rm_nulls(exp_buffer + old_length, cc);
- 	}
  	/* cc should be decremented as well, but since it will not */
  	/* be used before being set again, there is no need */
  	exp_buffer_end = exp_buffer + buf_length;
--- 553,586 ----
  
  	cc = expect_read(InterpPtr,&f,1,&ignore,remtime,key);
  	if (cc == EXP_EOF) {
+ 	    debuglog("expectlib(%d): eof\r\n", pid);
  	    /* do nothing */
  	} else if (cc == EXP_TIMEOUT) {
! 	    debuglog("expectlib(%d): timed out\r\n", pid);
  	} else if (cc == EXP_RECONFIGURE) {
  	    goto restart;
+ 	} else {
+ 	    debuglog("expectlib(%d): read %d bytes\n", pid, cc);
+ 	    exp_buffer[buf_length+cc] = 0;
+ 	    debuglog("expectlib(%d): read %s\n", pid,
+ 		     exp_printify(&exp_buffer[buf_length]));
+ #if 0
+ 
+ 	    int i;
+ 	    debuglog("\t");
+ 	    for (i = 0; i < cc; i++) {
+ 		debuglog("%02x ", (unsigned char) exp_buffer[buf_length+i]);
+ 	    }
+ 	    debuglog("\n");
+ #endif
  	}
  	if (cc < 0) {
  	    return cc;
  	}
  
  	buf_length += cc;
  	exp_buffer_end += buf_length;
  
  	/* cc should be decremented as well, but since it will not */
  	/* be used before being set again, there is no need */
  	exp_buffer_end = exp_buffer + buf_length;
***************
*** 552,558 ****
  	exp_match_end = exp_buffer;
  
      after_read:
! 	debuglog("expect: does {%s} match ",exp_printify(exp_buffer));
  	/* pattern supplied */
  	for (ec=ecases;ec->type != exp_end;ec++) {
  	    int matched = -1;
--- 588,595 ----
  	exp_match_end = exp_buffer;
  
      after_read:
! 	debuglog("expectlib(%d): does {%s} match ",
! 		 pid, exp_printify(exp_buffer));
  	/* pattern supplied */
  	for (ec=ecases;ec->type != exp_end;ec++) {
  	    int matched = -1;
***************
*** 665,671 ****
      va_end(args);
  
      if (!(ecases = (struct exp_case *)
! 	malloc((1+i)*sizeof(struct exp_case))))
  	sysreturn(ENOMEM);
  
  	/* now set up the actual cases */
--- 702,708 ----
      va_end(args);
  
      if (!(ecases = (struct exp_case *)
! 	ckalloc((1+i)*sizeof(struct exp_case))))
  	sysreturn(ENOMEM);
  
  	/* now set up the actual cases */
***************
*** 687,696 ****
      for (ec=ecases;ec->type != exp_end;ec++) {
  	/* free only if regexp and we compiled it for user */
  	if (ec->type == exp_regexp) {
! 	    free((char *)ec->re);
  	}
      }
!     free((char *)ecases);
      return(i);
  }
  
--- 724,733 ----
      for (ec=ecases;ec->type != exp_end;ec++) {
  	/* free only if regexp and we compiled it for user */
  	if (ec->type == exp_regexp) {
! 	    ckfree((char *)ec->re);
  	}
      }
!     ckfree((char *)ecases);
      return(i);
  }
  
***************
*** 774,780 ****
      static int bufsize = 0;
  
      if (buf == NULL) {
! 	buf = malloc(4000);
  	bufsize = 4000;
      }
  
--- 811,817 ----
      static int bufsize = 0;
  
      if (buf == NULL) {
! 	buf = ckalloc(4000);
  	bufsize = 4000;
      }
  
***************
*** 784,790 ****
      do {
  	len = _vsnprintf(buf, bufsize, fmt, args);
  	if (len == -1) {
! 	    realloc(buf, bufsize * 2);
  	    if (buf == NULL) {
  		return -1;
  	    }
--- 821,827 ----
      do {
  	len = _vsnprintf(buf, bufsize, fmt, args);
  	if (len == -1) {
! 	    ckrealloc(buf, bufsize * 2);
  	    if (buf == NULL) {
  		return -1;
  	    }
***************
*** 1081,1083 ****
--- 1118,1137 ----
  	return(0);
  }
  #endif /* !__WIN32__ */
+ 
+ void
+ exp_setdebug(file, mode)
+     char *file;
+     int mode;
+ {
+     char buf[1024];
+     if (file) {
+ 	sprintf(buf, "exp_internal -f %s %d", file, mode);
+     } else {
+ 	sprintf(buf, "exp_internal %d", mode);
+     }
+     if (!expectlib_init()) {
+ 	return;
+     }
+     Tcl_Eval(InterpPtr, buf);
+ }
Index: expect-5.21/generic/expect.h
diff -c expect-5.21/generic/expect.h:1.3 expect-5.21/generic/expect.h:1.4
*** expect-5.21/generic/expect.h:1.3	Tue Aug 12 23:40:43 1997
--- expect-5.21/generic/expect.h	Sat Dec  6 01:18:59 1997
***************
*** 152,155 ****
  EXTERN void exp_perror		_ANSI_ARGS_((char *string));
  EXTERN char *exp_strerror	_ANSI_ARGS_((void));
  EXTERN int exp_setblocking	_ANSI_ARGS_((ExpHandle handle, int mode));
! 
--- 152,155 ----
  EXTERN void exp_perror		_ANSI_ARGS_((char *string));
  EXTERN char *exp_strerror	_ANSI_ARGS_((void));
  EXTERN int exp_setblocking	_ANSI_ARGS_((ExpHandle handle, int mode));
! EXTERN void exp_setdebug	_ANSI_ARGS_((char *file, int mode));
Index: expect-5.21/win/etest.tcl
diff -c expect-5.21/win/etest.tcl:1.14 expect-5.21/win/etest.tcl:1.15
*** expect-5.21/win/etest.tcl:1.14	Mon Nov 24 01:52:09 1997
--- expect-5.21/win/etest.tcl	Thu Dec 18 06:39:06 1997
***************
*** 372,377 ****
--- 372,382 ----
      vwait x
  }
  
+ proc crashtest {} {
+     set exp_nt_debug 1
+     spawn testcrash.exe
+ }
+ 
  #zeego
  
  #spawn d:/mksnt/mksnt/ls.exe
Index: expect-5.21/win/expWin.h
diff -c expect-5.21/win/expWin.h:1.3 expect-5.21/win/expWin.h:1.4
*** expect-5.21/win/expWin.h:1.3	Tue Sep  9 12:20:40 1997
--- expect-5.21/win/expWin.h	Thu Dec 18 06:41:07 1997
***************
*** 25,31 ****
  #define EXP_KILL_CTRL_BREAK 0x4
  
  #define EXP_LOG(format, args) \
!     ExpSyslog("Expect SlaveDriver %s: %d" format, __FILE__, __LINE__, args)
  
  /*
   * The following defines identify the various types of applications that 
--- 25,31 ----
  #define EXP_KILL_CTRL_BREAK 0x4
  
  #define EXP_LOG(format, args) \
!     ExpSyslog("Expect SlaveDriver (%s: %d): " format, __FILE__, __LINE__, args)
  
  /*
   * The following defines identify the various types of applications that 
Index: expect-5.21/win/expWinCommand.c
diff -c expect-5.21/win/expWinCommand.c:1.7 expect-5.21/win/expWinCommand.c:1.9
*** expect-5.21/win/expWinCommand.c:1.7	Tue Sep  9 12:21:04 1997
--- expect-5.21/win/expWinCommand.c	Thu Dec 18 06:41:58 1997
***************
*** 187,192 ****
--- 187,193 ----
      int leaveopen = 0;
      char *val;
      int hide;
+     int debug;
      char **nargv = NULL;
      int i, j;
      int usePipes = 0;
***************
*** 312,317 ****
--- 313,319 ----
  				PIPE_TYPE_BYTE | PIPE_WAIT, 1, 8192, 8192,
  				20000, NULL);
      if (hSlaveDrv == NULL) {
+ 	debuglog("CreateNamedPipe failed: error=0x%08x\r\n", GetLastError());
  	TclWinConvertError(GetLastError());
  	exp_error(interp, "unable to create named pipe: %s",
  		  Tcl_PosixError(interp));
***************
*** 320,336 ****
  
      hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
      if (hEvent == NULL) {
  	TclWinConvertError(GetLastError());
  	exp_error(interp, "unable to create event: %s",
  		  Tcl_PosixError(interp));
  	goto end;
      }
  
      nargv = (char **) ckalloc(sizeof(char *) * (argc+4));
      nargv[0] = execPath;
      nargv[1] = pipeName;
      nargv[2] = usePipes ? "1" : "0";
!     j = 3;
      if (imagePath[0]) {
  	nargv[j++] = imagePath;
      }
--- 322,351 ----
  
      hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
      if (hEvent == NULL) {
+ 	debuglog("CreateEvent failed: error=0x%08x\r\n", GetLastError());
  	TclWinConvertError(GetLastError());
  	exp_error(interp, "unable to create event: %s",
  		  Tcl_PosixError(interp));
  	goto end;
      }
  
+     val = exp_get_var(interp, "exp_nt_debug");
+     if (val) {
+ 	if (! Tcl_GetBoolean(interp, val, &debug) == TCL_OK) {
+ 	    Tcl_ResetResult(interp);
+ 	    debug = 1;
+ 	}
+     } else {
+ 	debug = 0;
+     }
+ 
      nargv = (char **) ckalloc(sizeof(char *) * (argc+4));
      nargv[0] = execPath;
      nargv[1] = pipeName;
      nargv[2] = usePipes ? "1" : "0";
!     nargv[3] = debug    ? "1" : "0";
! 
!     j = 4;
      if (imagePath[0]) {
  	nargv[j++] = imagePath;
      }
***************
*** 350,367 ****
      slaveDrvPid = 0;
      globalPid = 1;
  #else
-     val = exp_get_var(interp, "exp_nt_debug");
-     if (val) {
- 	if (Tcl_GetBoolean(interp, val, &hide) == TCL_OK) {
- 	    hide = !hide;
- 	} else {
- 	    Tcl_ResetResult(interp);
- 	    hide = 0;
- 	}
-     } else {
- 	hide = 1;
-     }
  
      dwRet = ExpCreateProcess(argc, nargv, NULL, NULL, NULL,
  			     TRUE, hide, FALSE, FALSE,
  			     &slaveDrvPid, &globalPid);
--- 365,372 ----
      slaveDrvPid = 0;
      globalPid = 1;
  #else
  
+     hide = !debug;
      dwRet = ExpCreateProcess(argc, nargv, NULL, NULL, NULL,
  			     TRUE, hide, FALSE, FALSE,
  			     &slaveDrvPid, &globalPid);
Index: expect-5.21/win/expWinSlave.h
diff -c expect-5.21/win/expWinSlave.h:1.7 expect-5.21/win/expWinSlave.h:1.8
*** expect-5.21/win/expWinSlave.h:1.7	Wed Sep 10 02:03:33 1997
--- expect-5.21/win/expWinSlave.h	Thu Dec 18 06:42:45 1997
***************
*** 48,53 ****
--- 48,54 ----
  extern EXP_KEY ExpAsciiToKeyArray[];
  extern DWORD   ExpConsoleInputMode;
  extern HANDLE  ExpConsoleOut;
+ extern int     ExpDebug;
  
  extern void			ExpAddToWaitQueue(HANDLE handle);
  extern void			ExpKillProcessList();
Index: expect-5.21/win/expWinSlaveDbg.c
diff -c expect-5.21/win/expWinSlaveDbg.c:1.17 expect-5.21/win/expWinSlaveDbg.c:1.18
*** expect-5.21/win/expWinSlaveDbg.c:1.17	Thu Nov 13 04:11:49 1997
--- expect-5.21/win/expWinSlaveDbg.c	Thu Dec 18 06:44:13 1997
***************
*** 27,32 ****
--- 27,33 ----
   */
  
  #include <windows.h>
+ #include <imagehlp.h>
  #include <stddef.h>
  #include "tclInt.h"
  #include "tclPort.h"
***************
*** 120,126 ****
      struct _ExpBreakpoint *nextPtr;
  };
  
! #define EXP_CREATEPROCESS_MEM_OFFSET 4096
  #define PAGESIZE 0x1000
  #define PAGEMASK (PAGESIZE-1)
  
--- 121,134 ----
      struct _ExpBreakpoint *nextPtr;
  };
  
! typedef struct {
!     BOOL loaded;
!     HANDLE hFile;
!     LPVOID baseAddr;
!     PCHAR  modName;
!     PIMAGE_DEBUG_INFORMATION dbgInfo;
! } ExpModule;
! 
  #define PAGESIZE 0x1000
  #define PAGEMASK (PAGESIZE-1)
  
***************
*** 145,150 ****
--- 153,160 ----
      BYTE  pMemoryCache[PAGESIZE]; /* Subprocess memory cache */
      OVERLAPPED overlapped;	/* Overlapped structure for writing to master */
      Tcl_HashTable *funcTable;	/* Function table name to address mapping */
+     Tcl_HashTable *moduleTable;	/* Win32 modules that have been loaded */
+     ExpModule *exeModule;	/* Executable module info */
      struct _ExpProcess *nextPtr;
  };
  
***************
*** 156,161 ****
--- 166,173 ----
  static COORD CursorPosition;
  static BOOL CursorKnown = FALSE;
  
+ static char *SymbolPath;
+ 
  /*
   * Static functions in this file:
   */
***************
*** 218,225 ****
  static void		OnXCreateThread(ExpProcess *, LPDEBUG_EVENT);
  static void		OnXDeleteThread(ExpProcess *, LPDEBUG_EVENT);
  static void		OnXFirstBreakpoint(ExpProcess *, LPDEBUG_EVENT);
- static void		OnXSecondBreakpoint(ExpProcess *, LPDEBUG_EVENT);
  static void		OnXLoadDll(ExpProcess *, LPDEBUG_EVENT);
  static void		OnXSingleStep(ExpProcess *, LPDEBUG_EVENT);
  
  #ifndef UNICODE
--- 230,239 ----
  static void		OnXCreateThread(ExpProcess *, LPDEBUG_EVENT);
  static void		OnXDeleteThread(ExpProcess *, LPDEBUG_EVENT);
  static void		OnXFirstBreakpoint(ExpProcess *, LPDEBUG_EVENT);
  static void		OnXLoadDll(ExpProcess *, LPDEBUG_EVENT);
+ static void		OnXUnloadDll(ExpProcess *, LPDEBUG_EVENT);
+ static void		OnXSecondBreakpoint(ExpProcess *, LPDEBUG_EVENT);
+ static void		OnXSecondChanceException(ExpProcess *,LPDEBUG_EVENT);
  static void		OnXSingleStep(ExpProcess *, LPDEBUG_EVENT);
  
  #ifndef UNICODE
***************
*** 285,290 ****
--- 299,307 ----
      ZeroMemory(&proc->overlapped, sizeof(OVERLAPPED));
      proc->funcTable = malloc(sizeof(Tcl_HashTable));
      Tcl_InitHashTable(proc->funcTable, TCL_STRING_KEYS);
+     proc->moduleTable = malloc(sizeof(Tcl_HashTable));
+     Tcl_InitHashTable(proc->moduleTable, TCL_ONE_WORD_KEYS);
+     proc->exeModule = NULL;
      proc->nextPtr = ProcessList;
      ProcessList = proc;
      return proc;
***************
*** 323,328 ****
--- 340,347 ----
      }
      Tcl_DeleteHashTable(proc->funcTable);
      free(proc->funcTable);
+     Tcl_DeleteHashTable(proc->moduleTable);
+     free(proc->moduleTable);
  
      for (pprev = NULL, pcurr = ProcessList; pcurr != NULL;
  	 pcurr = pcurr->nextPtr)
***************
*** 390,395 ****
--- 409,415 ----
  ExpSlaveDebugThread(LPVOID *lparg)
  {
      ExpSlaveDebugArg *arg = (ExpSlaveDebugArg *) lparg;
+     ExpProcess *proc;
  
      HMaster = arg->out;		/* Set the master program */
  
***************
*** 414,444 ****
  	ExitThread(0);
      }
  
!     if (! arg->passThrough) {
! 	ExpProcess *proc;
  
!         ExpAddToWaitQueue(arg->process);
  
! 	proc = ExpProcessNew();
  	proc->hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, arg->globalPid);
- 	proc->hPid = arg->globalPid;
  
  	if (proc->hProcess == NULL) {
! 	  arg->lastError = GetLastError();
! 	  ExitThread(0);
  	}
  
  	proc->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  
  	ExpCommonDebugger();
      } else {
  	ExpProcess *proc;
- 	ExpAddToWaitQueue(arg->process);
  
  	proc = ExpProcessNew();
  	proc->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  	proc->hProcess = arg->process;
! 	proc->hPid = arg->globalPid;
      }
  
      return 0;			/* Never executes */
--- 434,463 ----
  	ExitThread(0);
      }
  
!     proc = ExpProcessNew();
!     proc->hPid = arg->globalPid;
  
!     if (! arg->passThrough) {
  
! 	CloseHandle(arg->process);
  	proc->hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, arg->globalPid);
  
  	if (proc->hProcess == NULL) {
! 	    arg->lastError = GetLastError();
! 	    ExitThread(0);
  	}
+ 	ExpAddToWaitQueue(proc->hProcess);
  
  	proc->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  
  	ExpCommonDebugger();
      } else {
  	ExpProcess *proc;
  
  	proc = ExpProcessNew();
  	proc->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  	proc->hProcess = arg->process;
! 	ExpAddToWaitQueue(proc->hProcess);
      }
  
      return 0;			/* Never executes */
***************
*** 467,472 ****
--- 486,507 ----
      DWORD dwContinueStatus;	/* exception continuation */
      DWORD err;
      ExpProcess *proc;
+     DWORD n, i;
+ 
+     n = GetEnvironmentVariable("Path", NULL, 0);
+     n += GetEnvironmentVariable("_NT_SYMBOL_PATH", NULL, 0) + 1;
+     n += GetEnvironmentVariable("_NT_ALT_SYMBOL_PATH", NULL, 0) + 1;
+     n += GetEnvironmentVariable("SystemRoot", NULL, 0) + 1;
+ 
+     SymbolPath = malloc(n);
+ 
+     i = GetEnvironmentVariable("Path", SymbolPath, n);
+     SymbolPath[i++] = ';';
+     i += GetEnvironmentVariable("_NT_SYMBOL_PATH", &SymbolPath[i], n-i);
+     SymbolPath[i++] = ';';
+     i += GetEnvironmentVariable("_NT_ALT_SYMBOL_PATH", &SymbolPath[i], n-i);
+     SymbolPath[i++] = ';';
+     i += GetEnvironmentVariable("SystemRoot", &SymbolPath[i], n-i);
  
      for(;;) {
  	dwContinueStatus = DBG_CONTINUE;
***************
*** 544,551 ****
  	    case EXCEPTION_DATATYPE_MISALIGNMENT:
  	    case EXCEPTION_ACCESS_VIOLATION:
  	    default:
  		dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
- 
  	    }
  	    break;
  
--- 579,592 ----
  	    case EXCEPTION_DATATYPE_MISALIGNMENT:
  	    case EXCEPTION_ACCESS_VIOLATION:
  	    default:
+ 		/*
+ 		 * An exception was hit and it was not handled by the program.
+ 		 * Now it is time to get a backtrace.
+ 		 */
+ 		if (! debEvent.u.Exception.dwFirstChance) {
+ 		    OnXSecondChanceException(proc, &debEvent);
+ 		}
  		dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
  	    }
  	    break;
  
***************
*** 574,580 ****
--- 615,623 ----
  
  	case EXIT_PROCESS_DEBUG_EVENT:
  	    CloseHandle(proc->overlapped.hEvent);
+ #if 0 /* This gets closed in WaitQueueThread */
  	    CloseHandle(proc->hProcess);
+ #endif
  	    err = debEvent.u.ExitProcess.dwExitCode;
  	    ExpProcessFree(proc);
  	    /*
***************
*** 590,602 ****
  	    break;
  
  	case UNLOAD_DLL_DEBUG_EVENT:
! #if 0
! 	    fprintf(stderr, "0x%08x: Unloading\n", debEvent.u.UnloadDll.lpBaseOfDll);
! #endif
! 	    /*
! 	     * Display a message that the DLL has
! 	     * been unloaded.
! 	     */
  	    break;
  
  	case OUTPUT_DEBUG_STRING_EVENT:
--- 633,639 ----
  	    break;
  
  	case UNLOAD_DLL_DEBUG_EVENT:
! 	    OnXUnloadDll(proc, &debEvent);
  	    break;
  
  	case OUTPUT_DEBUG_STRING_EVENT:
***************
*** 613,618 ****
--- 650,736 ----
  /*
   *----------------------------------------------------------------------
   *
+  * LoadedModule --
+  *
+  *	A module with the specifed name was loaded.  Add it to our
+  *	list of loaded modules and print any debugging information
+  *	if debugging is enabled.
+  *
+  * Results:
+  *	If the module is known, return TRUE.  Otherwise, return FALSE
+  *
+  *----------------------------------------------------------------------
+  */
+ 
+ static int
+ LoadedModule(ExpProcess *proc, HANDLE hFile, LPVOID modname, int isUnicode,
+     LPVOID baseAddr, DWORD debugOffset)
+ {
+ #undef PRINTF
+ #if 0
+ #define PRINTF(x) printf x
+ #else
+ #define PRINTF(x)
+ #endif
+     int known = 1;
+     PVOID ptr;
+     Tcl_HashEntry *tclEntry;
+     int isNew;
+     char mbstr[512];
+     char *s = NULL;
+     ExpModule *modPtr;
+ 
+     if (modname) {
+ 
+ 	/*
+ 	 * This modname is a pointer to the name of the
+ 	 * DLL in the process space of the subprocess
+ 	 */
+ 	if (ReadSubprocessMemory(proc, modname, &ptr, sizeof(PVOID)) && ptr) {
+ 	    if (isUnicode) {
+ 		WCHAR name[512];
+ 		ReadSubprocessStringW(proc, ptr, name, 512);
+ 		PRINTF(("0x%08x: Loaded %S\n", baseAddr, name));
+ 		wcstombs(mbstr, name, sizeof(mbstr));
+ 	    } else {
+ 		ReadSubprocessStringA(proc, ptr, mbstr, sizeof(mbstr));
+ 		PRINTF(("0x%08x: Loaded %s\n", baseAddr, mbstr));
+ 	    }
+ 	    s = strdup(mbstr);
+ 
+ 	} else {
+ 	    PRINTF(("0x%08x: Loaded module, but couldn't read subprocess' memory\n", baseAddr));
+ 	    known = 0;
+ 	}
+ 	if (debugOffset) {
+ 	    PRINTF((" with debugging info at offset 0x%08x\n",
+ 		   debugOffset));
+ 	}
+     } else {
+ 	PRINTF(("0x%08x: Loaded module with no known name\n", baseAddr));
+     }
+     tclEntry = Tcl_CreateHashEntry(proc->moduleTable, baseAddr, &isNew);
+ 
+     modPtr = (ExpModule *) malloc(sizeof(ExpModule));
+     modPtr->loaded = FALSE;
+     modPtr->hFile = hFile;
+     modPtr->baseAddr = baseAddr;
+     modPtr->modName = s;
+     modPtr->dbgInfo = NULL;
+     if (proc->exeModule == NULL) {
+ 	proc->exeModule = modPtr;
+     }
+ 
+     Tcl_SetHashValue(tclEntry, modPtr);
+ 
+     return known;
+ #undef PRINTF
+ }
+ 
+ 
+ /*
+  *----------------------------------------------------------------------
+  *
   * OnXCreateProcess --
   *
   *	This routine is called when a CREATE_PROCESS_DEBUG_EVENT
***************
*** 627,648 ****
  static void
  OnXCreateProcess(ExpProcess *proc, LPDEBUG_EVENT pDebEvent)
  {
      ExpThreadInfo *threadInfo;
  
      if (proc == NULL) {
  	proc = ExpProcessNew();
  	proc->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  	
! 	DuplicateHandle(GetCurrentProcess(),
! 			pDebEvent->u.CreateProcessInfo.hProcess,
! 			GetCurrentProcess(),
! 			&proc->hProcess, SYNCHRONIZE|PROCESS_ALL_ACCESS,
! 			FALSE, 0);
  	proc->hPid = pDebEvent->dwProcessId;
  
  	ExpAddToWaitQueue(proc->hProcess);
      }
  
      /*
       * As needed, examine or change the registers of the
       * process's initial thread with the GetThreadContext and
--- 745,773 ----
  static void
  OnXCreateProcess(ExpProcess *proc, LPDEBUG_EVENT pDebEvent)
  {
+     int unknown;
      ExpThreadInfo *threadInfo;
+     CREATE_PROCESS_DEBUG_INFO *info = &pDebEvent->u.CreateProcessInfo;
  
      if (proc == NULL) {
  	proc = ExpProcessNew();
  	proc->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  	
! 	if (!DuplicateHandle(GetCurrentProcess(),
! 			     info->hProcess,
! 			     GetCurrentProcess(),
! 			     &proc->hProcess, PROCESS_ALL_ACCESS,
! 			     FALSE, 0)) {
! 	    fprintf(stderr, "Unable to duplicate handle\n");
! 	}
  	proc->hPid = pDebEvent->dwProcessId;
  
  	ExpAddToWaitQueue(proc->hProcess);
      }
  
+     unknown = !LoadedModule(proc, info->hFile, info->lpImageName,
+ 	info->fUnicode, info->lpBaseOfImage, info->dwDebugInfoFileOffset);
+ 
      /*
       * As needed, examine or change the registers of the
       * process's initial thread with the GetThreadContext and
***************
*** 655,672 ****
  
      threadInfo = (ExpThreadInfo *) malloc(sizeof(ExpThreadInfo));
      threadInfo->dwThreadId = pDebEvent->dwThreadId;
!     threadInfo->hThread = pDebEvent->u.CreateProcessInfo.hThread;
      threadInfo->nextPtr = proc->threadList;
      proc->threadCount++;
      proc->threadList = threadInfo;
-     
-     /*
-      * We don't use the file handle, so close it.
-      */
-     if (pDebEvent->u.CreateProcessInfo.hFile != NULL) {
- 	CloseHandle(pDebEvent->u.CreateProcessInfo.hFile);
-     }
-     CloseHandle(pDebEvent->u.CreateProcessInfo.hProcess);
  }
  
  /*
--- 780,789 ----
  
      threadInfo = (ExpThreadInfo *) malloc(sizeof(ExpThreadInfo));
      threadInfo->dwThreadId = pDebEvent->dwThreadId;
!     threadInfo->hThread = info->hThread;
      threadInfo->nextPtr = proc->threadList;
      proc->threadCount++;
      proc->threadList = threadInfo;
  }
  
  /*
***************
*** 928,933 ****
--- 1045,1051 ----
  #if 0
      fprintf(stderr, "OnXBreakpoint: proc=0x%x, count=%d, addr=0x%08x\n", proc, proc->nBreakCount, exceptInfo->ExceptionRecord.ExceptionAddress);
  #endif
+ 
      pbrkpt = NULL;
      for (brkpt = proc->brkptList; brkpt != NULL;
  	 pbrkpt = brkpt, brkpt = brkpt->nextPtr) {
***************
*** 949,954 ****
--- 1067,1073 ----
  
      context.ContextFlags = CONTEXT_FULL;
      GetThreadContext(tinfo->hThread, &context);
+ 
      if (! brkpt->returning) {
  	ExpBreakpoint *bpt;
  	/*
***************
*** 1021,1026 ****
--- 1140,1307 ----
  /*
   *----------------------------------------------------------------------
   *
+  * OnXSecondChanceException --
+  *
+  *	Handle a second chance exception
+  *
+  *----------------------------------------------------------------------
+  */
+ 
+ static void
+ OnXSecondChanceException(ExpProcess *proc,  LPDEBUG_EVENT pDebEvent)
+ {
+     BOOL b;
+     STACKFRAME frame;
+     CONTEXT context;
+     ExpThreadInfo *tinfo;
+     Tcl_HashEntry *tclEntry;
+     Tcl_HashSearch tclSearch;
+     ExpModule *modPtr;
+     DWORD displacement;
+     BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512];
+     PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
+     char *s;
+ 
+     if (!ExpDebug) {
+ 	return;
+     }
+ 
+     for (tinfo = proc->threadList; tinfo != NULL; tinfo = tinfo->nextPtr) {
+ 	if (pDebEvent->dwThreadId == tinfo->dwThreadId) {
+ 	    break;
+ 	}
+     }
+     assert(tinfo != NULL);
+ 
+     context.ContextFlags = CONTEXT_FULL;
+     GetThreadContext(tinfo->hThread, &context);
+ 
+     /*
+      * XXX: From what I can tell, SymInitialize is broken on Windows NT 4.0
+      * if you try to have it iterate the modules in a process.  It always
+      * returns an object mismatch error.  Instead, initialize without iterating
+      * the modules.  Contrary to what MSDN documentation says,
+      * Microsoft debuggers do not exclusively use the imagehlp API.  In
+      * fact, the only thing VC 5.0 uses is the StackWalk function.
+      * Windbg uses a few more functions, but it doesn't use SymInitialize.
+      * We will then do the hard work of finding all the
+      * modules and doing the right thing.
+      */
+ 
+     if (! SymInitialize(proc->hProcess, SymbolPath, FALSE)){
+ 	fprintf(stderr, "Unable to get backtrace (Debug 1): 0x%08x\n",
+ 	    GetLastError());
+ 	goto error;
+     }
+ 
+ #ifdef _X86_
+     memset(&frame, 0, sizeof(frame));
+     frame.AddrPC.Mode = AddrModeFlat;
+     frame.AddrPC.Segment = 0;
+     frame.AddrPC.Offset = context.Eip;
+ 
+     frame.AddrReturn.Mode = AddrModeFlat;
+     frame.AddrReturn.Segment = 0;
+     frame.AddrReturn.Offset = context.Ebp; /* I think this is correct */
+ 
+     frame.AddrFrame.Mode = AddrModeFlat;
+     frame.AddrFrame.Segment = 0;
+     frame.AddrFrame.Offset = context.Ebp;
+ 
+     frame.AddrStack.Mode = AddrModeFlat;
+     frame.AddrStack.Segment = 0;
+     frame.AddrStack.Offset = context.Esp;
+ 
+     frame.FuncTableEntry = NULL;
+     frame.Params[0] = context.Eax;
+     frame.Params[1] = context.Ecx;
+     frame.Params[2] = context.Edx;
+     frame.Params[3] = context.Ebx;
+     frame.Far = FALSE;
+     frame.Virtual = FALSE;
+     frame.Reserved[0] = 0;
+     frame.Reserved[1] = 0;
+     frame.Reserved[2] = 0;
+     /* frame.KdHelp.* is not set */
+ 
+     /*
+      * Iterate through the loaded modules and load symbols for each one.
+      */
+     tclEntry = Tcl_FirstHashEntry(proc->moduleTable, &tclSearch);
+     while (tclEntry) {
+ 	modPtr = (ExpModule *) Tcl_GetHashValue(tclEntry);
+ 	if (! modPtr->loaded) {
+ 	    modPtr->dbgInfo = MapDebugInformation(modPtr->hFile, NULL,
+ 		SymbolPath, (DWORD)modPtr->baseAddr);
+ 
+ 	    SymLoadModule(proc->hProcess, modPtr->hFile,
+ 		NULL, NULL, (DWORD) modPtr->baseAddr, 0);
+ 	    modPtr->loaded = TRUE;
+ 	}
+ 
+ 	tclEntry = Tcl_NextHashEntry(&tclSearch);
+     }
+ 
+ 
+     if (proc->exeModule && proc->exeModule->dbgInfo && 
+ 	proc->exeModule->dbgInfo->ImageFileName) {
+ 	s = proc->exeModule->dbgInfo->ImageFileName;
+     } else {
+ 	s = "";
+     }
+     fprintf(stderr, "Backtrace for %s\n", s);
+     fprintf(stderr, "-------------------------------------\n");
+     EXP_LOG("Backtrace for %s", s);
+     while (1) {
+         pSymbol->SizeOfStruct = sizeof(symbolBuffer);
+         pSymbol->MaxNameLength = 512;
+ 
+ 	b = StackWalk(IMAGE_FILE_MACHINE_I386, proc->hProcess,
+ 	    tinfo->hThread, &frame, &context, NULL,
+ 	    SymFunctionTableAccess, SymGetModuleBase,
+ 	    NULL);
+ 
+ 	if (b == FALSE || frame.AddrPC.Offset == 0) {
+ 	    break;
+ 	}
+ 	    
+         if (SymGetSymFromAddr(proc->hProcess, frame.AddrPC.Offset,
+ 	    &displacement, pSymbol) )
+         {
+ 	    DWORD base;
+ 	    char buf[1024];
+ 
+ 	    base = SymGetModuleBase(proc->hProcess, frame.AddrPC.Offset);
+ 	    tclEntry = Tcl_FindHashEntry(proc->moduleTable, (void *) base);
+ 	    modPtr = (ExpModule *) Tcl_GetHashValue(tclEntry);
+ 	    if (modPtr->dbgInfo && modPtr->dbgInfo->ImageFileName) {
+ 		s = modPtr->dbgInfo->ImageFileName;
+ 	    } else {
+ 		s = "";
+ 	    }
+             fprintf(stderr, "%.20s %08x\t%s+%X\n", s, frame.AddrPC.Offset,
+ 		pSymbol->Name, displacement);
+ 	    sprintf(buf, "%.20s %08x\t%s+%X", s, frame.AddrPC.Offset,
+ 		pSymbol->Name, displacement);
+ 	    EXP_LOG("%s", buf);
+ 	} else {
+ 	    fprintf(stderr, "%08x\n", frame.AddrPC.Offset);
+ 	    EXP_LOG("%08x\t", frame.AddrPC.Offset);
+ 	}
+     }
+ 
+ error:
+     if (ExpDebug) {
+ 	Sleep(10000);
+     }
+ #else
+ #  error "Unsupported architecture"	    
+ #endif
+ }
+ 
+ /*
+  *----------------------------------------------------------------------
+  *
   * OnXSingleStep --
   *
   *	This routine is called when a EXCEPTION_DEBUG_EVENT with
***************
*** 1059,1065 ****
   *----------------------------------------------------------------------
   */
  
! void
  OnXLoadDll(ExpProcess *proc, LPDEBUG_EVENT pDebEvent)
  {
      WORD w;
--- 1340,1346 ----
   *----------------------------------------------------------------------
   */
  
! static void
  OnXLoadDll(ExpProcess *proc, LPDEBUG_EVENT pDebEvent)
  {
      WORD w;
***************
*** 1081,1119 ****
      Tcl_HashEntry *tclEntry;
      int isNew;
  
! #if 0 /* Debugging purposes */
!     int unknown = 0;
! 
!     if (info->lpImageName) {
! 
! 	/*
! 	 * This info->lpImageName is a pointer to the name of the
! 	 * DLL in the process space of the subprocess
! 	 */
! 	if (ReadSubprocessMemory(proc, info->lpImageName, &ptr, sizeof(PVOID)) &&
! 	    ptr)
! 	{
! 	    if (info->fUnicode) {
! 		WCHAR name[256];
! 		ReadSubprocessStringW(proc, ptr, name, 256);
! 		printf("0x%08x: Loaded %S\n", info->lpBaseOfDll, name);
! 	    } else {
! 		CHAR name[256];
! 		ReadSubprocessStringA(proc, ptr, name, 256);
! 		printf("0x%08x: Loaded %S\n", info->lpBaseOfDll, name);
! 	    }
! 	} else {
! 	    printf("0x%08x: Loaded DLL, but couldn't read subprocess' memory\n", info->lpBaseOfDll);
! 	    unknown = 1;
! 	}
! 	if (info->dwDebugInfoFileOffset) {
! 	    printf(" with debugging info at offset 0x%08x\n",
! 		   info->dwDebugInfoFileOffset);
! 	}
!     } else {
! 	printf("0x%08x: Loaded DLL with no known name\n", info->lpBaseOfDll);
!     }
! #endif
  
      base = (DWORD) info->lpBaseOfDll;
  
--- 1362,1370 ----
      Tcl_HashEntry *tclEntry;
      int isNew;
  
!     int unknown = !LoadedModule(proc, info->hFile,
! 	info->lpImageName, info->fUnicode,
! 	info->lpBaseOfDll, info->dwDebugInfoFileOffset);
  
      base = (DWORD) info->lpBaseOfDll;
  
***************
*** 1190,1196 ****
  #endif
  
      if (stricmp(dllname, "kernel32.dll") != 0) {
- 	CloseHandle(info->hFile);
  	return;
      }
  
--- 1441,1446 ----
***************
*** 1227,1233 ****
      p += w;
  
      psh = (PIMAGE_SECTION_HEADER) p;
!     CloseHandle(info->hFile);
  }
  
  /*
--- 1477,1531 ----
      p += w;
  
      psh = (PIMAGE_SECTION_HEADER) p;
! }
! 
! /*
!  *----------------------------------------------------------------------
!  *
!  * OnXUnloadDll --
!  *
!  *	This routine is called when a UNLOAD_DLL_DEBUG_EVENT is seen
!  *
!  * Results:
!  *	None
!  *
!  * Side Effects:
!  *	Some information is printed
!  *
!  *----------------------------------------------------------------------
!  */
! 
! static void
! OnXUnloadDll(ExpProcess *proc, LPDEBUG_EVENT pDebEvent)
! {
!     Tcl_HashEntry *tclEntry;
!     ExpModule *modPtr;
! 
!     /*
!      * Display a message that the DLL has
!      * been unloaded.
!      */
! #if 0
!     fprintf(stderr, "0x%08x: Unloading\n", pDebEvent->u.UnloadDll.lpBaseOfDll);
! #endif
!     
!     tclEntry = Tcl_FindHashEntry(proc->moduleTable,
! 	pDebEvent->u.UnloadDll.lpBaseOfDll);
! 
!     if (tclEntry != NULL) {
! 	modPtr = (ExpModule *) Tcl_GetHashValue(tclEntry);
! 	if (modPtr->hFile) {
! 	    CloseHandle(modPtr->hFile);
! 	}
! 	if (modPtr->modName) {
! 	    free(modPtr->modName);
! 	}
! 	if (modPtr->dbgInfo) {
! 	    UnmapDebugInformation(modPtr->dbgInfo);
! 	}
! 	free(modPtr);
! 	Tcl_DeleteHashEntry(tclEntry);
!     }
  }
  
  /*
Index: expect-5.21/win/expWinSlaveDrv.c
diff -c expect-5.21/win/expWinSlaveDrv.c:1.12 expect-5.21/win/expWinSlaveDrv.c:1.13
*** expect-5.21/win/expWinSlaveDrv.c:1.12	Thu Nov 13 04:12:36 1997
--- expect-5.21/win/expWinSlaveDrv.c	Thu Dec 18 06:43:12 1997
***************
*** 79,84 ****
--- 79,85 ----
  
  static HANDLE hMutex;
  HANDLE ExpConsoleOut;
+ int    ExpDebug;
  
  #define EXP_MAX_QLEN 200
  HANDLE ExpWaitEvent;		/* Set after modifying wait queue */
***************
*** 168,173 ****
--- 169,177 ----
      if (argv[2][0] == '1') {
  	passThrough = 1;
      }
+     if (argv[3][0] == '1') {
+ 	ExpDebug = 1;
+     }
      
      hMaster = CreateFile(argv[1], GENERIC_READ | GENERIC_WRITE,
  			 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
***************
*** 218,225 ****
  
      debugInfo.passThrough = passThrough;
      debugInfo.out = hMaster;
!     debugInfo.argc = argc-3;
!     debugInfo.argv = &argv[3];
      debugInfo.slaveStdin = NULL;
      debugInfo.slaveStdout = hSlaveOutW;
      debugInfo.slaveStderr = hSlaveOutW;
--- 222,229 ----
  
      debugInfo.passThrough = passThrough;
      debugInfo.out = hMaster;
!     debugInfo.argc = argc-4;
!     debugInfo.argv = &argv[4];
      debugInfo.slaveStdin = NULL;
      debugInfo.slaveStdout = hSlaveOutW;
      debugInfo.slaveStderr = hSlaveOutW;
***************
*** 336,346 ****
  
  	    /*
  	     * Code to echo characters back.  When echoing, convert '\r'
! 	     * characters into '\n'.  This should first determine
! 	     * what mode the slave process' console is in for echoing,
! 	     * but that requires making this process a debugger process.
! 	     * XXX: Later on, that is a likely course of action.  For now,
! 	     * don't even think about it.
  	     */
  
  	    if (ExpConsoleInputMode & ENABLE_ECHO_INPUT) {
--- 340,350 ----
  
  	    /*
  	     * Code to echo characters back.  When echoing, convert '\r'
! 	     * characters into '\n'.  This first determines what mode the
! 	     * slave process' console is in for echoing.  Still, it isn't
! 	     * perfect.  The mode can be changed between the time the
! 	     * characters come through here and the time the characters
! 	     * are actually read in by the process.
  	     */
  
  	    if (ExpConsoleInputMode & ENABLE_ECHO_INPUT) {
***************
*** 739,751 ****
      DWORD i, j;
      DWORD nWritten;
      DWORD pos;
- 
-     /*
-      * XXX: MAX_RECORDS used to be 1000, but I was seeing a missing character
-      * when running telnet.  Even running from the command line, I could
-      * occasionally see it.  This is an attempted workaround since it doesn't
-      * seem to show up when writing a character at a time.
-      */
  
  #define MAX_RECORDS 1000
      for (pos = 0, i = 0; i < n; i++) {
--- 743,748 ----
Index: expect-5.21/win/expWinTty.c
diff -c expect-5.21/win/expWinTty.c:1.8 expect-5.21/win/expWinTty.c:1.9
*** expect-5.21/win/expWinTty.c:1.8	Sun Oct 19 03:04:53 1997
--- expect-5.21/win/expWinTty.c	Mon Nov 24 12:09:00 1997
***************
*** 25,30 ****
--- 25,31 ----
  int exp_stdin_is_tty;
  int exp_stdout_is_tty;
  
+ static int consoleInitialized = FALSE;
  static DWORD originalConsoleOutMode;
  static DWORD originalConsoleInMode;
  static DWORD consoleOutMode;
***************
*** 35,46 ****
--- 36,53 ----
  int
  exp_israw()
  {
+     if (! consoleInitialized) {
+ 	return 0;
+     }
      return !(consoleOutMode & ENABLE_PROCESSED_OUTPUT);
  }
  
  int
  exp_isecho()
  {
+     if (! consoleInitialized) {
+ 	return 1;
+     }
      return consoleInMode & ENABLE_ECHO_INPUT;
  }
  
***************
*** 65,81 ****
  exp_tty_raw(set)
      int set;
  {
      if (set == 1) {
! 	if (consoleOutMode & ENABLE_PROCESSED_OUTPUT) {
! 	    consoleOutMode &= ~ENABLE_PROCESSED_OUTPUT;
! 	    SetConsoleMode(hConsoleOut, consoleOutMode);
! 	}
      } else {
! 	if (! (consoleOutMode & ENABLE_PROCESSED_OUTPUT)) {
! 	    consoleOutMode |= ENABLE_PROCESSED_OUTPUT;
! 	    SetConsoleMode(hConsoleOut, consoleOutMode);
! 	}
      }
  }
  
  /*
--- 72,86 ----
  exp_tty_raw(set)
      int set;
  {
+     if (! consoleInitialized) {
+ 	return;
+     }
      if (set == 1) {
! 	consoleOutMode &= ~ENABLE_PROCESSED_OUTPUT;
      } else {
! 	consoleOutMode |= ENABLE_PROCESSED_OUTPUT;
      }
+     SetConsoleMode(hConsoleOut, consoleOutMode);
  }
  
  /*
***************
*** 99,115 ****
  exp_tty_echo(set)
      int set;
  {
      if (set == 1) {
! 	if (! (consoleInMode & ENABLE_ECHO_INPUT)) {
! 	    consoleInMode |= ENABLE_ECHO_INPUT;
! 	    SetConsoleMode(hConsoleIn, consoleInMode);
! 	}
      } else {
! 	if (consoleInMode & ENABLE_ECHO_INPUT) {
! 	    consoleInMode &= ~ENABLE_ECHO_INPUT;
! 	    SetConsoleMode(hConsoleIn, consoleInMode);
! 	}
      }
  }
  
  /*
--- 104,118 ----
  exp_tty_echo(set)
      int set;
  {
+     if (! consoleInitialized) {
+ 	return;
+     }
      if (set == 1) {
! 	consoleInMode |= ENABLE_ECHO_INPUT;
      } else {
! 	consoleInMode &= ~ENABLE_ECHO_INPUT;
      }
+     SetConsoleMode(hConsoleIn, consoleInMode);
  }
  
  /*
***************
*** 427,439 ****
      /*
       * Get the original console modes
       */
!     GetConsoleMode(hOut, &originalConsoleOutMode);
      consoleOutMode = originalConsoleOutMode;
      hConsoleOut = hOut;
  
!     GetConsoleMode(hIn, &originalConsoleInMode);
      consoleInMode = originalConsoleInMode;
      hConsoleIn = hIn;
  }
  
  /*
--- 430,447 ----
      /*
       * Get the original console modes
       */
!     if (GetConsoleMode(hOut, &originalConsoleOutMode) == FALSE) {
! 	return;
!     }
      consoleOutMode = originalConsoleOutMode;
      hConsoleOut = hOut;
  
!     if (GetConsoleMode(hIn, &originalConsoleInMode) == FALSE) {
! 	return;
!     }
      consoleInMode = originalConsoleInMode;
      hConsoleIn = hIn;
+     consoleInitialized = TRUE;
  }
  
  /*
Index: expect-5.21/win/expectlib.def
diff -c expect-5.21/win/expectlib.def:1.1 expect-5.21/win/expectlib.def:1.2
*** expect-5.21/win/expectlib.def:1.1	Thu Aug 14 01:08:09 1997
--- expect-5.21/win/expectlib.def	Sat Dec  6 01:14:09 1997
***************
*** 22,27 ****
--- 22,28 ----
  	exp_perror
  	exp_strerror
  	exp_setblocking
+ 	exp_setdebug
  
  	; Global variables
  	exp_pid
Index: expect-5.21/win/makefile
diff -c expect-5.21/win/makefile:1.15 expect-5.21/win/makefile:1.16
*** expect-5.21/win/makefile:1.15	Thu Nov 13 04:14:30 1997
--- expect-5.21/win/makefile	Thu Dec 18 06:40:19 1997
***************
*** 74,87 ****
  TESTMODEM	= testmodem.exe
  TESTWSTATION	= testwstation.exe
  TESTWPROG	= testwprog.exe
  DEBUGGER	= debugger.exe
  LIBEXPECT	= expectlib52.lib
  LIBEXPECTDLL	= expectlib52.dll
  
  TEST_APPS = $(TESTCALC) $(TESTCONSOUT) $(TESTSIG) $(TESTCLIB) \
! 	$(TESTA2) $(TESTMODEM)
  TEST_OTHERS = $(TESTWSTATION) $(TESTWPROG)
  
  EXPECTOBJS = \
  	$(TMPDIR)\exp_main_exp.obj
  
--- 74,92 ----
  TESTMODEM	= testmodem.exe
  TESTWSTATION	= testwstation.exe
  TESTWPROG	= testwprog.exe
+ TESTCRASH	= testcrash.exe
  DEBUGGER	= debugger.exe
  LIBEXPECT	= expectlib52.lib
  LIBEXPECTDLL	= expectlib52.dll
  
  TEST_APPS = $(TESTCALC) $(TESTCONSOUT) $(TESTSIG) $(TESTCLIB) \
! 	$(TESTA2) $(TESTMODEM) $(TESTCRASH)
  TEST_OTHERS = $(TESTWSTATION) $(TESTWPROG)
  
+ TESTCRASH_OBJS = \
+ 	$(TMPDIR)\testcrash.obj \
+ 	$(TMPDIR)\msjexhnd.obj
+ 
  EXPECTOBJS = \
  	$(TMPDIR)\exp_main_exp.obj
  
***************
*** 351,356 ****
--- 356,366 ----
  	$(link32) $(ldebug) $(guilflags) $(guilibsdll) -out:$@ \
  		$(TMPDIR)\testwprog.obj 
  
+ $(TESTCRASH): $(TESTCRASH_OBJS)
+ 	set LIB=$(TOOLS32)\lib
+ 	$(link32) $(ldebug) $(conlflags) -out:$@ $(conlibsdll) \
+ 		$(guilibsdll) $(TESTCRASH_OBJS)
+ 
  $(TESTCLIB): $(WINDIR)\testclib.c
  	$(cc32) $(CON_CFLAGS) $(EXPECT_INCLUDES) -Fo$(TMPDIR)\ $?
  	set LIB=$(TOOLS32)\lib
***************
*** 360,366 ****
  $(SLAVEDRV): $(SLAVE_OBJS)
  	set LIB=$(TOOLS32)\lib
  	$(link32) $(ldebug) $(conlflags) -out:$@ $(conlibsdll) \
! 		$(guilibsdll) $(SLAVE_OBJS)
  #	$(link32) $(ldebug) $(conlflags) $(guilibs) -out:$@ $(SLAVE_OBJS)
  
  $(EXPECTDLL): $(EXP_DLL_OBJS) $(TMPDIR)\expect.def $(TMPDIR)\expect.res
--- 370,376 ----
  $(SLAVEDRV): $(SLAVE_OBJS)
  	set LIB=$(TOOLS32)\lib
  	$(link32) $(ldebug) $(conlflags) -out:$@ $(conlibsdll) \
! 		$(guilibsdll) imagehlp.lib $(SLAVE_OBJS)
  #	$(link32) $(ldebug) $(conlflags) $(guilibs) -out:$@ $(SLAVE_OBJS)
  
  $(EXPECTDLL): $(EXP_DLL_OBJS) $(TMPDIR)\expect.def $(TMPDIR)\expect.res
***************
*** 400,405 ****
--- 410,418 ----
  	echo exp_logfile_all >> $@
  	echo exp_loguser >> $@
  	echo exp_is_debugging >> $@
+ 
+ $(TMPDIR)\expWinSlaveDbgW.obj: $(WINDIR)\expWinSlaveDbg.c
+ 	$(cc32) $(EXPECT_CFLAGS) -DUNICODE -D_UNICODE -Fo$@ $?
  
  #
  # Implicit rules
Index: expect-5.21/win/msjexhnd.cpp
diff -c /dev/null expect-5.21/win/msjexhnd.cpp:1.1
*** /dev/null	Thu Dec 18 10:39:21 1997
--- expect-5.21/win/msjexhnd.cpp	Thu Dec 18 06:46:16 1997
***************
*** 0 ****
--- 1,456 ----
+ //==========================================
+ // Matt Pietrek
+ // Microsoft Systems Journal, May 1997
+ // FILE: MSJEXHND.CPP
+ //==========================================
+ #include <windows.h>
+ #include <tchar.h>
+ #include <imagehlp.h>
+ #include "msjexhnd.h"
+ 
+ //============================== Global Variables =============================
+ 
+ //
+ // Declare the static variables of the MSJExceptionHandler class
+ //
+ TCHAR MSJExceptionHandler::m_szLogFileName[MAX_PATH];
+ LPTOP_LEVEL_EXCEPTION_FILTER MSJExceptionHandler::m_previousFilter;
+ HANDLE MSJExceptionHandler::m_hReportFile;
+ 
+ MSJExceptionHandler::SYMINITIALIZEPROC MSJExceptionHandler::_SymInitialize = 0;
+ MSJExceptionHandler::SYMCLEANUPPROC MSJExceptionHandler::_SymCleanup = 0;
+ MSJExceptionHandler::STACKWALKPROC MSJExceptionHandler::_StackWalk = 0;
+ 
+ MSJExceptionHandler::SYMFUNCTIONTABLEACCESSPROC
+                                 MSJExceptionHandler::_SymFunctionTableAccess = 0;
+ 
+ MSJExceptionHandler::SYMGETMODULEBASEPROC
+                                       MSJExceptionHandler::_SymGetModuleBase = 0;
+ 
+ MSJExceptionHandler::SYMGETSYMFROMADDRPROC
+                                      MSJExceptionHandler::_SymGetSymFromAddr = 0;
+ 
+ MSJExceptionHandler g_MSJExceptionHandler;  // Declare global instance of class
+ 
+ //============================== Class Methods =============================
+ 
+ //=============
+ // Constructor 
+ //=============
+ MSJExceptionHandler::MSJExceptionHandler( )
+ {
+     // Install the unhandled exception filter function
+     m_previousFilter = SetUnhandledExceptionFilter(MSJUnhandledExceptionFilter);
+ 
+     // Figure out what the report file will be named, and store it away
+     GetModuleFileName( 0, m_szLogFileName, MAX_PATH );
+ 
+     // Look for the '.' before the "EXE" extension.  Replace the extension
+     // with "RPT"
+     PTSTR pszDot = _tcsrchr( m_szLogFileName, _T('.') );
+     if ( pszDot )
+     {
+         pszDot++;   // Advance past the '.'
+         if ( _tcslen(pszDot) >= 3 )
+             _tcscpy( pszDot, _T("RPT") );   // "RPT" -> "Report"
+     }
+ }
+ 
+ //============
+ // Destructor 
+ //============
+ MSJExceptionHandler::~MSJExceptionHandler( )
+ {
+     SetUnhandledExceptionFilter( m_previousFilter );
+ }
+ 
+ //==============================================================
+ // Lets user change the name of the report file to be generated 
+ //==============================================================
+ void MSJExceptionHandler::SetLogFileName( PTSTR pszLogFileName )
+ {
+     _tcscpy( m_szLogFileName, pszLogFileName );
+ }
+ 
+ //===========================================================
+ // Entry point where control comes on an unhandled exception 
+ //===========================================================
+ LONG WINAPI MSJExceptionHandler::MSJUnhandledExceptionFilter(
+                                              PEXCEPTION_POINTERS pExceptionInfo )
+ {
+     m_hReportFile = CreateFile( m_szLogFileName,
+                                 GENERIC_WRITE,
+                                 0,
+                                 0,
+                                 OPEN_ALWAYS,
+                                 FILE_FLAG_WRITE_THROUGH,
+                                 0 );
+ 
+     if ( m_hReportFile )
+     {
+         SetFilePointer( m_hReportFile, 0, 0, FILE_END );
+ 
+         GenerateExceptionReport( pExceptionInfo );
+ 
+         CloseHandle( m_hReportFile );
+         m_hReportFile = 0;
+     }
+ 
+     if ( m_previousFilter )
+         return m_previousFilter( pExceptionInfo );
+     else
+         return EXCEPTION_CONTINUE_SEARCH;
+ }
+ 
+ //===========================================================================
+ // Open the report file, and write the desired information to it.  Called by 
+ // MSJUnhandledExceptionFilter                                               
+ //===========================================================================
+ void MSJExceptionHandler::GenerateExceptionReport(
+     PEXCEPTION_POINTERS pExceptionInfo )
+ {
+     // Start out with a banner
+     _tprintf( _T("//=====================================================\n") );
+ 
+     PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
+ 
+     // First print information about the type of fault
+     _tprintf(   _T("Exception code: %08X %s\n"),
+                 pExceptionRecord->ExceptionCode,
+                 GetExceptionString(pExceptionRecord->ExceptionCode) );
+ 
+     // Now print information about where the fault occured
+     TCHAR szFaultingModule[MAX_PATH];
+     DWORD section, offset;
+     GetLogicalAddress(  pExceptionRecord->ExceptionAddress,
+                         szFaultingModule,
+                         sizeof( szFaultingModule ),
+                         section, offset );
+ 
+     _tprintf( _T("Fault address:  %08X %02X:%08X %s\n"),
+               pExceptionRecord->ExceptionAddress,
+               section, offset, szFaultingModule );
+ 
+     PCONTEXT pCtx = pExceptionInfo->ContextRecord;
+ 
+     // Show the registers
+     #ifdef _M_IX86  // Intel Only!
+     _tprintf( _T("\nRegisters:\n") );
+ 
+     _tprintf(_T("EAX:%08X\nEBX:%08X\nECX:%08X\nEDX:%08X\nESI:%08X\nEDI:%08X\n"),
+              pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi );
+ 
+     _tprintf( _T("CS:EIP:%04X:%08X\n"), pCtx->SegCs, pCtx->Eip );
+     _tprintf( _T("SS:ESP:%04X:%08X  EBP:%08X\n"),
+               pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
+     _tprintf( _T("DS:%04X  ES:%04X  FS:%04X  GS:%04X\n"),
+               pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs );
+     _tprintf( _T("Flags:%08X\n"), pCtx->EFlags );
+ 
+     #endif
+ 
+     if ( !InitImagehlpFunctions() )
+     {
+         OutputDebugString(_T("IMAGEHLP.DLL or its exported procs not found"));
+         
+         #ifdef _M_IX86  // Intel Only!
+         // Walk the stack using x86 specific code
+         IntelStackWalk( pCtx );
+         #endif
+ 
+         return;
+     }
+ 
+     ImagehlpStackWalk( pCtx );
+ 
+     _SymCleanup( GetCurrentProcess() );
+ 
+     _tprintf( _T("\n") );
+ }
+ 
+ //======================================================================
+ // Given an exception code, returns a pointer to a static string with a 
+ // description of the exception                                         
+ //======================================================================
+ LPTSTR MSJExceptionHandler::GetExceptionString( DWORD dwCode )
+ {
+     #define EXCEPTION( x ) case EXCEPTION_##x: return _T(#x);
+ 
+     switch ( dwCode )
+     {
+         EXCEPTION( ACCESS_VIOLATION )
+         EXCEPTION( DATATYPE_MISALIGNMENT )
+         EXCEPTION( BREAKPOINT )
+         EXCEPTION( SINGLE_STEP )
+         EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
+         EXCEPTION( FLT_DENORMAL_OPERAND )
+         EXCEPTION( FLT_DIVIDE_BY_ZERO )
+         EXCEPTION( FLT_INEXACT_RESULT )
+         EXCEPTION( FLT_INVALID_OPERATION )
+         EXCEPTION( FLT_OVERFLOW )
+         EXCEPTION( FLT_STACK_CHECK )
+         EXCEPTION( FLT_UNDERFLOW )
+         EXCEPTION( INT_DIVIDE_BY_ZERO )
+         EXCEPTION( INT_OVERFLOW )
+         EXCEPTION( PRIV_INSTRUCTION )
+         EXCEPTION( IN_PAGE_ERROR )
+         EXCEPTION( ILLEGAL_INSTRUCTION )
+         EXCEPTION( NONCONTINUABLE_EXCEPTION )
+         EXCEPTION( STACK_OVERFLOW )
+         EXCEPTION( INVALID_DISPOSITION )
+         EXCEPTION( GUARD_PAGE )
+         EXCEPTION( INVALID_HANDLE )
+     }
+ 
+     // If not one of the "known" exceptions, try to get the string
+     // from NTDLL.DLL's message table.
+ 
+     static TCHAR szBuffer[512] = { 0 };
+ 
+     FormatMessage(  FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
+                     GetModuleHandle( _T("NTDLL.DLL") ),
+                     dwCode, 0, szBuffer, sizeof( szBuffer ), 0 );
+ 
+     return szBuffer;
+ }
+ 
+ //==============================================================================
+ // Given a linear address, locates the module, section, and offset containing  
+ // that address.                                                               
+ //                                                                             
+ // Note: the szModule paramater buffer is an output buffer of length specified 
+ // by the len parameter (in characters!)                                       
+ //==============================================================================
+ BOOL MSJExceptionHandler::GetLogicalAddress(
+         PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD& offset )
+ {
+     MEMORY_BASIC_INFORMATION mbi;
+ 
+     if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) )
+         return FALSE;
+ 
+     DWORD hMod = (DWORD)mbi.AllocationBase;
+ 
+     if ( !GetModuleFileName( (HMODULE)hMod, szModule, len ) )
+         return FALSE;
+ 
+     // Point to the DOS header in memory
+     PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
+ 
+     // From the DOS header, find the NT (PE) header
+     PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);
+ 
+     PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
+ 
+     DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address
+ 
+     // Iterate through the section table, looking for the one that encompasses
+     // the linear address.
+     for (   unsigned i = 0;
+             i < pNtHdr->FileHeader.NumberOfSections;
+             i++, pSection++ )
+     {
+         DWORD sectionStart = pSection->VirtualAddress;
+         DWORD sectionEnd = sectionStart
+                     + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
+ 
+         // Is the address in this section???
+         if ( (rva >= sectionStart) && (rva <= sectionEnd) )
+         {
+             // Yes, address is in the section.  Calculate section and offset,
+             // and store in the "section" & "offset" params, which were
+             // passed by reference.
+             section = i+1;
+             offset = rva - sectionStart;
+             return TRUE;
+         }
+     }
+ 
+     return FALSE;   // Should never get here!
+ }
+ 
+ //============================================================
+ // Walks the stack, and writes the results to the report file 
+ //============================================================
+ void MSJExceptionHandler::IntelStackWalk( PCONTEXT pContext )
+ {
+     _tprintf( _T("\nCall stack:\n") );
+ 
+     _tprintf( _T("Address   Frame     Logical addr  Module\n") );
+ 
+     DWORD pc = pContext->Eip;
+     PDWORD pFrame, pPrevFrame;
+     
+     pFrame = (PDWORD)pContext->Ebp;
+ 
+     do
+     {
+         TCHAR szModule[MAX_PATH] = _T("");
+         DWORD section = 0, offset = 0;
+ 
+         GetLogicalAddress((PVOID)pc, szModule,sizeof(szModule),section,offset );
+ 
+         _tprintf( _T("%08X  %08X  %04X:%08X %s\n"),
+                   pc, pFrame, section, offset, szModule );
+ 
+         pc = pFrame[1];
+ 
+         pPrevFrame = pFrame;
+ 
+         pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack
+ 
+         if ( (DWORD)pFrame & 3 )    // Frame pointer must be aligned on a
+             break;                  // DWORD boundary.  Bail if not so.
+ 
+         if ( pFrame <= pPrevFrame )
+             break;
+ 
+         // Can two DWORDs be read from the supposed frame address?          
+         if ( IsBadWritePtr(pFrame, sizeof(PVOID)*2) )
+             break;
+ 
+     } while ( 1 );
+ }
+ 
+ //============================================================
+ // Walks the stack, and writes the results to the report file 
+ //============================================================
+ void MSJExceptionHandler::ImagehlpStackWalk( PCONTEXT pContext )
+ {
+     _tprintf( _T("\nCall stack:\n") );
+ 
+     _tprintf( _T("Address   Frame\n") );
+ 
+     // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
+ 
+     STACKFRAME sf;
+     memset( &sf, 0, sizeof(sf) );
+ 
+     // Initialize the STACKFRAME structure for the first call.  This is only
+     // necessary for Intel CPUs, and isn't mentioned in the documentation.
+     sf.AddrPC.Offset       = pContext->Eip;
+     sf.AddrPC.Mode         = AddrModeFlat;
+     sf.AddrStack.Offset    = pContext->Esp;
+     sf.AddrStack.Mode      = AddrModeFlat;
+     sf.AddrFrame.Offset    = pContext->Ebp;
+     sf.AddrFrame.Mode      = AddrModeFlat;
+ 
+     while ( 1 )
+     {
+         if ( ! _StackWalk(  IMAGE_FILE_MACHINE_I386,
+                             GetCurrentProcess(),
+                             GetCurrentThread(),
+                             &sf,
+                             pContext,
+                             0,
+                             _SymFunctionTableAccess,
+                             _SymGetModuleBase,
+                             0 ) )
+             break;
+ 
+         if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure
+             break;                      // the frame is OK.  Bail if not.
+ 
+         _tprintf( _T("%08X  %08X  "), sf.AddrPC.Offset, sf.AddrFrame.Offset );
+ 
+         // IMAGEHLP is wacky, and requires you to pass in a pointer to an
+         // IMAGEHLP_SYMBOL structure.  The problem is that this structure is
+         // variable length.  That is, you determine how big the structure is
+         // at runtime.  This means that you can't use sizeof(struct).
+         // So...make a buffer that's big enough, and make a pointer
+         // to the buffer.  We also need to initialize not one, but TWO
+         // members of the structure before it can be used.
+ 
+         BYTE symbolBuffer[ sizeof(IMAGEHLP_SYMBOL) + 512 ];
+         PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
+         pSymbol->SizeOfStruct = sizeof(symbolBuffer);
+         pSymbol->MaxNameLength = 512;
+                         
+         DWORD symDisplacement = 0;  // Displacement of the input address,
+                                     // relative to the start of the symbol
+ 
+         if ( _SymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset,
+                                 &symDisplacement, pSymbol) )
+         {
+             _tprintf( _T("%hs+%X\n"), pSymbol->Name, symDisplacement );
+             
+         }
+         else    // No symbol found.  Print out the logical address instead.
+         {
+             TCHAR szModule[MAX_PATH] = _T("");
+             DWORD section = 0, offset = 0;
+ 
+             GetLogicalAddress(  (PVOID)sf.AddrPC.Offset,
+                                 szModule, sizeof(szModule), section, offset );
+ 
+             _tprintf( _T("%04X:%08X %s\n"),
+                       section, offset, szModule );
+         }
+     }
+ 
+ }
+ 
+ //============================================================================
+ // Helper function that writes to the report file, and allows the user to use 
+ // printf style formating                                                     
+ //============================================================================
+ int __cdecl MSJExceptionHandler::_tprintf(const TCHAR * format, ...)
+ {
+     TCHAR szBuff[1024];
+     int retValue;
+     DWORD cbWritten;
+     va_list argptr;
+           
+     va_start( argptr, format );
+     retValue = wvsprintf( szBuff, format, argptr );
+     va_end( argptr );
+ 
+     WriteFile( m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0 );
+ 
+     return retValue;
+ }
+ 
+ 
+ //=========================================================================
+ // Load IMAGEHLP.DLL and get the address of functions in it that we'll use 
+ //=========================================================================
+ BOOL MSJExceptionHandler::InitImagehlpFunctions( void )
+ {
+     HMODULE hModImagehlp = LoadLibrary( _T("IMAGEHLP.DLL") );
+     if ( !hModImagehlp )
+         return FALSE;
+ 
+     _SymInitialize = (SYMINITIALIZEPROC)GetProcAddress( hModImagehlp,
+                                                         "SymInitialize" );
+     if ( !_SymInitialize )
+         return FALSE;
+ 
+     _SymCleanup = (SYMCLEANUPPROC)GetProcAddress( hModImagehlp, "SymCleanup" );
+     if ( !_SymCleanup )
+         return FALSE;
+ 
+     _StackWalk = (STACKWALKPROC)GetProcAddress( hModImagehlp, "StackWalk" );
+     if ( !_StackWalk )
+         return FALSE;
+ 
+     _SymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC)
+                         GetProcAddress( hModImagehlp, "SymFunctionTableAccess" );
+ 
+     if ( !_SymFunctionTableAccess )
+         return FALSE;
+ 
+     _SymGetModuleBase=(SYMGETMODULEBASEPROC)GetProcAddress( hModImagehlp,
+                                                             "SymGetModuleBase");
+     if ( !_SymGetModuleBase )
+         return FALSE;
+ 
+     _SymGetSymFromAddr=(SYMGETSYMFROMADDRPROC)GetProcAddress( hModImagehlp,
+                                                               "SymGetSymFromAddr" );
+     if ( !_SymGetSymFromAddr )
+         return FALSE;
+ 
+     if ( !_SymInitialize( GetCurrentProcess(), 0, TRUE ) )
+         return FALSE;
+ 
+     return TRUE;        
+ }
Index: expect-5.21/win/msjexhnd.h
diff -c /dev/null expect-5.21/win/msjexhnd.h:1.1
*** /dev/null	Thu Dec 18 10:39:21 1997
--- expect-5.21/win/msjexhnd.h	Thu Dec 18 06:46:16 1997
***************
*** 0 ****
--- 1,76 ----
+ /*
+  * msjexhnd.h
+  */
+ 
+ #ifndef __MSJEXHND_H__
+ #define __MSJEXHND_H__
+ 
+ class MSJExceptionHandler
+ {
+       public:
+       
+       MSJExceptionHandler( );
+       ~MSJExceptionHandler( );
+      
+       void SetLogFileName( PTSTR pszLogFileName );
+ 
+       private:
+ 
+       // entry point where control comes on an unhandled exception
+       static LONG WINAPI MSJUnhandledExceptionFilter(
+                                            PEXCEPTION_POINTERS pExceptionInfo );
+ 
+       // where report info is extracted and generated    
+       static void GenerateExceptionReport( PEXCEPTION_POINTERS pExceptionInfo );
+ 
+       // Helper functions
+       static LPTSTR GetExceptionString( DWORD dwCode );
+       static BOOL GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len,
+                                     DWORD& section, DWORD& offset );
+       static void IntelStackWalk( PCONTEXT pContext );
+       #if 1
+       static void ImagehlpStackWalk( PCONTEXT pContext );
+       #endif
+       static int __cdecl _tprintf(const TCHAR * format, ...);
+ 
+       #if 1
+       static BOOL InitImagehlpFunctions( void );
+       #endif
+           
+       // Variables used by the class
+       static TCHAR m_szLogFileName[MAX_PATH];
+       static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter;
+       static HANDLE m_hReportFile;
+ 
+       #if 1
+       // Make typedefs for some IMAGEHLP.DLL functions so that we can use them
+       // with GetProcAddress
+       typedef BOOL (__stdcall * SYMINITIALIZEPROC)( HANDLE, LPSTR, BOOL );
+       typedef BOOL (__stdcall *SYMCLEANUPPROC)( HANDLE );
+ 
+       typedef BOOL (__stdcall * STACKWALKPROC)
+                    ( DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID,
+                     PREAD_PROCESS_MEMORY_ROUTINE,PFUNCTION_TABLE_ACCESS_ROUTINE,
+                     PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE );
+ 
+       typedef LPVOID (__stdcall *SYMFUNCTIONTABLEACCESSPROC)( HANDLE, DWORD );
+ 
+       typedef DWORD (__stdcall *SYMGETMODULEBASEPROC)( HANDLE, DWORD );
+ 
+       typedef BOOL (__stdcall *SYMGETSYMFROMADDRPROC)
+                                     ( HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL );
+ 
+       static SYMINITIALIZEPROC _SymInitialize;
+       static SYMCLEANUPPROC _SymCleanup;
+       static STACKWALKPROC _StackWalk;
+       static SYMFUNCTIONTABLEACCESSPROC _SymFunctionTableAccess;
+       static SYMGETMODULEBASEPROC _SymGetModuleBase;
+       static SYMGETSYMFROMADDRPROC _SymGetSymFromAddr;
+ 
+       #endif
+ 
+ };
+ 
+ extern MSJExceptionHandler g_MSJExceptionHandler;  // global instance of class
+ 
+ #endif
Index: expect-5.21/win/testclib.c
diff -c expect-5.21/win/testclib.c:1.4 expect-5.21/win/testclib.c:1.5
*** expect-5.21/win/testclib.c:1.4	Thu Nov 13 04:15:20 1997
--- expect-5.21/win/testclib.c	Sat Dec  6 01:16:12 1997
***************
*** 317,322 ****
--- 317,325 ----
  {
      int i;
  
+     /* Call this with 1 to turn on verbose debugging info */
+     exp_setdebug(NULL, 1);
+ 
      for (i = 0; i < 100; i++) {
  	printf("-------------------- Iteration %d ------------------------\n", i);
  	result(1, test1());
Index: expect-5.21/win/testcrash.c
diff -c /dev/null expect-5.21/win/testcrash.c:1.1
*** /dev/null	Thu Dec 18 10:39:21 1997
--- expect-5.21/win/testcrash.c	Thu Dec 18 06:44:52 1997
***************
*** 0 ****
--- 1,51 ----
+ /*
+  * testcrash.c
+  *
+  *	This program is used to check stack tracebacks with Expect.
+  *	It purposely crashes with several frames on the stack.
+  */
+ 
+ #include <stdio.h>
+ 
+ 
+ int
+ crash(int a, int b)
+ {
+     char *p = NULL;
+ 
+     /* Crash occurs here */
+     *p = 0;
+ 
+     return a*b;
+ }
+ 
+ int
+ subroutine4(int a, int b)
+ {
+     return crash(a+1, b+2);
+ }
+ 
+ int
+ subroutine3(int a, int b)
+ {
+     return subroutine4(a+1,b+2);
+ }
+ 
+ int
+ subroutine2(int a, int b)
+ {
+     return subroutine3(a+1,b+2);
+ }
+ 
+ int
+ subroutine1(int a, int b)
+ {
+     return subroutine2(a+1,b+2);
+ }
+ 
+ int
+ main(int argc, char **argv)
+ {
+     return subroutine1(100,200);
+     return 0;
+ }
Index: expect-5.21/win/tests/crash.tcl
diff -c /dev/null expect-5.21/win/tests/crash.tcl:1.1
*** /dev/null	Thu Dec 18 10:39:22 1997
--- expect-5.21/win/tests/crash.tcl	Thu Dec 18 06:38:38 1997
***************
*** 0 ****
--- 1,4 ----
+ set exp_nt_debug 1
+ spawn ../testcrash.exe
+ set x 1
+ vwait x
Index: expect-5.21/win/tests/crash.test
diff -c /dev/null expect-5.21/win/tests/crash.test:1.1
*** /dev/null	Thu Dec 18 10:39:22 1997
--- expect-5.21/win/tests/crash.test	Thu Dec 18 06:38:39 1997
***************
*** 0 ****
--- 1,79 ----
+ # Commands covered: test traceback facility
+ #
+ # This file contains a collection of tests for one or more of the Expect
+ # command capabilities.  Sourcing this file into Tcl runs the tests and
+ # generates output for errors.  No output means no errors were found.
+ #
+ # Copyright (c) 1997 Mitel, Inc.
+ #
+ # See the file "license.terms" for information on usage and redistribution
+ # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ #
+ # RCS: $Header: /disk/3/CVSROOT/Mitel/expect/win/tests/crash.test,v 1.1 1997/12/18 14:38:39 chaffee Exp $
+ 
+ if {[string compare test [info procs test]] == 1} then {source defs}
+ 
+ catch {unset a}
+ catch {unset x}
+ 
+ # Expected output:
+ 
+ #Backtrace for testcrash.exe
+ #-------------------------------------
+ #testcrash.exe 00401013	crash+13
+ #testcrash.exe 0040103e	subroutine4+17
+ #testcrash.exe 00401062	subroutine3+17
+ #testcrash.exe 00401086	subroutine2+17
+ #testcrash.exe 004010aa	subroutine1+17
+ #testcrash.exe 004010c9	main+12
+ #testcrash.exe 00401dfc	mainCRTStartup+FC
+ #kernel32.dbg 77f1afc1	GetProcessPriorityBoost+117
+ 
+ 
+ # Note: this test will fail if testcrash.exe was not compiled with
+ # debugging enabled.
+ 
+ # XXX: This test is not yet correct or working.
+ 
+ test crash-1.0 {subprocess crash tests} {
+     if [catch {
+ 	# spawn tclsh80.exe crash.tcl
+ 	spawn ../testcrash.exe
+ 
+ 	if {0} {
+ 
+ 	expect {
+ 	    "Backtrace for" {} {set ok 1}
+ 	    timeout {error "Unexpected timeout 1"}
+ 	    eof     {error "Unexpected eof 1"}
+ 	}
+ 	expect {
+ 	    "	crash" {} {set ok 1}
+ 	    timeout {error "Unexpected timeout 2"}
+ 	    eof     {error "Unexpected eof 2"}
+ 	}
+ 	expect {
+ 	    "	subroutine4" {} {set ok 1}
+ 	    timeout {error "Unexpected timeout 3"}
+ 	    eof     {error "Unexpected eof 3"}
+ 	}
+ 	expect {
+ 	    "	subroutine3" {} {set ok 1}
+ 	    timeout {error "Unexpected timeout 4"}
+ 	    eof     {error "Unexpected eof 4"}
+ 	}
+ 	expect {
+ 	    "	subroutine2" {} {set ok 1}
+ 	    timeout {error "Unexpected timeout 5"}
+ 	    eof     {error "Unexpected eof 5"}
+ 	}
+ 	expect {
+ 	    "	subroutine1" {} {set ok 1}
+ 	    timeout {error "Unexpected timeout 6"}
+ 	    eof     {error "Unexpected eof 6"}
+ 	}
+ 	}
+ 	catch { kill }
+ 	catch { wait }
+     } msg] {set msg} else {set ok 1}
+ } {1}
