/* we_debug.c                                             */
/* Copyright (C) 1993 Fred Kruse                          */
/* Copyright (C) 2026 Juan Manuel Mendez Rey              */
/* This is free software; you can redistribute it and/or  */
/* modify it under the terms of the                       */
/* GNU General Public License, see the file COPYING.      */

#include "messages.h"
#include "edit.h"
#include "we_fdloop.h"

#ifndef NO_XWINDOWS
#include "WeXterm.h"
#endif

#ifdef DEBUGGER

#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <poll.h>
#include <pty.h>
#include <stdarg.h>
#include <sys/wait.h>

/* Trace logging for jdb debugging -- writes to /tmp/xwpe-jdb-trace.txt.
   Compile with -DJDB_TRACE to enable, or leave undefined for no-op. */
#ifdef JDB_TRACE
static FILE *_jdb_trace_fp = NULL;
static void jdb_trace(const char *fmt, ...)
{
 va_list ap;
 if (!_jdb_trace_fp)
  _jdb_trace_fp = fopen("/tmp/xwpe-jdb-trace.txt", "a");
 if (!_jdb_trace_fp) return;
 va_start(ap, fmt);
 vfprintf(_jdb_trace_fp, fmt, ap);
 va_end(ap);
 fflush(_jdb_trace_fp);
}
#else
#define jdb_trace(...) ((void)0)
#endif

#ifndef TERMCAP
/* Because term.h defines "buttons" it messes up we_gpm.c if in edit.h */
#include <term.h>
#endif

#define D_CBREAK -2
#define CTRLC CtrlC
#define SVLINES 12

int e_d_delbreak(FENSTER *f);
int e_d_error(char *s);
int print_to_end_of_buffer(BUFFER * b,char * str,int wrap_limit);


#define MAXOUT  2 * MAXSCOL * MAXSLNS

char *e_debugger, *e_deb_swtch = NULL, *e_d_save_schirm;
int e_d_swtch = 0, rfildes[2], ofildes, e_d_pid = 0;

/* Async debugger state for event-driven step/run.
   When e_d_async_pending is set, the debugger is waiting for gdb
   to respond.  The main event loop processes keyboard and pty
   events normally.  When gdb responds, the callback processes
   the response and clears the flag. */
int e_d_async_pending = 0;
static int e_d_async_main_brk = 0;
static FENSTER *e_d_async_f = NULL;
int e_d_nbrpts = 0, e_d_zwtchs = 0, *e_d_ybrpts, *e_d_nrbrpts;

/* number of watch expressions in Watches window */
int e_d_nwtchs = 0; 

/* e_d_nrwtchs[i] is the y coordinate (count starts at 0) of the first line of
   the i-th watch in the Watches window */
int *e_d_nrwtchs; 

char **e_d_swtchs; /* e_d_swtchs[i] is the i-th watch expression (a char*) */

int e_d_nstack;
char e_d_file[128], **e_d_sbrpts;
char *e_d_out = NULL;
int e_deb_type = 0, e_deb_mode = 0;
char e_d_out_str[SVLINES][256];
char *e_d_sp[SVLINES];
char e_d_tty[80];

/* Pty for capturing debugged program's output */
int e_d_pty_master = -1;
int e_d_pty_slave = -1;
char e_d_pty_slave_name[128];
char *e_d_prog_output = NULL;
int e_d_prog_output_len = 0;
int e_d_prog_output_cap = 0;

int e_d_pty_verase(void)
{
 struct termios t;

 if (e_d_pty_master >= 0 && tcgetattr(e_d_pty_master, &t) == 0)
  return t.c_cc[VERASE];
 return 0x7F;
}

extern int wfildes[2], efildes[2];
extern struct termios otermio, ntermio, ttermio;
extern struct e_s_prog e_sv_prog;
extern BUFFER *e_p_w_buffer;
extern char *att_no;
extern char *e_tmp_dir;

#ifdef NOTPARM
/* char *tparm(); */
char *tgoto();
#endif
#ifdef DEFTPUTS
int tputs();
#endif

char *npipe[5] = {  NULL, NULL, NULL, NULL, NULL  };

/* Drain any available data from the pty master into e_d_prog_output.
   Uses non-blocking read so it never blocks the editor. */
void e_d_pty_drain(void)
{
 char buf[4096];
 int n, flags;

 if (e_d_pty_master < 0)
  return;
 usleep(100000); /* 100ms: let kernel deliver pty data */
 flags = fcntl(e_d_pty_master, F_GETFL, 0);
 fcntl(e_d_pty_master, F_SETFL, flags | O_NONBLOCK);
 while ((n = read(e_d_pty_master, buf, sizeof(buf))) > 0)
 {
  if (e_d_prog_output_len + n > e_d_prog_output_cap)
  {
   int newcap = e_d_prog_output_cap ? e_d_prog_output_cap * 2 : 8192;
   while (newcap < e_d_prog_output_len + n)
    newcap *= 2;
   e_d_prog_output = REALLOC(e_d_prog_output, newcap);
   e_d_prog_output_cap = newcap;
  }
  memcpy(e_d_prog_output + e_d_prog_output_len, buf, n);
  e_d_prog_output_len += n;
 }
 fcntl(e_d_pty_master, F_SETFL, flags & ~O_NONBLOCK);
}

static void e_d_pty_strip_cr(char *s, int len)
{
 int r, w;
 for (r = 0, w = 0; r < len; r++)
  if (s[r] != '\r')
   s[w++] = s[r];
 s[w] = '\0';
}

/* forward declarations */
static void e_d_pty_flush_line(FENSTER *, int);
void e_d_pty_flush_to_messages(FENSTER *);

static char _nb_buf[512];
static int _nb_len = 0;

static int e_d_check_prompt(signed char *s, int i)
{
 if (e_deb_type == 0 && i > 4 && s[i] == ' ' && !strncmp((char*)s+i-5, "(gdb)", 5))
  return 1;
 if (e_deb_type == 2 && i > 4 && s[i] == ' ' && !strncmp((char*)s+i-5, "(dbx)", 5))
  return 1;
 if (e_deb_type == 5 && i > 4 && s[i] == ' ' && !strncmp((char*)s+i-5, "(Pdb)", 5))
  return 1;
 if (e_deb_type == 1 && s[i] == '*')
  return 1;
 if (e_deb_type == 3 && s[i] == '>')
  return 1;
 if (e_deb_type == 4 && i > 0 &&
     ((s[i] == ' ' && s[i-1] == ']') || (s[i] == ' ' && s[i-1] == '>')))
  return 1;
 return 0;
}

int e_d_line_read_nb(int n, signed char *s, int max)
{
 int flags, nread, i;

 while (e_e_line_read(efildes[0], s, max) >= 0)
  ;
 flags = fcntl(n, F_GETFL, 0);
 fcntl(n, F_SETFL, flags | O_NONBLOCK);
 nread = read(n, _nb_buf + _nb_len, sizeof(_nb_buf) - _nb_len - 1);
 fcntl(n, F_SETFL, flags & ~O_NONBLOCK);
 if (nread <= 0 && _nb_len == 0)
  return (nread == 0) ? -1 : -2;
 if (nread > 0)
  _nb_len += nread;
 _nb_buf[_nb_len] = '\0';
 for (i = 0; i < _nb_len; i++)
 {
  if (_nb_buf[i] == '\n' || _nb_buf[i] == '\0')
  {
   int len = (i < max - 1) ? i : max - 2;
   memcpy(s, _nb_buf, len);
   s[len] = _nb_buf[i];
   s[len + 1] = '\0';
   _nb_len -= (i + 1);
   if (_nb_len > 0)
    memmove(_nb_buf, _nb_buf + i + 1, _nb_len);
   return e_d_check_prompt(s, len) ? 1 : 2;
  }
 }
 if (e_d_check_prompt((signed char *)_nb_buf, _nb_len - 1))
 {
  int len = (_nb_len < max - 1) ? _nb_len : max - 2;
  memcpy(s, _nb_buf, len);
  s[len] = '\0';
  _nb_len = 0;
  return 1;
 }
 return -2;
}

static FENSTER *e_d_find_messages_window(void)
{
 int i;
 ECNT *cn = WpeEditor;
 for (i = cn->mxedt; i > 0; i--)
  if (!strcmp(cn->f[i]->datnam, "Messages"))
   return cn->f[i];
 return NULL;
}

/* forward declarations */
static void e_d_drain_pty_to_messages(void);
static void e_d_messages_place_cursor(FENSTER *mf);

static char _pty_line_buf[1024];
static int _pty_line_len = 0;
int _messages_activated = 0;

static void e_d_messages_erase_char(BUFFER *b, int last)
{
 int len = b->bf[last].len;

 if (len > 0)
 {
  b->bf[last].len = len - 1;
  b->bf[last].s[len - 1] = '\0';
  b->b.x = len - 1;
 }
}

static void e_d_messages_insert_char(BUFFER *b, int last, int c)
{
 int len = b->bf[last].len;

 if (len < b->mx.x - 1)
 {
  b->bf[last].s = REALLOC(b->bf[last].s, len + 2);
  b->bf[last].s[len] = c;
  b->bf[last].s[len + 1] = '\0';
  b->bf[last].len = len + 1;
  b->b.x = len + 1;
 }
}

static void e_d_messages_append_char(FENSTER *mf, int c)
{
 BUFFER *b = mf->b;
 int last = b->mxlines - 1;

 if (c == '\r')
  return;
 if (c == '\n' || last < 0)
 {
  e_new_line(b->mxlines, b);
  b->b.y = b->mxlines - 1;
  b->b.x = 0;
  return;
 }
 if (c == '\b' || c == 0x7F)
 {
  e_d_messages_erase_char(b, last);
  return;
 }
 b->b.y = last;
 e_d_messages_insert_char(b, last, c);
}

static int _pty_owns_last_line = 0;

static void e_d_pty_ensure_own_line(FENSTER *mf)
{
 BUFFER *b = mf->b;
 int last = b->mxlines - 1;

 if (_pty_owns_last_line)
  return;
 if (last >= 0 && b->bf[last].len > 0)
  e_d_messages_append_char(mf, '\n');
 _pty_owns_last_line = 1;
}

static void e_d_prog_output_append(char *data, int len)
{
 if (e_d_prog_output_len + len >= e_d_prog_output_cap)
 {
  e_d_prog_output_cap = (e_d_prog_output_len + len + 1024) * 2;
  e_d_prog_output = REALLOC(e_d_prog_output, e_d_prog_output_cap);
 }
 memcpy(e_d_prog_output + e_d_prog_output_len, data, len);
 e_d_prog_output_len += len;
}

/* Public hooks so the Ctrl-F9 run path (we_prog.c) can feed the same captured
   buffer the debugger uses, letting the Alt-F5 "User Screen" replay the last
   program's raw output verbatim regardless of how it was launched.
   e_d_prog_output_reset() starts a fresh capture at the top of each run. */
void e_d_prog_output_reset(void)
{
 e_d_prog_output_len = 0;
}

void e_d_prog_output_add(char *data, int len)
{
 e_d_prog_output_append(data, len);
}

static int e_d_pty_read_to_messages(FENSTER *mf)
{
 char buf[256];
 int n, total = 0, i, flags;

 if (e_d_pty_master < 0)
  return 0;
 flags = fcntl(e_d_pty_master, F_GETFL, 0);
 fcntl(e_d_pty_master, F_SETFL, flags | O_NONBLOCK);
 while ((n = read(e_d_pty_master, buf, sizeof(buf))) > 0)
 {
  e_d_pty_ensure_own_line(mf);
  e_d_prog_output_append(buf, n);
  for (i = 0; i < n; i++)
  {
   if (buf[i] == '\n')
    _pty_owns_last_line = 0;
   e_d_messages_append_char(mf, (unsigned char)buf[i]);
  }
  total += n;
 }
 fcntl(e_d_pty_master, F_SETFL, flags & ~O_NONBLOCK);
 return total;
}

static void e_d_messages_redraw(FENSTER *mf)
{
 e_messages_scroll_to_bottom(mf);
 e_ed_rahmen(mf, 1);
 e_schirm(mf, 1);
 e_cursor(mf, 1);
 e_refresh();
}

void e_d_echo_input_char(int c)
{
}

static void e_d_wait_for_input(int gdb_fd)
{
 wpe_fd_add(gdb_fd, POLLIN, NULL, NULL);
 if (e_d_pty_master >= 0)
  wpe_fd_add(e_d_pty_master, POLLIN, NULL, NULL);
 wpe_fd_poll(50);
 wpe_fd_del(gdb_fd);
 if (e_d_pty_master >= 0)
  wpe_fd_del(e_d_pty_master);
}

static void e_d_on_gdb_readable(int fd, void *data);
static void e_d_on_pty_readable(int fd, void *data);

typedef struct {
 char buf[SVLINES][256];
 char *sp[SVLINES];
 int active;
 int main_brk;
 struct FNST *f;
} e_d_accum_t;

static e_d_accum_t e_d_accum;

static void e_d_flush_inferior_stdout(void)
{
 char resp[256];

 if (e_deb_type != 0 || e_d_pty_master < 0)
  return;
 write(rfildes[1], "call (void)fflush(0)\n", 21);
 while (e_d_line_read(wfildes[0], resp, 256, 0, 0) == 0)
  ;
}

static void e_d_accum_complete(void)
{
 FENSTER *f = e_d_accum.f;
 int i;

 e_d_flush_inferior_stdout();
 e_d_drain_pty_to_messages();
 wpe_fd_del(wfildes[0]);
 if (e_d_pty_master >= 0)
  wpe_fd_del(e_d_pty_master);
 e_d_accum.active = 0;
 e_d_async_pending = 0;

 for (i = 0; i < SVLINES; i++)
  e_d_sp[i] = e_d_accum.sp[i];

 for (i = 0; i < SVLINES; i++)
 {
  if (strstr(e_d_sp[i], "exited"))
  {
   e_d_p_message("Program exited.", f, 0);
   e_d_quit(f);
   return;
  }
 }
 { int _i;
   _i = e_d_fst_check(f);
   if (_i < 0) _i = e_d_snd_check(f);
   if (_i < 0) _i = e_d_trd_check(f);
   if (_i < 0)
   {
    e_d_switch_out(0);
    e_error("Program exited. Debugger stopped.", 0, f->fb);
    e_d_quit(f);
    return;
   }
 }
 e_d_accum.f = NULL;
 _messages_activated = 0;
 e_refresh();
}

static void e_d_accum_line(char *line, int ret)
{
 char *spt;
 int i;

 if (!e_d_accum.active)
  return;
 if (ret < 0)
 {
  e_d_accum.active = 0;
  e_d_async_pending = 0;
  wpe_fd_del(wfildes[0]);
  if (e_d_pty_master >= 0)
   wpe_fd_del(e_d_pty_master);
  e_d_quit(e_d_accum.f);
  return;
 }
 if (ret == 1)
 {
  e_d_accum_complete();
  return;
 }
 if (!line[0])
  return;
 spt = e_d_accum.sp[0];
 for (i = 1; i < SVLINES; i++)
  e_d_accum.sp[i-1] = e_d_accum.sp[i];
 e_d_accum.sp[SVLINES-1] = spt;
 strncpy(spt, line, 255);
 spt[255] = '\0';
}

static void e_d_accum_init(FENSTER *f, int main_brk)
{
 int i;

 for (i = 0; i < SVLINES; i++)
 {
  e_d_accum.sp[i] = e_d_accum.buf[i];
  e_d_accum.buf[i][0] = '\0';
 }
 e_d_accum.active = 1;
 e_d_accum.main_brk = main_brk;
 e_d_accum.f = f;
 e_d_async_pending = 1;
 _nb_len = 0;
 _pty_owns_last_line = 0;
 wpe_fd_add(wfildes[0], POLLIN, e_d_on_gdb_readable, f);
 if (e_d_pty_master >= 0)
  wpe_fd_add(e_d_pty_master, POLLIN, e_d_on_pty_readable, f);
}

void e_d_place_cursor_in_messages(void)
{
 FENSTER *mf = e_d_find_messages_window();

 if (mf)
  e_d_messages_place_cursor(mf);
}

static void e_d_activate_messages_window(void)
{
 _messages_activated = 1;
}

static void e_d_messages_place_cursor(FENSTER *mf)
{
 BUFFER *b = mf->b;
 SCHIRM *s = mf->s;
 int last = b->mxlines - 1;
 int row, col;

 if (last < 0) return;
 if (!_pty_owns_last_line && b->bf[last].len > 0)
 {
  row = mf->a.y + 1 + (last + 1 - s->c.y);
  col = mf->a.x + 1;
 }
 else
 {
  row = mf->a.y + 1 + (last - s->c.y);
  { extern int e_utf8_visual_len(unsigned char *, int);
    col = mf->a.x + 1 +
     e_utf8_visual_len((unsigned char *)b->bf[last].s, b->bf[last].len);
  }
 }
 if (row > mf->e.y - 1) row = mf->e.y - 1;
 if (col > mf->e.x - 1) col = mf->e.x - 1;
 fk_locate(col, row);
 e_refresh();
}

static void e_d_on_pty_readable(int fd, void *data)
{
 FENSTER *mf = e_d_find_messages_window();
 int n;

 if (!mf) return;
 n = e_d_pty_read_to_messages(mf);
 if (n > 0)
 {
  e_d_messages_redraw(mf);
  e_d_messages_place_cursor(mf);
 }
}

static void e_d_on_gdb_readable(int fd, void *data)
{
 signed char buf[256];
 int ret;

 if (!e_d_accum.active)
  return;
 while (e_d_accum.active)
 {
  ret = e_d_line_read_nb(wfildes[0], buf, 256);
  if (ret == -2)
   return;
  e_d_accum_line((char *)buf, ret);
 }
}

