Subject: Re: Bash termcap and history file
From: [email-masked] (Jason Tishler)

16 Jan 1997 13:07:49 -0800
Approved: [email-masked]
Distribution: cygnus
Message-ID: <[email-masked]>
Reply-To: [email-masked]
Mime-Version: 1.0
Content-Type: multipart/mixed; boundary="------------D3E5CFE35A9"
X-Mailer: Mozilla 3.0Gold (WinNT; I)
Original-To: gnu-win32 <[email-masked]>
Original-CC: [email-masked]
Original-Sender: [email-masked]

This is a multi-part message in MIME format.

--------------D3E5CFE35A9
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Sorry for the extra blank lines in my previous email (sigh) ...

Jason Tishler wrote:
> I am also having the above problems.  Has anyone been successful in
> getting bash to read their history file on start up?

VERSION:
=======

Beta 17.1 under NT 3.51 and 4.0.

SUMMARY:
=======

I found the reason why bash fails to read history files on start up.  It
has to due with the difference between the end-of-line (EOL) delimiter
used in Windows and UNIX.  In Windows, EOL is the two character seqence
"\r\n"; in UNIX, EOL is one character "\n".

When bash reads a history file, it compares the size of the file as
reported by fstat(2) to the number of bytes returned by read(2).  If
they are not equal, then bash will ignore the contents of the history
file.

The file size reported by fstat(2) includes the "\r" characters in the
EOLs.  On the other hand, the number of bytes returned by read(2) does
not include the "\r" characters because read(2) strips them out from the
input.  Hence, these quantities will never be equal.  (In fact, they
always differ by the number of lines in the history file.)

Note that the problem I found seems general and may affect other
gnu-win32 tools.

I'm willing to post my version of bash (~256K) that fixes the history
problem to the mailing list if it is appropriate and if people are
interested.  Sorry, but I don't have a publically accessible ftp site.

GORY DETAILS:
==== =======

The following is the pertinent code from bash/lib/readline/history.c:

    int
    read_history_range (filename, from, to)
    ...
    {
    ...
      input = history_filename (filename);
      file = open (input, O_RDONLY, 0666);

      if ((file < 0) || (fstat (file, &finfo) == -1))
        goto error_and_exit;
    ...
/*X*/ if (read (file, buffer, finfo.st_size) != finfo.st_size)
        {
      error_and_exit:
    ...
          return (errno);
        }
    ...
    }

The line marked /*X*/ above shows where the problem is -- the if
statement will never be true.

Using the code from history_truncate_file() (in history.c) as a model, I
changed read_history_range() to save the number of bytes returned by
read(2) in chars_read.  Then, I used chars_read instead of finfo.st_size
during the parsing of the history file.  My version of
read_history_range() is attached and the diffs are the following:

    561c561
    <   int file, current_line;
    ---
    >   int file, current_line, chars_read;
    572c572
    <   if (read (file, buffer, finfo.st_size) != finfo.st_size)
    ---
    >   if ((chars_read = read (file, buffer, finfo.st_size)) <= 0)
    591c591
    <     to = finfo.st_size;
    ---
    >     to = chars_read;
    597c597
    <   while (line_start < finfo.st_size && current_line < from)
    ---
    >   while (line_start < chars_read && current_line < from)
    599c599
    <       for (line_end = line_start; line_end < finfo.st_size; line_end++)
    ---
    >       for (line_end = line_start; line_end < chars_read; line_end++)
    610c610
    <   for (line_end = line_start; line_end < finfo.st_size; line_end++)
    ---
    >   for (line_end = line_start; line_end < chars_read; line_end++)

As one can see, the above changes were minor.

Jason

-- 
Jason Tishler                        Phone: +1 (212) 989-4455 ext. 120
Box Hill Systems Corporation         Fax:   +1 (212) 989-6817
161 Avenue of the Americas           Email: [email-masked]
New York, NY 10013 USA               WWW:   http://www.boxhill.com

--------------D3E5CFE35A9
Content-Type: text/plain; charset=us-ascii; name="read_history_range"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="read_history_range"

int
read_history_range (filename, from, to)
     char *filename;
     int from, to;
{
  register int line_start, line_end;
  char *input, *buffer = (char *)NULL;
  int file, current_line, chars_read;
  struct stat finfo;

  input = history_filename (filename);
  file = open (input, O_RDONLY, 0666);

  if ((file < 0) || (fstat (file, &finfo) == -1))
    goto error_and_exit;

  buffer = xmalloc ((int)finfo.st_size + 1);

  if ((chars_read = read (file, buffer, finfo.st_size)) <= 0)
    {
  error_and_exit:
      if (file >= 0)
	close (file);

      if (input)
	free (input);

      if (buffer)
	free (buffer);

      return (errno);
    }

  close (file);

  /* Set TO to larger than end of file if negative. */
  if (to < 0)
    to = chars_read;

  /* Start at beginning of file, work to end. */
  line_start = line_end = current_line = 0;

  /* Skip lines until we are at FROM. */
  while (line_start < chars_read && current_line < from)
    {
      for (line_end = line_start; line_end < chars_read; line_end++)
	if (buffer[line_end] == '\n')
	  {
	    current_line++;
	    line_start = line_end + 1;
	    if (current_line == from)
	      break;
	  }
    }

  /* If there are lines left to gobble, then gobble them now. */
  for (line_end = line_start; line_end < chars_read; line_end++)
    if (buffer[line_end] == '\n')
      {
	buffer[line_end] = '\0';

	if (buffer[line_start])
	  add_history (buffer + line_start);

	current_line++;

	if (current_line >= to)
	  break;

	line_start = line_end + 1;
      }

  if (input)
    free (input);

  if (buffer)
    free (buffer);

  return (0);
}

--------------D3E5CFE35A9--

-
For help on using this list, send a message to
"[email-masked]" with one line of text: "help".