[ANN] HighLine 0.4.0

HighLine 0.4.0 Released

···

=======================

Not a lot of features in this release, but two that sure are nice to have. This version brings word wrap and paged printing capabilities to all output HighLine's output. Limits for both operations are adjustable with new accessors `wrap_at =` and `page_at =` on all HighLine objects.

Let me just say a big thanks to Greg Brown, who did a lot of the initial work on getting word wrap into HighLine. Thanks Greg!

If anyone uses this, feedback is welcome (james@grayproductions.net). I do have a TODO list of features I would like to add, but I'm also open to suggestions of how to grow the project and make in useful to all.

What is HighLine?
-----------------

(from the README)

HighLine was designed to ease the tedious tasks of doing console input and output with low-level methods like gets() and puts(). HighLine provides a robust system for requesting data from a user, without needing to code all the error checking and validation rules and without needing to convert the typed Strings into what your program really needs. Just tell HighLine what you're after, and let it do all the work.

What's new in this release?
---------------------------

(highlights from the CHANGELOG)

* Implemented line wrapping with adjustable limit.
* Implemented paged printing with adjustable limit.

Plus documentation and examples for the new features.

Where can I learn more?
-----------------------

HighLine is hosted on RubyForge.

Project page: http://rubyforge.org/projects/highline/
Documentation: http://highline.rubyforge.org/
Downloads: http://rubyforge.org/frs/?group_id=683

How do I get HighLine?
----------------------

HighLine is a gem, so as long as you have RubyGems installed it's as simple as:

$ sudo gem install highline

If you need to install RubyGems, you can download it from:

http://rubyforge.org/frs/?group_id=126&release_id=1885

HighLine can also be installed manually. Just download the latest release and follow the instructions in INSTALL:

http://rubyforge.org/frs/?group_id=683&release_id=2143

James Edward Gray II

Hello James,

good job on the new release, I plan to use HighLine in an application I
am building right now, however I would really like it if you could add
an ask_password method. Right now, I use this small hackish method:

    # File lib/helpers.rb, line 12
12: def self.ask_password(prompt)
13: print prompt
14: system("stty -echo echonl")
15: pass = gets.chomp
16: system("stty echo -echonl")
17: return pass
18: end

Maybe something to add to your TODO :slight_smile:

Vincent.

Did you check the TODO? :smiley:

Seriously, I thank you for the request. It is already on the list and I will add it.

My biggest problem here is that I'm trying to keep HighLine pretty cross-platform (Unix and Windows is enough for me). I understand the method you just posted, but if anyone can tell me the Window's equivalent it would sure help me along...

Thanks!

James Edward Gray II

···

On May 7, 2005, at 4:19 PM, Vincent Foley wrote:

Maybe something to add to your TODO :slight_smile:

Vincent Foley wrote:

Hello James,

good job on the new release, I plan to use HighLine in an application I
am building right now, however I would really like it if you could add
an ask_password method. Right now, I use this small hackish method:

    # File lib/helpers.rb, line 12
12: def self.ask_password(prompt)
13: print prompt
14: system("stty -echo echonl")
15: pass = gets.chomp
16: system("stty echo -echonl")
17: return pass
18: end

Maybe something to add to your TODO :slight_smile:

Especially if you can do it multi-platform (the above does not work on Windows.)

Here is a Windows version that I just wrote:

require 'Win32API'

@getch = Win32API.new("crtdll", "_getch", , 'L')

def ask_password(prompt)
   print prompt
   pass = ''
   while ((c = @getch.call) != 13)
     pass << c.chr
   end
   pass
end

pass = ask_password("Please give me your password: ")
puts
puts "Got '#{pass}'"
__END__

There may be a better way, but this works. James, feel free to put this in HighLine, just give me the credit :slight_smile:

Obviously this will need to be partitioned for the different platforms.

Another cool thing is you could take a block that checks the password for strength :slight_smile:

Ryan

James Edward Gray II said:

I understand the method you just posted, but if anyone can tell me
the Window's equivalent it would sure help me along...

Dunno about Windows but I think that using ruby-termios would be
better then calling an external program.