static void e_d_pty_drain_nonblock(void)
{
 char buf[256];
 int n, flags;

 if (e_d_pty_master < 0)
  return;
 flags = fcntl(e_d_pty_master, F_GETFL, 0);
 fcntl(e_d_pty_master, F_SETFL, flags | O_NONBLOCK);
 while ((n = read(e_d_pty_master, buf, sizeof(buf))) > 0)
 {
  if (e_d_prog_output_len + n >= e_d_prog_output_cap)
  {
   e_d_prog_output_cap = e_d_prog_output_len + n + 1024;
   e_d_prog_output = REALLOC(e_d_prog_output, e_d_prog_output_cap);
  }
  memcpy(e_d_prog_output + e_d_prog_output_len, buf, n);
  e_d_prog_output_len += n;
  e_d_prog_output[e_d_prog_output_len] = '\0';
 }
 fcntl(e_d_pty_master, F_SETFL, flags & ~O_NONBLOCK);
}

static void e_d_pty_flush_line(FENSTER *f, int force)
{
 if (_pty_line_len == 0)
  return;
 if (!force && !memchr(_pty_line_buf, '\n', _pty_line_len))
  return;
 _pty_line_buf[_pty_line_len] = '\0';
 e_d_pty_strip_cr(_pty_line_buf, _pty_line_len);
 if (_pty_line_buf[0])
  e_d_p_message(_pty_line_buf, f, 0);
 _pty_line_len = 0;
}

void e_d_pty_flush_to_messages(FENSTER *f)
{
 char buf[256];
 int n, flags;

 if (e_d_pty_master < 0)
  return;
 flags = fcntl(e_d_pty_master, F_GETFL, 0);
 fcntl(e_d_pty_master, F_SETFL, flags | O_NONBLOCK);
 while ((n = read(e_d_pty_master, buf, sizeof(buf))) > 0)
 {
  int space = sizeof(_pty_line_buf) - _pty_line_len - 1;
  if (n > space) n = space;
  memcpy(_pty_line_buf + _pty_line_len, buf, n);
  _pty_line_len += n;
  e_d_pty_flush_line(f, 0);
 }
 fcntl(e_d_pty_master, F_SETFL, flags & ~O_NONBLOCK);
}

static void e_d_drain_pty_to_messages(void)
{
 FENSTER *mf;

 if (e_d_pty_master < 0)
  return;
 mf = e_d_find_messages_window();
 if (!mf)
  return;
 if (e_d_pty_read_to_messages(mf) > 0)
 {
  e_d_messages_redraw(mf);
  e_d_messages_place_cursor(mf);
 }
}

/* Close pty fds but KEEP the output buffer.
   The buffer is preserved so Ctrl-G P can show program output
   even after the debugger session ends. It is freed when a
   new debug session starts (in e_exec_deb). */
void e_d_pty_close(void)
{
 if (e_d_pty_master >= 0)
 {
  e_d_pty_drain();  /* capture any remaining output before closing */
  close(e_d_pty_master);
  e_d_pty_master = -1;
 }
 if (e_d_pty_slave >= 0)
 {
  close(e_d_pty_slave);
  e_d_pty_slave = -1;
 }
 e_d_pty_slave_name[0] = '\0';
 /* Buffer is intentionally NOT freed here -- see comment above. */
 e_d_prog_output_cap = 0;
}

char *e_d_msg[] = {  "Ctrl C pressed\nQuit Debugger ?",
			"Quit Debugger ?",
			"End of Debugging",
			"Program is not running",
			"No symbol in current context\n",
			"No Source-Code",       /*  Number 5  */
			"Start Debugger",
			"Can\'t start Debugger",
			"Starting program:",
			"Can\'t start Program",
			"Program exited",       /*   Number 10   */
			"Program terminated",
			"Program received signal",
			"Unknown Break\nQuit Debugger ?",
			"Can\'t find file %s",
			"Can\'t create named pipe",  /*   Number 15   */
			"Breakpoint",
			"(sig ",
			"program exited",
			"signal",
			"interrupt",      /* Number 20  */
			"stopped in",
			"BREAKPOINT",
			"Child process terminated normally",
			"software termination",
			"Continuing.\n",  /* Number 25  */
			"No stack.",
			"no process"
		  };

extern char *e_p_msg[];

int e_deb_inp(FENSTER *f)
{
 ECNT *cn = f->ed;
 int c = 0;

 f = cn->f[cn->mxedt];
 c = e_getch();
 switch(c)
 {
  case 'p':
  case ('p' - 'a' + 1):
   e_deb_out(f);
   break;
  case 'o':
  case ('o' - 'a' + 1):
   e_deb_options(f);
   break;
  case 'b':
  case ('b' - 'a' + 1):
   e_breakpoint(f);
   break;
  case 'm':
  case ('m' - 'a' + 1):
   e_remove_breakpoints(f);
   break;
  case 'a':
  case 1:
   e_remove_all_watches(f);
   break;
  case 'd':
  case ('d' - 'a' + 1):
   e_delete_watches(f);
   break;
  case 'w':
  case ('w' - 'a' + 1):
   e_make_watches(f);
   break;
  case 'e':
  case ('e' - 'a' + 1):
   e_edit_watches(f);
   break;
  case 'k':
  case ('k' - 'a' + 1):
   e_deb_stack(f);
   break;
  case 't':
  case ('t' - 'a' + 1):
   e_deb_trace(f);
   break;
  case 's':
  case ('s' - 'a' + 1):
   e_deb_next(f);
   break;
  case 'r':
  case 'c':
  case ('r' - 'a' + 1):
  case ('c' - 'a' + 1):
   e_deb_run(f);
   break;
  case 'q':
  case ('q' - 'a' + 1):
   e_d_quit(f);
   break;
  case 'u':
  case ('u'-'a'+1):
   e_d_goto_cursor(f);
   break;
  case 'f':
  case ('f'-'a'+1):
   e_d_finish_func(f);
   break;
  default:
   return(c);
 }
 return(0);
}

int e_d_q_quit(FENSTER *f)
{
 int ret = e_message(1, e_d_msg[ERR_QUITDEBUG], f);

 if (ret == 'Y')
  e_d_quit(f);
 return(0);
}

int e_debug_switch(FENSTER *f, int c)
{
 switch (c)
 {
  case F7:
   e_deb_trace(f);
   break;
  case F8:
   e_deb_next(f);
   break;
  case CF10:
   e_deb_run(f);
   break;
  case CF2:
   e_d_q_quit(f);
   break;
  default:
   if (f->ed->edopt & ED_CUA_STYLE)
   {
    switch (c)
    {
     case F5:
      e_breakpoint(f);
      break;
     case CF5:
      e_make_watches(f);
      break;
     case CF3:
      e_deb_stack(f);
      break;
     default:
      return(c);
    }
   }
   else
   {
    switch (c)
    {
     case CF8:
      e_breakpoint(f);
      break;
     case CF7:
      e_make_watches(f);
      break;
     case CF6:
      e_deb_stack(f);
      break;
     default:
      return(c);
    }
   }
 }
 return(0);
}

/*  Input Routines   */
int e_e_line_read(int n, signed char *s, int max)
{
 int i, ret = 0;

 for (i = 0; i < max - 1; i++)
 {
  ret = read(n, s + i, 1);
  if (ret != 1 || s[i] == EOF || s[i] == '\n'|| s[i] == '\0')
   break;
 }
 if (ret != 1 && i == 0)
  return(-1);
 s[i+1] = '\0';
 jdb_trace("e_e_line_read: fd=%d i=%d ret=%d s='%s'\n", n, i, ret, (char*)s);
 if (e_deb_type == 1 && s[i] == '*')
  return(1);
 if (e_deb_type == 3 && s[i] == '>')
  return(1);
 else if (e_deb_type == 2 && i > 4 && s[i] == ' ' && !strncmp(s+i-5, "(dbx)", 5))
  return(1);
 else if (e_deb_type == 0 && i > 4 && s[i] == ' ' && !strncmp(s+i-5, "(gdb)", 5))
  return(1);
 else if (e_deb_type == 5 && i > 4 && s[i] == ' ' && !strncmp(s+i-5, "(Pdb)", 5))
  return(1);
 else if (e_deb_type == 4 && i > 0 &&
   ((s[i] == ' ' && s[i-1] == ']') ||
    (s[i] == ' ' && s[i-1] == '>')))
  return(1);
 return(2);
}

int e_d_line_read(int n, signed char *s, int max, int sw, int esw)
{
 static char wt = 0, esc_sv = 0, str[12];
 int i, j, ret = 0, kbdflgs;

 if (esw)
 {  if((ret = e_e_line_read(efildes[0], s, max)) >= 0) return(ret);  }
 else
 {  while(e_e_line_read(efildes[0], s, max) >= 0);  }
 for(i = 0; i < max - 1; i++)
 {
  if (esc_sv)  {  s[i] = WPE_ESC;  esc_sv = 0;  continue;  }
  kbdflgs = fcntl(n, F_GETFL, 0 );
  fcntl( n, F_SETFL, kbdflgs | O_NONBLOCK);
  while ((ret = read(n, s + i, 1)) <= 0 && i == 0 && wt >= sw)
  {
   if (ret == 0)
   {
    jdb_trace("e_d_line_read: EOF on pipe (debugger exited)\n");
    fcntl(n, F_SETFL, kbdflgs & ~O_NONBLOCK);
    return(-1);
   }
   e_d_wait_for_input(n);
   if (e_d_getchar() == D_CBREAK) return(-1);
  }
  fcntl( n, F_SETFL, kbdflgs & ~O_NONBLOCK);
  if (ret == -1) break;
  else if (ret != 1 ||  s[i] == EOF || s[i] == '\0') break;
  else if(s[i] == '\n' || s[i] == WPE_CR)  break;
  else if(s[i] == WPE_ESC)
  {  s[i] = '\0';  esc_sv = 1;  break;  }
 }
 if (ret != 1)
 {
  s[i] = '\0';
  jdb_trace("e_d_line_read: ret!=1 path, i=%d s='%s' (hex:", i, (char*)s);
  { int _k; for (_k = 0; _k < i; _k++)
    jdb_trace(" %02x", (unsigned char)s[_k]);
  }
  jdb_trace(")\n");
  if(e_deb_type == 1 && i > 0 && s[i-1] == '*')
  {  str[0] = 0;  wt = 0;   return(1);  }
  else if(e_deb_type == 3 && i > 0 && s[i-1] == '>')
  {  str[0] = 0;  wt = 0;   return(1);  }
  else if(e_deb_type == 4 && i > 1 &&
    ((s[i-1] == ' ' && s[i-2] == ']') ||
     (s[i-1] == ' ' && s[i-2] == '>')))
  {  jdb_trace("e_d_line_read: jdb prompt DETECTED\n");
     str[0] = 0;  wt = 0;   return(1);  }
  else if(e_deb_type == 4)
  {  jdb_trace("e_d_line_read: jdb prompt NOT detected (i=%d, s[i-1]=%02x s[i-2]=%02x)\n",
               i, i>0?(unsigned char)s[i-1]:0, i>1?(unsigned char)s[i-2]:0);
  }
  if(e_deb_type == 0)
  {
   if(i > 5 && !strncmp(s+i-6, "(gdb) ", 6))
   {  str[0] = 0;  wt = 0;   return(1);  }
   else if(i < 6)
   {
    for (j = 0; j <= i; j++) str[6+j] = s[j];
    if (!strncmp(str+i-6, "(gdb) ", 6))
    {  str[0] = '\0';  wt = 0;  return(1);  }
   }
  }
  else if (e_deb_type == 2)
  {
   if(i > 5 && !strncmp(s+i-6, "(dbx) ", 6))
   {  str[0] = 0;  wt = 0;   return(1);  }
   else if(i < 6)
   {
    for(j = 0; j <= i; j++) str[6+j] = s[j];
    if(!strncmp(str+i-6, "(dbx) ", 6))
    {  str[0] = '\0';  wt = 0;  return(1);  }
   }
  }
  else if (e_deb_type == 5)
  {
   if(i > 5 && !strncmp(s+i-6, "(Pdb) ", 6))
   {  str[0] = 0;  wt = 0;   return(1);  }
   else if(i < 6)
   {
    for(j = 0; j <= i; j++) str[6+j] = s[j];
    if(!strncmp(str+i-6, "(Pdb) ", 6))
    {  str[0] = '\0';  wt = 0;  return(1);  }
   }
  }
 }
 else
 {
  s[i+1] = '\0';
  jdb_trace("e_d_line_read: normal line, i=%d s='%s'\n", i, (char*)s);
 }
 if (i != 0) wt = 0;
 else wt++;
 if(i > 4) for(j = 0; j < 6; j++) str[j] = s[j+i-5];
 return(0);
}

int e_d_dum_read()
{
 char str[256];
 int ret;

 jdb_trace("e_d_dum_read: entry\n");
 while ((ret = e_d_line_read(wfildes[0], str, 128, 0, 0)) == 0 || ret == 2)
 {
  jdb_trace("e_d_dum_read: line_read ret=%d str='%s'\n", ret, str);
  if (ret == 2)
   e_d_error(str);
 }
 jdb_trace("e_d_dum_read: exit ret=%d str='%s'\n", ret, str);
 return(ret);
}

/* Output Routines */
int e_d_p_exec(FENSTER *f)
{
 ECNT *cn = f->ed;
 BUFFER *b;
 SCHIRM *s;
 int ret, i, is, j;
 char str[512];

 for (i = cn->mxedt; i > 0 && strcmp(cn->f[i]->datnam, "Messages"); i--)
  ;
 if (i <= 0)
 {  e_edit(cn, "Messages");  i = cn->mxedt;  }
 f = cn->f[i];
 b = cn->f[i]->b;
 s = cn->f[i]->s;
 if (b->bf[b->mxlines-1].len != 0)
  e_new_line(b->mxlines, b);
 for (j = 0, i = is = b->mxlines-1;
   (ret = e_d_line_read(wfildes[0], str, 512, 0, 0)) == 0; )
 {
  if (ret == -1)
   return(ret);
  print_to_end_of_buffer(b, str, b->mx.x);
 }
 if (ret == 1)
 {
  e_new_line(i, b);
  for (j = 0; j < NUM_COLS_ON_SCREEN_SAFE - 2 && str[j] != '\n' &&
    str[j] != '\0'; j++)
   *(b->bf[i].s+j) = str[j];
  b->b.y = i;
  b->b.x = b->bf[i].len = j;
  b->bf[i].nrc = j;
 }
 b->b.y = b->mxlines-1;
 b->b.x = 0;

 e_rep_win_tree(cn);
 return(ret);
}

/*    Help Routines   */
int e_d_getchar()
{
 int i = 1, fd;
 char c = 0, kbdflgs = 0;
 static FILE *_gc_dbg = NULL;

 if (WpeIsXwin()) fd = rfildes[0];
 else fd = 0;

/*   if(WpeIsXwin() || e_deb_mode)    */
 {
  kbdflgs = fcntl(fd, F_GETFL, 0 );
  fcntl( fd, F_SETFL, kbdflgs | O_NONBLOCK);
 }
#ifndef NO_XWINDOWS
 if (WpeIsXwin())
 {
  XEvent _ev;
  KeySym _ks;
  char _buf[8];

  XFlush(WpeXInfo.display);
  while (XPending(WpeXInfo.display))
  {
   XNextEvent(WpeXInfo.display, &_ev);
   if (_ev.type == KeyPress)
   {
    if (XLookupString(&_ev.xkey, _buf, sizeof(_buf), &_ks, NULL) == 1)
    {
     c = _buf[0];
     break;
    }
   }
   else if (_ev.type == Expose)
   {
    XCopyArea(WpeXInfo.display, WpeXInfo.backbuf, WpeXInfo.window,
      WpeXInfo.gc, _ev.xexpose.x, _ev.xexpose.y,
      _ev.xexpose.width, _ev.xexpose.height,
      _ev.xexpose.x, _ev.xexpose.y);
   }
  }
 }
#endif
 if (c || (i = read(fd, &c, 1)) == 1)
 {
  if (c == CTRLC)
  {
/*	 if (WpeIsXwin() || e_deb_mode)    */
   fcntl(fd, F_SETFL, kbdflgs & ~O_NONBLOCK);
   e_d_switch_out(0);
   i = e_message(1, e_d_msg[ERR_CTRLCPRESS], WpeEditor->f[WpeEditor->mxedt]);
   if (i == 'Y')
   {
    e_d_quit(WpeEditor->f[WpeEditor->mxedt]);
    return(D_CBREAK);
   }
   else
    return(c);
  }
  else if (e_d_pty_master >= 0 && ((c >= 32 && c < 127) || c == '\r' || c == '\n' || c == '\b'))
  {
   char _ch = (c == '\r') ? '\n' : c;
   write(e_d_pty_master, &_ch, 1);
   e_d_echo_input_char(c);
  }
  else
   write(rfildes[1], &c, 1);
 }
/*   if (WpeIsXwin() || e_deb_mode)   */
 fcntl(fd, F_SETFL, kbdflgs & ~O_NONBLOCK);
 return(i == 1 ? c : 0);
}

int e_d_is_watch(int c, FENSTER *f)
{
 if (strcmp(f->datnam, "Watches"))
  return(0);
 if(c == EINFG)
  return(e_make_watches(f));
 else if (c == ENTF)
  return(e_delete_watches(f));
 else
  return(0);
}

