This is the mail archive of the cygwin mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: readline-5.1 && CGDB


On Mon, Jan 30, 2006 at 07:11:45AM -0700, Eric Blake wrote:
> According to Bob Rossi on 1/28/2006 9:19 PM:
> > On Linux, something totally different happens. When I initialize
> > readline, it eventually calls tgetent, which happens to set LINES and
> > COLS to the correct size of the terminal. On cygwin this doesn't happen.
> > The call readline makes to tgetent leaves LINES and COLS alone. Then,
> > in Cygwin when I get to initscr, LINES and COLS is set to 25x80, unless
> > I set the LINES and COLUMNS environment variables before the call to
> > initscr.
> 
> http://www.die.net/doc/linux/man/man3/tgetent.3.html does not document
> that tgetent() messes with the environment, but if that is the case, you
> may be onto something.  Perhaps it really is something cygwin1.dll needs
> to patch to be more similar to linux.

I've finally produced a small test case like you asked me too. It did
take some time, but it should certainly help discover the problem. I've
attached the program.

Basically, you can run the program like './main' and that will have
readline operate on stdout, or you can run it like './main use_pty' and
that will have readline operate on a PTY that I have created. Besides
the code that opens the PTY, there is only the main function, and a
function that modifies the readline library.

If you run with the use_pty option, then ncurses thinks the terminal
size is 25x80 after the call to rl_reset_terminal. Here's the output:

    [bar@bar-nt ~/cvs/cgdb/builddir/tmp] $ ./main.exe use_pty
    before rline_initialize LINES=0 COLS=0

	    before rl_callback LINES=0 COLS=0
	    after rl_callback LINES=75 COLS=85

	    before rl_reset_terminal LINES=75 COLS=85
	    after rl_reset_terminal LINES=25 COLS=80

    after rline_initialize LINES=25 COLS=80

    before initscr LINES=25 COLS=80
    after initscr LINES=25 COLS=80

However, if you run without that option, then ncurses thinks the terminal 
size is the correct terminal size. Here's the output:

    [bar@bar-nt ~/cvs/cgdb/builddir/tmp] $ ./main
    before rline_initialize LINES=0 COLS=0

	    before rl_callback LINES=0 COLS=0
    (tgdb)  after rl_callback LINES=75 COLS=85

	    before rl_reset_terminal LINES=75 COLS=85
	    after rl_reset_terminal LINES=75 COLS=85

    after rline_initialize LINES=75 COLS=85

    before initscr LINES=75 COLS=85
    after initscr LINES=75 COLS=85

I've tracked down the code in readline to the line that manipulates the
LINES/COLS curses variables when initialize readline with a PTY. (This
same code does not modify the LINES/COLS variable when readline is
initialized with stdout).

The code is in terminal.c:_rl_init_terminal_io line 420 for me. It looks
like 
         tgetent_ret = tgetent (term_buffer, term);

where term is the string "dumb".

What I can't understand is, why would tgetent act differently when I
initialize readline with a created PTY, instead of stdout?

Thanks,
Bob Rossi

#include <stdio.h>
#include <curses.h>
#include <readline/readline.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>

/*****************************************************************************
 **************************** PTY CODE ***************************************
 ****************************************************************************/
#define SLAVE_SIZE 64

static size_t strlcpy_local(char *dst, const char *src, size_t size) {
    const char *s = src;
    char *d = dst;
    size_t n = size;

    if (n)
        while (--n && (*d++ = *s++)) {}

    if (n == 0) {
        if (size)
            *d = '\0';

        while (*s++) {}
    }

    return s - src - 1;
}