I'm using something like this in ruby-managesieve's sievectl utility
(IIRC I just copied it from one of the usage examples in the termios
lib :slight_smile:

  oldt = Termios.tcgetattr(STDIN)
  newt = oldt.dup
  newt.lflag &= ~Termios::ECHO
  Termios.tcsetattr(STDIN, Termios::TCSANOW, newt)
  print 'Password: '
  info['password'] = STDIN.gets
  Termios.tcsetattr(STDIN, Termios::TCSANOW, oldt)

Regards,
Andre

James Edward Gray II wrote:

My biggest problem here is that I'm trying to keep HighLine pretty cross-platform (Unix and Windows is enough for me). I understand the method you just posted, but if anyone can tell me the Window's equivalent it would sure help me along...

You can see that in my other response.

Something else I was just thinking is that if you could get the Unix version of getch as a proc object, it could be used instead of the Win32API version I used. I believe that can be gotten from the Curses library.

That would minimize the difference between each platform, and wouldn't require external programs like Vincent's does.

My only concern would be whether the enter key was always decimal 13 on each machine...I do know that newlines in text files on each platform are different (CRLF on Windows, CR in Unix, LF in Mac...though maybe OS X is like Unix.)

Ryan

I have a question about this solution. You're stopping when you see a carriage-return above, but what does Windows really tact onto the end of the line? Is it a carriage return line feed pair, as I suspect? Put another way, if you changed the above from 13 to 10, would it still work?

Thanks.

James Edward Gray II

P.S. I'm not a Window's user. That should be painfully obvious by now, but I just wanted to explain why I'm asking so many dumb questions. :slight_smile:

···

On May 7, 2005, at 4:48 PM, Ryan Leavengood wrote:

Here is a Windows version that I just wrote:

require 'Win32API'

@getch = Win32API.new("crtdll", "_getch", , 'L')

def ask_password(prompt)
  print prompt
  pass = ''
  while ((c = @getch.call) != 13)
    pass << c.chr
  end
  pass
end

pass = ask_password("Please give me your password: ")
puts
puts "Got '#{pass}'"
__END__

There may be a better way, but this works. James, feel free to put this in HighLine, just give me the credit :slight_smile:

Last question on this, I promise. Does Windows echo the carriage return (and possibly line feed), when character reading like this?

James Edward Gray II

···

On May 7, 2005, at 4:48 PM, Ryan Leavengood wrote:

require 'Win32API'

@getch = Win32API.new("crtdll", "_getch", , 'L')

def ask_password(prompt)
  print prompt
  pass = ''
  while ((c = @getch.call) != 13)
    pass << c.chr
  end
  pass
end

pass = ask_password("Please give me your password: ")
puts
puts "Got '#{pass}'"
__END__

Indeed, I read it just after I posted :slight_smile: Sorry about that. However, I
think I have an idea that could be fun which isn't yet on your TODO:
readline support. It would be nice if people could have a history of
things they wrote, if they could use the arrows to go fix a simple
mistake instead of deleting until the mistake and typing the rest
again. How about that?

Vincent.

[snip]

My only concern would be whether the enter key was always decimal 13 on
each machine...I do know that newlines in text files on each platform
are different (CRLF on Windows, CR in Unix, LF in Mac...though maybe OS
X is like Unix.)

Ryan

I think you have that a little backwards. unix is LF, and Mac is CR
although OS X is LF also.

Darwin Logan-Capaldos-Computer.local 7.9.0 Darwin Kernel Version
7.9.0: Wed Mar 30 20:11:17 PST 2005;
root:xnu/xnu-517.12.7.obj~1/RELEASE_PPC Power Macintosh powerpc
logan:/Users/logan% irb
irb(main):001:0> ?\n
=> 10

···

On 5/7/05, Ryan Leavengood <mrcode@netrox.net> wrote:

James Edward Gray II wrote:

I have a question about this solution. You're stopping when you see a carriage-return above, but what does Windows really tact onto the end of the line?

Nothing, by using getch I get every character and DOS/Windows doesn't do anything. That is why I put that extra puts there. Otherwise the result looks like this:

Please give me your password: Got 'ryan'

Is it a carriage return line feed pair, as I suspect?

Nope.

Put another way, if you changed the above from 13 to 10, would it still work?

Nope. In that case you have to type Ctrl-J to end, whereas with 13 you can hit the enter key or type Ctrl-M. I'm starting to think you will see the same behavior on Unix and Mac.

> Last question on this, I promise. Does Windows echo the carriage
> return (and possibly line feed), when character reading like this?

As I said above, it echoes nothing.

I'm definitely thinking my solution would make a good general multi-platform password prompter, with only the implementation of getch requiring multi-platform code.

Ryan

I'm playing around with termios over here, to see if it's a viable replacement to the stty hack. I'm having some trouble getting what I need out of it though.

The above code reads a line from the terminal. HighLine requires code to read a single character. Can someone please show me how to get that out of termios?

Making this change will make HighLine depend on a C extension. Everyone agrees that's better than the external stty program?

Thanks.

James Edward Gray II

···

On May 7, 2005, at 4:37 PM, Andre Nathan wrote:

Dunno about Windows but I think that using ruby-termios would be
better then calling an external program.

I'm using something like this in ruby-managesieve's sievectl utility
(IIRC I just copied it from one of the usage examples in the termios
lib :slight_smile:

  oldt = Termios.tcgetattr(STDIN)
  newt = oldt.dup
  newt.lflag &= ~Termios::ECHO
  Termios.tcsetattr(STDIN, Termios::TCSANOW, newt)
  print 'Password: '
  info['password'] = STDIN.gets
  Termios.tcsetattr(STDIN, Termios::TCSANOW, oldt)

I put in on the list. I'll definitely look into it.

Thanks for the suggestion.

James Edward Gray II

P.S. The release with the no echo option, and a few other goodies, should be out later today.

···

On May 9, 2005, at 3:09 PM, Vincent Foley wrote:

Indeed, I read it just after I posted :slight_smile: Sorry about that. However, I
think I have an idea that could be fun which isn't yet on your TODO:
readline support. It would be nice if people could have a history of
things they wrote, if they could use the arrows to go fix a simple
mistake instead of deleting until the mistake and typing the rest
again. How about that?

Logan Capaldo wrote:
>

I think you have that a little backwards. unix is LF, and Mac is CR
although OS X is LF also.

Darwin Logan-Capaldos-Computer.local 7.9.0 Darwin Kernel Version
7.9.0: Wed Mar 30 20:11:17 PST 2005;
root:xnu/xnu-517.12.7.obj~1/RELEASE_PPC Power Macintosh powerpc
logan:/Users/logan% irb
irb(main):001:0> ?\n
=> 10

Yes, you are right. Hmmm, this is kind of confusing though. Does ?\n return 13 on a non-OS-X Mac? I would think that \n and \r would always mean NL (i.e. decimal 10) and CR (i.e. decimal 13), no matter what the operating system.

But for the matter at hand, I just want to make sure that pressing the enter key on a Unix and Mac machine always produces decimal 13.

Ryan

James Edward Gray II wrote:

I have a question about this solution. You're stopping when you see a carriage-return above, but what does Windows really tact onto the end of the line?

Nothing, by using getch I get every character and DOS/Windows doesn't do anything. That is why I put that extra puts there. Otherwise the result looks like this:

Please give me your password: Got 'ryan'

Thanks.

I'm starting to think you will see the same behavior on Unix and Mac.

Na, Unix uses line feeds:

$ ruby -e 'p gets[-1]'
This will be a line feed =>
10

James Edward Gray II

···

On May 7, 2005, at 6:49 PM, Ryan Leavengood wrote:

How did you handle the noecho thing on different platforms? I looked
at the getpass.py Python module, they used a module called msvcrt which
had a getch() function which reads a character without echoing it. The
whole Windows function seemed rather hackish in my opinion.

James Edward Gray II wrote:

Na, Unix uses line feeds:

$ ruby -e 'p gets[-1]'
This will be a line feed =>
10

I get the same result on Windows, so I suspect Ruby is doing some stuff behind the scenes. You may want to try my password prompter with the getch of the curses libraries on a Unix box just to be sure.

Ryan

I used pretty much what Ryan showed.

I'm not too troubled by using the Win32API to character read. That seems pretty solid. I've seen that solution in many places over the years and HighLine was using it even before we started talking passwords and Ryan showed his version. (If you look back, you'll see that I wrote about it in the summary to Ruby Quiz #5.) I'm almost never on Windows, but the couple of times I've tried it there, it worked perfectly.

Hunting for a carriage return feels a little more shaky to me, but I would still be doing that if I was using a different getch() routine, right? It too seems to work.

Personally, I find the Unix hack more troubling. HighLine has been using the external stty for character reading and still is. I agree with Andre that there's probably a better way. I played with termios quite a bit for this release, but I'm not convinced it's a better solution. Just to give one example, the stty trick works on Mac OS X out of the box, but I believe you can only install termios if you've installed Apple's optional Developer Tools. (I haven't tested that.) Here again, I've never seen the stty "hack" not work. For now at least, I'm calling it good enough.

The moral: File a bug report if you have problems. Oh, and be sure it includes your patch it fix it. :slight_smile: Nothing like releasing code into the wild to see just how much it doesn't work, eh?

I really do want HighLine to work (well!) on at least Windows and Unix. I am trying to make the decisions to make that happen with my imperfect information. Feel free to enhance my knowledge at any time!

James Edward Gray II

···

On May 9, 2005, at 5:24 PM, Vincent Foley wrote:

How did you handle the noecho thing on different platforms? I looked
at the getpass.py Python module, they used a module called msvcrt which
had a getch() function which reads a character without echoing it. The
whole Windows function seemed rather hackish in my opinion.

Duh. You're right:

$ ruby -e 'system "stty raw"; p $stdin.getc; syst "stty -raw"'
13

Thanks for the lesson. :wink:

James Edward Gray II

···

On May 7, 2005, at 8:10 PM, Ryan Leavengood wrote:

I get the same result on Windows, so I suspect Ruby is doing some stuff behind the scenes. You may want to try my password prompter with the getch of the curses libraries on a Unix box just to be sure.

James Edward Gray II said:

Just to give one example, the stty trick works on Mac OS X
out of the box, but I believe you can only install termios if you've
installed Apple's optional Developer Tools.

Hmm. Maybe keep stty as a backup mechanism then... try to require
termios and fall back to stty if it fails. Would that be too ugly?

Best regards,
Andre