int e_d_quit_basic(FENSTER *f)
{
 int i, kbdflgs;
 /* Ignore SIGPIPE before writing to pipes -- the debugger may have
    already exited and closed its end, causing write() to SIGPIPE. */
 signal(SIGPIPE, SIG_IGN);

 if (!e_d_swtch)
  return 0;
 if (rfildes[1] >= 0)
 {
  if (e_deb_type == 0 || e_deb_type == 3)
   write(rfildes[1], "q\ny\n", 4);
  else if (e_deb_type == 1)
   write(rfildes[1], "q\n", 2);
  else if (e_deb_type == 2)
   write(rfildes[1], "quit\n", 5);
  else if (e_deb_type == 4)
   write(rfildes[1], "quit\n", 5);
  else if (e_deb_type == 5)
   write(rfildes[1], "q\ny\n", 4);  /* pdb: quit + confirm */
 }
 jdb_trace("e_d_quit_basic: quitting debugger type=%d\n", e_deb_type);
 kbdflgs = fcntl(0, F_GETFL, 0 );
 fcntl(0, F_SETFL, kbdflgs & ~O_NONBLOCK);
 e_d_swtch = 0;
 if (e_d_pid)
 {
  kill(e_d_pid, SIGKILL);
  waitpid(e_d_pid, NULL, 0);  /* reap zombie */
  e_d_pid = 0;
 }
 if (!WpeIsXwin())
 {
  if (e_d_out)
   FREE(e_d_out);
  e_d_out = NULL;
 }
 if (rfildes[1] >= 0)
 {  close(rfildes[1]);  rfildes[1] = -1;  }
 if (wfildes[0] >= 0)
 {  close(wfildes[0]);  wfildes[0] = -1;  }
 if (efildes[0] >= 0)
 {  close(efildes[0]);  efildes[0] = -1;  }
 if (rfildes[0] >= 0)
 {  close(rfildes[0]);  rfildes[0] = -1;  }
 if (wfildes[1] >= 0)
 {  close(wfildes[1]);  wfildes[1] = -1;  }
 e_d_pty_close();
 _pty_line_len = 0;
 _nb_len = 0;
 _messages_activated = 0;
 e_d_async_pending = 0;
 e_d_accum.active = 0;
 e_d_accum.f = NULL;
 e_d_nbrpts = 0;
 if (WpeIsXwin())
 {
  remove(npipe[0]);
  remove(npipe[1]);
  remove(npipe[2]);
  remove(npipe[3]);
  remove(npipe[4]);
 }
 else
 {
  if (efildes[1] >= 0)
  {  close(efildes[1]);  efildes[1] = -1;  }
  if (!e_deb_mode)
   e_g_sys_end();
  else
  {
   e_d_switch_out(1);
   fk_locate(MAXSCOL, MAXSLNS);
   e_putp("\r\n");
   e_putp(att_no);
   e_d_switch_out(0);
  }
 }
}

int e_d_quit(FENSTER *f)
{
 ECNT *cn = f->ed;
 int i;
 jdb_trace("e_d_quit: CALLED, e_d_swtch=%d, e_deb_type=%d\n",
           e_d_swtch, e_deb_type);
 e_d_quit_basic(f);
 e_d_p_message(e_d_msg[ERR_ENDDEBUG], f, 1);
 WpeMouseChangeShape(WpeEditingShape);
 e_d_delbreak(f);
 /* Switch back to the source file, not Messages.
    The user expects to continue editing after quitting the debugger. */
 for (i = cn->mxedt; i > 0; i--)
  if (e_check_c_file(cn->f[i]->datnam))
  {
   e_switch_window(cn->edt[i], cn->f[cn->mxedt]);
   return(0);
  }
 return(0);
}

/*    Watches   */
int e_d_add_watch(char *str, FENSTER *f)
{
 int ret;

 ret = e_add_arguments(str, "Add Watch", f, 0, AltA, &f->ed->wdf);
 if (ret != WPE_ESC)
 {
  f->ed->wdf = e_add_df(str, f->ed->wdf);
 }
 fk_cursor(1);
 return(ret);
}

int e_remove_all_watches(FENSTER *f)
{
 ECNT *cn = f->ed;
 int i, n;

 if (e_d_nwtchs < 1) return(0);
 for (n = 0; n < e_d_nwtchs;n++) FREE(e_d_swtchs[n]);
 FREE(e_d_swtchs);
 FREE(e_d_nrwtchs);
 e_d_nwtchs = 0;
 for (i = cn->mxedt; i > 0; i--)
 {
  if (!strcmp(cn->f[i]->datnam, "Watches"))
  {
   e_switch_window(cn->edt[i], cn->f[cn->mxedt]);
   e_close_window(cn->f[cn->mxedt]);
   break;
  }
 }
 e_close_buffer(e_p_w_buffer);
 e_p_w_buffer = NULL;
 return(0);
}

int e_delete_watches(FENSTER *f)
{
 ECNT *cn = f->ed;
 BUFFER *b = cn->f[cn->mxedt]->b;
 int n;

 f = cn->f[cn->mxedt];
 if (e_d_nwtchs < 1 || strcmp(f->datnam, "Watches"))
  return(0);
 for (n = 0; n < e_d_nwtchs && e_d_nrwtchs[n] <= b->b.y; n++)
  ;
 FREE(e_d_swtchs[n - 1]);
 for (; n < e_d_nwtchs; n++)
  e_d_swtchs[n - 1] = e_d_swtchs[n];
 e_d_nwtchs--;
 e_d_swtchs = REALLOC(e_d_swtchs, e_d_nwtchs * sizeof(char *));
 e_d_nrwtchs = REALLOC(e_d_nrwtchs, e_d_nwtchs * sizeof(int));
 e_d_p_watches(f, 0);
 return(0);
}

int e_make_watches(FENSTER *f)
{
 char str[128];
 int i, y;

 if ((f->ed->mxedt > 0) && (strcmp(f->datnam, "Watches") == 0))
 { /* sets y=number of watch we're inserting */
  for(y = 0; y < e_d_nwtchs && e_d_nrwtchs[y] < f->b->b.y; y++)
   ;
 }
 else
  y = e_d_nwtchs;
 if (f->ed->wdf && f->ed->wdf->anz > 0)
  strcpy(str, f->ed->wdf->name[0]);
 else
  str[0] = '\0';
 if (e_d_add_watch(str, f))
 {
  e_d_nwtchs++;
  if (e_d_nwtchs == 1)
  {
   e_d_swtchs = MALLOC(sizeof(char *));
   e_d_nrwtchs = MALLOC(sizeof(int));
  }
  else
  {
   e_d_swtchs = REALLOC(e_d_swtchs, e_d_nwtchs * sizeof(char *));
   e_d_nrwtchs = REALLOC(e_d_nrwtchs, e_d_nwtchs * sizeof(int));
  }
  
  /*
    move watch number y and following up one position so that we can insert 
    at position y 
  */
  for (i = e_d_nwtchs - 1; i > y; i--)
  {
   e_d_swtchs[i] = e_d_swtchs[i-1];
   
   /* The following instruction is pointless as e_d_nrwtchs[i] is invalidated
      by inserting the new watch and has to be recomputed by e_d_p_watches()
   */
   e_d_nrwtchs[i] = e_d_nrwtchs[i-1];
  }
  e_d_swtchs[y] = MALLOC(strlen(str) + 1); /* insert...                    */
  strcpy(e_d_swtchs[y], str);              /*       ... new watch at pos y */
  e_d_p_watches(f, 1);
  return(0);
 }
 return(-1);
}

int e_edit_watches(FENSTER *f)
{
 BUFFER *b = f->ed->f[f->ed->mxedt]->b;
 char str[128];
 int l;

 if (strcmp(f->datnam, "Watches"))
  return(0);
 for (l = 0; l < e_d_nwtchs && e_d_nrwtchs[l] <= b->b.y; l++)
  ;
 if (l == e_d_nwtchs && b->bf[b->b.y].len == 0)
  return(e_make_watches(f));
 strcpy(str, e_d_swtchs[l - 1]);
 if (e_d_add_watch(str, f))
 {
  e_d_swtchs[l - 1] = REALLOC(e_d_swtchs[l - 1], strlen(str) + 1);
  strcpy(e_d_swtchs[l - 1], str);
  e_d_p_watches(f, 0);
  return(0);
 }
 return(-1);
}

/* Among other things, e_d_p_watches() must recompute e_d_nrwtchs when
   called from e_edit_watches(), 
   but has code paths that don't do this ==> possible BUG
*/
int e_d_p_watches(FENSTER *f, int sw)
{
 ECNT *cn = f->ed;
 BUFFER *b;
 SCHIRM *s;
 int iw, i, k = 0, l, ret;
 char str1[256], *str; /* is 256 always large enough? */
 char *str2;

 e_d_switch_out(0);
 if ((e_d_swtch > 2) && (e_d_p_stack(f, 0) == -1))
  return(-1);
 /* Find the watch window */
 for (iw = cn->mxedt; iw > 0 && strcmp(cn->f[iw]->datnam, "Watches"); iw--);
 if (iw == 0 && !e_d_nwtchs)
 { /* if no watches and the mysterious iw!=0 then just repaint window tree */
  e_rep_win_tree(cn);
  return(0);
 }
 else if (iw == 0)
 {
  if(e_edit(cn, "Watches"))
  {
   return(-1);
  }
  else
  {
   iw = cn->mxedt;
  }
 }
 f = cn->f[iw];
 b = cn->f[iw]->b;
 s = cn->f[iw]->s;
 
 /* free all lines of BUFFER b */
 e_p_red_buffer(b);
 FREE(b->bf[0].s);
 b->mxlines=0;

 for (i = 0, l = 0; l < e_d_nwtchs; l++)
 {
  str = str1;
  
  /* Create appropriate command for the debugger */
  if (e_deb_type == 0 || e_deb_type == 3)
  {
   sprintf(str1, "p %s\n", e_d_swtchs[l]);
  }
  else if (e_deb_type == 1)
  {
   sprintf(str1, "%s/\n", e_d_swtchs[l]);
  }
  else if (e_deb_type == 2)
  {
   sprintf(str1, "print %s\n", e_d_swtchs[l]);
  }

  /* Send command to debugger */
  if(e_d_swtch)
  {
   write(rfildes[1], str1, strlen(str1));
  }

  /* If no debugger or no response, give message of no symbol in context */
  if (!e_d_swtch || (ret = e_d_line_read(wfildes[0], str1, 256, 0, 0)) == 1)
  {
   strcpy(str1, e_d_msg[ERR_NOSYMBOL]);
   k = 0;
  }
  else /* Debugger successfully returned a value */
  {
   if (ret == -1)
   {
    return(ret); /* BUG? e_d_nrwtchs not initialized if this return is taken */
   }
   str = MALLOC(strlen(str1) + 1);
   strcpy(str, str1);
   while ((ret = e_d_line_read(wfildes[0], str1, 256, 0, 0)) == 0 || ret == 2)
   {
    if (ret == -1) return(ret); /* BUG? e_d_nrwtchs not initialized if this return is taken */
    if (ret == 2) e_d_error(str1);
    str = REALLOC(str, (k = strlen(str)) + strlen(str1) + 1);
    if (str[k-1] == '\n') str[k-1] = ' ';
    strcat(str, str1);
   }

   /* Find the beginning of the information (depends on debugger output
     format) */
   if (e_deb_type == 0 || e_deb_type == 2 || e_deb_type == 3)
   {
    if (e_deb_type == 3 && str[0] == '0')
    {
     for(k = 1; str[k] != '\0' && !isspace(str[k]); k++);
    }
    else
     for(k = 0; str[k] != '\0' && str[k] != '='; k++);
    if (str[k] == '\0')
    {
     if (str != str1) FREE(str);
     str = str1;
     strcpy(str, e_d_msg[ERR_NOSYMBOL]);
     k = 0;
    }
    for(k++; str[k] != '\0' && isspace(str[k]); k++);
   }
   else if(e_deb_type == 1)
   {
    for (k = 0; str[k] != '\0' && str[k] != ':'; k++);
    if (str[k] == '\0') k = 0;
    else k++;
   }
  }

  /* Print variable name */
  for ( ; str[k] != '\0' && isspace(str[k]); k++);
  str2 = WpeMalloc(strlen(e_d_swtchs[l]) + strlen(str + k) + 4);
  sprintf(str2, "%s: %s", e_d_swtchs[l], str + k);

  e_d_nrwtchs[l] = b->mxlines;
  print_to_end_of_buffer(b, str2, b->mx.x);

  /* Free any allocated string */
  WpeFree(str2);
  if(str != str1)
  {
   FREE(str);
  }
 }

 e_new_line(b->mxlines, b);
 fk_cursor(1);
/* if (b->b.y > i || sw) b->b.y = i;*/
 if (sw && iw != cn->mxedt) e_switch_window(cn->edt[iw], cn->f[cn->mxedt]);
 else e_rep_win_tree(cn);
/* e_d_switch_out(0);   */
 return(0);
}

int e_p_show_watches(FENSTER *f)
{
 int i;

 for (i = f->ed->mxedt; i > 0; i--)
  if (!strcmp(f->ed->f[i]->datnam, "Watches"))
  {
   e_switch_window(f->ed->edt[i], f->ed->f[f->ed->mxedt]);
   break;
  }
 if (i <= 0 && e_edit(f->ed, "Watches"))
 {
  return(-1);
 }
 return(0);
}

/***************************************/
/***  reinitialize watches from prj  ***/
int e_d_reinit_watches(FENSTER * f,char * prj)
{
 int i,e,g,q,y,r;
 char * prj2;

 for(i = f->ed->mxedt; i > 0; i--)
 {
  if (!strcmp(f->ed->f[i]->datnam, "Watches"))
  {  
   e_remove_all_watches(f->ed->f[f->ed->edt[i]]);
   break; 
  }
 }
 g=strlen(prj);
 prj2=MALLOC(sizeof(char)*(g+1));
 strcpy(prj2,prj);
 q=0;
 y=0;
 r=0;
 while(q<g) 
 {
  e=q;
  while(prj2[e]!=';' && e<g) e++;
  prj2[e]='\0';
  q=e+1;
  r++;
 } 
 e_d_nwtchs=r;
 e_d_swtchs = (char **) MALLOC(e_d_nwtchs * sizeof(char *));
 e_d_nrwtchs =(int *) MALLOC(e_d_nwtchs * sizeof(int));   

 for(e=0,q=0;e<r;e++)
 {
  e_d_swtchs[e] = MALLOC(strlen(prj2+q)+1);
  strcpy(e_d_swtchs[e], prj2+q); 
  q+=strlen(prj2+q)+1;
 } 
 FREE(prj2);
 e_d_p_watches(f, 1);   
 return 0;
}
/***************************************/

/*  stack   */
int e_deb_stack(FENSTER *f)
{
 e_d_switch_out(0);
 return(e_d_p_stack(f, 1));
}

int e_d_p_stack(FENSTER *f, int sw)
{
 ECNT *cn = f->ed;
 BUFFER *b;
 SCHIRM *s;
 int is, i, j, k, l, ret;
 char str[256];

 if (e_d_swtch < 3)
  return(e_error(e_d_msg[ERR_NOTRUNNING], 0, f->fb));
 for (i = 0; i < SVLINES; i++)
  e_d_out_str[i][0] = '\0';
 for (is = cn->mxedt; is > 0 && strcmp(cn->f[is]->datnam, "Stack"); is--)
  ;
 if (!sw && is == 0)
  return(0);
 if(is == 0)
 {
  if (e_edit(cn, "Stack"))
   return(-1);
  else
   is = cn->mxedt;
 }
 f = cn->f[is];
 b = cn->f[is]->b;
 s = cn->f[is]->s;
 if (!e_d_swtch)
  return(0);
 if (e_deb_type == 0)
  write(rfildes[1], "bt\n", 3);
 else if (e_deb_type == 1 || e_deb_type == 3)
  write(rfildes[1], "t\n", 2);
 else if (e_deb_type == 2)
  write(rfildes[1], "where\n", 6);
 while ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == 2)
  e_d_error(str);
 if (ret == -1)
  return(-1);
 i = j = 0;
 if (ret == 1)
 {
  e_d_error(e_d_msg[ERR_PROGEXIT]);
  return(e_d_quit(f));
 }
 while(ret != 1)
 {
  k = 0;
  do
  {
   if (i >= b->mxlines)
    e_new_line(i, b);
   if ((i > 0 && j == 0 && *(b->bf[i-1].s+b->bf[i-1].len-1) == '\\') ||
     (!e_deb_type && j == 0 && (k > 0 || str[k] != '#')))
   {
    for(j = 0; j < 3; j++)
     b->bf[i].s[j] = ' ';
   }
   for(; isspace(str[k]); k++)
    ;
   for(; j < NUM_COLS_ON_SCREEN_SAFE - 2 && str[k] != '\n' && str[k] != '\0';
     j++, k++)
    *(b->bf[i].s+j) = str[k];
   if (str[k] != '\0')
   {
    if (str[k] != '\n')
    {
     for(l = j-1; l > 2 && !isspace(b->bf[i].s[l]) && b->bf[i].s[l] != '=';
       l--)
      ;
     if (l > 2)
     {
      k -= (j - l - 1);
      for (l++; l < j; l++)
       b->bf[i].s[l] = ' ';
     }
     *(b->bf[i].s+j) = '\\';
     *(b->bf[i].s+j+1) = '\n';
     *(b->bf[i].s+j+2) = '\0';
     j++;
    }
    else
    {
     *(b->bf[i].s+j) = '\n';
     *(b->bf[i].s+j+1) = '\0';
    }
   }
   b->bf[i].len = j;
   b->bf[i].nrc = j + 1;
   if (j != 0 && str[k] != '\0')
   {
    i++;
    j = 0;
   }
   else
    j--;
  }
  while (str[k] != '\n' && str[k] != '\0');
  while ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == 2)
   e_d_error(str);
  if (ret == -1)
   return(-1);
 }
 for (; i < b->mxlines; i++)
  e_del_line(i, b, s);
 if (sw && is != cn->mxedt)
  e_switch_window(cn->edt[is], cn->f[cn->mxedt]);
 e_rep_win_tree(cn);
 return(0);
}