int pty_open(
  int *masterfd, 
  int *slavefd, 
  char *slavename, 
  size_t slavenamesize, 
  const struct termios *slave_termios, 
  const struct winsize *slave_winsize) 
{
 char *name;

 if (!masterfd || !slavefd || !slavename || slavenamesize < 64)
  return -1;

 if ((*masterfd = open("/dev/ptmx", 2 | 0x8000)) == -1)
  return -1;

 if (grantpt(*masterfd) == -1)
 {
  close(*masterfd);
  return -1;
 }

 if (unlockpt(*masterfd) == -1)
 {
  close(*masterfd);
  return -1;
 }

 if (!(name = ptsname(*masterfd)))
 {
  close(*masterfd);
  return -1;
 }

 if (strlcpy_local(slavename, name, slavenamesize) >= slavenamesize)
 {
  close(*masterfd);
  return -1;
 }

 if ((*slavefd = open(slavename, 2 | 0x8000)) == -1)
 {
  close(*masterfd);
  return -1;
 }

 if (slave_termios && tcsetattr(*slavefd, 2, slave_termios) == -1)
 {
  close(*masterfd);
  close(*slavefd);
  return -1;
 }

 if (slave_winsize && ioctl(*slavefd, (('T' << 8) | 2), slave_winsize) == -1)
 {
  close(*masterfd);
  close(*slavefd);
  return -1;
 }

 return 0;
}

struct pty_pair 
{
  int masterfd;
  int slavefd;
  char slavename[SLAVE_SIZE];
};
typedef struct pty_pair *pty_pair_ptr;

pty_pair_ptr pty_pair_create (void)
{
  int val;
  static char local_slavename[SLAVE_SIZE];
  pty_pair_ptr ptr = (pty_pair_ptr)malloc (sizeof (struct pty_pair));
  if (!ptr)
    return NULL;

  ptr->masterfd = -1;
  ptr->slavefd = -1;
  ptr->slavename[0] = 0;

  val = pty_open (&(ptr->masterfd), &(ptr->slavefd), local_slavename, SLAVE_SIZE, NULL, NULL);
  if (val == -1)
    return NULL;   

  strncpy(ptr->slavename, local_slavename, SLAVE_SIZE);

  return ptr;
}

/*****************************************************************************
 **************************** APPLICATION CODE *******************************
 ****************************************************************************/

void
command (char *b)
{}

typedef void command_cb (char *);

int
rline_initialize (int slavefd, command_cb *command)
{
  FILE *input, *output;

  input = fdopen (slavefd, "r");
  if (!input)
    return -1;

  output = fdopen (slavefd, "w");
  if (!output)
    return -1;

  rl_instream = input;
  rl_outstream = output;

  /* Tell readline what the prompt is if it needs to put it back */
  fprintf (stderr, "\tbefore rl_callback LINES=%d COLS=%d\n", LINES, COLS);
  rl_callback_handler_install("(tgdb) ", command);
  fprintf (stderr, "\tafter rl_callback LINES=%d COLS=%d\n\n", LINES, COLS);

  /* Set the terminal type to dumb so the output of readline can be
   * understood by tgdb */
  fprintf (stderr, "\tbefore rl_reset_terminal LINES=%d COLS=%d\n", LINES, COLS);
  if (rl_reset_terminal ("dumb") == -1)
    return -1;
  fprintf (stderr, "\tafter rl_reset_terminal LINES=%d COLS=%d\n\n", LINES, COLS);

  return 0;
}

int main (int argc, char **argv)
{
#if 0
  int c;
  read(0, &c, 1);
#endif

  pty_pair_ptr pty_pair;

  pty_pair = pty_pair_create ();
  if (!pty_pair)
    return -1;

  fprintf (stderr, "before rline_initialize LINES=%d COLS=%d\n\n", LINES, COLS);

  /** 
   * If you initialize readline with stdout, everything appears to be fine.
   * However, if you initialize readline with a created slave PTY, the LINES
   * COLS are incorrect after the call to rl_reset_terminal.
   */
  if (argc == 2 && strcmp (argv[1], "use_pty") == 0)
    rline_initialize (pty_pair->slavefd, command);
  else
    rline_initialize (0, command);

  fprintf (stderr, "after rline_initialize LINES=%d COLS=%d\n\n", LINES, COLS);


  fprintf (stderr, "before initscr LINES=%d COLS=%d\n", LINES, COLS);
  initscr ();
  fprintf (stderr, "after initscr LINES=%d COLS=%d\n", LINES, COLS);
  endwin ();
  return 0;
}

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]