001    /**
002     * jline - Java console input library
003     * Copyright (c) 2002,2003 Marc Prud'hommeaux marc@apocalypse.org
004     *
005     * This library is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU Lesser General Public
007     * License as published by the Free Software Foundation; either
008     * version 2.1 of the License, or (at your option) any later version.
009     *
010     * This library is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013     * Lesser General Public License for more details.
014     *
015     * You should have received a copy of the GNU Lesser General Public
016     * License along with this library; if not, write to the Free Software
017     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018     */
019    package jline;
020    
021    import java.io.*;
022    import java.text.*;
023    import java.util.*;
024    
025    /** 
026     *  A reader for console applications. It supports custom tab-completion,
027     *  saveable command history, and command line editing. On some
028     *  platforms, platform-specific commands will need to be
029     *  issued before the reader will function properly. See
030     *  {@link Terminal#initializeTerminal} for convenience methods for
031     *  issuing platform-specific setup commands.
032     *  <p>
033     *  <strong>TODO:</strong>
034     *  <ul>
035     *      <li>i18n</li>
036     *      <li>minimize redundant redrawing of similar buffers</li>
037     *      <li>obtain terminal width and height for columnation</li>
038     *      <li>Enable some sort of pages for large candidate results</li>
039     *      <li>Add ANSI color support for completors (e.g., directories in green)</li>
040     *      <li>Implement mid-line tab-completion</li>
041     *  </ul>
042     *
043     *  @author  <a href="mailto:marc@apocalypse.org">Marc Prud'hommeaux</a>
044     */
045    public class ConsoleReader
046    {
047            String prompt;
048    
049            public static final String CR = System.getProperty ("line.separator");
050    
051            public static final char BACKSPACE = '\b';
052            public static final char RESET_LINE = '\r';
053            public static final char KEYBOARD_BELL = '\07';
054    
055    
056            public static final short ARROW_START                   = 27;
057            public static final short ARROW_PREFIX                  = 91;
058            public static final short ARROW_LEFT                    = 68;
059            public static final short ARROW_RIGHT                   = 67;
060            public static final short ARROW_UP                              = 65;
061            public static final short ARROW_DOWN                    = 66;
062    
063    
064            /**
065             *      Logical constants for key operations.
066             */
067    
068            /** 
069             *  Unknown operation.
070             */
071            public static final short UNKNOWN                               = -99;
072    
073            /** 
074             *  Operation that moves to the beginning of the buffer.
075             */
076            public static final short MOVE_TO_BEG                   = -1;
077    
078            /** 
079             *  Operation that moves to the end of the buffer.
080             */
081            public static final short MOVE_TO_END                   = -3;
082    
083            /** 
084             *  Operation that moved to the previous character in the buffer.
085             */
086            public static final short PREV_CHAR                             = -4;
087    
088            /** 
089             *  Operation that issues a newline.
090             */
091            public static final short NEWLINE                               = -6;
092    
093            /** 
094             *  Operation that deletes the buffer from the current character to the end.
095             */
096            public static final short KILL_LINE                             = -7;
097    
098            /** 
099             *  Operation that clears the screen.
100             */
101            public static final short CLEAR_SCREEN                  = -8;
102    
103            /** 
104             *  Operation that sets the buffer to the next history item.
105             */
106            public static final short NEXT_HISTORY                  = -9;
107    
108            /** 
109             *  Operation that sets the buffer to the previous history item.
110             */
111            public static final short PREV_HISTORY                  = -11;
112    
113            /** 
114             *  Operation that redisplays the current buffer.
115             */
116            public static final short REDISPLAY                             = -13;
117    
118            /** 
119             *  Operation that deletes the buffer from the cursor to the beginning.
120             */
121            public static final short KILL_LINE_PREV                = -15;
122    
123            /** 
124             *  Operation that deletes the previous word in the buffer.
125             */
126            public static final short DELETE_PREV_WORD              = -16;
127    
128            /** 
129             *  Operation that moves to the next character in the buffer.
130             */
131            public static final short NEXT_CHAR                             = -19;
132    
133            /** 
134             *  Operation that moves to the previous character in the buffer.
135             */
136            public static final short REPEAT_PREV_CHAR              = -20;
137    
138            /** 
139             *  Operation that searches backwards in the command history.
140             */
141            public static final short SEARCH_PREV                   = -21;
142    
143            /** 
144             *  Operation that repeats the character.
145             */
146            public static final short REPEAT_NEXT_CHAR              = -24;
147    
148            /** 
149             *  Operation that searches forward in the command history.
150             */
151            public static final short SEARCH_NEXT                   = -25;
152    
153            /** 
154             *  Operation that moved to the previous whitespace.
155             */
156            public static final short PREV_SPACE_WORD               = -27;
157    
158            /** 
159             *  Operation that moved to the end of the current word.
160             */
161            public static final short TO_END_WORD                   = -29;
162    
163            /** 
164             *  Operation that 
165             */
166            public static final short REPEAT_SEARCH_PREV    = -34;
167    
168            /** 
169             *  Operation that 
170             */
171            public static final short PASTE_PREV                    = -36;
172    
173            /** 
174             *  Operation that 
175             */
176            public static final short REPLACE_MODE                  = -37;
177    
178            /** 
179             *  Operation that 
180             */
181            public static final short SUBSTITUTE_LINE               = -38;
182    
183            /** 
184             *  Operation that 
185             */
186            public static final short TO_PREV_CHAR                  = -39;
187    
188            /** 
189             *  Operation that 
190             */
191            public static final short NEXT_SPACE_WORD               = -40;
192    
193            /** 
194             *  Operation that 
195             */
196            public static final short DELETE_PREV_CHAR              = -41;
197    
198            /** 
199             *  Operation that 
200             */
201            public static final short ADD                                   = -42;
202    
203            /** 
204             *  Operation that 
205             */
206            public static final short PREV_WORD                             = -43;
207    
208            /** 
209             *  Operation that 
210             */
211            public static final short CHANGE_META                   = -44;
212    
213            /** 
214             *  Operation that 
215             */
216            public static final short DELETE_META                   = -45;
217    
218            /** 
219             *  Operation that 
220             */
221            public static final short END_WORD                              = -46;
222    
223            /** 
224             *  Operation that 
225             */
226            public static final short INSERT                                = -48;
227    
228            /** 
229             *  Operation that 
230             */
231            public static final short REPEAT_SEARCH_NEXT    = -49;
232    
233            /** 
234             *  Operation that 
235             */
236            public static final short PASTE_NEXT                    = -50;
237    
238            /** 
239             *  Operation that 
240             */
241            public static final short REPLACE_CHAR                  = -51;
242    
243            /** 
244             *  Operation that 
245             */
246            public static final short SUBSTITUTE_CHAR               = -52;
247    
248            /** 
249             *  Operation that 
250             */
251            public static final short TO_NEXT_CHAR                  = -53;
252    
253            /** 
254             *  Operation that undoes the previous operation.
255             */
256            public static final short UNDO                                  = -54;
257    
258            /** 
259             *  Operation that moved to the next word.
260             */
261            public static final short NEXT_WORD                             = -55;
262    
263            /** 
264             *  Operation that deletes the previous character.
265             */
266            public static final short DELETE_NEXT_CHAR              = -56;
267    
268            /** 
269             *  Operation that toggles between uppercase and lowercase.
270             */
271            public static final short CHANGE_CASE                   = -57;
272    
273            /** 
274             *  Operation that performs completion operation on the current word.
275             */
276            public static final short COMPLETE                              = -58;
277    
278            /** 
279             *  Operation that exits the command prompt.
280             */
281            public static final short EXIT                                  = -59;
282    
283    
284            /** 
285             *  Map that contains the operation name to keymay operation mapping.
286             */
287            public static SortedMap KEYMAP_NAMES;
288    
289            static
290            {
291                    Map names = new TreeMap ();
292    
293                    names.put ("MOVE_TO_BEG",                       new Short (MOVE_TO_BEG));
294                    names.put ("MOVE_TO_END",                       new Short (MOVE_TO_END));
295                    names.put ("PREV_CHAR",                         new Short (PREV_CHAR));
296                    names.put ("NEWLINE",                           new Short (NEWLINE));
297                    names.put ("KILL_LINE",                         new Short (KILL_LINE));
298                    names.put ("CLEAR_SCREEN",                      new Short (CLEAR_SCREEN));
299                    names.put ("NEXT_HISTORY",                      new Short (NEXT_HISTORY));
300                    names.put ("PREV_HISTORY",                      new Short (PREV_HISTORY));
301                    names.put ("REDISPLAY",                         new Short (REDISPLAY));
302                    names.put ("KILL_LINE_PREV",            new Short (KILL_LINE_PREV));
303                    names.put ("DELETE_PREV_WORD",          new Short (DELETE_PREV_WORD));
304                    names.put ("NEXT_CHAR",                         new Short (NEXT_CHAR));
305                    names.put ("REPEAT_PREV_CHAR",          new Short (REPEAT_PREV_CHAR));
306                    names.put ("SEARCH_PREV",                       new Short (SEARCH_PREV));
307                    names.put ("REPEAT_NEXT_CHAR",          new Short (REPEAT_NEXT_CHAR));
308                    names.put ("SEARCH_NEXT",                       new Short (SEARCH_NEXT));
309                    names.put ("PREV_SPACE_WORD",           new Short (PREV_SPACE_WORD));
310                    names.put ("TO_END_WORD",                       new Short (TO_END_WORD));
311                    names.put ("PREV_CHAR",                         new Short (PREV_CHAR));
312                    names.put ("REPEAT_SEARCH_PREV",        new Short (REPEAT_SEARCH_PREV));
313                    names.put ("PASTE_PREV",                        new Short (PASTE_PREV));
314                    names.put ("REPLACE_MODE",                      new Short (REPLACE_MODE));
315                    names.put ("SUBSTITUTE_LINE",           new Short (SUBSTITUTE_LINE));
316                    names.put ("TO_PREV_CHAR",                      new Short (TO_PREV_CHAR));
317                    names.put ("NEXT_SPACE_WORD",           new Short (NEXT_SPACE_WORD));
318                    names.put ("DELETE_PREV_CHAR",          new Short (DELETE_PREV_CHAR));
319                    names.put ("ADD",                                       new Short (ADD));
320                    names.put ("PREV_WORD",                         new Short (PREV_WORD));
321                    names.put ("CHANGE_META",                       new Short (CHANGE_META));
322                    names.put ("DELETE_META",                       new Short (DELETE_META));
323                    names.put ("END_WORD",                          new Short (END_WORD));
324                    names.put ("NEXT_CHAR",                         new Short (NEXT_CHAR));
325                    names.put ("INSERT",                            new Short (INSERT));
326                    names.put ("REPEAT_SEARCH_NEXT",        new Short (REPEAT_SEARCH_NEXT));
327                    names.put ("PASTE_NEXT",                        new Short (PASTE_NEXT));
328                    names.put ("REPLACE_CHAR",                      new Short (REPLACE_CHAR));
329                    names.put ("SUBSTITUTE_CHAR",           new Short (SUBSTITUTE_CHAR));
330                    names.put ("TO_NEXT_CHAR",                      new Short (TO_NEXT_CHAR));
331                    names.put ("UNDO",                                      new Short (UNDO));
332                    names.put ("NEXT_WORD",                         new Short (NEXT_WORD));
333                    names.put ("DELETE_NEXT_CHAR",          new Short (DELETE_NEXT_CHAR));
334                    names.put ("CHANGE_CASE",                       new Short (CHANGE_CASE));
335                    names.put ("COMPLETE",                          new Short (COMPLETE));
336                    names.put ("EXIT",                                      new Short (EXIT));
337                    
338                    KEYMAP_NAMES = new TreeMap (Collections.unmodifiableMap (names));
339    
340            }
341    
342    
343            /** 
344             *  The map for logical operations.
345             */
346            private final short [] keybindings;
347    
348    
349            /** 
350             *  If true, issue an audible keyboard bell when appropriate.
351             */
352            private boolean bellEnabled = true;
353    
354            /** 
355             *  If true, then this console echoes characters.
356             */
357            private boolean echo = true;
358    
359            /** 
360             *  The number of tab-completion candidates above which a warning
361             *  will be prompted before showing all the candidates.
362             */
363            private int autoprintThreshhold = 100; // same default as bash
364    
365    
366            private CompletionHandler completionHandler
367                    = new CandidateListCompletionHandler ();
368    
369    
370            InputStream in;
371            final Writer out;
372            final CursorBuffer buf = new CursorBuffer ();
373            static PrintWriter debugger;
374            History history = new History ();
375            final List completors = new LinkedList ();
376    
377            private Character echoCharacter = null;
378    
379    
380            /** 
381             *  Create a new reader using {@link FileDescriptor#in} for input
382             *  and {@link System#out} for output. {@link FileDescriptor#in} is
383             *  used because it has a better chance of being unbuffered.
384             */
385            public ConsoleReader ()
386                    throws IOException
387            {
388                    this (new FileInputStream (FileDescriptor.in),
389                            new PrintWriter (System.out));
390            }
391    
392    
393            /** 
394             *  Create a new reader using the specified {@link InputStream}
395             *  for input and the specific writer for output, using the
396             *  default keybindings resource.
397             */
398            public ConsoleReader (InputStream in, Writer out)
399                    throws IOException
400            {
401                    this (in, out,
402                            ConsoleReader.class.getResourceAsStream ("keybindings.properties"));
403            }
404    
405    
406            /** 
407             *  Create a new reader.
408             *  
409             *  @param  in                  the input
410             *  @param  out                 the output
411             *  @param  bindings    the key bindings to use
412             */
413            public ConsoleReader (InputStream in, Writer out, InputStream bindings)
414                    throws IOException
415            {
416                    setInput (in);
417                    this.out = out;
418    
419                    this.keybindings = new short [Byte.MAX_VALUE * 2];
420    
421                    Arrays.fill (this.keybindings, UNKNOWN);
422    
423                    /**
424                     *      Loads the key bindings. Bindings file is in the format:
425                     *
426                     *      keycode: operation name
427                     */
428                    if (bindings != null)
429                    {
430                            Properties p = new Properties ();
431                            p.load (bindings);
432                            bindings.close ();
433    
434                            for (Iterator i = p.keySet ().iterator (); i.hasNext (); )
435                            {
436                                    String val = (String)i.next ();
437                                    try
438                                    {
439                                            Short code = new Short (val);
440                                            String op = (String)p.getProperty (val);
441    
442                                            Short opval = (Short)KEYMAP_NAMES.get (op);
443    
444                                            if (opval != null)
445                                            {
446                                                    keybindings [code.shortValue ()] = opval.shortValue ();
447                                            }
448                                    }
449                                    catch (NumberFormatException nfe)
450                                    {
451                                    }
452                            }
453                    }
454    
455    
456                    /**
457                     *      Perform unmodifiable bindings.
458                     */
459                    keybindings [ARROW_START] = ARROW_START;
460            }
461    
462    
463            /** 
464             *  Set the stream for debugging. Development use only.
465             */
466            public void setDebug (PrintWriter debugger)
467            {
468                    this.debugger = debugger;
469            }
470    
471    
472            /** 
473             *  Set the stream to be used for console input.
474             */
475            public void setInput (InputStream in)
476            {
477                    this.in = in;
478            }
479    
480    
481            /** 
482             *  Returns the stream used for console input.
483             */
484            public InputStream getInput ()
485            {
486                    return this.in;
487            }
488    
489    
490            /** 
491             *  Read the next line and return the contents of the buffer.
492             */
493            public String readLine ()
494                    throws IOException
495            {
496                    return readLine (null);
497            }
498    
499    
500            /** 
501             *  @param  bellEnabled  if true, enable audible keyboard bells if
502             *                                      an alert is required.
503             */
504            public void setBellEnabled (boolean bellEnabled)
505            {
506                    this.bellEnabled = bellEnabled;
507            }
508    
509    
510            /** 
511             *  @return  true is audible keyboard bell is enabled.
512             */
513            public boolean getBellEnabled ()
514            {
515                    return this.bellEnabled;
516            }
517    
518    
519            /** 
520             *      Query the terminal to find the current width;
521             *
522             *      @see     Terminal#getTerminalWidth
523             *  @return  the width of the current terminal.
524             */
525            public int getTermwidth ()
526            {
527                    return Terminal.setupTerminal ().getTerminalWidth ();
528            }
529    
530    
531            /** 
532             *      Query the terminal to find the current width;
533             *
534             *      @see     Terminal#getTerminalheight
535             *
536             *  @return  the height of the current terminal.
537             */
538            public int getTermheight ()
539            {
540                    return Terminal.setupTerminal ().getTerminalHeight ();
541            }
542    
543    
544            /** 
545             *  @param  autoprintThreshhold  the number of candidates to print
546             *                                                      without issuing a warning.
547             */
548            public void setAutoprintThreshhold (int autoprintThreshhold)
549            {
550                    this.autoprintThreshhold = autoprintThreshhold;
551            }
552    
553    
554            /** 
555             *  @return  the number of candidates to print without issing a warning.
556             */
557            public int getAutoprintThreshhold ()
558            {
559                    return this.autoprintThreshhold;
560            }
561    
562    
563            int getKeyForAction (short logicalAction)
564            {
565                    for (int i = 0; i < keybindings.length; i++)
566                    {
567                            if (keybindings [i] == logicalAction)
568                            {
569                                    return i;
570                            }
571                    }
572    
573                    return -1;
574            }
575    
576    
577            /** 
578             *  Clear the echoed characters for the specified character code.
579             */
580            int clearEcho (int c)
581                    throws IOException
582            {
583                    int num = countEchoCharacters ((char)c);
584                    back (num);
585                    drawBuffer (num);
586    
587                    return num;
588            }
589    
590    
591            int countEchoCharacters (char c)
592            {
593                    // tabs as special: we need to determine the number of spaces
594                    // to cancel based on what out current cursor position is
595                    if (c == 9)
596                    {
597                            int tabstop = 8; // will this ever be different?
598                            int position = getCursorPosition ();
599                            return tabstop - (position % tabstop);
600                    }
601    
602                    return getPrintableCharacters (c).length ();
603            }
604    
605    
606            /** 
607             *  Return the number of characters that will be printed when the
608             *  specified character is echoed to the screen. Adapted from
609             *      cat by Torbjorn Granlund, as repeated in stty by
610             *      David MacKenzie.
611             */
612            StringBuffer getPrintableCharacters (char ch)
613            {
614                    StringBuffer sbuff = new StringBuffer ();
615                    if (ch >= 32)
616                    { 
617                            if (ch < 127)
618                            {
619                                    sbuff.append (ch);
620                            }
621                            else if (ch == 127)
622                            { 
623                                    sbuff.append ('^');
624                                    sbuff.append ('?');
625                            }
626                            else
627                            {
628                                    sbuff.append ('M');
629                                    sbuff.append ('-');
630                                    if (ch >= 128 + 32)
631                                    {
632                                            if (ch < 128 + 127)
633                                            {
634                                                    sbuff.append ((char)(ch - 128));
635                                            }
636                                            else
637                                            {
638                                                    sbuff.append ('^');
639                                                    sbuff.append ('?');
640                                            }
641                                    }
642                                    else
643                                    {
644                                            sbuff.append ('^');
645                                            sbuff.append ((char)(ch - 128 + 64));
646                                    }
647                            }
648                    }
649                    else
650                    {
651                            sbuff.append ('^');
652                            sbuff.append ((char)(ch + 64));
653                    }
654    
655                    return sbuff;
656            }
657    
658    
659            int getCursorPosition ()
660            {
661                    // FIXME: does not handle anything but a line with a prompt
662                    return (prompt == null ? 0 : prompt.length ())
663                            + buf.cursor; // absolute position
664            }
665    
666    
667            /** 
668             *  Read a line from the <i>in</i> {@link InputStream}, and
669             *  return the line (without any trailing newlines).
670             *  
671             *  @param  prompt      the prompt to issue to the console, may be null.
672             *  @return     a line that is read from the terminal, or null if there
673             *              was null input (e.g., <i>CTRL-D</i> was pressed).
674             */
675            public String readLine (String prompt)
676                    throws IOException
677            {
678                    this.prompt = prompt;
679    
680                    if (prompt != null && prompt.length () > 0)
681                    {
682                            out.write (prompt);
683                            out.flush ();
684                    }
685    
686                    int c;
687    
688                    while (true)
689                    {
690                            if ((c = readCharacter ()) == -1)
691                                    return null;
692    
693                            boolean success = true;
694    
695                            // extract the appropriate key binding
696                            short code = keybindings [c];
697    
698                            // debug ("keypress: " + (int)c + ": " + code);
699    
700                            switch (code)
701                            {
702                                    case EXIT: // ctrl-d
703                                            if (buf.buffer.length () == 0)
704                                                    return null;
705                                    case COMPLETE: // tab
706                                            success = complete ();
707                                            break;
708                                    case MOVE_TO_BEG:
709                                            success = setCursorPosition (0);
710                                            break;
711                                    case KILL_LINE: // CTRL-K
712                                            success = killLine ();
713                                            break;
714                                    case KILL_LINE_PREV: // CTRL-U
715                                            success = resetLine ();
716                                            break;
717                                    case ARROW_START:
718                                            // debug ("ARROW_START");
719    
720                                            switch (c = readCharacter ())
721                                            {
722                                                    case ARROW_PREFIX:
723                                                            // debug ("ARROW_PREFIX");
724    
725                                                            switch (c = readCharacter ())
726                                                            {
727                                                                    case ARROW_LEFT: // left arrow
728                                                                            // debug ("LEFT");
729                                                                            success = moveCursor (-1) != 0;
730                                                                            break;
731                                                                    case ARROW_RIGHT: // right arrow
732                                                                            // debug ("RIGHT");
733                                                                            success = moveCursor (1) != 0;
734                                                                            break;
735                                                                    case ARROW_UP: // up arrow
736                                                                            // debug ("UP");
737                                                                            success = moveHistory (false);
738                                                                            break;
739                                                                    case ARROW_DOWN: // down arrow
740                                                                            // debug ("DOWN");
741                                                                            success = moveHistory (true);
742                                                                            break;
743                                                                    default:
744                                                                            break;
745    
746                                                            }
747                                                            break;
748                                                    default:
749                                                            break;
750                                            }
751                                            break;
752                                    case NEWLINE: // enter
753                                            printNewline (); // output newline
754                                            return finishBuffer ();
755                                    case DELETE_PREV_CHAR: // backspace
756                                            success = backspace ();
757                                            break;
758                                    case MOVE_TO_END:
759                                            success = moveToEnd ();
760                                            break;
761                                    case PREV_CHAR:
762                                            success = moveCursor (-1) != 0;
763                                            break;
764                                    case NEXT_HISTORY:
765                                            success = moveHistory (true);
766                                            break;
767                                    case PREV_HISTORY:
768                                            success = moveHistory (false);
769                                            break;
770                                    case REDISPLAY:
771                                            break;
772                                    case DELETE_PREV_WORD:
773                                            success = deletePreviousWord ();
774                                            break;
775                                    case PREV_WORD:
776                                            success = previousWord ();
777                                            break;
778    
779                                    case UNKNOWN:
780                                    default:
781                                            putChar (c, true);
782                            }
783    
784                            if (!(success))
785                                    beep ();
786    
787                            flushConsole ();
788                    }
789            }
790    
791    
792            /** 
793             *  Move up or down the history tree.
794             *  
795             *  @param  direction  less than 0 to move up the tree, down otherwise
796             */
797            private final boolean moveHistory (boolean next)
798                    throws IOException
799            {
800                    if (next && !history.next ())
801                       return false;
802                    else if (!next && !history.previous ())
803                            return false;   
804    
805                    setBuffer (history.current ());
806                    return true;
807            }
808    
809    
810            /** 
811             *  Kill the buffer ahead of the current cursor position.
812             *  
813             *  @return  true if successful
814             */
815            public boolean killLine ()
816                    throws IOException
817            {
818                    int cp = buf.cursor;
819                    int len = buf.buffer.length ();
820                    if (cp >= len)
821                            return false;
822    
823                    int num = buf.buffer.length () - cp;
824                    clearAhead (num);
825                    for (int i = 0; i < num; i++)
826                            buf.buffer.deleteCharAt (len - i - 1);
827                    return true;
828            }
829    
830    
831            /** 
832             *  Use the completors to modify the buffer with the
833             *  appropriate completions.
834             *  
835             *  @return  true if successful
836             */
837            private final boolean complete ()
838                    throws IOException
839            {
840                    // debug ("tab for (" + buf + ")");
841    
842                    if (completors.size () == 0)
843                            return false;
844    
845                    List candidates = new LinkedList ();
846                    String bufstr = buf.buffer.toString ();
847                    int cursor = buf.cursor;
848    
849                    int position = -1;
850    
851                    for (Iterator i = completors.iterator (); i.hasNext (); )
852                    {
853                            Completor comp = (Completor)i.next ();
854                            if ((position = comp.complete (bufstr, cursor, candidates)) != -1)
855                                    break;
856                    }
857    
858                    // no candidates? Fail.
859                    if (candidates.size () == 0)
860                            return false;
861    
862                    return completionHandler.complete (this, candidates, position);
863            }
864    
865    
866            public CursorBuffer getCursorBuffer ()
867            {
868                    return buf;
869            }
870    
871    
872            /** 
873             *  Output the specified {@link Collection} in proper columns.
874             *  
875             *  @param  stuff  the stuff to print
876             */
877            public void printColumns (Collection stuff)
878                    throws IOException
879            {
880                    if (stuff == null || stuff.size () == 0)
881                            return;
882    
883                    int width = getTermwidth ();
884                    int maxwidth = 0;
885                    for (Iterator i = stuff.iterator (); i.hasNext ();
886                            maxwidth = Math.max (maxwidth, i.next ().toString ().length ()));
887    
888                    StringBuffer line = new StringBuffer ();
889    
890                    for (Iterator i = stuff.iterator (); i.hasNext (); )
891                    {
892                            String cur = (String)i.next ();
893    
894                            if (line.length () + maxwidth > width)
895                            {
896                                    printString (line.toString ().trim ());
897                                    printNewline ();
898                                    line.setLength (0);
899                            }
900    
901                            pad (cur, maxwidth + 3, line);
902                    }
903    
904                    if (line.length () > 0)
905                    {
906                            printString (line.toString ().trim ());
907                            printNewline ();
908                            line.setLength (0);
909                    }
910            }
911    
912    
913            /** 
914             *  Append <i>toPad</i> to the specified <i>appendTo</i>, as
915             *  well as (<i>toPad.length () - len</i>) spaces.
916             *  
917             *  @param  toPad               the {@link String} to pad
918             *  @param  len                 the target length
919             *  @param  appendTo    the {@link StringBuffer} to which to append the
920             *                                      padded {@link String}.
921             */
922            private final void pad (String toPad, int len, StringBuffer appendTo)
923            {
924                    appendTo.append (toPad);
925                    for (int i = 0; i < (len - toPad.length ());
926                            i++, appendTo.append (' '));
927            }
928    
929    
930            /** 
931             *  Add the specified {@link Completor} to the list of handlers
932             *  for tab-completion.
933             *  
934             *  @param  completor  the {@link Completor} to add
935             *  @return     true if it was successfully added
936             */
937            public boolean addCompletor (Completor completor)
938            {
939                    return completors.add (completor);
940            }
941    
942    
943            /** 
944             *  Remove the specified {@link Completor} from the list of handlers
945             *  for tab-completion.
946             *  
947             *  @param  completor  the {@link Completor} to remove
948             *  @return     true if it was successfully removed
949             */
950            public boolean removeCompletor (Completor completor)
951            {
952                    return completors.remove (completor);
953            }
954    
955    
956            /** 
957             *  Returns an unmodifiable list of all the completors.
958             */
959            public Collection getCompletors ()
960            {
961                    return Collections.unmodifiableList (completors);
962            }
963    
964    
965            /** 
966             *  Erase the current line.
967             *  
968             *  @return  false if we failed (e.g., the buffer was empty)
969             */
970            final boolean resetLine ()
971                    throws IOException
972            {
973                    if (buf.cursor == 0)
974                            return false;
975    
976                    backspaceAll ();
977    
978                    return true;
979            }
980    
981    
982            /** 
983             *  Move the cursor position to the specified absolute index.
984             */
985            public final boolean setCursorPosition (int position)
986                    throws IOException
987            {
988                    return moveCursor (position - buf.cursor) != 0;
989            }
990    
991    
992            /** 
993             *  Set the current buffer's content to the specified
994             *  {@link String}. The visual console will be modified
995             *  to show the current buffer.
996             *  
997             *  @param  buffer  the new contents of the buffer.
998             */
999            private final void setBuffer (String buffer)
1000                    throws IOException
1001            {
1002                    // don't bother modifying it if it is unchanged
1003                    if (buffer.equals (buf.buffer.toString ()))
1004                            return;
1005    
1006                    // obtain the difference between the current buffer and the new one
1007                    int sameIndex = 0;
1008                    for (int i = 0, l1 = buffer.length (), l2 = buf.buffer.length ();
1009                            i < l1 && i < l2; i++)
1010                    {
1011                            if (buffer.charAt (i) == buf.buffer.charAt (i))
1012                                    sameIndex++;
1013                            else
1014                                    break;
1015                    }
1016    
1017                    int diff = buf.buffer.length () - sameIndex;
1018    
1019                    backspace (diff); // go back for the differences
1020                    killLine (); // clear to the end of the line
1021                    buf.buffer.setLength (sameIndex); // the new length
1022                    putString (buffer.substring (sameIndex)); // append the differences
1023            }
1024    
1025    
1026            /** 
1027             *  Clear the line and redraw it.
1028             */
1029            public final void redrawLine ()
1030                    throws IOException
1031            {
1032                    printCharacter (RESET_LINE);
1033                    flushConsole ();
1034                    drawLine ();
1035            }
1036    
1037    
1038            /** 
1039             *  Output put the prompt + the current buffer
1040             */
1041            public final void drawLine ()
1042                    throws IOException
1043            {
1044                    if (prompt != null)
1045                            printString (prompt);
1046                    printString (buf.buffer.toString ());
1047            }
1048    
1049    
1050            /** 
1051             *  Output a platform-dependant newline.
1052             */
1053            public final void printNewline ()
1054                    throws IOException
1055            {
1056                    printString (CR);
1057                    flushConsole ();
1058            }
1059    
1060    
1061            /** 
1062             *  Clear the buffer and add its contents to the history.
1063             *  
1064             *  @return  the former contents of the buffer.
1065             */
1066            final String finishBuffer ()
1067            {
1068                    String str = buf.buffer.toString ();
1069    
1070                    // we only add it to the history if the buffer is not empty
1071                    if (str.length () > 0)
1072                            history.addToHistory (str);
1073    
1074                    history.moveToEnd ();
1075    
1076                    buf.buffer.setLength (0);
1077                    buf.cursor = 0;
1078                    return str;
1079            }
1080    
1081    
1082            /** 
1083             *  Write out the specified string to the buffer and the
1084             *  output stream.
1085             */
1086            public final void putString (String str)
1087                    throws IOException
1088            {
1089                    buf.insert (str);
1090                    printString (str);
1091                    drawBuffer ();
1092            }
1093    
1094    
1095            /** 
1096             *  Output the specified string to the output stream (but not the
1097             *  buffer).
1098             */
1099            public final void printString (String str)
1100                    throws IOException
1101            {
1102                    printCharacters (str.toCharArray ());
1103            }
1104    
1105    
1106            /** 
1107             *  Output the specified character, both to the buffer
1108             *  and the output stream.
1109             */
1110            private final void putChar (int c)
1111                    throws IOException
1112            {
1113                    putChar (c, true);
1114            }
1115    
1116    
1117            /** 
1118             *  Output the specified character, both to the buffer
1119             *  and the output stream.
1120             */
1121            private final void putChar (int c, boolean print)
1122                    throws IOException
1123            {
1124                    buf.insert ((char)c);
1125    
1126                    if (print)
1127                    {
1128                            printCharacter (c);
1129                            drawBuffer ();
1130                    }
1131            }
1132    
1133    
1134            /** 
1135             *  Redraw the rest of the buffer from the cursor onwards. This
1136             *  is necessary for inserting text into the buffer.
1137             *
1138             *  @param clear        the number of characters to clear after the
1139             *                              end of the buffer
1140             */
1141            private final void drawBuffer (int clear)
1142                    throws IOException
1143            {
1144                    // debug ("drawBuffer: " + clear);
1145    
1146                    char [] chars = buf.buffer.substring (buf.cursor).toCharArray ();
1147                    printCharacters (chars);
1148    
1149                    clearAhead (clear);
1150                    back (chars.length);
1151                    flushConsole ();
1152            }
1153    
1154    
1155            /** 
1156             *  Redraw the rest of the buffer from the cursor onwards. This
1157             *  is necessary for inserting text into the buffer.
1158             */
1159            private final void drawBuffer ()
1160                    throws IOException
1161            {
1162                    drawBuffer (0);
1163            }
1164    
1165    
1166            /** 
1167             *  Clear ahead the specified number of characters
1168             *  without moving the cursor.
1169             */
1170            private final void clearAhead (int num)
1171                    throws IOException
1172            {
1173                    if (num == 0)
1174                            return;
1175    
1176                    // debug ("clearAhead: " + num);
1177    
1178                    // print blank extra characters
1179                    printCharacters (' ', num);
1180    
1181                    // we need to flush here so a "clever" console
1182                    // doesn't just ignore the redundancy of a space followed by
1183                    // a backspace.
1184                    flushConsole ();
1185    
1186                    // reset the visual cursor
1187                    back (num);
1188    
1189                    flushConsole ();
1190            }
1191    
1192    
1193            /** 
1194             *  Move the visual cursor backwards without modifying the
1195             *  buffer cursor.
1196             */
1197            private final void back (int num)
1198                    throws IOException
1199            {
1200                    printCharacters (BACKSPACE, num);
1201                    flushConsole ();
1202            }
1203    
1204    
1205            /** 
1206             *  Issue an audible keyboard bell, if
1207             *  {@link #getBellEnabled} return true.
1208             */
1209            public final void beep ()
1210                    throws IOException
1211            {
1212                    if (!(getBellEnabled ()))
1213                            return;
1214    
1215                    printCharacter (KEYBOARD_BELL);
1216                    // need to flush so the console actually beeps
1217                    flushConsole ();
1218            }
1219    
1220    
1221            /** 
1222             *  Output the specified character to the output stream
1223             *  without manipulating the current buffer.
1224             */
1225            private final void printCharacter (int c)
1226                    throws IOException
1227            {
1228                    out.write (c);
1229            }
1230    
1231    
1232            /** 
1233             *  Output the specified characters to the output stream
1234             *  without manipulating the current buffer.
1235             */
1236            private final void printCharacters (char [] c)
1237                    throws IOException
1238            {
1239                    out.write (c);
1240            }
1241    
1242    
1243            private final void printCharacters (char c, int num)
1244                    throws IOException
1245            {
1246                    if (num == 1)
1247                    {
1248                            printCharacter (c);
1249                    }
1250                    else
1251                    {
1252                            char [] chars = new char [num];
1253                            Arrays.fill (chars, c);
1254                            printCharacters (chars);
1255                    }
1256            }
1257    
1258    
1259            /** 
1260             *  Flush the console output stream. This is important for
1261             *  printout out single characters (like a backspace or keyboard)
1262             *  that we want the console to handle immedately.
1263             */
1264            public final void flushConsole ()
1265                    throws IOException
1266            {
1267                    out.flush ();
1268            }
1269    
1270    
1271            private final int backspaceAll ()
1272                    throws IOException
1273            {
1274                    return backspace (Integer.MAX_VALUE);
1275            }
1276    
1277    
1278            /** 
1279             *  Issue <code>num</code> backspaces.
1280             *  
1281             *  @return  the number of characters backed up
1282             */
1283            private final int backspace (int num)
1284                    throws IOException
1285            {
1286                    if (buf.cursor == 0)
1287                            return 0;
1288    
1289                    int count = 0;
1290    
1291                    count = moveCursor (-1 * num) * -1;
1292                    // debug ("Deleting from " + buf.cursor + " for " + count);
1293    
1294                    buf.buffer.delete (buf.cursor, buf.cursor + count);
1295                    drawBuffer (count);
1296    
1297                    return count;
1298            }
1299    
1300    
1301            /** 
1302             *  Issue a backspace.
1303             *
1304             *  @return  true if successful
1305             */
1306            public final boolean backspace ()
1307                    throws IOException
1308            {
1309                    return backspace (1) == 1;
1310            }
1311    
1312    
1313            private final boolean moveToEnd ()
1314                    throws IOException
1315            {
1316                    if (moveCursor (1) == 0)
1317                            return false;
1318    
1319                    while (moveCursor (1) != 0);
1320    
1321                    return true;
1322            }
1323    
1324    
1325            /** 
1326             *  Delete the character at the current position and
1327             *  redraw the remainder of the buffer.
1328             */
1329            private final boolean deleteCurrentCharacter ()
1330                    throws IOException
1331            {
1332                    buf.buffer.deleteCharAt (buf.cursor);
1333                    drawBuffer (1);
1334                    return true;
1335            }
1336    
1337    
1338            private final boolean previousWord ()
1339                    throws IOException
1340            {
1341                    while (Character.isWhitespace (buf.current ()) && moveCursor (-1)!= 0);
1342                    while (!Character.isWhitespace (buf.current ()) && moveCursor (-1)!= 0);
1343    
1344                    return true;
1345            }
1346    
1347    
1348            private final boolean deletePreviousWord ()
1349                    throws IOException
1350            {
1351                    while (Character.isWhitespace (buf.current ()) && backspace ());
1352                    while (!Character.isWhitespace (buf.current ()) && backspace ());
1353    
1354                    return true;
1355            }
1356    
1357    
1358            /** 
1359             *  Move the cursor <i>where</i> characters.
1360             *  
1361             *  @param  where  if less than 0, move abs(<i>where</i>) to the left,
1362             *                              otherwise move <i>where</i> to the right.
1363             *
1364             *  @return  the number of spaces we moved
1365             */
1366            private final int moveCursor (int where)
1367                    throws IOException
1368            {
1369                    if (buf.cursor == 0 && where < 0)
1370                            return 0;
1371    
1372                    if (buf.cursor == buf.buffer.length () && where > 0)
1373                            return 0;
1374    
1375                    if (buf.cursor + where < 0)
1376                            where = -buf.cursor;
1377                    else if (buf.cursor + where > buf.buffer.length ())
1378                            where = buf.buffer.length () - buf.cursor;
1379    
1380                    moveInternal (where);
1381                    return where;
1382            }
1383    
1384    
1385            /** 
1386             *  debug.
1387             *  
1388             *  @param  str  the message to issue.
1389             */
1390            public static void debug (String str)
1391            {
1392                    if (debugger != null)
1393                    {
1394                            debugger.println (str);
1395                            debugger.flush ();
1396                    }
1397            }
1398    
1399    
1400            /** 
1401             *  Move the cursor <i>where</i> characters, withough checking
1402             *  the current buffer.
1403             *
1404             *  @see        #where
1405             *  
1406             *  @param  where  the number of characters to move to the right or left.
1407             */
1408            private final void moveInternal (int where)
1409                    throws IOException
1410            {
1411                    // debug ("move cursor " + where + " ("
1412                            // + buf.cursor + " => " + (buf.cursor + where) + ")");
1413    
1414                    buf.cursor += where;
1415    
1416                    char c;
1417    
1418                    if (where < 0)
1419                    {
1420                            c = BACKSPACE;
1421                    }
1422                    else if (buf.cursor == 0)
1423                    {
1424                            return;
1425                    }
1426                    else
1427                    {
1428                            c = buf.buffer.charAt (buf.cursor - 1); // draw replacement
1429                    }
1430    
1431                    printCharacters (c, Math.abs (where));
1432            }
1433    
1434    
1435            /** 
1436             *  Read a character from the console.
1437             *  
1438             *  @return  the character, or -1 if an EOF is received.
1439             */
1440            public final int readCharacter ()
1441                    throws IOException
1442            {
1443                    int c = in.read ();
1444                    // debug (c + "");
1445    
1446                    // clear any echo characters
1447                    if (echo)
1448                            clearEcho (c);
1449    
1450                    return c;
1451            }
1452    
1453    
1454            public void setHistory (History history)
1455            {
1456                    this.history = history;
1457            }
1458    
1459    
1460            public History getHistory ()
1461            {
1462                    return this.history;
1463            }
1464    
1465    
1466            public void setEcho (boolean echo)
1467            {
1468                    this.echo = echo;
1469            }
1470    
1471    
1472            public boolean getEcho ()
1473            {
1474                    return this.echo;
1475            }
1476    
1477    
1478            public void setCompletionHandler (CompletionHandler completionHandler)
1479            {
1480                    this.completionHandler = completionHandler;
1481            }
1482    
1483    
1484            public CompletionHandler getCompletionHandler ()
1485            {
1486                    return this.completionHandler;
1487            }
1488    
1489    
1490    
1491            /** 
1492             *      <p>
1493             *  Set the echo character. For example, to have "*" entered
1494             *  when a password is typed:
1495             *  </p>
1496             *
1497             *      <pre>
1498             *    myConsoleReader.setEchoCharacter (new Character ('*'));
1499             *      </pre>
1500             *
1501             *      <p>
1502             *      Setting the character to <pre>null</pre> will restore normal
1503             *      character echoing. Setting the character to
1504             *      <pre>new Character (0)</pre> will cause nothing to be echoed.
1505             *      </p>
1506             *  
1507             *  @param  echoCharacter       the character to echo to the console in
1508             *                                              place of the typed character.
1509             */
1510            public void setEchoCharacter (Character echoCharacter)
1511            {
1512                    this.echoCharacter = echoCharacter;
1513            }
1514    
1515    
1516            /** 
1517             *  Returns the echo character.
1518             */
1519            public Character getEchoCharacter ()
1520            {
1521                    return this.echoCharacter;
1522            }
1523    }
1524