int e_make_stack(FENSTER *f)
{
   char file[128], str[128], *tmpstr = MALLOC(1);
   int i, ret, line = 0, dif;
   BUFFER *b = f->ed->f[f->ed->mxedt]->b;
   e_d_switch_out(0);
   if(e_deb_type != 1)
   {  tmpstr[0] = '\0';
      if(e_deb_type == 0)
      {  for(i = dif = 0; i <= b->b.y; i++)
	    if(b->bf[i].s[0] == '#') dif = atoi((char *) (b->bf[i].s + 1));
	 for(i = b->b.y; i >= 0 && b->bf[i].s[0] != '#'; i--);
	 if(i < 0) return(1);
	 for(; i < b->mxlines; i++)
	 {  if(!(tmpstr = REALLOC(tmpstr, strlen(tmpstr) + b->bf[i].len + 2)))
	    {  e_error(e_msg[ERR_LOWMEM], 0, f->fb);  return(-1);  }
	    strcat(tmpstr, (char *) b->bf[i].s);
	    if(i == b->mxlines-1 || b->bf[i+1].s[0] == '#') break;
	    else if(tmpstr[strlen(tmpstr)-2] == '\\')
	       tmpstr[strlen(tmpstr)-2] = '\0';
	    else if(tmpstr[strlen(tmpstr)-1] == '\n')
	       tmpstr[strlen(tmpstr)-1] = '\0';
	 }
      }
      else
      {  for(i = 1, dif = 0; i <= b->b.y; i++)
	    if(b->bf[i-1].s[b->bf[i-1].len - 1] != '\\') dif++;
	 for(i = b->b.y; i > 0 && b->bf[i-1].s[b->bf[i-1].len - 1] == '\\';
	    i--);
	 if(i == 0 && b->bf[i].len == 0) return(1);
	 for(; i < b->mxlines; i++)
	 {  if(!(tmpstr = REALLOC(tmpstr, strlen(tmpstr) + b->bf[i].len + 2)))
	    {  e_error(e_msg[ERR_LOWMEM], 0, f->fb);  return(-1);  }
	    strcat(tmpstr, (char *) b->bf[i].s);
	    if(i == b->mxlines-1 || b->bf[i].s[b->bf[i].len - 1] != '\\')
	       break;
	    else if(tmpstr[strlen(tmpstr)-2] == '\\')
	       tmpstr[strlen(tmpstr)-2] = '\0';
	    else if(tmpstr[strlen(tmpstr)-1] == '\n')
	       tmpstr[strlen(tmpstr)-1] = '\0';
	 }
      }

      if(e_deb_type == 3 && (line = e_make_line_num2(tmpstr, file)) < 0)
	 return(e_error(e_d_msg[ERR_NOSOURCE], 0, f->fb));
      else if(e_deb_type != 3 && (line = e_make_line_num(tmpstr, file)) < 0)
	 return(e_error(e_d_msg[ERR_NOSOURCE], 0, f->fb));
      if(dif > e_d_nstack)
	 sprintf(str, "%s %d\n",
	 e_deb_type != 3 ? "up" : "down", dif - e_d_nstack);
      else if(dif < e_d_nstack)
	 sprintf(str, "%s %d\n",
	 e_deb_type != 3 ? "down" : "up", e_d_nstack - dif);
      if(dif != e_d_nstack)
      {  write(rfildes[1], str, strlen(str));
	 while((ret = e_d_line_read(wfildes[0], str, 128, 0, 0)) == 0 || ret == 2)
	    if( ret == 2) e_d_error(str);
	 if(ret == -1) return(ret);
	 e_d_nstack = dif;
      }
   }
   else if(e_deb_type == 1)
   {  for(i = b->b.y; i >= 0 && (line =
	 e_make_line_num2((char *)b->bf[i].s, file)) < 0; i--);
   }
   if(e_d_p_watches(f, 0) == -1) return(-1);
   e_d_goto_break(file, line, f);
   return(0);
}
/*******************************************************/
/** resyncing schirm - screen output with breakpoints **/

int e_brk_schirm(FENSTER *f)
{
 int i;
 int n;

 SCHIRM *s = f->s;
 s->brp= REALLOC(s->brp, sizeof(int));
 s->brp[0]=0;
 for(i=0;i<e_d_nbrpts;i++)
 {
  if(!strcmp(f->datnam,e_d_sbrpts[i]))
  {
   for(n=1;n<= (s->brp[0]);n++) if(e_d_ybrpts[i]==(s->brp[n])) break;
   if(n>s->brp[0]) 
   {
    /****  New break, not in schirm  ****/   
    (s->brp[0])++;
    s->brp = REALLOC(s->brp, (s->brp[0]+1) * sizeof(int));
    s->brp[s->brp[0]] = e_d_ybrpts[i]-1; 
   }
  }
 }
 return 0;
}
/*****************************************/

/*******************************************/
/***  reinitialize breakpoints from prj  ***/
int e_d_reinit_brks(FENSTER * f,char * prj)
{
   int line,e,g,q,r;
   char * p,*name,*prj2;
/***  remove breakpoints, schirms will be synced later  ***/

   e_remove_breakpoints(f);
   g=strlen(prj);
   prj2=MALLOC(sizeof(char)*(g+1));
   strcpy(prj2,prj);
   q=0;
   r=0;
   while(q<g) 
   {
     e=q;
     while(prj2[e]!=';' && e<g) e++;
     prj2[e]='\0';
     q=e+1;
     r++;
   } 
/**** for sure ****/   
   e_d_nbrpts=0;
   
/**** allocate memory for breakpoints ****/   
   e_d_sbrpts = MALLOC(sizeof(char *) * r);
   e_d_ybrpts = MALLOC(sizeof(int) * r);
   e_d_nrbrpts = MALLOC(sizeof(int) * r);
   
   name=prj2;
   for(q=0;q<r;q++)
   {
     p=strrchr(name,':');
     e=strlen(name);
     if(p!=NULL)
     {
       *p='\0';
       {
         p++;
         line=atoi(p);
         if(line>0) {
/**** hopefully we have filename and line number ****/

	   e_d_ybrpts[e_d_nbrpts]=line;
	   e_d_sbrpts[e_d_nbrpts]=MALLOC(sizeof(char)*(strlen(name)+1));
	   strcpy(e_d_sbrpts[e_d_nbrpts],name);
	   e_d_nbrpts++;
	   
/**** needed to keep schirm in sync ****/
	   
	   for(g = f->ed->mxedt; g > 0; g--)
            if(!strcmp(f->ed->f[g]->datnam, name))
            {  
              e_brk_schirm(f->ed->f[g]);
            }
         }
       }
     }
     name+=e+1;
   }
   FREE(prj2);
   return 0;
}


/**** Recalculate breakpoints , because of line/block
    deleting/adding ****/
int e_brk_recalc(FENSTER *f, int start, int len)
{
 ECNT *cn = f->ed;
 BUFFER *b;
 int n, rend, count, yline;
 int *br_lines;

 if ((len == 0) || (cn == NULL))
  return 1;
 b = cn->f[cn->mxedt]->b;

 rend = start - 1 + abs(len);
 yline = b->b.y;

/**** deleting removed breakpoints ****/
 if (len < 0)
 {
  for (n = 0; n < e_d_nbrpts; n++)
   if ((!strcmp(f->datnam, e_d_sbrpts[n])) &&
     (e_d_ybrpts[n] <= (rend + 1)) && (e_d_ybrpts[n] >= (start + 1))) 
   {
    b->b.y = e_d_ybrpts[n] - 1;
    e_make_breakpoint(f, 0);
   }
 }

/**** scanning for breakpoints to move ****/
 for (count = 0, n = 0; n < e_d_nbrpts; n++)
  if ((!strcmp(f->datnam, e_d_sbrpts[n])) && (e_d_ybrpts[n] >= (start + 1)))
   count++;
 if (count == 0)
  return 1;
 br_lines = (int*)malloc(sizeof(int) * count);
 for (n = 0, count = 0; n < e_d_nbrpts; n++)
  if ((!strcmp(f->datnam, e_d_sbrpts[n])) && (e_d_ybrpts[n] >= (start + 1))) 
  {
   br_lines[count++] = e_d_ybrpts[n];
  }

/**** moving breakpoints ****/
 for(n = 0; n < count; n++) 
 {
  b->b.y = br_lines[n] - 1;
  e_make_breakpoint(f, 0);
 }
 for(n = 0; n < count; n++) 
 {
  b->b.y = br_lines[n] + len - 1;
  e_make_breakpoint(f, 0);
 }
 b->b.y = yline;
 free(br_lines);
 return 0;
}
/*****************************************/

/*  Breakpoints   */
int e_breakpoint(FENSTER *f)
{
 return(e_make_breakpoint(f, 0));
}

int e_remove_breakpoints(FENSTER *f)
{
 ECNT *cn = f->ed;
 int i;

 if (e_d_swtch)
 {
  if (!e_deb_type)
   write(rfildes[1], "d\ny\n", 4);
  else if (e_deb_type == 1)
   write(rfildes[1], "D\n", 2);
  else if (e_deb_type == 2)
   write(rfildes[1], "delete all\n", 11);
  else if (e_deb_type == 3)
   write(rfildes[1], "db *\n", 2);
 }
 for (i = 0; i < e_d_nbrpts; i++)
  FREE(e_d_sbrpts[i]);
 for (i = cn->mxedt; i >= 0; i--)
  if (DTMD_ISTEXT(cn->f[i]->dtmd))
   cn->f[i]->s->brp[0] = 0;
 e_d_nbrpts = 0;
 if (e_d_sbrpts)
 {
  FREE(e_d_sbrpts);
  e_d_sbrpts = NULL;
 }
 if (e_d_ybrpts)
 {
  FREE(e_d_ybrpts);
  e_d_ybrpts = NULL;
 }
 if (e_d_nrbrpts)
 {
  FREE(e_d_nrbrpts);
  e_d_nrbrpts = NULL;
 }
 e_rep_win_tree(cn);
 return(0);
}

int e_mk_brk_main(FENSTER *f, int sw)
{
 int i, ret;
 char eing[128], str[256];

 if (sw)
 {
  if (e_d_swtch)
  {
   if (e_deb_type == 0) sprintf(eing, "d %d\n", e_d_nrbrpts[sw-1]);
   else if (e_deb_type == 2)
    sprintf(eing, "delete %d\n", e_d_nrbrpts[sw-1]);
   else if (e_deb_type == 3)
    sprintf(eing, "db %d\n", e_d_nrbrpts[sw-1]);
   else if (e_deb_type == 4)
   {
    char cls[128];
    strcpy(cls, e_d_file);
    WpeStringCutChar(cls, '.');
    sprintf(eing, "clear %s.%s\n", cls, e_get_start_symbol());
   }
   else if (e_deb_type == 1)
   {
    sprintf(eing, "e %s\n", e_get_start_symbol());
    write(rfildes[1], eing, strlen(eing));
    if (e_d_dum_read() == -1) return(-1);
    sprintf(eing, "%d d\n", e_d_ybrpts[sw-1]);
   }
   jdb_trace("e_mk_brk_main(del): sending '%s'\n", eing);
   write(rfildes[1], eing, strlen(eing));
   if (e_d_dum_read() == -1) return(-1);
  }
  FREE(e_d_sbrpts[sw-1]);
  for (i = sw-1; i < e_d_nbrpts - 1; i++)
  {
   e_d_sbrpts[i] = e_d_sbrpts[i+1];
   e_d_ybrpts[i] = e_d_ybrpts[i+1];
   e_d_nrbrpts[i] = e_d_nrbrpts[i+1];
  }
  e_d_nbrpts--;
  if (e_d_nbrpts == 0)
  {
   FREE(e_d_sbrpts);
   e_d_sbrpts = NULL;
   FREE(e_d_ybrpts);
   e_d_ybrpts = NULL;
   FREE(e_d_nrbrpts);
   e_d_nrbrpts = NULL;
  }
 }
 else
 {
  e_d_nbrpts++;
  if (e_d_nbrpts == 1)
  {
   e_d_sbrpts = MALLOC(sizeof(char *));
   e_d_ybrpts = MALLOC(sizeof(int));
   e_d_nrbrpts = MALLOC(sizeof(int));
  }
  else
  {
   e_d_sbrpts = REALLOC(e_d_sbrpts, e_d_nbrpts * sizeof(char *));
   e_d_ybrpts = REALLOC(e_d_ybrpts, e_d_nbrpts * sizeof(int));
   e_d_nrbrpts = REALLOC(e_d_nrbrpts, e_d_nbrpts * sizeof(int));
  }
  e_d_sbrpts[e_d_nbrpts - 1] = MALLOC(1);
  if (e_d_swtch)
  {
   if (e_deb_type == 0)
   {
    sprintf(eing, "b %s\n", e_get_start_symbol());
    write(rfildes[1], eing, strlen(eing));
    while ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == 0 &&
      strncmp(str, "Breakpoint", 10))
     ;
    if (ret == -1) return(ret);
    if (ret == 2) e_d_error(str);
    e_d_nrbrpts[e_d_nbrpts - 1] = atoi(str+11);
    if (ret != 1 && e_d_dum_read() == -1) return(-1);
   }
   else if (e_deb_type == 2)
   {
    sprintf(eing, "stop in %s\n", e_get_start_symbol());
    write(rfildes[1], eing, strlen(eing));
    while ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == 0 &&
      str[0] != '(')
     ;
    if (ret == -1) return(ret);
    if (ret == 2) e_d_error(str);
    e_d_nrbrpts[e_d_nbrpts - 1] = atoi(str+1);
    if (ret != 1 && e_d_dum_read() == -1) return(-1);
   }
   else if (e_deb_type == 3)
   {
    sprintf(eing, "b %s\n", e_get_start_symbol());
    write(rfildes[1], eing, strlen(eing));
    while ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == 0 &&
      strncmp(str, "Added:", 6))
     ;
    ret = e_d_line_read(wfildes[0], str, 256, 0, 0);
    if (ret == -1) return(ret);
    if (ret == 2) e_d_error(str);
    e_d_nrbrpts[e_d_nbrpts - 1] = atoi(str+1);
    if (ret != 1 && e_d_dum_read() == -1) return(-1);
   }
   else if (e_deb_type == 4)
   {
    { char cls[128];
      strcpy(cls, e_d_file);
      WpeStringCutChar(cls, '.');
      sprintf(eing, "stop in %s.%s\n", cls, e_get_start_symbol());
    }
    jdb_trace("e_mk_brk_main: sending '%s'\n", eing);
    write(rfildes[1], eing, strlen(eing));
    if (e_d_dum_read() == -1) return(-1);
    e_d_nrbrpts[e_d_nbrpts - 1] = e_d_nbrpts;
   }
   else if (e_deb_type == 1)
   {
    sprintf(eing, "e %s\n", e_get_start_symbol());
    write(rfildes[1], eing, strlen(eing));
    if (e_d_dum_read() == -1) return(-1);
    sprintf(eing, "b\n");
    write(rfildes[1], eing, strlen(eing));
    if ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == -1)
     return(ret);
    if (ret == 2) e_d_error(str);
    if (ret != 1)
    {
     for (i = 0; str[i] && str[i] != ':'; i++)
      ;
     if (str[i])
      e_d_ybrpts[e_d_nbrpts - 1] = atoi(str+i+1);
     if (e_d_dum_read() == -1) return(-1);
    }
   }
  }
 }
 return(sw ? 0 : e_d_nbrpts);
}

int e_make_breakpoint(FENSTER *f, int sw)
{
 ECNT *cn = f->ed;
 SCHIRM *s = cn->f[cn->mxedt]->s;
 BUFFER *b = cn->f[cn->mxedt]->b;
 int ret, i;
 char eing[128], str[256];

 if (!sw)
 {
  if (!e_check_c_file(f->datnam))
   return(e_error(e_p_msg[ERR_NO_CFILE], 0, f->fb));
  for(i = 0; i < s->brp[0] && s->brp[i+1] != b->b.y; i++)
   ;
  if (i < s->brp[0])
  {
   for (i++; i < s->brp[0]; i++) s->brp[i] = s->brp[i+1];
   (s->brp[0])--;
   for (i = 0; i < e_d_nbrpts && (strcmp(e_d_sbrpts[i], f->datnam) ||
     e_d_ybrpts[i] != b->b.y+1); i++)
    ;
   if (e_d_swtch)
   {
    if (e_deb_type == 0) sprintf(eing, "d %d\n", e_d_nrbrpts[i]);
    else if (e_deb_type == 2)
     sprintf(eing, "delete %d\n", e_d_nrbrpts[i]);
    else if (e_deb_type == 3)
     sprintf(eing, "db %d\n", e_d_nrbrpts[i]);
    else if (e_deb_type == 4)
    {
     /* jdb: "clear Class:line" */
     char cls[128];
     strcpy(cls, e_d_sbrpts[i]);
     WpeStringCutChar(cls, '.');
     sprintf(eing, "clear %s:%d\n", cls, e_d_ybrpts[i]);
    }
    else if (e_deb_type == 1)
    {
     sprintf(eing, "e %s\n", e_d_sbrpts[i]);
     write(rfildes[1], eing, strlen(eing));
     if (e_d_dum_read() == -1) return(-1);
     sprintf(eing, "%d d\n", e_d_ybrpts[i]);
    }
    write(rfildes[1], eing, strlen(eing));
    if (e_d_dum_read() == -1) return(-1);
   }
   FREE(e_d_sbrpts[i]);
   for (; i < e_d_nbrpts - 1; i++)
   {
    e_d_sbrpts[i] = e_d_sbrpts[i+1];
    e_d_ybrpts[i] = e_d_ybrpts[i+1];
    e_d_nrbrpts[i] = e_d_nrbrpts[i+1];
   }
   e_d_nbrpts--;
   if (e_d_nbrpts == 0)
   {
    FREE(e_d_sbrpts);
    e_d_sbrpts = NULL;
    FREE(e_d_ybrpts);
    e_d_ybrpts = NULL;
    FREE(e_d_nrbrpts);
    e_d_nrbrpts = NULL;
   }
  }
  else
  {
   e_d_nbrpts++;
   if (e_d_nbrpts == 1)
   {
    e_d_sbrpts = MALLOC(sizeof(char *));
    e_d_ybrpts = MALLOC(sizeof(int));
    e_d_nrbrpts = MALLOC(sizeof(int));
   }
   else
   {
    e_d_sbrpts = REALLOC(e_d_sbrpts, e_d_nbrpts * sizeof(char *));
    e_d_ybrpts = REALLOC(e_d_ybrpts, e_d_nbrpts * sizeof(int));
    e_d_nrbrpts = REALLOC(e_d_nrbrpts, e_d_nbrpts * sizeof(int));
   }
   e_d_sbrpts[e_d_nbrpts - 1] = MALLOC(strlen(f->datnam) + 1);
   strcpy(e_d_sbrpts[e_d_nbrpts - 1], f->datnam);
   e_d_ybrpts[e_d_nbrpts - 1] = b->b.y + 1;
   if (e_d_swtch)
   {
    if (e_deb_type == 0)
    {
     snprintf(eing, sizeof(eing), "b %s:%d\n", f->datnam, b->b.y + 1);
     write(rfildes[1], eing, strlen(eing));
     while ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == 0 &&
       strncmp(str, "Breakpoint", 10))
      ;
     if (ret == -1) return(ret);
     if (ret == 2) e_d_error(str);
     e_d_nrbrpts[e_d_nbrpts - 1] = atoi(str+11);
     if (ret != 1 && e_d_dum_read() == -1) return(-1);
    }
    else if (e_deb_type == 2)
    {
     snprintf(eing, sizeof(eing), "stop at \"%s\":%d\n", f->datnam, b->b.y + 1);
     write(rfildes[1], eing, strlen(eing));
     while ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == 0 &&
       str[0] != '(')
      ;
     if (ret == -1) return(ret);
     if (ret == 2) e_d_error(str);
     e_d_nrbrpts[e_d_nbrpts - 1] = atoi(str+1);
     if (ret != 1 && e_d_dum_read() == -1) return(-1);
    }
    else if (e_deb_type == 3)
    {
     snprintf(eing, sizeof(eing), "b %s:%d\n", f->datnam, b->b.y + 1);
     write(rfildes[1], eing, strlen(eing));
     while ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == 0 &&
       strncmp(str, "Added:", 6))
      ;
     ret = e_d_line_read(wfildes[0], str, 256, 0, 0);
     if (ret == -1) return(ret);
     if (ret == 2) e_d_error(str);
     e_d_nrbrpts[e_d_nbrpts - 1] = atoi(str+1);
     if (ret != 1 && e_d_dum_read() == -1) return(-1);
    }
    else if (e_deb_type == 1)
    {
     snprintf(eing, sizeof(eing), "e %s\n", f->datnam);
     write(rfildes[1], eing, strlen(eing));
     if (e_d_dum_read() == -1) return(-1);
     sprintf(eing, "%d b\n", b->b.y + 1);
     write(rfildes[1], eing, strlen(eing));
     if (e_d_dum_read() == -1) return(-1);
    }
    else if (e_deb_type == 4)
    {
     /* jdb: "stop at Class:line" -- use filename without extension as class */
     { char cls[128];
       strcpy(cls, f->datnam);
       WpeStringCutChar(cls, '.');
       sprintf(eing, "stop at %s:%d\n", cls, b->b.y + 1);
     }
     write(rfildes[1], eing, strlen(eing));
     if (e_d_dum_read() == -1) return(-1);
     e_d_nrbrpts[e_d_nbrpts - 1] = e_d_nbrpts;
    }
    else if (e_deb_type == 5)
    {
     /* pdb: "b line" for current file, "b file:line" for others */
     sprintf(eing, "b %d\n", b->b.y + 1);
     write(rfildes[1], eing, strlen(eing));
     if (e_d_dum_read() == -1) return(-1);
     e_d_nrbrpts[e_d_nbrpts - 1] = e_d_nbrpts;
    }
   }
   (s->brp[0])++;
   s->brp = REALLOC(s->brp, (s->brp[0]+1) * sizeof(int));
   s->brp[s->brp[0]] = b->b.y;
  }
 }
 else
 {
  if(e_deb_type == 0)
  {
   for (i = 0; i < e_d_nbrpts; i++)
   {
    sprintf(eing, "b %s:%d\n", e_d_sbrpts[i], e_d_ybrpts[i]);
    write(rfildes[1], eing, strlen(eing));
    while ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == 0 &&
      strncmp(str, "Breakpoint", 10))
     ;
    if (ret == -1) return(ret);
    if (ret == 2) e_d_error(str);
    e_d_nrbrpts[e_d_nbrpts - 1] = atoi(str+11);
    if (ret != 1 && e_d_dum_read() == -1) return(-1);
   }
  }
  else if (e_deb_type == 2)
  {
   for (i = 0; i < e_d_nbrpts; i++)
   {
    sprintf(eing, "stop at \"%s\":%d\n", e_d_sbrpts[i], e_d_ybrpts[i]);
    write(rfildes[1], eing, strlen(eing));
    while ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == 0 &&
      str[0] != '(')
     ;
    if (ret == -1) return(ret);
    if (ret == 2) e_d_error(str);
    e_d_nrbrpts[e_d_nbrpts - 1] = atoi(str+1);
    if (ret != 1 && e_d_dum_read() == -1) return(-1);
   }
  }
  else if (e_deb_type == 3)
  {
   for (i = 0; i < e_d_nbrpts; i++)
   {
    snprintf(eing, sizeof(eing), "b %s:%d\n", f->datnam, b->b.y + 1);
    write(rfildes[1], eing, strlen(eing));
    while ((ret = e_d_line_read(wfildes[0], str, 256, 0, 0)) == 0 &&
      strncmp(str, "Added:", 6))
     ;
    ret = e_d_line_read(wfildes[0], str, 256, 0, 0);
    if (ret == -1) return(ret);
    if (ret == 2) e_d_error(str);
    e_d_nrbrpts[e_d_nbrpts - 1] = atoi(str+1);
    if (ret != 1 && e_d_dum_read() == -1) return(-1);
   }
  }
  else if (e_deb_type == 4)
  {
   for (i = 0; i < e_d_nbrpts; i++)
   {
    /* jdb: "stop at Class:line" -- strip .java extension from filename */
    { char cls[128];
      strcpy(cls, e_d_sbrpts[i]);
      WpeStringCutChar(cls, '.');
      sprintf(eing, "stop at %s:%d\n", cls, e_d_ybrpts[i]);
    }
    jdb_trace("e_make_breakpoint(sw=1): sending '%s'\n", eing);
    write(rfildes[1], eing, strlen(eing));
    if (e_d_dum_read() == -1) return(-1);
    e_d_nrbrpts[i] = i + 1;
   }
  }
  else if (e_deb_type == 5)
  {
   for (i = 0; i < e_d_nbrpts; i++)
   {
    /* pdb: "b file:line" */
    sprintf(eing, "b %s:%d\n", e_d_sbrpts[i], e_d_ybrpts[i]);
    write(rfildes[1], eing, strlen(eing));
    if (e_d_dum_read() == -1) return(-1);
    e_d_nrbrpts[i] = i + 1;
   }
  }
  else
  {
   for (i = 0; i < e_d_nbrpts; i++)
   {
    sprintf(eing, "e %s\n", e_d_sbrpts[i]);
    write(rfildes[1], eing, strlen(eing));
    if (e_d_dum_read() == -1) return(-1);
    sprintf(eing, "%d b\n", e_d_ybrpts[i]);
    write(rfildes[1], eing, strlen(eing));
    if (e_d_dum_read() == -1) return(-1);
   }
  }
 }
 e_schirm(f, 1);
 return(1);
}

/*   start Debugger   */
int e_exec_deb(FENSTER *f, char *prog)
{
 int i;

 if (e_d_swtch)
  return(1);
 e_d_swtch = 1;
 fflush(stdout);
 if (WpeIsXwin())
 {
  for (i = 0; i < 5; i++)
  {
   if (npipe[i])
    FREE(npipe[i]);
   npipe[i] = MALLOC(128);
   sprintf(npipe[i], "%s/we_pipe%d", e_tmp_dir, i);
   remove(npipe[i]);
  }
  if (mkfifo(npipe[0], S_IRUSR | S_IWUSR) < 0 ||
    mkfifo(npipe[1], S_IRUSR | S_IWUSR) < 0 ||
    mkfifo(npipe[2], S_IRUSR | S_IWUSR) < 0 ||
    mkfifo(npipe[3], S_IRUSR | S_IWUSR) < 0 ||
    mkfifo(npipe[4], S_IRUSR | S_IWUSR) < 0)
  {
   e_error(e_d_msg[ERR_CANTPIPE], 0, f->fb);
   return(0);
  }
 }
 else
 {
  if (pipe(rfildes))
  {
   e_error(e_p_msg[ERR_PIPEOPEN], 0, f->fb);
   return(0);
  }
  if (pipe(wfildes))
  {
   e_error(e_p_msg[ERR_PIPEOPEN], 0, f->fb);
   return(0);
  }
  if (pipe(efildes))
  {
   e_error(e_p_msg[ERR_PIPEOPEN], 0, f->fb);
   return(0);
  }
 }

 if (e_d_prog_output) { FREE(e_d_prog_output); e_d_prog_output = NULL; }
 e_d_prog_output_len = 0;
 e_d_prog_output_cap = 0;
 if (openpty(&e_d_pty_master, &e_d_pty_slave, e_d_pty_slave_name,
             NULL, NULL) < 0)
 {
  e_d_pty_master = -1;
  e_d_pty_slave = -1;
  e_d_pty_slave_name[0] = '\0';
 }

 if ((e_d_pid = fork()) > 0)
 {
  if (WpeIsXwin())
  {
   if ((wfildes[0] = open(npipe[1], O_RDONLY)) < 0)
   {
    e_error(e_p_msg[ERR_PIPEOPEN], 0, f->fb);
    return(0);
   }
   for (i = 0;
     i < 80 && read(wfildes[0], &e_d_tty[i], 1) == 1 && e_d_tty[i] != '\0' &&
       e_d_tty[i] != '\n';
     i++)
    ;
   if (e_d_tty[i] == '\n')
    e_d_tty[i] = '\0';
   close(wfildes[0]);
   if ((rfildes[0] = open(e_d_tty, O_RDONLY)) < 0 ||
     (wfildes[1] = open(e_d_tty, O_WRONLY)) < 0)
   {
    e_error(e_p_msg[ERR_PIPEOPEN], 0, f->fb);
    return(0);
   }
   if ((rfildes[1] = open(npipe[0], O_WRONLY)) < 0 ||
     (wfildes[0] = open(npipe[1], O_RDONLY)) < 0 ||
     (efildes[0] = open(npipe[2], O_RDONLY)) < 0)
   {
    e_error(e_p_msg[ERR_PIPEOPEN], 0, f->fb);
    return(0);
   }
   if (e_deb_mode)
   {
    tcgetattr(rfildes[0], &ntermio);
/*   ioctl(rfildes[0], TCGETA, &ntermio);*/
    ntermio.c_iflag = 0;
    ntermio.c_oflag = 0;
    ntermio.c_lflag = 0;
    ntermio.c_cc[VMIN] = 1;
    ntermio.c_cc[VTIME] = 0;
#ifdef VSWTCH
    ntermio.c_cc[VSWTCH] = 0;
#endif
/*   ioctl(rfildes[0], TCSETA, &ntermio);*/
    tcsetattr(rfildes[0], TCSADRAIN, &ntermio);
   }
   usleep(200000);
#ifndef NO_XWINDOWS
   XSetInputFocus(WpeXInfo.display, WpeXInfo.window,
     RevertToParent, CurrentTime);
   XFlush(WpeXInfo.display);
#endif
  }
  else
  {
   FILE *fpp;

   /* Close the pipe ends that belong to the child.
      - rfildes[0] is the child's stdin read-end (parent uses rfildes[1])
      - wfildes[1] is the child's stdout write-end (parent uses wfildes[0])
      - efildes[1] is the child's stderr write-end (parent uses efildes[0])
      Without closing these, read() on wfildes[0]/efildes[0] never returns
      EOF when the child exits, because the parent's copy of the write-end
      keeps the pipe open.  This caused infinite loops in e_d_line_read.
      Note: only for non-X11.  In X11 mode, these fds are overwritten
      with tty/named-pipe fds above and the originals are leaked. */
   { int _ret_fd = wfildes[1];
     close(rfildes[0]);  rfildes[0] = -1;
     close(wfildes[1]);  wfildes[1] = -1;
     close(efildes[1]);  efildes[1] = -1;
     if (e_d_pty_slave >= 0)
     {
      close(e_d_pty_slave);
      e_d_pty_slave = -1;
     }

     if (!(fpp = popen("tty", "r")))
     {
      e_error(e_p_msg[ERR_PIPEOPEN], 0, f->fb);
      return(0);
     }
     fgets(e_d_tty, 80, fpp);
     { int _len = strlen(e_d_tty);
       if (_len > 0 && e_d_tty[_len-1] == '\n') e_d_tty[_len-1] = '\0';
     }
     pclose(fpp);
     return(_ret_fd);
   }
  }
  return(wfildes[1]);
 }
 else if (e_d_pid < 0)
 {
  e_error(e_p_msg[ERR_PROCESS], 0, f->fb);
  return(0);
 }

#ifndef NO_XWINDOWS
 if (WpeIsXwin())
 {
  FILE *fp;
  char file[128];

  sprintf(file, "%s/we_sys", e_tmp_dir);
  fp = fopen(file, "w+");
  fprintf(fp, "#!/bin/sh\n");
  if (e_d_pty_master >= 0)
   fprintf(fp, "echo '%s' > %s\n", e_d_pty_slave_name, npipe[1]);
  else
   fprintf(fp, "tty > %s\n", npipe[1]);
  if (e_deb_type == 5)
   fprintf(fp,
     "%s -m pdb %s < %s > %s 2> %s\n"
     "echo 'type <Return> to continue' > /dev/tty\nread i < /dev/tty\n",
     e_debugger, prog, npipe[0], npipe[1], npipe[2]);
  else if (!e_deb_swtch)
   fprintf(fp,
     "%s %s < %s > %s 2> %s\n"
     "echo 'type <Return> to continue' > /dev/tty\nread i < /dev/tty\n",
     e_debugger, prog, npipe[0], npipe[1], npipe[2]);
  else
   fprintf(fp,
     "%s %s %s < %s > %s 2> %s\n"
     "echo 'type <Return> to continue' > /dev/tty\nread i < /dev/tty\n",
     e_debugger, e_deb_swtch, prog, npipe[0], npipe[1], npipe[2]);
  fprintf(fp, "rm -f %s\n", file);
  fclose(fp);
  chmod(file, 0755);

  if (e_d_pty_master >= 0)
  {
   close(e_d_pty_master);
   execl(file, file, NULL);
  }
  else
  {
   execlp(XTERM_CMD, XTERM_CMD, "+sb", "-geometry", "80x25-0-0",
     "-T", "xwpe-output", "-e", user_shell, "-c", file, NULL);
  }
  remove(file);
 }
 else
#endif
 {
  /* Close pty master in child -- only the parent reads from it.
     Keep the slave open: gdb needs it accessible for "run > /dev/pts/N". */
  if (e_d_pty_master >= 0)
   close(e_d_pty_master);

  /* Close the pipe ends not used by the child.
     The child uses: rfildes[0] (stdin), wfildes[1] (stdout), efildes[1] (stderr).
     Close the others to prevent the debugged program from inheriting them,
     and to ensure proper EOF detection in the parent. */
  close(rfildes[1]);
  close(wfildes[0]);
  close(efildes[0]);

  close(0);
  if (fcntl(rfildes[0], F_DUPFD, 0) != 0)
  {
   fprintf(stderr, e_p_msg[ERR_PIPEEXEC], rfildes[0]);
   exit(1);
  }
  close(1);
  if (fcntl(wfildes[1], F_DUPFD, 1) != 1)
  {
   fprintf(stderr, e_p_msg[ERR_PIPEEXEC], wfildes[1]);
   exit(1);
  }
  close(2);
  if (fcntl(efildes[1], F_DUPFD, 2) != 2)
  {
   fprintf(stderr, e_p_msg[ERR_PIPEEXEC], efildes[1]);
   exit(1);
  }
  /* jdb does not handle non-blocking stdout/stderr.
     pdb works fine with non-blocking (needed for prompt detection). */
  if (e_deb_type != 4)
  {
   int _fl;
   _fl = fcntl(1, F_GETFL, 0 );
   fcntl(1, F_SETFL, _fl | O_NONBLOCK);
   _fl = fcntl(2, F_GETFL, 0 );
   fcntl( 2, F_SETFL, _fl | O_NONBLOCK);
  }
  if (e_deb_type == 5)
   execlp(e_debugger, e_debugger, "-m", "pdb", prog, NULL);
  else if (!e_deb_swtch)
   execlp(e_debugger, e_debugger, prog, NULL);
  else
   execlp(e_debugger, e_debugger, e_deb_swtch, prog, NULL);
 }
 fprintf(stderr,"%s %s %s\n", e_p_msg[ERR_IN_COMMAND], e_debugger, prog);
 exit(1);
}

int e_start_debug(FENSTER *f)
{
 ECNT *cn = f->ed;
 int i, file;
 char estr[128];

 efildes[0] = efildes[1] = -1;
 wfildes[0] = wfildes[1] = -1;
 rfildes[0] = rfildes[1] = -1;
 if (e_d_swtch)
  return(0);
 /* Find the source file window -- f may point to Messages if the
    cursor is there from a previous compile.  Same fix as #45. */
 for (i = cn->mxedt; i > 0; i--)
  if (strcmp(cn->f[i]->datnam, "Messages") && DTMD_ISTEXT(cn->f[i]->dtmd))
   break;
 if (i > 0)
 {
  e_switch_window(cn->edt[i], cn->f[cn->mxedt]);
  f = cn->f[cn->mxedt];
 }
 if (e_p_make(f))
  return(-1);
 for (i = cn->mxedt; i > 0; i--)
  if (e_check_c_file(cn->f[i]->datnam))
   break;
 if (i > 0)
 {
  strcpy(e_d_file, cn->f[i]->datnam);
  f = cn->f[i];
 }
 else if (e__project)
 {
  strcpy(e_d_file, cn->f[cn->mxedt-1]->datnam);
  f = cn->f[cn->mxedt-1];
 }
 for (i = 0; i < SVLINES; i++)
 {  e_d_sp[i] = e_d_out_str[i];  e_d_out_str[i][0] = '\0';  }
 /* Auto-select debugger by file extension.
    gdb cannot debug Java bytecode or Python scripts. */
 { int _l = strlen(e_d_file);
   if (_l > 5 && !strcmp(e_d_file + _l - 5, ".java"))
   {
    if (e_deb_type != 4)
     e_error("Java file: switching to jdb debugger.", -1, f->fb);
    e_deb_type = 4;
   }
   else if (_l > 3 && !strcmp(e_d_file + _l - 3, ".py"))
   {
    if (e_deb_type != 5)
     e_error("Python file: switching to pdb debugger.", -1, f->fb);
    e_deb_type = 5;
   }
 }
 jdb_trace("e_start_debug: e_d_file='%s', e_deb_type=%d, comp_sw=%d\n",
           e_d_file, e_deb_type, e_s_prog.comp_sw);
 if (e_deb_type == 1) {  e_debugger = "sdb";  e_deb_swtch = NULL;  }
 else if (e_deb_type == 2) {  e_debugger = "dbx";  e_deb_swtch = "-i";  }
 else if (e_deb_type == 3) {  e_debugger = "xdb";  e_deb_swtch = "-L";  }
 else if (e_deb_type == 4) {  e_debugger = "jdb";  e_deb_swtch = NULL;  }
 else if (e_deb_type == 5) {  e_debugger = "python3";  e_deb_swtch = NULL;  }
 else {  e_debugger = "gdb";  e_deb_swtch = NULL;  }
 e_d_pid = 0;
 if (e_test_command(e_debugger))
 {
  sprintf(estr, "Debugger \'%s\' not in Path", e_debugger);
  e_error(estr, 0, f->fb);
  return(-1);
 }
 /* Note: e_sys_ini()/e_sys_end() were here originally but they cause
    screen flicker (rmcup/smcup) which is unnecessary for the gdb fork.
    The pty handles program output; no screen switching needed. */
 if (e__project && (file = e_exec_deb(f, e_s_prog.exe_name )) == 0)
 {
  return(-2);
 }
 else if (!e__project)
 {
  if (e_s_prog.exe_name && e_s_prog.exe_name[0])
  {
   strcpy(estr, e_s_prog.exe_name);
  }
  else
  {
   strcpy(estr, f->datnam);
   if (e_deb_type == 5)
   {
    /* pdb: debug the .py source file with full path.
       e_d_file has the basename; we need dirct + datnam for the
       full path so pdb can find it from any working directory. */
    int _fi;
    for (_fi = cn->mxedt; _fi > 0; _fi--)
     if (e_check_c_file(cn->f[_fi]->datnam)) break;
    if (_fi > 0)
     snprintf(estr, sizeof(estr), "%s/%s", cn->f[_fi]->dirct, cn->f[_fi]->datnam);
    else
     strcpy(estr, e_d_file);
   }
   else
   {
    WpeStringCutChar(estr, '.');
    /* Non-GNU compilers (fpc) produce executables without .e extension.
       GNU compilers use the .e convention from xwpe's link step. */
    if (!(e_s_prog.comp_sw & 1))
     strcat(estr, ".e");
   }
  }
  jdb_trace("e_start_debug: launching '%s %s'\n", e_debugger, estr);
  if ((file = e_exec_deb(f, estr)) == 0)
  {
   jdb_trace("e_start_debug: e_exec_deb failed\n");
   return(-2);
  }
 }
 jdb_trace("e_start_debug: debugger launched OK, e_d_swtch=%d\n", e_d_swtch);
 e_d_p_message(e_d_msg[ERR_STARTDEBUG], f, 1);
 WpeMouseChangeShape(WpeDebuggingShape);
 if (cn->mxedt > 1)
  e_switch_window(cn->edt[cn->mxedt - 1], cn->f[cn->mxedt]);
 return(0);
}

int e_run_debug(FENSTER *f)
{
 ECNT *cn = f->ed;
 int kbdflgs, ret;

 jdb_trace("e_run_debug: entry, e_d_swtch=%d, e_deb_type=%d\n",
           e_d_swtch, e_deb_type);
 if (e_d_swtch < 1 && (ret = e_start_debug(f)) < 0) return(ret);
 if (e_d_swtch < 2)
 {
  kbdflgs = fcntl(efildes[0], F_GETFL, 0);
  fcntl( efildes[0], F_SETFL, kbdflgs | O_NONBLOCK);
  kbdflgs = fcntl(wfildes[0], F_GETFL, 0);
  fcntl( wfildes[0], F_SETFL, kbdflgs | O_NONBLOCK);

  jdb_trace("e_run_debug: reading banner...\n");
  /* pdb (and other interpreted debuggers) need time to start before
     their banner is available on stdout.  Wait for data with poll(). */
  if (e_deb_type == 5)
  {
   struct pollfd _pfd = { .fd = wfildes[0], .events = POLLIN };
   poll(&_pfd, 1, 3000);  /* wait up to 3s for pdb to write banner */
  }
  if (e_d_dum_read() == -1) return(-1);
  jdb_trace("e_run_debug: banner read OK\n");
  /* Disable gdb's confirmation prompts ("Start it from the beginning?",
     "Kill the program being debugged?", etc.) to prevent busy loops
     when xwpe sends commands that gdb would otherwise block on. */
  if (e_deb_type == 0)
  {
   write(rfildes[1], "set confirm off\n", 16);
   if (e_d_dum_read() == -1) return(-1);
   if (e_d_pty_master >= 0)
   {
    char _tty_cmd[160];
    sprintf(_tty_cmd, "set inferior-tty %s\n", e_d_pty_slave_name);
    write(rfildes[1], _tty_cmd, strlen(_tty_cmd));
    if (e_d_dum_read() == -1) return(-1);
   }
  }
  if (e_deb_type == 3)
  {
   write(rfildes[1], "sm\n", 3);
   if (e_d_dum_read() == -1) return(-1);
  }
  jdb_trace("e_run_debug: setting breakpoints (nbrpts=%d)...\n", e_d_nbrpts);
  if (e_make_breakpoint(cn->f[cn->mxedt], 1) == -1) return(-1);
  jdb_trace("e_run_debug: breakpoints set OK, e_d_swtch -> 2\n");
  e_d_swtch = 2;
#ifndef NO_XWINDOWS
  if (WpeIsXwin())
  {
   XSetInputFocus(WpeXInfo.display, WpeXInfo.window,
     RevertToParent, CurrentTime);
   XFlush(WpeXInfo.display);
  }
#endif
 }
 return(0);
}

/*  Run  */
int e_deb_run(FENSTER *f)
{
 ECNT *cn = f->ed;
 char eing[256];
 int ret, len, prsw = 0, main_brk = 0;

 jdb_trace("e_deb_run: entry, e_d_swtch=%d, e_deb_type=%d\n",
           e_d_swtch, e_deb_type);
 if (e_d_swtch < 2 && (ret = e_run_debug(f)) < 0)
 {
  jdb_trace("e_deb_run: e_run_debug failed ret=%d, quitting\n", ret);
  e_d_quit(f);
  if (ret == -1) {  e_show_error(0, f);  return(ret);  }
  return(e_error(e_d_msg[ERR_CANTDEBUG], 0, f->fb));
 }
 /* Interpreted debuggers (jdb, pdb) don't need tty redirect */
 jdb_trace("e_deb_run: tty check, e_deb_type=%d, e_d_tty='%s'\n",
           e_deb_type, e_d_tty);
 if (e_deb_type != 4 && e_deb_type != 5)
 {
  for (ret = 0; isspace(e_d_tty[ret]); ret++)
   ;
  if (e_d_tty[ret] != DIRC)
  {
   jdb_trace("e_deb_run: tty check FAILED, quitting\n");
   e_d_quit(f);
   sprintf(eing, "tty error: %s", e_d_tty);
   return(e_d_error(eing));
  }
 }
 if (e_d_swtch < 3 && e_d_nbrpts <= 0)
 {
  if ((main_brk = e_mk_brk_main(f, 0)) < -1) return(main_brk);
 }
 if (e_deb_type == 2)
 {
  if (e_d_swtch < 3)
  {
   if (e_prog.arguments)
    sprintf(eing, "run %s > %s\n", e_prog.arguments, e_d_tty);
   else
    sprintf(eing, "run > %s\n", e_d_tty);
  }
  else
  {
   strcpy(eing, "cont\n");
   prsw = 1;
  }
 }
 else
 {
  if (e_d_swtch < 3)
  {
   /* When pty is active, redirect stdout to the pty slave path.
      gdb's "set inferior-tty" doesn't deliver output to the master
      when gdb itself communicates via pipes, so we use explicit
      shell-style redirect instead. */
   if (e_deb_type == 4)
   {
    /* jdb: plain "run" -- no redirect, no arguments through run */
    strcpy(eing, "run\n");
   }
   else if (e_deb_type == 5)
   {
    /* pdb: program already loaded and paused at line 1.
       "continue" runs to first breakpoint. */
    strcpy(eing, "c\n");
    prsw = 1;  /* treat as continue, not first run */
   }
   else if (e_d_pty_master >= 0 && e_deb_type == 0)
   {
    if (e_prog.arguments)
     sprintf(eing, "r %s\n", e_prog.arguments);
    else
     strcpy(eing, "r\n");
   }
   else
   {
    if (e_prog.arguments)
     sprintf(eing, "r %s > %s\n", e_prog.arguments, e_d_tty);
    else
     sprintf(eing, "r > %s\n", e_d_tty);
   }
  }
  else
  {
   if (e_deb_type == 4)
    strcpy(eing, "cont\n");
   else
    strcpy(eing, "c\n");
   prsw = 1;
  }
 }
 f = cn->f[cn->mxedt];
 e_d_nstack = 0;
 e_d_delbreak(f);
 e_d_switch_out(1);
 jdb_trace("e_deb_run: sending '%s' (prsw=%d)\n", eing, prsw);
 write(rfildes[1], eing, strlen(eing));
 if ((e_deb_type == 4 || e_deb_type == 5) && !prsw)
 {
  /* jdb: "run" returns "> " immediately but the VM starts
     asynchronously. Use poll() with timeout to wait for the
     breakpoint prompt "main[1] " without busy-looping.
     Technique from JDEE (Emacs jdb integration): accumulate
     output and scan for "Breakpoint hit" or the prompt pattern.
     poll() avoids CPU waste during JVM startup (~2-3 seconds). */
  struct pollfd _pfd = { .fd = wfildes[0], .events = POLLIN };
  char _jbuf[4096];
  int _jlen = 0, _found = 0, _n;

  jdb_trace("e_deb_run: entering Phase 2 poll loop for jdb\n");
  while (!_found)
  {
   int _pret = poll(&_pfd, 1, 10000);
   if (_pret <= 0)
   {  jdb_trace("e_deb_run: poll timeout!\n");
      e_error("jdb: VM startup timeout.", 0, f->fb);
      e_d_quit(f); return(-1);  }
   _n = read(wfildes[0], _jbuf + _jlen, sizeof(_jbuf) - _jlen - 1);
   if (_n == 0)
   {  /* EOF: jdb process exited */
      jdb_trace("e_deb_run: EOF on pipe (jdb exited)\n");
      e_d_quit(f); return(-1);  }
   if (_n < 0) continue;
   _jlen += _n;
   _jbuf[_jlen] = '\0';
   jdb_trace("e_deb_run: poll read %d bytes total=%d: [%s]\n",
             _n, _jlen, _jbuf);
   if (strstr(_jbuf, "] "))
    _found = 1;
   if (strstr(_jbuf, "exited") || strstr(_jbuf, "Unable to"))
   {  jdb_trace("e_deb_run: error/exit detected in output, quitting\n");
      e_d_quit(f); return(-1);  }
  }
  jdb_trace("e_deb_run: Phase 2 done, _found=%d\n", _found);
  /* Parse jdb breakpoint output to position cursor.
     jdb format: "Breakpoint hit: ..., line=N bci=0\nN    code\nmain[1] "
     Extract line number and navigate to it. */
  { char *_lp = strstr(_jbuf, "line=");
    if (_lp)
    { int _line = atoi(_lp + 5);
      BUFFER *_b = cn->f[cn->mxedt]->b;
      SCHIRM *_s = cn->f[cn->mxedt]->s;
      if (_line > 0 && _line <= _b->mxlines)
      { _s->da.y = _b->b.y = _line - 1;
        _s->da.x = _b->b.x = 0;
        _s->de.x = MAXSCOL;
        e_schirm(cn->f[cn->mxedt], 1);
        e_cursor(cn->f[cn->mxedt], 1);
      }
    }
  }
  e_d_swtch = 3;
  e_d_switch_out(0);
  return(0);  /* skip e_read_output -- jdb output already parsed */
 }
 else if (e_deb_type == 0 || ((e_deb_type == 2  || e_deb_type == 3) && !prsw))
 {
  while((ret = e_d_line_read(wfildes[0], eing, 256, 0, 0)) == 2 ||
    !eing[0] || (!e_deb_type && prsw && ((len = (strlen(eing)-12)) < 0 ||
    strcmp(eing + len, e_d_msg[ERR_CONTINUE]))) ||
    (e_d_swtch < 3 && ((!e_deb_type && strncmp(e_d_msg[ERR_STARTPROG],eing, 17)) ||
    (e_deb_type == 2 && strncmp("Running:",eing, 8)))))
  {
   if (ret == 2)
    e_d_error(eing);
   else if (ret < 0)
    return(e_d_quit(f));
  }
 }
 if (!prsw)
  e_d_p_message(eing, f, 0);
 if (e_d_swtch < 3 && ((!e_deb_type && strncmp(e_d_msg[ERR_STARTPROG],eing, 17)) ||
   (e_deb_type == 2 && strncmp("Running:",eing, 8))))
 {
  e_d_quit(f);
  return(e_error(e_d_msg[ERR_CANTPROG], 0, f->fb));
 }
 e_d_swtch = 3;
 if (e_d_pty_master >= 0 && e_deb_type == 0)
 {
  e_d_accum_init(f, main_brk);
  return(0);
 }
 { int _ro = e_read_output(f);
   if (main_brk)
    e_mk_brk_main(f, main_brk);
   return(_ro);
 }
}

int e_deb_trace(FENSTER *f)
{
 return(e_d_step_next(f, 0));
}

int e_deb_next(FENSTER *f)
{
 return(e_d_step_next(f, 1));
}

int e_d_step_next(FENSTER *f, int sw)
{
 int ret, main_brk = 0;

 jdb_trace("e_d_step_next: entry, sw=%d, e_d_swtch=%d, e_deb_type=%d, "
           "e_d_nbrpts=%d\n", sw, e_d_swtch, e_deb_type, e_d_nbrpts);
 if (e_d_swtch < 2 && (ret = e_run_debug(f)) < 0)
 {
  jdb_trace("e_d_step_next: e_run_debug failed ret=%d\n", ret);
  e_d_quit(f);
  if (ret == -1)
  {  e_show_error(0, f);  return(ret);  }
  return(e_error(e_d_msg[ERR_CANTDEBUG], 0, f->fb));
 }
 if (e_d_swtch < 3)
 {
  /* If the user has breakpoints set, use those instead of adding
     a temporary breakpoint at main(). This way the first F8/F7
     stops at the user's breakpoint, not at main's entry point. */
  if (e_d_nbrpts <= 0)
  {
   jdb_trace("e_d_step_next: no breakpoints, calling e_mk_brk_main\n");
   if ((main_brk = e_mk_brk_main(f, 0)) < -1) return(main_brk);
   jdb_trace("e_d_step_next: e_mk_brk_main returned main_brk=%d\n", main_brk);
  }
  ret = e_deb_run(f);
  jdb_trace("e_d_step_next: e_deb_run returned ret=%d\n", ret);
  if (e_d_nbrpts <= 0)
   e_mk_brk_main(f, main_brk);
  return(ret);
 }
 e_d_delbreak(f);
 e_d_switch_out(1);
 if (sw && (e_deb_type == 0 || e_deb_type == 5)) write(rfildes[1], "n\n", 2);
 else if (sw && (e_deb_type == 1 || e_deb_type == 3)) write(rfildes[1], "S\n", 2);
 else if (sw && (e_deb_type == 2 || e_deb_type == 4)) write(rfildes[1], "next\n", 5);
 else if (e_deb_type == 2 || e_deb_type == 4) write(rfildes[1], "step\n", 5);
 else write(rfildes[1], "s\n", 2);
 e_d_nstack = 0;
 if (e_deb_type == 4)
 {
  /* jdb: use poll to read step response, parse line number */
  struct pollfd _pfd = { .fd = wfildes[0], .events = POLLIN };
  char _jbuf[4096];
  int _jlen = 0, _n, _found = 0;
  while (!_found)
  {
   if (poll(&_pfd, 1, 5000) <= 0) break;
   _n = read(wfildes[0], _jbuf + _jlen, sizeof(_jbuf) - _jlen - 1);
   if (_n == 0)
   {  /* EOF: jdb exited */
      /* Clean exit -- no popup, just quit like gdb does */
      return(e_d_quit(f));  }
   if (_n < 0) continue;
   _jlen += _n;
   _jbuf[_jlen] = '\0';
   jdb_trace("e_d_step_next: poll read %d bytes total=%d: [%s]\n",
             _n, _jlen, _jbuf);
   if (strstr(_jbuf, "] "))
    _found = 1;
   if (strstr(_jbuf, "exited"))
   {  e_error("Program exited. Debugger stopped.", 0, f->fb);
      return(e_d_quit(f));  }
  }
  if (_found)
  {
   char *_lp = strstr(_jbuf, "line=");
   if (_lp)
   { int _line = atoi(_lp + 5);
     BUFFER *_b = f->ed->f[f->ed->mxedt]->b;
     SCHIRM *_s = f->ed->f[f->ed->mxedt]->s;
     if (_line > 0 && _line <= _b->mxlines)
     { _s->da.y = _b->b.y = _line - 1;
       _s->da.x = _b->b.x = 0;
       _s->de.x = MAXSCOL;
       e_schirm(f->ed->f[f->ed->mxedt], 1);
       e_cursor(f->ed->f[f->ed->mxedt], 1);
     }
   }
   /* jdb step response: "Step completed: ..., line=N\nN    code\nmain[1] "
      Extract line from the last numbered line */
   if (!_lp)
   { char *_nl = strrchr(_jbuf, '\n');
     if (_nl) { int _line = atoi(_nl + 1);
       if (_line > 0) {
        BUFFER *_b = f->ed->f[f->ed->mxedt]->b;
        SCHIRM *_s = f->ed->f[f->ed->mxedt]->s;
        if (_line <= _b->mxlines)
        { _s->da.y = _b->b.y = _line - 1;
          _s->da.x = _b->b.x = 0;
          _s->de.x = MAXSCOL;
          e_schirm(f->ed->f[f->ed->mxedt], 1);
          e_cursor(f->ed->f[f->ed->mxedt], 1);
        }
       }
     }
   }
  }
  /* Extract program output from jdb response buffer.
     Program output is text between "> " prompt and "Step completed:"
     or "Breakpoint hit:" -- anything that's not a jdb protocol line. */
  { char *_p = _jbuf, *_end;
    /* Skip past initial "> " prompt */
    if ((_p = strstr(_jbuf, "> ")) != NULL) _p += 2;
    else _p = _jbuf;
    /* Find where jdb protocol resumes */
    _end = strstr(_p, "Step completed:");
    if (!_end) _end = strstr(_p, "Breakpoint hit:");
    if (!_end) _end = strstr(_p, "main[");
    if (_end && _end > _p)
    { int _olen = _end - _p;
      /* Strip leading/trailing whitespace */
      while (_olen > 0 && (_p[0] == '\n' || _p[0] == '\r')) { _p++; _olen--; }
      while (_olen > 0 && (_p[_olen-1] == '\n' || _p[_olen-1] == '\r')) _olen--;
      if (_olen > 0)
      { if (e_d_prog_output_len + _olen + 1 > e_d_prog_output_cap)
        { e_d_prog_output_cap = (e_d_prog_output_len + _olen + 1024) * 2;
          e_d_prog_output = realloc(e_d_prog_output, e_d_prog_output_cap);
        }
        memcpy(e_d_prog_output + e_d_prog_output_len, _p, _olen);
        e_d_prog_output_len += _olen;
        e_d_prog_output[e_d_prog_output_len++] = '\n';
      }
    }
  }
  e_d_switch_out(0);
  return(0);
 }
 if (e_deb_type == 5)
 {
  /* pdb: use poll to read step response, parse active line.
     pdb output format: "> file(line)func()\n-> code\n(Pdb) " */
  struct pollfd _pfd = { .fd = wfildes[0], .events = POLLIN };
  char _buf[4096];
  int _len = 0, _n, _found = 0;
pdb_poll_again:
  while (!_found)
  {
   if (poll(&_pfd, 1, 5000) <= 0) break;
   _n = read(wfildes[0], _buf + _len, sizeof(_buf) - _len - 1);
   if (_n == 0) return(e_d_quit(f));  /* EOF: pdb exited */
   if (_n < 0) continue;
   _len += _n;
   _buf[_len] = '\0';
   if (strstr(_buf, "(Pdb) ")) _found = 1;
  }
  if (_found)
  {
   /* Capture program output from EVERY step response.
      Any line that isn't pdb metadata (prompt, active line, debugger
      messages) is program output (e.g. print() results). */
   { char _line[256];
     int _bi = 0;
     while (_buf[_bi])
     {
      int _li = 0;
      while (_buf[_bi] && _buf[_bi] != '\n' && _li < 255)
       _line[_li++] = _buf[_bi++];
      _line[_li] = '\0';
      if (_buf[_bi] == '\n') _bi++;
      if (_li == 0) continue;
      /* Skip pdb metadata */
      if (_line[0] == '>' || !strncmp(_line, "-> ", 3) ||
          !strncmp(_line, "(Pdb)", 5) ||
          !strncmp(_line, "--Return--", 10) ||
          !strncmp(_line, "--Call--", 8) ||
          strstr(_line, "The program"))
       continue;
      /* Program output -- save for Ctrl-G P */
      if (e_d_prog_output_len + _li + 2 > e_d_prog_output_cap)
      { e_d_prog_output_cap = (e_d_prog_output_len + _li + 1024) * 2;
        e_d_prog_output = realloc(e_d_prog_output, e_d_prog_output_cap);
      }
      if (e_d_prog_output)
      {
       memcpy(e_d_prog_output + e_d_prog_output_len, _line, _li);
       e_d_prog_output_len += _li;
       e_d_prog_output[e_d_prog_output_len++] = '\n';
      }
     }
   }
   /* Check for program exit -- pdb auto-restarts after finish */
   if (strstr(_buf, "The program finished"))
   {
    e_d_switch_out(0);
    e_error("End of code. Ctrl-G P for output.", 0, f->fb);
    return(e_d_quit(f));
   }
   /* Parse "> file(line)func()" to extract file and line number.
      If stopped in an internal Python frame (<string>, <frozen ...>),
      automatically step again until we reach user code or program exit. */
   { char *_gt = strstr(_buf, "> ");
     if (_gt && _gt[2] == '<')
     {
      /* Internal frame -- step again automatically */
      write(rfildes[1], "n\n", 2);
      _len = 0;
      _found = 0;
      goto pdb_poll_again;
     }
     if (_gt)
     {
      char *_op = strchr(_gt + 2, '(');
      if (_op)
      {
       int _line = atoi(_op + 1);
       if (_line > 0)
       {
        SCHIRM *_s = f->ed->f[f->ed->mxedt]->s;
        _s->da.y = _line - 1;
        e_d_swtch = 3;
        e_cursor(f, 1);
        e_schirm(f, 1);
       }
      }
     }
   }
  }
  e_d_switch_out(0);
  return(0);
 }
 if (e_d_pty_master >= 0 && e_deb_type == 0)
 {
  e_d_accum_init(f, 0);
  return(0);
 }
 return(e_read_output(f));
}

int e_d_goto_func(FENSTER *f, int flag)
{
 ECNT *cn = f->ed;
 BUFFER *b = cn->f[cn->mxedt]->b;
 int ret = 0, main_brk = 0;
 char str[128];

 if (e_deb_type!=0)/* if gdb */
  return 0;
 if (e_d_swtch < 2 && (ret = e_run_debug(f)) < 0)
 {
  e_d_quit(f);
  if (ret == -1)
  {  e_show_error(0, f);  return(ret);  }
  return(e_error(e_d_msg[ERR_CANTDEBUG], 0, f->fb));
 }
 if (e_d_swtch < 3)
 {
  if ((main_brk = e_mk_brk_main(f, 0)) < -1)
   return(main_brk);
  ret = e_deb_run(f);
  e_mk_brk_main(f, main_brk);
  return(ret);
 }
 e_d_delbreak(f);
 e_d_switch_out(1);
 switch(flag)
 {
  case 'U':
   sprintf(str,"until %d\n",b->b.y+1);
   break;
  case 'F':
   sprintf(str,"finish\n");
   break;
  default:
   *str=0;
   break;
 }
 e_d_nstack = 0;
 if (*str)
 {
  write(rfildes[1], str, strlen(str));
  ret=e_read_output(f);
  /* Executing Finish twice may not work properly. */
 }
 return ret;
}

int e_d_goto_cursor(FENSTER *f)
{
 return e_d_goto_func(f,'U');
}

int e_d_finish_func(FENSTER *f)
{
 return e_d_goto_func(f,'F');
}

int e_d_fst_check(FENSTER *f)
{
 int i, j, k = 0, l, ret = 0;

 e_d_switch_out(0);
 for (i = 0; i < SVLINES - 1; i++)
 {
  if ((e_deb_type != 2 && !strncmp(e_d_sp[i], e_d_msg[ERR_PROGEXIT], 14)) ||
    ((e_deb_type == 2 && !strncmp(e_d_sp[i], e_d_msg[ERR_PROGEXIT2], 14)) ||
    (e_deb_type == 3 && !strncmp(e_d_sp[i], e_d_msg[ERR_NORMALTERM], strlen(e_d_msg[ERR_NORMALTERM])))))
  {
   e_d_error(e_d_sp[i]);		/*  Program exited   */
   e_d_quit(f);
   return(i);
  }
  else if ((e_deb_type == 0 || e_deb_type == 1) && !strncmp(e_d_sp[i], e_d_msg[ERR_PROGTERM], 18))
  {
   e_error(e_d_msg[ERR_PROGTERM], 0, f->fb);
   e_d_quit(f);
   return(i);
  }
  else if (e_deb_type == 0 && !strncmp(e_d_sp[i], e_d_msg[ERR_PROGSIGNAL], 23))
  {
   e_d_pr_sig(e_d_sp[i], f);
   return(i);
  }
  else if (e_deb_type == 3 && !strncmp(e_d_sp[i], e_d_msg[ERR_SOFTTERM], strlen(e_d_msg[ERR_SOFTTERM])))
  {
   e_d_pr_sig(e_d_sp[i], f);
   return(i);
  }
  else if (e_deb_type == 2 && (!strncmp(e_d_sp[i], e_d_msg[ERR_SIGNAL], 6) ||
    !strncmp(e_d_sp[i], e_d_msg[ERR_INTERRUPT], 9)))
  {
   e_d_pr_sig(e_d_sp[i], f);
   return(i);
  }
  else if (e_deb_type == 1 && strstr(e_d_sp[i], e_d_msg[ERR_SIGNAL]))
  {
   e_d_pr_sig(e_d_sp[i], f);
   return(i);
  }
  else if (e_deb_type == 3 && i == SVLINES-2 && strstr(e_d_sp[i], ": "))
  {
   e_d_pr_sig(e_d_sp[i-1], f);
   return(i-1);
  }
  else if (!strncmp(e_d_sp[i], e_d_msg[ERR_BREAKPOINT], 10) ||
    (e_deb_type == 1 && !strncmp(e_d_sp[i], e_d_msg[ERR_BREAKPOINT2], 10)))
  {
   if (!e_deb_type)
   {
    for (j = SVLINES - 2; j > i; j--)      /*  Breakpoint   */
    {
     if ((ret = atoi(e_d_sp[j])) > 0)
      for(k = 0; e_d_sp[j][k] && isdigit(e_d_sp[j][k]); k++)
       ;
     if (e_d_sp[j][k] == '\t')
      break;
    }
    if (j > i)
    {
     for (k = strlen(e_d_sp[j-1]); k >= 0 && e_d_sp[j-1][k] != ':'; k--)
      ;
     if (k >= 0 && atoi(e_d_sp[j-1]+k+1) == ret)
     {
      if (e_make_line_num(e_d_sp[j-1], e_d_sp[SVLINES-1]) >= 0)
       strcpy(e_d_file, e_d_sp[SVLINES-1]);
     }
     if (e_d_p_watches(f, 0) == -1)
      return(-1);
     e_d_goto_break(e_d_file, ret, f);
     return(i > 0 ? i-1 : 0);
    }
   }
   else if (e_deb_type == 1)	      /*  Breakpoint   */
   {
    if(!strncmp(e_d_sp[i]+10, " at", 3) &&
      (ret = e_make_line_num(e_d_sp[i+1], e_d_sp[SVLINES-1])) >= 0)
    {
     strcpy(e_d_file, e_d_sp[SVLINES-1]);
     if (e_d_p_watches(f, 0) == -1)
      return(-1);
     e_d_goto_break(e_d_file, ret, f);
     return(i);
    }
   }
  }
  else if (e_deb_type == 2 && !strncmp(e_d_sp[i], e_d_msg[ERR_STOPPEDIN], 10))
  {
   for (j = i + 1; j < SVLINES - 1; j++)		      /*  Breakpoint   */
   {
    for (k = 0; e_d_sp[j][k] == ' ' && e_d_sp[j][k] != '\0'; k++)
     ;
    if ((ret = atoi(e_d_sp[j]+k)) > 0)
    {
     if (!strstr(e_d_sp[j-1], " line "))
      break;
     for (k = strlen(e_d_sp[j-1]); k >= 0 && e_d_sp[j-1][k] != '\"'; k--)
      ;
     for(k--; k >= 0 && e_d_sp[j-1][k] != '\"'; k--)
      ;
     if (k >= 0)
     {
      for(k++, l = 0; e_d_sp[j-1][k] != '\0' && e_d_sp[j-1][k] != '\"';
        k++, l++)
       e_d_file[l] = e_d_sp[j-1][k];
      e_d_file[l] = '\0';
     }
     if (e_d_p_watches(f, 0) == -1)
      return(-1);
     e_d_goto_break(e_d_file, ret, f);
     return(i);
    }
   }
  }
 }
 return(-2);
}

int e_d_snd_check(FENSTER *f)
{
 int i, j, k, ret;

 e_d_switch_out(0);
 for (i = SVLINES - 2; i >= 0; i--)
 {
  if (!e_deb_type && (ret = atoi(e_d_sp[i])) > 0)
  {
   for (k = 0; e_d_sp[i][k] && isdigit(e_d_sp[i][k]); k++)
    ;
   if (e_d_sp[i][k] != '\t')
    continue;
   if (i > 0)
   {
    for (k = strlen(e_d_sp[i-1]); k >= 0 && e_d_sp[i-1][k] != ':'; k--)
     ;
    if (k >= 0 && atoi(e_d_sp[i-1]+k+1) == ret)
    {
     i--;
     if (e_make_line_num(e_d_sp[i], e_d_sp[SVLINES-1]) >= 0)
      strcpy(e_d_file, e_d_sp[SVLINES-1]);
     do
     {
      for (; k >= 0 && e_d_sp[i][k] != ')'; k--)
       ;
      if (k < 0)
      {  i--;  k = strlen(e_d_sp[i]);  }
      else
       break;
     }  while (i >= 0);
     do
     {
      for (j = 1, k--; k >= 0 && j > 0; k--)
      {
       if (e_d_sp[i][k] == ')')
        j++;
       else if (e_d_sp[i][k] == '(')
        j--;
      }
      if (k < 0)
      {  i--;  k = strlen(e_d_sp[i]);  }
     } while (i >= 0 && j > 0);
     if (k == 0 && i > 0)
     {  i--;  k = strlen(e_d_sp[i]);  }
     for (k--; k >= 0 && isspace(e_d_sp[i][k]); k--)
      ;
     if (k < 0 && i > 0)
      i--;
    }
   }
   if (e_d_p_watches(f, 0) == -1)
    return(-1);
   e_d_goto_break(e_d_file, ret, f);
   return(i);
  }
  else if (e_deb_type == 1 && (ret = atoi(e_d_sp[i])) > 0)
  {
   for (; i > 0; i--)
   {
    for (j = strlen(e_d_sp[i-1])-1; j >= 0 && isspace(e_d_sp[i-1][j]); j--)
     ;
    if (j < 0)
    {  i--;  continue;  }
    for (j--; j >= 0 && !isspace(e_d_sp[i-1][j]); j--)
     ;
    if (j < 0)
     i--;
    for (j--; j >= 0 && isspace(e_d_sp[i-1][j]); j--)
     ;
    if (j < 0)
     i--;
    for (j--; j >= 0 && !isspace(e_d_sp[i-1][j]); j--)
     ;
    if (!strncmp(e_d_sp[i-1]+j+1, "in ", 3))
    {
     strcpy(e_d_file, e_d_sp[i-1]+j+4);
     for (k = i+2; !e_d_file[0]; k++)
      strcpy(e_d_file, e_d_sp[i-1]+j+4);
     for (k = strlen(e_d_file)-1; k >= 0 && isspace(e_d_file[k]); k--)
      ;
     e_d_file[k+1] = '\0';
     if (e_d_p_watches(f, 0) == -1)
      return(-1);
     e_d_goto_break(e_d_file, ret, f);
     return(i);
    }
   }
   return(-2);
  }
  else if (e_deb_type == 3 && i == SVLINES-2 &&
    (ret = e_make_line_num(e_d_sp[i], e_d_sp[SVLINES-1])) >= 0)
  {
   strcpy(e_d_file, e_d_sp[SVLINES-1]);
   if (e_d_p_watches(f, 0) == -1)
    return(-1);
   e_d_goto_break(e_d_file, ret, f);
   return(i);
  }
 }
 return(-2);
}

int e_d_trd_check(FENSTER *f)
{
 int ret;
 char str[256];

 str[0] = '\0';
 e_d_switch_out(0);
 if ((ret = e_d_pr_sig(str, f)) == -1) return(-1);
 else if (ret == -2) e_d_error(e_d_msg[ERR_NOSOURCE]);
 return(0);
}

int e_read_output(FENSTER *f)
{
 char *spt;
 int i, ret;

 jdb_trace("e_read_output: CALLED, e_deb_type=%d\n", e_deb_type);
 for (i = 0; i < SVLINES; i++)
 {  e_d_sp[i] = e_d_out_str[i];  e_d_out_str[i][0] = '\0';  }
 while ((ret = e_d_line_read(wfildes[0], e_d_sp[SVLINES-1], 256, 0, 0)) == 2)
  e_d_error(e_d_sp[SVLINES-1]);
 if (ret < 0) return(-1);
 e_d_switch_out(0);
 while(ret != 1)
 {
  spt = e_d_sp[0];
  for (i = 1; i < SVLINES; i++)
   e_d_sp[i-1] = e_d_sp[i];
  e_d_sp[SVLINES-1] = spt;
  do
  {
   while((ret = e_d_line_read(wfildes[0], e_d_sp[SVLINES-1], 256, 0, 0)) == 2)
    e_d_error(e_d_sp[SVLINES-1]);
   if (ret < 0) return(-1);
  } while(!ret && !*e_d_sp[SVLINES-1]);
 }
 for (i = 0; i < SVLINES; i++)
 {
  if (strstr(e_d_sp[i], "exited"))
  {
   e_d_pty_flush_to_messages(f);
   e_d_p_message("Program exited.", f, 0);
   e_d_quit(f);
   return(0);
  }
 }
 /* Flush the inferior's stdout (which is fully buffered because it
    goes to a pty, not a real terminal) and drain any output.
    This is the technique used by gdbgui and Eclipse CDT. */
 if (e_deb_type == 0 && e_d_pty_master >= 0)
 {
  char _flush_resp[256];
  write(rfildes[1], "call (void)fflush(0)\n", 21);
  while (e_d_line_read(wfildes[0], _flush_resp, 256, 0, 0) == 0)
   ;
  e_d_pty_flush_to_messages(f);
 }
 /* pdb: parse "> file(line)func()" from the buffered output lines */
 if (e_deb_type == 5)
 {
  int _found = 0;
  /* Check for program exit FIRST -- pdb restarts the program
     automatically after it finishes, so "The program finished" appears
     before the new "> file(line)" line. We must catch exit before
     navigating to the restart position. */
  for (i = 0; i < SVLINES; i++)
  {
   if (strstr(e_d_sp[i], "The program finished"))
   {
    /* Capture program output from the buffer before quitting */
    int _j;
    for (_j = 0; _j < SVLINES; _j++)
    {
     char *_s = e_d_sp[_j];
     if (_s[0] && !strstr(_s, "> ") && !strstr(_s, "-> ") &&
         !strstr(_s, "(Pdb)") && !strstr(_s, "The program"))
     {
      int _olen = strlen(_s);
      while (_olen > 0 && (_s[_olen-1] == '\n' || _s[_olen-1] == '\r')) _olen--;
      if (_olen > 0)
      {
       if (e_d_prog_output_len + _olen + 1 > e_d_prog_output_cap)
       { e_d_prog_output_cap = (e_d_prog_output_len + _olen + 1024) * 2;
         e_d_prog_output = realloc(e_d_prog_output, e_d_prog_output_cap);
       }
       memcpy(e_d_prog_output + e_d_prog_output_len, _s, _olen);
       e_d_prog_output_len += _olen;
       e_d_prog_output[e_d_prog_output_len++] = '\n';
      }
     }
    }
    e_error("End of code. Ctrl-G P for output.", 0, f->fb);
    return(e_d_quit(f));
   }
  }
  for (i = 0; i < SVLINES && !_found; i++)
  {
   char *_gt = strstr(e_d_sp[i], "> ");
   if (_gt)
   {
    char *_op = strchr(_gt + 2, '(');
    if (_op)
    {
     int _line = atoi(_op + 1);
     char _file[128];
     int _flen = _op - (_gt + 2);
     if (_flen > 0 && _flen < 127 && _line > 0)
     {
      strncpy(_file, _gt + 2, _flen);
      _file[_flen] = '\0';
      e_d_goto_break(_file, _line, f);
      e_d_swtch = 3;
      _found = 1;
     }
    }
   }
  }
  if (_found) return(0);
  /* Fall through to gdb checks as fallback */
 }
 if ((i = e_d_fst_check(f)) == -1) return(-1);
 if (i < 0 && (i = e_d_snd_check(f)) == -1) return(-1);
 if (i < 0 && (i = e_d_trd_check(f)) == -1) return(-1);
 if (i < 0)
 {
  /* Could not determine where the debugger stopped.  This typically
     happens when the program exited or gdb entered system code.
     Quit the debugger cleanly instead of showing multiple popups. */
  e_d_switch_out(0);
  e_error("Program exited. Debugger stopped.", 0, f->fb);
  return(e_d_quit(f));
 }
 return(0);
}

int e_d_pr_sig(char *str, FENSTER *f)
{
 int i, line = -1, ret = 0;
 char file[128], str2[256];

 if (str && str[0])
  e_d_error(str);
 for (i = 0; i < SVLINES; i++)
  e_d_out_str[i][0] = '\0';
 if (e_deb_type != 1 && e_deb_type != 3)
 {
  write(rfildes[1], "where\n", 6);
  for (i = 0; ((ret = e_d_line_read(wfildes[0], str, 256, 0, 1)) == 0 &&
    (line = e_make_line_num(str, file)) < 0) || ret == 2; i++)
  {
   if (!strncmp(str, e_d_msg[ERR_NOSTACK], 9))
   {
    e_error(e_d_msg[ERR_PROGEXIT], 0, f->fb);
    while (ret == 0 || ret == 2)
     if ((ret = e_d_line_read(wfildes[0], str2, 256, 0, 0)) == 2)
      e_d_error(str2);
    e_d_quit(f);
    return(0);
   }
   else if (ret == 2)
    e_d_error(str);
  }
 }
 else
 {
  write(rfildes[1], "t\n", 2);
  for (i = 0; ((ret = e_d_line_read(wfildes[0], str, 256, 0, 1)) == 0 &&
    (line = e_make_line_num2(str, file)) < 0) || ret == 2; i++)
  {
   if (!strncmp(str, e_d_msg[ERR_NOPROCESS], 10))
   {
    e_error(e_d_msg[ERR_PROGEXIT], 0, f->fb);
    while (ret == 0 || ret == 2)
     if ((ret = e_d_line_read(wfildes[0], str2, 256, 0, 0)) == 2)
      e_d_error(str2);
    e_d_quit(f);
    return(0);
   }
   else if (ret == 2)
    e_d_error(str);
  }
 }
 if (ret == 1 && i == 0)
 {
  e_d_error(e_d_msg[ERR_PROGEXIT]);
  return(e_d_quit(f));
 }
 while (ret == 0 || ret == 2)
  if ((ret = e_d_line_read(wfildes[0], str2, 256, 0, 0)) == 2)
   e_d_error(str2);
 if (ret == -1)
  return(ret);
 if (line >= 0)
 {
  strcpy(e_d_file, file);
  e_d_goto_break(file, line, f);
  return(0);
 }
 else
  return(-2);
}

int e_make_line_num(char *str, char *file)
{
 char *sp;
 int i, n, num;

 if (e_deb_type == 0)
 {
  for (n = strlen(str); n >= 0 && str[n] != ':'; n--)
   ;
  if (n < 0)
   return(-1);
  for (i = n-1; i >= 0 && !isspace(str[i]); i--)
   ;
  for (n = i+1; str[n] != ':'; n++)
   file[n-i-1] = str[n];
  file[n-i-1] = '\0';
  return(atoi(str+n+1));
 }
 else if (e_deb_type == 2)
 {
  if (!(sp = strstr(str, " line ")))
   return(-1);
  if (!(num = atoi(sp+6)))
   return(-1);
  for (i = 6;  sp[i] != '\"'; i++)
   ;
  sp += (i+1);
  for (i = 0; (file[i] = sp[i]) != '\"' && file[i] != '\0'; i++)
   ;
  if (file[i] == '\0')
   return(-1);
  file[i] = '\0';
  return(num);
 }
 else if (e_deb_type == 3)
 {
  for (sp = str, i = 0; (file[i] = sp[i]) && sp[i] != ':'; i++)
   ;
  if (!sp[i])
   return(-1);
  file[i] = '\0';
  for (i++; sp[i] && sp[i] != ':'; i++)
   ;
  if (!sp[i])
   return(-1);
  for (i++; sp[i] && isspace(sp[i]); i++)
   ;
  if (!isdigit(sp[i]))
   return(-1);
  sp += i;
  return(atoi(sp));
 }
 else
 {
  for (i = 0; str[i] != '\0' && str[i] != ':'; i++)
   ;
  if ((!str[i]) || (num = atoi(str+i+1)) < 0)
   return(-1);
  write(rfildes[1], "e\n", 2);
  while ((i = e_d_line_read(wfildes[0], str, 256, 0, 0)) ==  2)
   e_d_error(str);
  if (i < 0)
   return(-1);
  for (i = 0; str[i] != '\0' && str[i] != '\"'; i++)
   ;
  for (sp = str + i + 1, i = 0; (file[i] = sp[i]) && file[i] != '\"'; i++)
   ;
  file[i] = '\0';
  if (e_d_dum_read() == -1)
   return(-1);
  return(num);
 }
}

int e_make_line_num2(char *str, char *file)
{
 char *sp;
 int i;

 for (i = 0; str[i] != '[' && str[i] != '\0'; i++)
  ;
 if (!str[i]) return(-1);
 for (sp = str+i+1, i = 0; (file[i] = sp[i]) != ':' && file[i] != '\0'; i++)
  ;
 if (file[i] == '\0') return(-1);
 file[i] = '\0';
 for (i++; isspace(sp[i]); i++)
  ;
 return(atoi(sp+i));
}

int e_d_goto_break(char *file, int line, FENSTER *f)
{
 ECNT *cn = f->ed;
 BUFFER *b;
 SCHIRM *s;
 FENSTER ftmp;
 int i;
 char str[120];

/*   if(schirm != e_d_save_schirm) e_d_switch_out(0);  */
 e_d_switch_out(0);
 ftmp.ed = cn;
 ftmp.fb = f->fb;
 WpeFilenameToPathFile(file, &ftmp.dirct, &ftmp.datnam);
 for (i = 0; i < SVLINES; i++)
  e_d_out_str[i][0] = '\0';
 for (i = cn->mxedt; i > 0; i--)
  if (!strcmp(cn->f[i]->datnam, ftmp.datnam) &&
    !strcmp(cn->f[i]->dirct, ftmp.dirct))
  {
   /*  for(j = 0; j <= cn->mxedt; j++)
	 if(!strcmp(cn->f[j]->datnam, "Stack"))
	 {  if(cn->f[i]->e.x > 2*MAXSCOL/3-1) cn->f[i]->e.x = 2*MAXSCOL/3-1;
	 break;
	 }  */
   e_switch_window(cn->edt[i], cn->f[cn->mxedt]);
   break;
  }
 f = cn->f[cn->mxedt];
 FREE(ftmp.dirct);
 FREE(ftmp.datnam);
 if (i <= 0)
 {
  if (access(file, 0))
  {
   /* Source file not found -- stepped into system/library code.
      Set a temporary breakpoint at the next user function (main)
      and continue, skipping all libc frames. */
   if (e_deb_type == 0)
   {
    write(rfildes[1], "tbreak main\n", 12);
    if (e_d_dum_read() == -1) { e_d_quit(f); return(-1); }
    write(rfildes[1], "c\n", 2);
    return(e_read_output(f));
   }
   sprintf(str, e_d_msg[ERR_CANTFILE], file);
   return(e_error(str, 0, f->fb));
  }
  if (e_edit(cn, file))
   return(WPE_ESC);
  b = cn->f[cn->mxedt]->b;
  s = cn->f[cn->mxedt]->s;
 }
 f = cn->f[cn->mxedt];
 b = cn->f[cn->mxedt]->b;
 s = cn->f[cn->mxedt]->s;
 s->da.y = b->b.y = line - 1;
 s->da.x = b->b.x = 0;
 s->de.x = MAXSCOL;
 e_schirm(f, 1);
 e_cursor(f, 1);
 e_d_pty_flush_to_messages(f);
#ifndef NO_XWINDOWS
 if (WpeIsXwin())
  e_x_reclaim_focus();
#endif
 return(0);
}

int e_d_delbreak(FENSTER *f)
{
 ECNT *cn = f->ed;
 int i;

 for (i = cn->mxedt; i >= 0; i--)
  if (DTMD_ISTEXT(cn->f[i]->dtmd))
   cn->f[i]->s->da.y = -1;
 e_rep_win_tree(cn);
 e_refresh();
 return(0);
}

int e_d_error(char *s)
{
 int len;

 e_d_switch_out(0);
 if (s[(len = strlen(s) - 1)] == '\n')
  s[len] = '\0';
 return(e_error(s, 0, WpeEditor->fb));
}

int e_d_putchar(int c)
{
 if (!WpeIsXwin()) c = fk_putchar(c);
 else
 {
  char cc = c;
  c = write(wfildes[1], &cc, 1);
 }
 return(c);
}

int e_deb_options(FENSTER *f)
{
 int ret;
 W_OPTSTR *o = e_init_opt_kst(f);

 if (!o) return(-1);
 o->xa = 20;  o->ya = 4;  o->xe = 60;  o->ye = 16;
 o->bgsw = 0;
 o->name = "Debug-Options";
 o->crsw = AltO;
 e_add_txtstr(4, 2, "Debugger:", o);
 e_add_txtstr(20, 2, "Mode:", o);
 { /* Auto-select debugger by file extension */
   ECNT *cn2 = f->ed;
   char *_fn = cn2->f[cn2->mxedt]->datnam;
   int _fnl = strlen(_fn);
   if (_fnl > 5 && !strcmp(_fn + _fnl - 5, ".java"))
    e_deb_type = 4;
   else if (_fnl > 3 && !strcmp(_fn + _fnl - 3, ".py"))
    e_deb_type = 5;
   /* Map e_deb_type to radio button index:
      0=Gdb, 1=Sdb, 2=Dbx, 3=Jdb, 4=Pdb (radio index)
      e_deb_type: 0=gdb, 1=sdb, 2=dbx, 3=xdb, 4=jdb, 5=pdb */
   int _sel = (e_deb_type == 5) ? 4 :
              (e_deb_type == 4) ? 3 :
              (e_deb_type == 3) ? 2 : e_deb_type;
 e_add_pswstr(0, 5, 3, 0, AltG, 0, "Gdb    ", o);
 e_add_pswstr(0, 5, 4, 0, AltS, 0, "Sdb    ", o);
#ifdef XDB
 e_add_pswstr(0, 5, 5, 0, AltX, 0, "Xdb    ", o);
#else
 e_add_pswstr(0, 5, 5, 0, AltD, 0, "Dbx    ", o);
#endif
 e_add_pswstr(0, 5, 6, 0, AltJ, 0, "Jdb    ", o);
 e_add_pswstr(0, 5, 7, 0, AltP, _sel, "Pdb    ", o);
 }
 e_add_pswstr(1, 21, 3, 0, AltN, 0, "Normal     ", o);
 e_add_pswstr(1, 21, 4, 0, AltF, e_deb_mode, "Full Screen", o);
 e_add_bttstr(10, 10, 1, AltO, " Ok ", NULL, o);
 e_add_bttstr(25, 10, -1, WPE_ESC, "Cancel", NULL, o);
 ret = e_opt_kst(o);
 if (ret != WPE_ESC)
 {
  { int sel = o->pstr[0]->num;
#ifdef XDB
    if (sel == 2) e_deb_type = 3;
    else if (sel == 3) e_deb_type = 4;
    else if (sel == 4) e_deb_type = 5;
    else e_deb_type = sel;
#else
    if (sel == 3) e_deb_type = 4;
    else if (sel == 4) e_deb_type = 5;
    else e_deb_type = sel;
#endif
  }
  e_deb_mode = o->pstr[1]->num;
 }
 freeostr(o);
 return(0);
}

int e_g_sys_ini()
{
 if (!e_d_swtch || e_deb_mode)
  return(0);
 tcgetattr(0, &ttermio);
 return(tcsetattr(0, TCSADRAIN, &otermio));
}

int e_g_sys_end()
{
 if (!e_d_swtch || e_deb_mode)
  return(0);
 return(tcsetattr(0, TCSADRAIN, &ttermio));
}

int e_test_command(char *str)
{
 int i = -1, k;
 char tmp[256], *path = getenv("PATH");

 if (!path) return(-2);
 do
 {
  for(i++, k = 0; (tmp[k] = path[i]) && path[i] != ':'; k++, i++)
   ;
  if (k == 0)
  {  tmp[0] = '.';  k++;  }
  tmp[k] = '/';
  tmp[k+1] = '\0';
  strcat(tmp, str);
  if (!access(tmp, X_OK)) return(0);
 } while(path[i]);
 return(-1);
}
#endif

