From ceb5ca06c6ed8827fe5069db7033fc696325b681 Mon Sep 17 00:00:00 2001 From: ache Date: Mon, 9 May 1994 15:59:10 +0000 Subject: Port readline lib into gnu system tree. Really we don't need copy of this library into each program (gdb f.e.) P.S. it is much cleaner port than /usr/ports version and based on /usr/ports version. /usr/ports version will be removed. --- gnu/lib/libreadline/COPYING | 257 ++ gnu/lib/libreadline/ChangeLog | 120 + gnu/lib/libreadline/Makefile | 22 + gnu/lib/libreadline/README.FreeBSD | 14 + gnu/lib/libreadline/doc/Makefile | 30 + gnu/lib/libreadline/doc/hist.texinfo | 106 + gnu/lib/libreadline/doc/hstech.texinfo | 311 ++ gnu/lib/libreadline/doc/hsuser.texinfo | 153 + gnu/lib/libreadline/doc/rlman.texinfo | 103 + gnu/lib/libreadline/doc/rltech.texinfo | 1010 ++++++ gnu/lib/libreadline/doc/rluser.texinfo | 489 +++ gnu/lib/libreadline/emacs_keymap.c | 472 +++ gnu/lib/libreadline/examples/Makefile | 12 + gnu/lib/libreadline/examples/fileman.c | 392 ++ gnu/lib/libreadline/examples/manexamp.c | 96 + gnu/lib/libreadline/funmap.c | 214 ++ gnu/lib/libreadline/history.c | 1646 +++++++++ gnu/lib/libreadline/keymaps.c | 173 + gnu/lib/libreadline/readline.c | 5927 +++++++++++++++++++++++++++++++ gnu/lib/libreadline/readline/chardefs.h | 50 + gnu/lib/libreadline/readline/keymaps.h | 51 + gnu/lib/libreadline/readline/readline.h | 179 + gnu/lib/libreadline/tcsh_hack.readme | 50 + gnu/lib/libreadline/vi_keymap.c | 474 +++ gnu/lib/libreadline/vi_mode.c | 987 +++++ 25 files changed, 13338 insertions(+) create mode 100644 gnu/lib/libreadline/COPYING create mode 100644 gnu/lib/libreadline/ChangeLog create mode 100644 gnu/lib/libreadline/Makefile create mode 100644 gnu/lib/libreadline/README.FreeBSD create mode 100644 gnu/lib/libreadline/doc/Makefile create mode 100644 gnu/lib/libreadline/doc/hist.texinfo create mode 100644 gnu/lib/libreadline/doc/hstech.texinfo create mode 100644 gnu/lib/libreadline/doc/hsuser.texinfo create mode 100644 gnu/lib/libreadline/doc/rlman.texinfo create mode 100644 gnu/lib/libreadline/doc/rltech.texinfo create mode 100644 gnu/lib/libreadline/doc/rluser.texinfo create mode 100644 gnu/lib/libreadline/emacs_keymap.c create mode 100644 gnu/lib/libreadline/examples/Makefile create mode 100644 gnu/lib/libreadline/examples/fileman.c create mode 100644 gnu/lib/libreadline/examples/manexamp.c create mode 100644 gnu/lib/libreadline/funmap.c create mode 100644 gnu/lib/libreadline/history.c create mode 100644 gnu/lib/libreadline/keymaps.c create mode 100644 gnu/lib/libreadline/readline.c create mode 100644 gnu/lib/libreadline/readline/chardefs.h create mode 100644 gnu/lib/libreadline/readline/keymaps.h create mode 100644 gnu/lib/libreadline/readline/readline.h create mode 100644 gnu/lib/libreadline/tcsh_hack.readme create mode 100644 gnu/lib/libreadline/vi_keymap.c create mode 100644 gnu/lib/libreadline/vi_mode.c diff --git a/gnu/lib/libreadline/COPYING b/gnu/lib/libreadline/COPYING new file mode 100644 index 0000000..1bb82d1 --- /dev/null +++ b/gnu/lib/libreadline/COPYING @@ -0,0 +1,257 @@ + + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +The Free Software Foundation has exempted Bash from the requirement of +Paragraph 2c of the General Public License. This is to say, there is +no requirement for Bash to print a notice when it is started +interactively in the usual way. We made this exception because users +and standards expect shells not to print such messages. This +exception applies to any program that serves as a shell and that is +based primarily on Bash as opposed to other GNU software. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work based +on the Program" means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this General + Public License. + + d) You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + + 7. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/gnu/lib/libreadline/ChangeLog b/gnu/lib/libreadline/ChangeLog new file mode 100644 index 0000000..67c5df8 --- /dev/null +++ b/gnu/lib/libreadline/ChangeLog @@ -0,0 +1,120 @@ +Thu Apr 25 12:03:49 1991 Brian Fox (bfox at gnuwest.fsf.org) + + * vi_mode.c (rl_vi_fword, fWord, etc. All functions use + the macro `isident()'. Fixed movement bug which prevents + continious movement through the text. + +Fri Jul 27 16:47:01 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c (parser_if) Allow "$if term=foo" construct. + +Wed May 23 16:10:33 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c (rl_dispatch) Correctly remember the last command + executed. Fixed typo in username_completion_function (). + +Mon Apr 9 19:55:48 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: username_completion_function (); For text passed in + with a leading `~', remember that this could be a filename (after + it is completed). + +Thu Apr 5 13:44:24 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: rl_search_history (): Correctly handle case of an + unfound search string, but a graceful exit (as with ESC). + + * readline.c: rl_restart_output (); The Apollo passes the address + of the file descriptor to TIOCSTART, not the descriptor itself. + +Tue Mar 20 05:38:55 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * readline.c: rl_complete (); second call in a row causes possible + completions to be listed. + + * readline.c: rl_redisplay (), added prompt_this_line variable + which is the first character character following \n in prompt. + +Sun Mar 11 04:32:03 1990 Brian Fox (bfox at gnuwest.fsf.org) + + * Signals are now supposedly handled inside of SYSV compilation. + +Wed Jan 17 19:24:09 1990 Brian Fox (bfox at sbphy.ucsb.edu) + + * history.c: history_expand (); fixed overwriting memory error, + added needed argument to call to get_history_event (). + +Thu Jan 11 10:54:04 1990 Brian Fox (bfox at sbphy.ucsb.edu) + + * readline.c: added mark_modified_lines to control the + display of an asterisk on modified history lines. Also + added a user variable called mark-modified-lines to the + `set' command. + +Thu Jan 4 10:38:05 1990 Brian Fox (bfox at sbphy.ucsb.edu) + + * readline.c: start_insert (). Only use IC if we don't have an im + capability. + +Fri Sep 8 09:00:45 1989 Brian Fox (bfox at aurel) + + * readline.c: rl_prep_terminal (). Only turn on 8th bit + as meta-bit iff the terminal is not using parity. + +Sun Sep 3 08:57:40 1989 Brian Fox (bfox at aurel) + + * readline.c: start_insert (). Uses multiple + insertion call in cases where that makes sense. + + rl_insert (). Read type-ahead buffer for additional + keys that are bound to rl_insert, and insert them + all at once. Make insertion of single keys given + with an argument much more efficient. + +Tue Aug 8 18:13:57 1989 Brian Fox (bfox at aurel) + + * readline.c: Changed handling of EOF. readline () returns + (char *)EOF or consed string. The EOF character is read from the + tty, or if the tty doesn't have one, defaults to C-d. + + * readline.c: Added support for event driven programs. + rl_event_hook is the address of a function you want called + while Readline is waiting for input. + + * readline.c: Cleanup time. Functions without type declarations + do not use return with a value. + + * history.c: history_expand () has new variable which is the + characters to ignore immediately following history_expansion_char. + +Sun Jul 16 08:14:00 1989 Brian Fox (bfox at aurel) + + * rl_prep_terminal () + BSD version turns off C-s, C-q, C-y, C-v. + + * readline.c -- rl_prep_terminal () + SYSV version hacks readline_echoing_p. + BSD version turns on passing of the 8th bit for the duration + of reading the line. + +Tue Jul 11 06:25:01 1989 Brian Fox (bfox at aurel) + + * readline.c: new variable rl_tilde_expander. + If non-null, this contains the address of a function to call if + the standard meaning for expanding a tilde fails. The function is + called with the text sans tilde (as in "foo"), and returns a + malloc()'ed string which is the expansion, or a NULL pointer if + there is no expansion. + + * readline.h - new file chardefs.h + Separates things that only readline.c needs from the standard + header file publishing interesting things about readline. + + * readline.c: + readline_default_bindings () now looks at terminal chararacters + and binds those as well. + +Wed Jun 28 20:20:51 1989 Brian Fox (bfox at aurel) + + * Made readline and history into independent libraries. + diff --git a/gnu/lib/libreadline/Makefile b/gnu/lib/libreadline/Makefile new file mode 100644 index 0000000..5be1dc5 --- /dev/null +++ b/gnu/lib/libreadline/Makefile @@ -0,0 +1,22 @@ +# $Id: Makefile,v 1.1 1993/12/28 07:54:51 ache Exp $ + +SHLIB_MAJOR=1 +SHLIB_MINOR=0 + +CFLAGS+= -I${.CURDIR} -I${.CURDIR}/readline -DVOID_SIGHANDLER -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp + +LIB= readline +SRCS+= readline.c history.c funmap.c keymaps.c +NOMAN= noman + +beforeinstall: + @-if [ ! -d ${DESTDIR}/usr/include/readline ]; then \ + mkdir ${DESTDIR}/usr/include/readline; \ + chown ${BINOWN}.${BINGRP} ${DESTDIR}/usr/include/readline; \ + chmod 755 ${DESTDIR}/usr/include/readline; \ + fi + cd ${.CURDIR}/readline; \ + install -c -o ${BINOWN} -g ${BINGRP} -m 444 \ + readline.h keymaps.h chardefs.h ${DESTDIR}/usr/include/readline + +.include diff --git a/gnu/lib/libreadline/README.FreeBSD b/gnu/lib/libreadline/README.FreeBSD new file mode 100644 index 0000000..7c917b0 --- /dev/null +++ b/gnu/lib/libreadline/README.FreeBSD @@ -0,0 +1,14 @@ +The GNU Readline library is a programming tool that provides a +consistent user interface for recalling lines of previously typed +input and performing editing tasks on input lines. + +paul@freefall.cdrom.com + +There was a bug with tcsh: when readline attempt to get tty +modes from background, it got no-echo editing tcsh mode. + +Workaround for this implemented via TIOCGWINSZ/TIOCSWINSZ +with same winsize structure: it does nothing expect polling +process from background. Look tcsh_hack.readme for details. + +ache@astral.msk.su diff --git a/gnu/lib/libreadline/doc/Makefile b/gnu/lib/libreadline/doc/Makefile new file mode 100644 index 0000000..786e099 --- /dev/null +++ b/gnu/lib/libreadline/doc/Makefile @@ -0,0 +1,30 @@ +# This makefile for Readline library documentation is in -*- text -*- mode. +# Emacs likes it that way. + +DVIOBJ = readline.dvi history.dvi +INFOBJ = readline.info history.info + +all: $(DVIOBJ) $(INFOBJ) + +readline.dvi: rlman.texinfo rluser.texinfo rltech.texinfo + tex rlman.texinfo + tex rlman.texinfo + mv rlman.dvi readline.dvi + +history.dvi: hist.texinfo hsuser.texinfo hstech.texinfo + tex hist.texinfo + tex hist.texinfo + mv hist.dvi history.dvi + +readline.info: rlman.texinfo rluser.texinfo rltech.texinfo + makeinfo rlman.texinfo + +history.info: hist.texinfo hsuser.texinfo hstech.texinfo + makeinfo hist.texinfo + +clean: + rm -f *.log *.cp *.ky *.tp *.vr *.fn *.aux *.pg *.toc core + +squeaky-clean: + rm -f *.log *.cp *.ky *.tp *.vr *.fn *.aux *.pg *.toc core *.dvi *.~* *.info + diff --git a/gnu/lib/libreadline/doc/hist.texinfo b/gnu/lib/libreadline/doc/hist.texinfo new file mode 100644 index 0000000..d110374 --- /dev/null +++ b/gnu/lib/libreadline/doc/hist.texinfo @@ -0,0 +1,106 @@ +\input texinfo @c -*-texinfo-*- +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename history.info +@settitle GNU Readline Library +@comment %**end of header (This is for running Texinfo on a region.) +@synindex fn vr +@setchapternewpage odd + +@ifinfo +This document describes the GNU History library, a programming tool that +provides a consistent user interface for recalling lines of previously +typed input. + +Copyright (C) 1988, 1991 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +pare preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). +@end ignore + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the Foundation. +@end ifinfo + +@titlepage +@sp 10 +@center @titlefont{GNU History Library} +@center Brian Fox +@center Free Software Foundation +@center Version 1.1 +@center April 1991 + +@c Include the Distribution inside the titlepage environment so +@c that headings are turned off. + +@page + +This document describes the GNU History library, a programming tool that +provides a consistent user interface for recalling lines of previously +typed input. + +Published by the Free Software Foundation @* +675 Massachusetts Avenue, @* +Cambridge, MA 02139 USA + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the Foundation. + +@vskip 0pt plus 1filll +Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc. +@end titlepage + +@ifinfo +@node Top +@top GNU History Library + +This document describes the GNU History library, a programming tool that +provides a consistent user interface for recalling lines of previously +typed input. + +@menu +* Using History Interactively:: GNU History User's Manual. +* Programming with GNU History:: GNU History Programmer's Manual. +* Concept Index:: Index of concepts described in this manual. +* Function and Variable Index:: Index of externally visible functions + and variables. +@end menu +@end ifinfo + +@include hsuser.texinfo +@include hstech.texinfo + +@node Concept Index +@appendix Concept Index +@printindex cp + +@node Function and Variable Index +@appendix Function and Variable Index +@printindex vr +@contents + +@bye diff --git a/gnu/lib/libreadline/doc/hstech.texinfo b/gnu/lib/libreadline/doc/hstech.texinfo new file mode 100644 index 0000000..c3fe3f6 --- /dev/null +++ b/gnu/lib/libreadline/doc/hstech.texinfo @@ -0,0 +1,311 @@ +@ignore +This file documents the user interface to the GNU History library. + +Copyright (C) 1988, 1991 Free Software Foundation, Inc. +Authored by Brian Fox. + +Permission is granted to make and distribute verbatim copies of this manual +provided the copyright notice and this permission notice are preserved on +all copies. + +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission notice +identical to this one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +GNU Copyright statement is available to the distributee, and provided that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ignore + +@node Programming with GNU History +@chapter Programming with GNU History + +This chapter describes how to interface the GNU History Library with +programs that you write. It should be considered a technical guide. +For information on the interactive use of GNU History, @pxref{Using +History Interactively}. + +@menu +* Introduction to History:: What is the GNU History library for? +* History Storage:: How information is stored. +* History Functions:: Functions that you can use. +* History Variables:: Variables that control behaviour. +* History Programming Example:: Example of using the GNU History Library. +@end menu + +@node Introduction to History +@section Introduction to History + +Many programs read input from the user a line at a time. The GNU history +library is able to keep track of those lines, associate arbitrary data with +each line, and utilize information from previous lines in making up new +ones. + +The programmer using the History library has available to him functions +for remembering lines on a history stack, associating arbitrary data +with a line, removing lines from the stack, searching through the stack +for a line containing an arbitrary text string, and referencing any line +on the stack directly. In addition, a history @dfn{expansion} function +is available which provides for a consistent user interface across many +different programs. + +The end-user using programs written with the History library has the +benifit of a consistent user interface, with a set of well-known +commands for manipulating the text of previous lines and using that text +in new commands. The basic history manipulation commands are similar to +the history substitution used by @code{Csh}. + +If the programmer desires, he can use the Readline library, which +includes some history manipulation by default, and has the added +advantage of Emacs style command line editing. + +@node History Storage +@section History Storage + +@example +typedef struct _hist_entry @{ + char *line; + char *data; +@} HIST_ENTRY; +@end example + +@node History Functions +@section History Functions + +This section describes the calling sequence for the various functions +present in GNU History. + +@defun {void using_history} () +Begin a session in which the history functions might be used. This +just initializes the interactive variables. +@end defun + +@defun {void add_history} (char *string) +Place @var{string} at the end of the history list. The associated data +field (if any) is set to @code{NULL}. +@end defun + +@defun {int where_history} () +Returns the number which says what history element we are now looking +at. +@end defun + +@defun {int history_set_pos} (int pos) +Set the position in the history list to @var{pos}. +@end defun + +@defun {int history_search_pos} (char *string, int direction, int pos) +Search for @var{string} in the history list, starting at @var{pos}, an +absolute index into the list. @var{direction}, if negative, says to search +backwards from @var{pos}, else forwards. Returns the absolute index of +the history element where @var{string} was found, or -1 otherwise. +@end defun + +@defun {HIST_ENTRY *remove_history} (); +Remove history element @var{which} from the history. The removed +element is returned to you so you can free the line, data, +and containing structure. +@end defun + +@defun {void stifle_history} (int max) +Stifle the history list, remembering only @var{max} number of entries. +@end defun + +@defun {int unstifle_history} (); +Stop stifling the history. This returns the previous amount the +history was stifled by. The value is positive if the history was +stifled, negative if it wasn't. +@end defun + +@defun {int read_history} (char *filename) +Add the contents of @var{filename} to the history list, a line at a +time. If @var{filename} is @code{NULL}, then read from +@file{~/.history}. Returns 0 if successful, or errno if not. +@end defun + +@defun {int read_history_range} (char *filename, int from, int to) +Read a range of lines from @var{filename}, adding them to the history list. +Start reading at the @var{from}'th line and end at the @var{to}'th. If +@var{from} is zero, start at the beginning. If @var{to} is less than +@var{from}, then read until the end of the file. If @var{filename} is +@code{NULL}, then read from @file{~/.history}. Returns 0 if successful, +or @code{errno} if not. +@end defun + +@defun {int write_history} (char *filename) +Append the current history to @var{filename}. If @var{filename} is +@code{NULL}, then append the history list to @file{~/.history}. Values +returned are as in @code{read_history ()}. +@end defun + +@defun {int append_history} (int nelements, char *filename) +Append @var{nelement} entries to @var{filename}. The entries appended +are from the end of the list minus @var{nelements} up to the end of the +list. +@end defun + +@defun {HIST_ENTRY *replace_history_entry} () +Make the history entry at @var{which} have @var{line} and @var{data}. +This returns the old entry so you can dispose of the data. In the case +of an invalid @var{which}, a @code{NULL} pointer is returned. +@end defun + +@defun {HIST_ENTRY *current_history} () +Return the history entry at the current position, as determined by +@code{history_offset}. If there is no entry there, return a @code{NULL} +pointer. +@end defun + +@defun {HIST_ENTRY *previous_history} () +Back up @var{history_offset} to the previous history entry, and return a +pointer to that entry. If there is no previous entry, return a +@code{NULL} pointer. +@end defun + +@defun {HIST_ENTRY *next_history} () +Move @code{history_offset} forward to the next history entry, and return +the a pointer to that entry. If there is no next entry, return a +@code{NULL} pointer. +@end defun + +@defun {HIST_ENTRY **history_list} () +Return a @code{NULL} terminated array of @code{HIST_ENTRY} which is the +current input history. Element 0 of this list is the beginning of time. +If there is no history, return @code{NULL}. +@end defun + +@defun {int history_search} (char *string, int direction) +Search the history for @var{string}, starting at @code{history_offset}. +If @var{direction} < 0, then the search is through previous entries, +else through subsequent. If @var{string} is found, then +@code{current_history ()} is the history entry, and the value of this +function is the offset in the line of that history entry that the +@var{string} was found in. Otherwise, nothing is changed, and a -1 is +returned. +@end defun + +@defun {int history_expand} (char *string, char **output) +Expand @var{string}, placing the result into @var{output}, a pointer +to a string. Returns: +@table @code +@item 0 +If no expansions took place (or, if the only change in +the text was the de-slashifying of the history expansion +character), +@item 1 +if expansions did take place, or +@item -1 +if there was an error in expansion. +@end table + +If an error ocurred in expansion, then @var{output} contains a descriptive +error message. +@end defun + +@defun {char *history_arg_extract} (int first, int last, char *string) +Extract a string segment consisting of the @var{first} through @var{last} +arguments present in @var{string}. Arguments are broken up as in +the GNU Bash shell. +@end defun + +@defun {int history_total_bytes} (); +Return the number of bytes that the primary history entries are using. +This just adds up the lengths of @code{the_history->lines}. +@end defun + +@node History Variables +@section History Variables + +This section describes the variables in GNU History that are externally +visible. + +@defvar {int history_base} +For convenience only. You set this when interpreting history commands. +It is the logical offset of the first history element. +@end defvar + +@node History Programming Example +@section History Programming Example + +The following snippet of code demonstrates simple use of the GNU History +Library. + +@smallexample +main () +@{ + char line[1024], *t; + int done = 0; + + line[0] = 0; + + while (!done) + @{ + fprintf (stdout, "history%% "); + t = gets (line); + + if (!t) + strcpy (line, "quit"); + + if (line[0]) + @{ + char *expansion; + int result; + + using_history (); + + result = history_expand (line, &expansion); + strcpy (line, expansion); + free (expansion); + if (result) + fprintf (stderr, "%s\n", line); + + if (result < 0) + continue; + + add_history (line); + @} + + if (strcmp (line, "quit") == 0) done = 1; + if (strcmp (line, "save") == 0) write_history (0); + if (strcmp (line, "read") == 0) read_history (0); + if (strcmp (line, "list") == 0) + @{ + register HIST_ENTRY **the_list = history_list (); + register int i; + + if (the_list) + for (i = 0; the_list[i]; i++) + fprintf (stdout, "%d: %s\n", + i + history_base, the_list[i]->line); + @} + if (strncmp (line, "delete", strlen ("delete")) == 0) + @{ + int which; + if ((sscanf (line + strlen ("delete"), "%d", &which)) == 1) + @{ + HIST_ENTRY *entry = remove_history (which); + if (!entry) + fprintf (stderr, "No such entry %d\n", which); + else + @{ + free (entry->line); + free (entry); + @} + @} + else + @{ + fprintf (stderr, "non-numeric arg given to `delete'\n"); + @} + @} + @} +@} +@end smallexample + + + diff --git a/gnu/lib/libreadline/doc/hsuser.texinfo b/gnu/lib/libreadline/doc/hsuser.texinfo new file mode 100644 index 0000000..cda0a68 --- /dev/null +++ b/gnu/lib/libreadline/doc/hsuser.texinfo @@ -0,0 +1,153 @@ +@ignore +This file documents the user interface to the GNU History library. + +Copyright (C) 1988, 1991 Free Software Foundation, Inc. +Authored by Brian Fox. + +Permission is granted to make and distribute verbatim copies of this manual +provided the copyright notice and this permission notice are preserved on +all copies. + +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission notice +identical to this one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +GNU Copyright statement is available to the distributee, and provided that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ignore + +@node Using History Interactively +@chapter Using History Interactively + +This chapter describes how to use the GNU History Library interactively, +from a user's standpoint. It should be considered a user's guide. For +information on using the GNU History Library in your own programs, +@pxref{Programming with GNU History}. + +@menu +* History Interaction:: What it feels like using History as a user. +@end menu + +@node History Interaction +@section History Interaction +@cindex expansion + +The History library provides a history expansion feature that is similar +to the history expansion in Csh. The following text describes the sytax +that you use to manipulate the history information. + +History expansion takes place in two parts. The first is to determine +which line from the previous history should be used during substitution. +The second is to select portions of that line for inclusion into the +current one. The line selected from the previous history is called the +@dfn{event}, and the portions of that line that are acted upon are +called @dfn{words}. The line is broken into words in the same fashion +that the Bash shell does, so that several English (or Unix) words +surrounded by quotes are considered as one word. + +@menu +* Event Designators:: How to specify which history line to use. +* Word Designators:: Specifying which words are of interest. +* Modifiers:: Modifying the results of susbstitution. +@end menu + +@node Event Designators +@subsection Event Designators +@cindex event designators + +An event designator is a reference to a command line entry in the +history list. + +@table @asis + +@item @code{!} +Start a history subsititution, except when followed by a space, tab, or +the end of the line... @key{=} or @key{(}. + +@item @code{!!} +Refer to the previous command. This is a synonym for @code{!-1}. + +@item @code{!n} +Refer to command line @var{n}. + +@item @code{!-n} +Refer to the command line @var{n} lines back. + +@item @code{!string} +Refer to the most recent command starting with @var{string}. + +@item @code{!?string}[@code{?}] +Refer to the most recent command containing @var{string}. + +@end table + +@node Word Designators +@subsection Word Designators + +A @key{:} separates the event specification from the word designator. It +can be omitted if the word designator begins with a @key{^}, @key{$}, +@key{*} or @key{%}. Words are numbered from the beginning of the line, +with the first word being denoted by a 0 (zero). + +@table @code + +@item 0 (zero) +The zero'th word. For many applications, this is the command word. + +@item n +The @var{n}'th word. + +@item ^ +The first argument. that is, word 1. + +@item $ +The last argument. + +@item % +The word matched by the most recent @code{?string?} search. + +@item x-y +A range of words; @code{-@var{y}} Abbreviates @code{0-@var{y}}. + +@item * +All of the words, excepting the zero'th. This is a synonym for @code{1-$}. +It is not an error to use @key{*} if there is just one word in the event. +The empty string is returned in that case. + +@end table + +@node Modifiers +@subsection Modifiers + +After the optional word designator, you can add a sequence of one or more +of the following modifiers, each preceded by a @key{:}. + +@table @code + +@item # +The entire command line typed so far. This means the current command, +not the previous command, so it really isn't a word designator, and doesn't +belong in this section. + +@item h +Remove a trailing pathname component, leaving only the head. + +@item r +Remove a trailing suffix of the form @samp{.}@var{suffix}, leaving the basename. + +@item e +Remove all but the suffix. + +@item t +Remove all leading pathname components, leaving the tail. + +@item p +Print the new command but do not execute it. +@end table diff --git a/gnu/lib/libreadline/doc/rlman.texinfo b/gnu/lib/libreadline/doc/rlman.texinfo new file mode 100644 index 0000000..9ac246d --- /dev/null +++ b/gnu/lib/libreadline/doc/rlman.texinfo @@ -0,0 +1,103 @@ +\input texinfo @c -*-texinfo-*- +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename readline.info +@settitle GNU Readline Library +@comment %**end of header (This is for running Texinfo on a region.) +@synindex fn vr +@setchapternewpage odd + +@ifinfo +This document describes the GNU Readline Library, a utility for aiding +in the consitency of user interface across discrete programs that need +to provide a command line interface. + +Copyright (C) 1988, 1991 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +pare preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). +@end ignore + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the Foundation. +@end ifinfo + +@titlepage +@sp 10 +@center @titlefont{GNU Readline Library} +@center Brian Fox +@center Free Software Foundation +@center Version 1.1 +@center April 1991 + +@page +This document describes the GNU Readline Library, a utility for aiding +in the consistency of user interface across discrete programs that need +to provide a command line interface. + +Published by the Free Software Foundation @* +675 Massachusetts Avenue, @* +Cambridge, MA 02139 USA + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the Foundation. + +@vskip 0pt plus 1filll +Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc. +@end titlepage + +@ifinfo +@node Top +@top GNU Readline Library + +This document describes the GNU Readline Library, a utility for aiding +in the consistency of user interface across discrete programs that need +to provide a command line interface. + +@menu +* Command Line Editing:: GNU Readline User's Manual. +* Programming with GNU Readline:: GNU Readline Programmer's Manual. +* Concept Index:: Index of concepts described in this manual. +* Function and Variable Index:: Index of externally visible functions + and variables. +@end menu +@end ifinfo + +@include rluser.texinfo +@include rltech.texinfo + +@node Concept Index +@appendix Concept Index +@printindex cp + +@node Function and Variable Index +@appendix Function and Variable Index +@printindex vr +@contents + +@bye + diff --git a/gnu/lib/libreadline/doc/rltech.texinfo b/gnu/lib/libreadline/doc/rltech.texinfo new file mode 100644 index 0000000..f52288e --- /dev/null +++ b/gnu/lib/libreadline/doc/rltech.texinfo @@ -0,0 +1,1010 @@ +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename rltech.info +@synindex fn vr +@comment %**end of header (This is for running Texinfo on a region.) +@setchapternewpage odd + +@ifinfo +This document describes the GNU Readline Library, a utility for aiding +in the consitency of user interface across discrete programs that need +to provide a command line interface. + +Copyright (C) 1988 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +pare preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). +@end ignore + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the Foundation. +@end ifinfo + +@node Programming with GNU Readline +@chapter Programming with GNU Readline + +This manual describes the interface between the GNU Readline Library and +user programs. If you are a programmer, and you wish to include the +features found in GNU Readline in your own programs, such as completion, +line editing, and interactive history manipulation, this documentation +is for you. + +@menu +* Default Behaviour:: Using the default behaviour of Readline. +* Custom Functions:: Adding your own functions to Readline. +* Custom Completers:: Supplanting or supplementing Readline's + completion functions. +@end menu + +@node Default Behaviour +@section Default Behaviour + +Many programs provide a command line interface, such as @code{mail}, +@code{ftp}, and @code{sh}. For such programs, the default behaviour of +Readline is sufficient. This section describes how to use Readline in +the simplest way possible, perhaps to replace calls in your code to +@code{gets ()}. + +@findex readline () +@cindex readline, function +The function @code{readline} prints a prompt and then reads and returns +a single line of text from the user. The line which @code{readline ()} +returns is allocated with @code{malloc ()}; you should @code{free ()} +the line when you are done with it. The declaration for @code{readline} +in ANSI C is + +@example +@code{char *readline (char *@var{prompt});} +@end example + +So, one might say +@example +@code{char *line = readline ("Enter a line: ");} +@end example +in order to read a line of text from the user. + +The line which is returned has the final newline removed, so only the +text of the line remains. + +If readline encounters an @code{EOF} while reading the line, and the +line is empty at that point, then @code{(char *)NULL} is returned. +Otherwise, the line is ended just as if a newline was typed. + +If you want the user to be able to get at the line later, (with +@key{C-p} for example), you must call @code{add_history ()} to save the +line away in a @dfn{history} list of such lines. + +@example +@code{add_history (line)}; +@end example + +For full details on the GNU History Library, see the associated manual. + +It is polite to avoid saving empty lines on the history list, since it +is rare than someone has a burning need to reuse a blank line. Here is +a function which usefully replaces the standard @code{gets ()} library +function: + +@example +/* A static variable for holding the line. */ +static char *line_read = (char *)NULL; + +/* Read a string, and return a pointer to it. Returns NULL on EOF. */ +char * +do_gets () +@{ + /* If the buffer has already been allocated, return the memory + to the free pool. */ + if (line_read != (char *)NULL) + @{ + free (line_read); + line_read = (char *)NULL; + @} + + /* Get a line from the user. */ + line_read = readline (""); + + /* If the line has any text in it, save it on the history. */ + if (line_read && *line_read) + add_history (line_read); + + return (line_read); +@} +@end example + +The above code gives the user the default behaviour of @key{TAB} +completion: completion on file names. If you do not want readline to +complete on filenames, you can change the binding of the @key{TAB} key +with @code{rl_bind_key ()}. + +@findex rl_bind_key () +@example +@code{int rl_bind_key (int @var{key}, (int (*)())@var{function});} +@end example + +@code{rl_bind_key ()} takes 2 arguments; @var{key} is the character that +you want to bind, and @var{function} is the address of the function to +run when @var{key} is pressed. Binding @key{TAB} to @code{rl_insert ()} +makes @key{TAB} just insert itself. + +@code{rl_bind_key ()} returns non-zero if @var{key} is not a valid +ASCII character code (between 0 and 255). + +@example +@code{rl_bind_key ('\t', rl_insert);} +@end example + +This code should be executed once at the start of your program; you +might write a function called @code{initialize_readline ()} which +performs this and other desired initializations, such as installing +custom completers, etc. + +@node Custom Functions +@section Custom Functions + +Readline provides a great many functions for manipulating the text of +the line. But it isn't possible to anticipate the needs of all +programs. This section describes the various functions and variables +defined in within the Readline library which allow a user program to add +customized functionality to Readline. + +@menu +* The Function Type:: C declarations to make code readable. +* Function Naming:: How to give a function you write a name. +* Keymaps:: Making keymaps. +* Binding Keys:: Changing Keymaps. +* Function Writing:: Variables and calling conventions. +* Allowing Undoing:: How to make your functions undoable. +@end menu + +@node The Function Type +@subsection The Function Type + +For the sake of readabilty, we declare a new type of object, called +@dfn{Function}. A @code{Function} is a C language function which +returns an @code{int}. The type declaration for @code{Function} is: + +@noindent +@code{typedef int Function ();} + +The reason for declaring this new type is to make it easier to write +code describing pointers to C functions. Let us say we had a variable +called @var{func} which was a pointer to a function. Instead of the +classic C declaration + +@code{int (*)()func;} + +we have + +@code{Function *func;} + +@node Function Naming +@subsection Naming a Function + +The user can dynamically change the bindings of keys while using +Readline. This is done by representing the function with a descriptive +name. The user is able to type the descriptive name when referring to +the function. Thus, in an init file, one might find + +@example +Meta-Rubout: backward-kill-word +@end example + +This binds the keystroke @key{Meta-Rubout} to the function +@emph{descriptively} named @code{backward-kill-word}. You, as the +programmer, should bind the functions you write to descriptive names as +well. Readline provides a function for doing that: + +@defun rl_add_defun (char *name, Function *function, int key) +Add @var{name} to the list of named functions. Make @var{function} be +the function that gets called. If @var{key} is not -1, then bind it to +@var{function} using @code{rl_bind_key ()}. +@end defun + +Using this function alone is sufficient for most applications. It is +the recommended way to add a few functions to the default functions that +Readline has built in already. If you need to do more or different +things than adding a function to Readline, you may need to use the +underlying functions described below. + +@node Keymaps +@subsection Selecting a Keymap + +Key bindings take place on a @dfn{keymap}. The keymap is the +association between the keys that the user types and the functions that +get run. You can make your own keymaps, copy existing keymaps, and tell +Readline which keymap to use. + +@defun {Keymap rl_make_bare_keymap} () +Returns a new, empty keymap. The space for the keymap is allocated with +@code{malloc ()}; you should @code{free ()} it when you are done. +@end defun + +@defun {Keymap rl_copy_keymap} (Keymap map) +Return a new keymap which is a copy of @var{map}. +@end defun + +@defun {Keymap rl_make_keymap} () +Return a new keymap with the printing characters bound to rl_insert, +the lowercase Meta characters bound to run their equivalents, and +the Meta digits bound to produce numeric arguments. +@end defun + +@node Binding Keys +@subsection Binding Keys + +You associate keys with functions through the keymap. Here are +functions for doing that. + +@defun {int rl_bind_key} (int key, Function *function) +Binds @var{key} to @var{function} in the currently selected keymap. +Returns non-zero in the case of an invalid @var{key}. +@end defun + +@defun {int rl_bind_key_in_map} (int key, Function *function, Keymap map) +Bind @var{key} to @var{function} in @var{map}. Returns non-zero in the case +of an invalid @var{key}. +@end defun + +@defun {int rl_unbind_key} (int key) +Make @var{key} do nothing in the currently selected keymap. +Returns non-zero in case of error. +@end defun + +@defun {int rl_unbind_key_in_map} (int key, Keymap map) +Make @var{key} be bound to the null function in @var{map}. +Returns non-zero in case of error. +@end defun + +@defun rl_generic_bind (int type, char *keyseq, char *data, Keymap map) +Bind the key sequence represented by the string @var{keyseq} to the arbitrary +pointer @var{data}. @var{type} says what kind of data is pointed to by +@var{data}; right now this can be a function (@code{ISFUNC}), a macro +(@code{ISMACR}), or a keymap (@code{ISKMAP}). This makes new keymaps as +necessary. The initial place to do bindings is in @var{map}. +@end defun + +@node Function Writing +@subsection Writing a New Function + +In order to write new functions for Readline, you need to know the +calling conventions for keyboard invoked functions, and the names of the +variables that describe the current state of the line gathered so far. + +@defvar {char *rl_line_buffer} +This is the line gathered so far. You are welcome to modify the +contents of this, but see Undoing, below. +@end defvar + +@defvar {int rl_point} +The offset of the current cursor position in @var{rl_line_buffer}. +@end defvar + +@defvar {int rl_end} +The number of characters present in @code{rl_line_buffer}. When +@code{rl_point} is at the end of the line, then @code{rl_point} and +@code{rl_end} are equal. +@end defvar + +The calling sequence for a command @code{foo} looks like + +@example +@code{foo (int count, int key)} +@end example + +where @var{count} is the numeric argument (or 1 if defaulted) and +@var{key} is the key that invoked this function. + +It is completely up to the function as to what should be done with the +numeric argument; some functions use it as a repeat count, other +functions as a flag, and some choose to ignore it. In general, if a +function uses the numeric argument as a repeat count, it should be able +to do something useful with a negative argument as well as a positive +argument. At the very least, it should be aware that it can be passed a +negative argument. + +@node Allowing Undoing +@subsection Allowing Undoing + +Supporting the undo command is a painless thing to do, and makes your +functions much more useful to the end user. It is certainly easy to try +something if you know you can undo it. I could use an undo function for +the stock market. + +If your function simply inserts text once, or deletes text once, and it +calls @code{rl_insert_text ()} or @code{rl_delete_text ()} to do it, then +undoing is already done for you automatically, and you can safely skip +this section. + +If you do multiple insertions or multiple deletions, or any combination +of these operations, you should group them together into one operation. +This can be done with @code{rl_begin_undo_group ()} and +@code{rl_end_undo_group ()}. + +@defun rl_begin_undo_group () +Begins saving undo information in a group construct. The undo +information usually comes from calls to @code{rl_insert_text ()} and +@code{rl_delete_text ()}, but they could be direct calls to +@code{rl_add_undo ()}. +@end defun + +@defun rl_end_undo_group () +Closes the current undo group started with @code{rl_begin_undo_group +()}. There should be exactly one call to @code{rl_end_undo_group ()} +for every call to @code{rl_begin_undo_group ()}. +@end defun + +Finally, if you neither insert nor delete text, but directly modify the +existing text (e.g. change its case), you call @code{rl_modifying ()} +once, just before you modify the text. You must supply the indices of +the text range that you are going to modify. + +@defun rl_modifying (int start, int end) +Tell Readline to save the text between @var{start} and @var{end} as a +single undo unit. It is assumed that subsequent to this call you will +modify that range of text in some way. +@end defun + +@subsection An Example + +Here is a function which changes lowercase characters to the uppercase +equivalents, and uppercase characters to the lowercase equivalents. If +this function was bound to @samp{M-c}, then typing @samp{M-c} would +change the case of the character under point. Typing @samp{10 M-c} +would change the case of the following 10 characters, leaving the cursor on +the last character changed. + +@example +/* Invert the case of the COUNT following characters. */ +invert_case_line (count, key) + int count, key; +@{ + register int start, end; + + start = rl_point; + + if (count < 0) + @{ + direction = -1; + count = -count; + @} + else + direction = 1; + + /* Find the end of the range to modify. */ + end = start + (count * direction); + + /* Force it to be within range. */ + if (end > rl_end) + end = rl_end; + else if (end < 0) + end = -1; + + if (start > end) + @{ + int temp = start; + start = end; + end = temp; + @} + + if (start == end) + return; + + /* Tell readline that we are modifying the line, so save the undo + information. */ + rl_modifying (start, end); + + for (; start != end; start += direction) + @{ + if (uppercase_p (rl_line_buffer[start])) + rl_line_buffer[start] = to_lower (rl_line_buffer[start]); + else if (lowercase_p (rl_line_buffer[start])) + rl_line_buffer[start] = to_upper (rl_line_buffer[start]); + @} + /* Move point to on top of the last character changed. */ + rl_point = end - direction; +@} +@end example + +@node Custom Completers +@section Custom Completers + +Typically, a program that reads commands from the user has a way of +disambiguating between commands and data. If your program is one of +these, then it can provide completion for either commands, or data, or +both commands and data. The following sections describe how your +program and Readline cooperate to provide this service to end users. + +@menu +* How Completing Works:: The logic used to do completion. +* Completion Functions:: Functions provided by Readline. +* Completion Variables:: Variables which control completion. +* A Short Completion Example:: An example of writing completer subroutines. +@end menu + +@node How Completing Works +@subsection How Completing Works + +In order to complete some text, the full list of possible completions +must be available. That is to say, it is not possible to accurately +expand a partial word without knowing what all of the possible words +that make sense in that context are. The GNU Readline library provides +the user interface to completion, and additionally, two of the most common +completion functions; filename and username. For completing other types +of text, you must write your own completion function. This section +describes exactly what those functions must do, and provides an example +function. + +There are three major functions used to perform completion: + +@enumerate +@item +The user-interface function @code{rl_complete ()}. This function is +called interactively with the same calling conventions as other +functions in readline intended for interactive use; i.e. @var{count}, +and @code{invoking-key}. It isolates the word to be completed and calls +@code{completion_matches ()} to generate a list of possible completions. +It then either lists the possible completions or actually performs the +completion, depending on which behaviour is desired. + +@item +The internal function @code{completion_matches ()} uses your +@dfn{generator} function to generate the list of possible matches, and +then returns the array of these matches. You should place the address +of your generator function in @code{rl_completion_entry_function}. + +@item +The generator function is called repeatedly from +@code{completion_matches ()}, returning a string each time. The +arguments to the generator function are @var{text} and @var{state}. +@var{text} is the partial word to be completed. @var{state} is zero the +first time the function is called, and a positive non-zero integer for +each subsequent call. When the generator function returns @code{(char +*)NULL} this signals @code{completion_matches ()} that there are no more +possibilities left. + +@end enumerate + +@defun rl_complete (int ignore, int invoking_key) +Complete the word at or before point. You have supplied the function +that does the initial simple matching selection algorithm (see +@code{completion_matches ()}). The default is to do filename completion. +@end defun + +Note that @code{rl_complete ()} has the identical calling conventions as +any other key-invokable function; this is because by default it is bound +to the @samp{TAB} key. + +@defvar {Function *rl_completion_entry_function} +This is a pointer to the generator function for @code{completion_matches +()}. If the value of @code{rl_completion_entry_function} is +@code{(Function *)NULL} then the default filename generator function is +used, namely @code{filename_entry_function ()}. +@end defvar + +@node Completion Functions +@subsection Completion Functions + +Here is the complete list of callable completion functions present in +Readline. + +@defun rl_complete_internal (int what_to_do) +Complete the word at or before point. @var{what_to_do} says what to do +with the completion. A value of @samp{?} means list the possible +completions. @samp{TAB} means do standard completion. @samp{*} means +insert all of the possible completions. +@end defun + +@defun rl_complete (int ignore, int invoking_key) +Complete the word at or before point. You have supplied the function +that does the initial simple matching selection algorithm (see +@code{completion_matches ()}). The default is to do filename +completion. This just calls @code{rl_complete_internal ()} with an +argument of @samp{TAB}. +@end defun + +@defun rl_possible_completions () +List the possible completions. See description of @code{rl_complete +()}. This just calls @code{rl_complete_internal ()} with an argument of +@samp{?}. +@end defun + +@defun {char **completion_matches} (char *text, char *(*entry_function) ()) +Returns an array of @code{(char *)} which is a list of completions for +@var{text}. If there are no completions, returns @code{(char **)NULL}. +The first entry in the returned array is the substitution for @var{text}. +The remaining entries are the possible completions. The array is +terminated with a @code{NULL} pointer. + +@var{entry_function} is a function of two args, and returns a +@code{(char *)}. The first argument is @var{text}. The second is a +state argument; it is zero on the first call, and non-zero on subsequent +calls. It returns a @code{NULL} pointer to the caller when there are +no more matches. +@end defun + +@defun {char *filename_completion_function} (char *text, int state) +A generator function for filename completion in the general case. Note +that completion in the Bash shell is a little different because of all +the pathnames that must be followed when looking up the completion for a +command. +@end defun + +@defun {char *username_completion_function} (char *text, int state) +A completion generator for usernames. @var{text} contains a partial +username preceded by a random character (usually @samp{~}). +@end defun + +@node Completion Variables +@subsection Completion Variables + +@defvar {Function *rl_completion_entry_function} +A pointer to the generator function for @code{completion_matches ()}. +@code{NULL} means to use @code{filename_entry_function ()}, the default +filename completer. +@end defvar + +@defvar {Function *rl_attempted_completion_function} +A pointer to an alternative function to create matches. +The function is called with @var{text}, @var{start}, and @var{end}. +@var{start} and @var{end} are indices in @code{rl_line_buffer} saying +what the boundaries of @var{text} are. If this function exists and +returns @code{NULL} then @code{rl_complete ()} will call the value of +@code{rl_completion_entry_function} to generate matches, otherwise the +array of strings returned will be used. +@end defvar + +@defvar {int rl_completion_query_items} +Up to this many items will be displayed in response to a +possible-completions call. After that, we ask the user if she is sure +she wants to see them all. The default value is 100. +@end defvar + +@defvar {char *rl_basic_word_break_characters} +The basic list of characters that signal a break between words for the +completer routine. The contents of this variable is what breaks words +in the Bash shell, i.e. " \t\n\"\\'`@@$><=". +@end defvar + +@defvar {char *rl_completer_word_break_characters} +The list of characters that signal a break between words for +@code{rl_complete_internal ()}. The default list is the contents of +@code{rl_basic_word_break_characters}. +@end defvar + +@defvar {char *rl_special_prefixes} +The list of characters that are word break characters, but should be +left in @var{text} when it is passed to the completion function. +Programs can use this to help determine what kind of completing to do. +@end defvar + +@defvar {int rl_ignore_completion_duplicates} +If non-zero, then disallow duplicates in the matches. Default is 1. +@end defvar + +@defvar {int rl_filename_completion_desired} +Non-zero means that the results of the matches are to be treated as +filenames. This is @emph{always} zero on entry, and can only be changed +within a completion entry generator function. +@end defvar + +@defvar {Function *rl_ignore_some_completions_function} +This function, if defined, is called by the completer when real filename +completion is done, after all the matching names have been generated. It +is passed a @code{NULL} terminated array of pointers to @code{(char *)} +known as @var{matches} in the code. The 1st element (@code{matches[0]}) +is the maximal substring that is common to all matches. This function +can re-arrange the list of matches as required, but each deleted +element of the array must be @code{free()}'d. +@end defvar + +@node A Short Completion Example +@subsection A Short Completion Example + +Here is a small application demonstrating the use of the GNU Readline +library. It is called @code{fileman}, and the source code resides in +@file{readline/examples/fileman.c}. This sample application provides +completion of command names, line editing features, and access to the +history list. + +@page +@smallexample +/* fileman.c -- A tiny application which demonstrates how to use the + GNU Readline library. This application interactively allows users + to manipulate files and their modes. */ + +#include +#include +#include +#include +#include +#include +#include + +/* The names of functions that actually do the manipulation. */ +int com_list (), com_view (), com_rename (), com_stat (), com_pwd (); +int com_delete (), com_help (), com_cd (), com_quit (); + +/* A structure which contains information on the commands this program + can understand. */ + +typedef struct @{ + char *name; /* User printable name of the function. */ + Function *func; /* Function to call to do the job. */ + char *doc; /* Documentation for this function. */ +@} COMMAND; + +COMMAND commands[] = @{ + @{ "cd", com_cd, "Change to directory DIR" @}, + @{ "delete", com_delete, "Delete FILE" @}, + @{ "help", com_help, "Display this text" @}, + @{ "?", com_help, "Synonym for `help'" @}, + @{ "list", com_list, "List files in DIR" @}, + @{ "ls", com_list, "Synonym for `list'" @}, + @{ "pwd", com_pwd, "Print the current working directory" @}, + @{ "quit", com_quit, "Quit using Fileman" @}, + @{ "rename", com_rename, "Rename FILE to NEWNAME" @}, + @{ "stat", com_stat, "Print out statistics on FILE" @}, + @{ "view", com_view, "View the contents of FILE" @}, + @{ (char *)NULL, (Function *)NULL, (char *)NULL @} +@}; + +/* The name of this program, as taken from argv[0]. */ +char *progname; + +/* When non-zero, this global means the user is done using this program. */ +int done = 0; +@page +main (argc, argv) + int argc; + char **argv; +@{ + progname = argv[0]; + + initialize_readline (); /* Bind our completer. */ + + /* Loop reading and executing lines until the user quits. */ + while (!done) + @{ + char *line; + + line = readline ("FileMan: "); + + if (!line) + @{ + done = 1; /* Encountered EOF at top level. */ + @} + else + @{ + /* Remove leading and trailing whitespace from the line. + Then, if there is anything left, add it to the history list + and execute it. */ + stripwhite (line); + + if (*line) + @{ + add_history (line); + execute_line (line); + @} + @} + + if (line) + free (line); + @} + exit (0); +@} + +/* Execute a command line. */ +execute_line (line) + char *line; +@{ + register int i; + COMMAND *find_command (), *command; + char *word; + + /* Isolate the command word. */ + i = 0; + while (line[i] && !whitespace (line[i])) + i++; + + word = line; + + if (line[i]) + line[i++] = '\0'; + + command = find_command (word); + + if (!command) + @{ + fprintf (stderr, "%s: No such command for FileMan.\n", word); + return; + @} + + /* Get argument to command, if any. */ + while (whitespace (line[i])) + i++; + + word = line + i; + + /* Call the function. */ + (*(command->func)) (word); +@} + +/* Look up NAME as the name of a command, and return a pointer to that + command. Return a NULL pointer if NAME isn't a command name. */ +COMMAND * +find_command (name) + char *name; +@{ + register int i; + + for (i = 0; commands[i].name; i++) + if (strcmp (name, commands[i].name) == 0) + return (&commands[i]); + + return ((COMMAND *)NULL); +@} + +/* Strip whitespace from the start and end of STRING. */ +stripwhite (string) + char *string; +@{ + register int i = 0; + + while (whitespace (string[i])) + i++; + + if (i) + strcpy (string, string + i); + + i = strlen (string) - 1; + + while (i > 0 && whitespace (string[i])) + i--; + + string[++i] = '\0'; +@} +@page +/* **************************************************************** */ +/* */ +/* Interface to Readline Completion */ +/* */ +/* **************************************************************** */ + +/* Tell the GNU Readline library how to complete. We want to try to complete + on command names if this is the first word in the line, or on filenames + if not. */ +initialize_readline () +@{ + char **fileman_completion (); + + /* Allow conditional parsing of the ~/.inputrc file. */ + rl_readline_name = "FileMan"; + + /* Tell the completer that we want a crack first. */ + rl_attempted_completion_function = (Function *)fileman_completion; +@} + +/* Attempt to complete on the contents of TEXT. START and END show the + region of TEXT that contains the word to complete. We can use the + entire line in case we want to do some simple parsing. Return the + array of matches, or NULL if there aren't any. */ +char ** +fileman_completion (text, start, end) + char *text; + int start, end; +@{ + char **matches; + char *command_generator (); + + matches = (char **)NULL; + + /* If this word is at the start of the line, then it is a command + to complete. Otherwise it is the name of a file in the current + directory. */ + if (start == 0) + matches = completion_matches (text, command_generator); + + return (matches); +@} + +/* Generator function for command completion. STATE lets us know whether + to start from scratch; without any state (i.e. STATE == 0), then we + start at the top of the list. */ +char * +command_generator (text, state) + char *text; + int state; +@{ + static int list_index, len; + char *name; + + /* If this is a new word to complete, initialize now. This includes + saving the length of TEXT for efficiency, and initializing the index + variable to 0. */ + if (!state) + @{ + list_index = 0; + len = strlen (text); + @} + + /* Return the next name which partially matches from the command list. */ + while (name = commands[list_index].name) + @{ + list_index++; + + if (strncmp (name, text, len) == 0) + return (name); + @} + + /* If no names matched, then return NULL. */ + return ((char *)NULL); +@} +@page +/* **************************************************************** */ +/* */ +/* FileMan Commands */ +/* */ +/* **************************************************************** */ + +/* String to pass to system (). This is for the LIST, VIEW and RENAME + commands. */ +static char syscom[1024]; + +/* List the file(s) named in arg. */ +com_list (arg) + char *arg; +@{ + sprintf (syscom, "ls -FClg %s", arg); + system (syscom); +@} + +com_view (arg) + char *arg; +@{ + if (!valid_argument ("view", arg)) + return; + + sprintf (syscom, "cat %s | more", arg); + system (syscom); +@} + +com_rename (arg) + char *arg; +@{ + too_dangerous ("rename"); +@} + +com_stat (arg) + char *arg; +@{ + struct stat finfo; + + if (!valid_argument ("stat", arg)) + return; + + if (stat (arg, &finfo) == -1) + @{ + perror (arg); + return; + @} + + printf ("Statistics for `%s':\n", arg); + + printf ("%s has %d link%s, and is %d bytes in length.\n", arg, + finfo.st_nlink, (finfo.st_nlink == 1) ? "" : "s", finfo.st_size); + printf (" Created on: %s", ctime (&finfo.st_ctime)); + printf (" Last access at: %s", ctime (&finfo.st_atime)); + printf ("Last modified at: %s", ctime (&finfo.st_mtime)); +@} + +com_delete (arg) + char *arg; +@{ + too_dangerous ("delete"); +@} + +/* Print out help for ARG, or for all of the commands if ARG is + not present. */ +com_help (arg) + char *arg; +@{ + register int i; + int printed = 0; + + for (i = 0; commands[i].name; i++) + @{ + if (!*arg || (strcmp (arg, commands[i].name) == 0)) + @{ + printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc); + printed++; + @} + @} + + if (!printed) + @{ + printf ("No commands match `%s'. Possibilties are:\n", arg); + + for (i = 0; commands[i].name; i++) + @{ + /* Print in six columns. */ + if (printed == 6) + @{ + printed = 0; + printf ("\n"); + @} + + printf ("%s\t", commands[i].name); + printed++; + @} + + if (printed) + printf ("\n"); + @} +@} + +/* Change to the directory ARG. */ +com_cd (arg) + char *arg; +@{ + if (chdir (arg) == -1) + perror (arg); + + com_pwd (""); +@} + +/* Print out the current working directory. */ +com_pwd (ignore) + char *ignore; +@{ + char dir[1024]; + + (void) getwd (dir); + + printf ("Current directory is %s\n", dir); +@} + +/* The user wishes to quit using this program. Just set DONE non-zero. */ +com_quit (arg) + char *arg; +@{ + done = 1; +@} + +/* Function which tells you that you can't do this. */ +too_dangerous (caller) + char *caller; +@{ + fprintf (stderr, + "%s: Too dangerous for me to distribute. Write it yourself.\n", + caller); +@} + +/* Return non-zero if ARG is a valid argument for CALLER, else print + an error message and return zero. */ +int +valid_argument (caller, arg) + char *caller, *arg; +@{ + if (!arg || !*arg) + @{ + fprintf (stderr, "%s: Argument required.\n", caller); + return (0); + @} + + return (1); +@} +@end smallexample diff --git a/gnu/lib/libreadline/doc/rluser.texinfo b/gnu/lib/libreadline/doc/rluser.texinfo new file mode 100644 index 0000000..ea20205 --- /dev/null +++ b/gnu/lib/libreadline/doc/rluser.texinfo @@ -0,0 +1,489 @@ +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename rluser.info +@synindex fn vr +@comment %**end of header (This is for running Texinfo on a region.) +@setchapternewpage odd + +@ignore +This file documents the end user interface to the GNU command line +editing feautres. It is to be an appendix to manuals for programs which +use these features. There is a document entitled "readline.texinfo" +which contains both end-user and programmer documentation for the GNU +Readline Library. + +Copyright (C) 1988 Free Software Foundation, Inc. + +Authored by Brian Fox. + +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission notice +identical to this one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). + +Permission is granted to make and distribute verbatim copies of this manual +provided the copyright notice and this permission notice are preserved on +all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +GNU Copyright statement is available to the distributee, and provided that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ignore + +@node Command Line Editing +@chapter Command Line Editing + +This text describes GNU's command line editing interface. + +@menu +* Introduction and Notation:: Notation used in this text. +* Readline Interaction:: The minimum set of commands for editing a line. +* Readline Init File:: Customizing Readline from a user's view. +@end menu + +@node Introduction and Notation +@section Introduction to Line Editing + +In this tex a the following notation is used to describe keystrokes. + +The text @key{C-k} is read as `Control-K' and describes the character +produced when the Control key is depressed and the @key{k} key is struck. + +The text @key{M-k} is read as `Meta-K' and describes the character +produced when the meta key (if you have one) is depressed, and the @key{k} +key is struck. If you do not have a meta key, the identical keystroke +can be generated by typing @key{ESC} @i{first}, and then typing @key{k}. +Either process is known as @dfn{metafying} the @key{k} key. + +The text @key{M-C-k} is read as `Meta-Control-k' and describes the +character produced by @dfn{metafying} @key{C-k}. + +In addition, several keys have their own names. Specifically, +@key{DEL}, @key{ESC}, @key{LFD}, @key{SPC}, @key{RET}, and @key{TAB} all +stand for themselves when seen in this text, or in an init file +(@pxref{Readline Init File}, for more info). + +@node Readline Interaction +@section Readline Interaction +@cindex interaction, readline + +Often during an interactive session you type in a long line of text, +only to notice that the first word on the line is misspelled. The +Readline library gives you a set of commands for manipulating the text +as you type it in, allowing you to just fix your typo, and not forcing +you to retype the majority of the line. Using these editing commands, +you move the cursor to the place that needs correction, and delete or +insert the text of the corrections. Then, when you are satisfied with +the line, you simply press @key{RETURN}. You do not have to be at the +end of the line to press @key{RETURN}; the entire line is accepted +regardless of the location of the cursor within the line. + +@menu +* Readline Bare Essentials:: The least you need to know about Readline. +* Readline Movement Commands:: Moving about the input line. +* Readline Killing Commands:: How to delete text, and how to get it back! +* Readline Arguments:: Giving numeric arguments to commands. +@end menu + +@node Readline Bare Essentials +@subsection Readline Bare Essentials + +In order to enter characters into the line, simply type them. The typed +character appears where the cursor was, and then the cursor moves one +space to the right. If you mistype a character, you can use @key{DEL} to +back up, and delete the mistyped character. + +Sometimes you may miss typing a character that you wanted to type, and +not notice your error until you have typed several other characters. In +that case, you can type @key{C-b} to move the cursor to the left, and then +correct your mistake. Aftwerwards, you can move the cursor to the right +with @key{C-f}. + +When you add text in the middle of a line, you will notice that characters +to the right of the cursor get `pushed over' to make room for the text +that you have inserted. Likewise, when you delete text behind the cursor, +characters to the right of the cursor get `pulled back' to fill in the +blank space created by the removal of the text. A list of the basic bare +essentials for editing the text of an input line follows. + +@table @asis +@item @key{C-b} +Move back one character. +@item @key{C-f} +Move forward one character. +@item @key{DEL} +Delete the character to the left of the cursor. +@item @key{C-d} +Delete the character underneath the cursor. +@item @w{Printing characters} +Insert itself into the line at the cursor. +@item @key{C-_} +Undo the last thing that you did. You can undo all the way back to an +empty line. +@end table + +@node Readline Movement Commands +@subsection Readline Movement Commands + + +The above table describes the most basic possible keystrokes that you need +in order to do editing of the input line. For your convenience, many +other commands have been added in addition to @key{C-b}, @key{C-f}, +@key{C-d}, and @key{DEL}. Here are some commands for moving more rapidly +about the line. + +@table @key +@item C-a +Move to the start of the line. +@item C-e +Move to the end of the line. +@item M-f +Move forward a word. +@item M-b +Move backward a word. +@item C-l +Clear the screen, reprinting the current line at the top. +@end table + +Notice how @key{C-f} moves forward a character, while @key{M-f} moves +forward a word. It is a loose convention that control keystrokes +operate on characters while meta keystrokes operate on words. + +@node Readline Killing Commands +@subsection Readline Killing Commands + +The act of @dfn{cutting} text means to delete the text from the line, and +to save away the deleted text for later use, just as if you had cut the +text out of the line with a pair of scissors. There is a + +@dfn{Killing} text means to delete the text from the line, but to save +it away for later use, usually by @dfn{yanking} it back into the line. +If the description for a command says that it `kills' text, then you can +be sure that you can get the text back in a different (or the same) +place later. + +Here is the list of commands for killing text. + +@table @key +@item C-k +Kill the text from the current cursor position to the end of the line. + +@item M-d +Kill from the cursor to the end of the current word, or if between +words, to the end of the next word. + +@item M-DEL +Kill fromthe cursor the start ofthe previous word, or if between words, to the start of the previous word. + +@item C-w +Kill from the cursor to the previous whitespace. This is different than +@key{M-DEL} because the word boundaries differ. + +@end table + +And, here is how to @dfn{yank} the text back into the line. Yanking +is + +@table @key +@item C-y +Yank the most recently killed text back into the buffer at the cursor. + +@item M-y +Rotate the kill-ring, and yank the new top. You can only do this if +the prior command is @key{C-y} or @key{M-y}. +@end table + +When you use a kill command, the text is saved in a @dfn{kill-ring}. +Any number of consecutive kills save all of the killed text together, so +that when you yank it back, you get it in one clean sweep. The kill +ring is not line specific; the text that you killed on a previously +typed line is available to be yanked back later, when you are typing +another line. + +@node Readline Arguments +@subsection Readline Arguments + +You can pass numeric arguments to Readline commands. Sometimes the +argument acts as a repeat count, other times it is the @i{sign} of the +argument that is significant. If you pass a negative argument to a +command which normally acts in a forward direction, that command will +act in a backward direction. For example, to kill text back to the +start of the line, you might type @key{M--} @key{C-k}. + +The general way to pass numeric arguments to a command is to type meta +digits before the command. If the first `digit' you type is a minus +sign (@key{-}), then the sign of the argument will be negative. Once +you have typed one meta digit to get the argument started, you can type +the remainder of the digits, and then the command. For example, to give +the @key{C-d} command an argument of 10, you could type @key{M-1 0 C-d}. + + +@node Readline Init File +@section Readline Init File + +Although the Readline library comes with a set of Emacs-like +keybindings, it is possible that you would like to use a different set +of keybindings. You can customize programs that use Readline by putting +commands in an @dfn{init} file in your home directory. The name of this +file is @file{~/.inputrc}. + +When a program which uses the Readline library starts up, the +@file{~/.inputrc} file is read, and the keybindings are set. + +@menu +* Readline Init Syntax:: Syntax for the commands in @file{~/.inputrc}. +* Readline Vi Mode:: Switching to @code{vi} mode in Readline. +@end menu + +@node Readline Init Syntax +@subsection Readline Init Syntax + +You can start up with a vi-like editing mode by placing + +@example +@code{set editing-mode vi} +@end example + +in your @file{~/.inputrc} file. + +You can have Readline use a single line for display, scrolling the input +between the two edges of the screen by placing + +@example +@code{set horizontal-scroll-mode On} +@end example + +in your @file{~/.inputrc} file. + +The syntax for controlling keybindings in the @file{~/.inputrc} file is +simple. First you have to know the @i{name} of the command that you +want to change. The following pages contain tables of the command name, the +default keybinding, and a short description of what the command does. + +Once you know the name of the command, simply place the name of the key +you wish to bind the command to, a colon, and then the name of the +command on a line in the @file{~/.inputrc} file. Here is an example: + +@example +# This is a comment line. +Meta-Rubout: backward-kill-word +Control-u: universal-argument +@end example + +@menu +* Commands For Moving:: Moving about the line. +* Commands For History:: Getting at previous lines. +* Commands For Text:: Commands for changing text. +* Commands For Killing:: Commands for killing and yanking. +* Numeric Arguments:: Specifying numeric arguments, repeat counts. +* Commands For Completion:: Getting Readline to do the typing for you. +* Miscellaneous Commands:: Other miscillaneous commands. +@end menu + +@node Commands For Moving +@subsubsection Commands For Moving +@table @code +@item beginning-of-line (C-a) +Move to the start of the current line. + +@item end-of-line (C-e) +Move to the end of the line. + +@item forward-char (C-f) +Move forward a character. + +@item backward-char (C-b) +Move back a character. + +@item forward-word (M-f) +Move forward to the end of the next word. + +@item backward-word (M-b) +Move back to the start of this, or the previous, word. + +@item clear-screen (C-l) +Clear the screen leaving the current line at the top of the screen. + +@end table + +@node Commands For History +@subsubsection Commands For Manipulating The History + +@table @code +@item accept-line (Newline, Return) +Accept the line regardless of where the cursor is. If this line is +non-empty, add it too the history list. If this line was a history +line, then restore the history line to its original state. + +@item previous-history (C-p) +Move `up' through the history list. + +@item next-history (C-n) +Move `down' through the history list. + +@item beginning-of-history (M-<) +Move to the first line in the history. + +@item end-of-history (M->) +Move to the end of the input history, i.e., the line you are entering! + +@item reverse-search-history (C-r) +Search backward starting at the current line and moving `up' through +the history as necessary. This is an incremental search. + +@item forward-search-history (C-s) +Search forward starting at the current line and moving `down' through +the the history as neccessary. + +@end table + +@node Commands For Text +@subsubsection Commands For Changing Text + +@table @code +@item delete-char (C-d) +Delete the character under the cursor. If the cursor is at the +beginning of the line, and there are no characters in the line, and +the last character typed was not C-d, then return EOF. + +@item backward-delete-char (Rubout) +Delete the character behind the cursor. A numeric arg says to kill +the characters instead of deleting them. + +@item quoted-insert (C-q, C-v) +Add the next character that you type to the line verbatim. This is +how to insert things like C-q for example. + +@item tab-insert (M-TAB) +Insert a tab character. + +@item self-insert (a, b, A, 1, !, ...) +Insert yourself. + +@item transpose-chars (C-t) +Drag the character before point forward over the character at point. +Point moves forward as well. If point is at the end of the line, then +transpose the two characters before point. Negative args don't work. + +@item transpose-words (M-t) +Drag the word behind the cursor past the word in front of the cursor +moving the cursor over that word as well. + +@item upcase-word (M-u) +Uppercase the current (or following) word. With a negative argument, +do the previous word, but do not move point. + +@item downcase-word (M-l) +Lowercase the current (or following) word. With a negative argument, +do the previous word, but do not move point. + +@item capitalize-word (M-c) +Uppercase the current (or following) word. With a negative argument, +do the previous word, but do not move point. + +@end table + +@node Commands For Killing +@subsubsection Killing And Yanking + +@table @code + +@item kill-line (C-k) +Kill the text from the current cursor position to the end of the line. + +@item backward-kill-line () +Kill backward to the beginning of the line. This is normally unbound. + +@item kill-word (M-d) +Kill from the cursor to the end of the current word, or if between +words, to the end of the next word. + +@item backward-kill-word (M-DEL) +Kill the word behind the cursor. + +@item unix-line-discard (C-u) +Do what C-u used to do in Unix line input. We save the killed text on +the kill-ring, though. + +@item unix-word-rubout (C-w) +Do what C-w used to do in Unix line input. The killed text is saved +on the kill-ring. This is different than backward-kill-word because +the word boundaries differ. + +@item yank (C-y) +Yank the top of the kill ring into the buffer at point. + +@item yank-pop (M-y) +Rotate the kill-ring, and yank the new top. You can only do this if +the prior command is yank or yank-pop. +@end table + +@node Numeric Arguments +@subsubsection Specifying Numeric Arguments +@table @code + +@item digit-argument (M-0, M-1, ... M--) +Add this digit to the argument already accumulating, or start a new +argument. M-- starts a negative argument. + +@item universal-argument () +Do what C-u does in emacs. By default, this is not bound. +@end table + + +@node Commands For Completion +@subsubsection Letting Readline Type For You + +@table @code +@item complete (TAB) +Attempt to do completion on the text before point. This is +implementation defined. Generally, if you are typing a filename +argument, you can do filename completion; if you are typing a command, +you can do command completion, if you are typing in a symbol to GDB, you +can do symbol name completion, if you are typing in a variable to Bash, +you can do variable name completion... + +@item possible-completions (M-?) +List the possible completions of the text before point. +@end table + +@node Miscellaneous Commands +@subsubsection Some Miscellaneous Commands +@table @code + +@item abort (C-g) +Ding! Stops things. + +@item do-uppercase-version (M-a, M-b, ...) +Run the command that is bound to your uppercase brother. + +@item prefix-meta (ESC) +Make the next character that you type be metafied. This is for +people without a meta key. @key{ESC-f} is equivalent to @key{M-f}. + +@item undo (C-_) +Incremental undo, separately remembered for each line. + +@item revert-line (M-r) +Undo all changes made to this line. This is like typing the `undo' +command enough times to get back to the beginning. +@end table + +@node Readline Vi Mode +@subsection Readline Vi Mode + +While the Readline library does not have a full set of Vi editing +functions, it does contain enough to allow simple editing of the line. + +In order to switch interactively between Emacs and Vi editing modes, use +the command M-C-j (toggle-editing-mode). + +When you enter a line in Vi mode, you are already placed in `insertion' +mode, as if you had typed an `i'. Pressing @key{ESC} switches you into +`edit' mode, where you can edit the text of the line with the standard +Vi movement keys, move to previous history lines with `k', and following +lines with `j', and so forth. diff --git a/gnu/lib/libreadline/emacs_keymap.c b/gnu/lib/libreadline/emacs_keymap.c new file mode 100644 index 0000000..0b07fb3 --- /dev/null +++ b/gnu/lib/libreadline/emacs_keymap.c @@ -0,0 +1,472 @@ +/* emacs_keymap.c -- the keymap for emacs_mode in readline (). */ + +/* Copyright (C) 1988,1989 Free Software Foundation, Inc. + + This file is part of GNU Readline, a library for reading lines + of text with interactive input and history editing. + + Readline is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 1, or (at your option) any + later version. + + Readline is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline; see the file COPYING. If not, write to the Free + Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef BUFSIZ +#include +#endif /* BUFSIZ */ + +#include "readline.h" + +/* An array of function pointers, one for each possible key. + If the type byte is ISKMAP, then the pointer is the address of + a keymap. */ + +KEYMAP_ENTRY_ARRAY emacs_standard_keymap = { + + /* Control keys. */ + { ISFUNC, (Function *)0x0 }, /* Control-@ */ + { ISFUNC, rl_beg_of_line }, /* Control-a */ + { ISFUNC, rl_backward }, /* Control-b */ + { ISFUNC, (Function *)0x0 }, /* Control-c */ + { ISFUNC, rl_delete }, /* Control-d */ + { ISFUNC, rl_end_of_line }, /* Control-e */ + { ISFUNC, rl_forward }, /* Control-f */ + { ISFUNC, rl_abort }, /* Control-g */ + { ISFUNC, rl_rubout }, /* Control-h */ + { ISFUNC, rl_complete }, /* Control-i */ + { ISFUNC, rl_newline }, /* Control-j */ + { ISFUNC, rl_kill_line }, /* Control-k */ + { ISFUNC, rl_clear_screen }, /* Control-l */ + { ISFUNC, rl_newline }, /* Control-m */ + { ISFUNC, rl_get_next_history }, /* Control-n */ + { ISFUNC, (Function *)0x0 }, /* Control-o */ + { ISFUNC, rl_get_previous_history }, /* Control-p */ + { ISFUNC, rl_quoted_insert }, /* Control-q */ + { ISFUNC, rl_reverse_search_history }, /* Control-r */ + { ISFUNC, rl_forward_search_history }, /* Control-s */ + { ISFUNC, rl_transpose_chars }, /* Control-t */ + { ISFUNC, rl_unix_line_discard }, /* Control-u */ + { ISFUNC, rl_quoted_insert }, /* Control-v */ + { ISFUNC, rl_unix_word_rubout }, /* Control-w */ + { ISKMAP, (Function *)emacs_ctlx_keymap }, /* Control-x */ + { ISFUNC, rl_yank }, /* Control-y */ + { ISFUNC, (Function *)0x0 }, /* Control-z */ + { ISKMAP, (Function *)emacs_meta_keymap }, /* Control-[ */ + { ISFUNC, (Function *)0x0 }, /* Control-\ */ + { ISFUNC, (Function *)0x0 }, /* Control-] */ + { ISFUNC, (Function *)0x0 }, /* Control-^ */ + { ISFUNC, rl_undo_command }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, rl_insert }, /* SPACE */ + { ISFUNC, rl_insert }, /* ! */ + { ISFUNC, rl_insert }, /* " */ + { ISFUNC, rl_insert }, /* # */ + { ISFUNC, rl_insert }, /* $ */ + { ISFUNC, rl_insert }, /* % */ + { ISFUNC, rl_insert }, /* & */ + { ISFUNC, rl_insert }, /* ' */ + { ISFUNC, rl_insert }, /* ( */ + { ISFUNC, rl_insert }, /* ) */ + { ISFUNC, rl_insert }, /* * */ + { ISFUNC, rl_insert }, /* + */ + { ISFUNC, rl_insert }, /* , */ + { ISFUNC, rl_insert }, /* - */ + { ISFUNC, rl_insert }, /* . */ + { ISFUNC, rl_insert }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_insert }, /* 0 */ + { ISFUNC, rl_insert }, /* 1 */ + { ISFUNC, rl_insert }, /* 2 */ + { ISFUNC, rl_insert }, /* 3 */ + { ISFUNC, rl_insert }, /* 4 */ + { ISFUNC, rl_insert }, /* 5 */ + { ISFUNC, rl_insert }, /* 6 */ + { ISFUNC, rl_insert }, /* 7 */ + { ISFUNC, rl_insert }, /* 8 */ + { ISFUNC, rl_insert }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, rl_insert }, /* : */ + { ISFUNC, rl_insert }, /* ; */ + { ISFUNC, rl_insert }, /* < */ + { ISFUNC, rl_insert }, /* = */ + { ISFUNC, rl_insert }, /* > */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_insert }, /* A */ + { ISFUNC, rl_insert }, /* B */ + { ISFUNC, rl_insert }, /* C */ + { ISFUNC, rl_insert }, /* D */ + { ISFUNC, rl_insert }, /* E */ + { ISFUNC, rl_insert }, /* F */ + { ISFUNC, rl_insert }, /* G */ + { ISFUNC, rl_insert }, /* H */ + { ISFUNC, rl_insert }, /* I */ + { ISFUNC, rl_insert }, /* J */ + { ISFUNC, rl_insert }, /* K */ + { ISFUNC, rl_insert }, /* L */ + { ISFUNC, rl_insert }, /* M */ + { ISFUNC, rl_insert }, /* N */ + { ISFUNC, rl_insert }, /* O */ + { ISFUNC, rl_insert }, /* P */ + { ISFUNC, rl_insert }, /* Q */ + { ISFUNC, rl_insert }, /* R */ + { ISFUNC, rl_insert }, /* S */ + { ISFUNC, rl_insert }, /* T */ + { ISFUNC, rl_insert }, /* U */ + { ISFUNC, rl_insert }, /* V */ + { ISFUNC, rl_insert }, /* W */ + { ISFUNC, rl_insert }, /* X */ + { ISFUNC, rl_insert }, /* Y */ + { ISFUNC, rl_insert }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, rl_insert }, /* [ */ + { ISFUNC, rl_insert }, /* \ */ + { ISFUNC, rl_insert }, /* ] */ + { ISFUNC, rl_insert }, /* ^ */ + { ISFUNC, rl_insert }, /* _ */ + { ISFUNC, rl_insert }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, rl_insert }, /* a */ + { ISFUNC, rl_insert }, /* b */ + { ISFUNC, rl_insert }, /* c */ + { ISFUNC, rl_insert }, /* d */ + { ISFUNC, rl_insert }, /* e */ + { ISFUNC, rl_insert }, /* f */ + { ISFUNC, rl_insert }, /* g */ + { ISFUNC, rl_insert }, /* h */ + { ISFUNC, rl_insert }, /* i */ + { ISFUNC, rl_insert }, /* j */ + { ISFUNC, rl_insert }, /* k */ + { ISFUNC, rl_insert }, /* l */ + { ISFUNC, rl_insert }, /* m */ + { ISFUNC, rl_insert }, /* n */ + { ISFUNC, rl_insert }, /* o */ + { ISFUNC, rl_insert }, /* p */ + { ISFUNC, rl_insert }, /* q */ + { ISFUNC, rl_insert }, /* r */ + { ISFUNC, rl_insert }, /* s */ + { ISFUNC, rl_insert }, /* t */ + { ISFUNC, rl_insert }, /* u */ + { ISFUNC, rl_insert }, /* v */ + { ISFUNC, rl_insert }, /* w */ + { ISFUNC, rl_insert }, /* x */ + { ISFUNC, rl_insert }, /* y */ + { ISFUNC, rl_insert }, /* z */ + + /* Final punctuation. */ + { ISFUNC, rl_insert }, /* { */ + { ISFUNC, rl_insert }, /* | */ + { ISFUNC, rl_insert }, /* } */ + { ISFUNC, rl_insert }, /* ~ */ + { ISFUNC, rl_rubout } /* RUBOUT */ +}; + +KEYMAP_ENTRY_ARRAY emacs_meta_keymap = { + + /* Meta keys. Just like above, but the high bit is set. */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-@ */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-a */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-b */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-c */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-d */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-e */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-f */ + { ISFUNC, rl_abort }, /* Meta-Control-g */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-h */ + { ISFUNC, rl_tab_insert }, /* Meta-Control-i */ + { ISFUNC, rl_vi_editing_mode }, /* Meta-Control-j */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-k */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-l */ + { ISFUNC, rl_vi_editing_mode }, /* Meta-Control-m */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-n */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-o */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-p */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-q */ + { ISFUNC, rl_revert_line }, /* Meta-Control-r */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-s */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-t */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-u */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-v */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-w */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-x */ + { ISFUNC, rl_yank_nth_arg }, /* Meta-Control-y */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-z */ + + { ISFUNC, (Function *)0x0 }, /* Meta-Control-[ */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-\ */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-] */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-^ */ + { ISFUNC, (Function *)0x0 }, /* Meta-Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, (Function *)0x0 }, /* Meta-SPACE */ + { ISFUNC, (Function *)0x0 }, /* Meta-! */ + { ISFUNC, (Function *)0x0 }, /* Meta-" */ + { ISFUNC, (Function *)0x0 }, /* Meta-# */ + { ISFUNC, (Function *)0x0 }, /* Meta-$ */ + { ISFUNC, (Function *)0x0 }, /* Meta-% */ + { ISFUNC, (Function *)0x0 }, /* Meta-& */ + { ISFUNC, (Function *)0x0 }, /* Meta-' */ + { ISFUNC, (Function *)0x0 }, /* Meta-( */ + { ISFUNC, (Function *)0x0 }, /* Meta-) */ + { ISFUNC, (Function *)0x0 }, /* Meta-* */ + { ISFUNC, (Function *)0x0 }, /* Meta-+ */ + { ISFUNC, (Function *)0x0 }, /* Meta-, */ + { ISFUNC, rl_digit_argument }, /* Meta-- */ + { ISFUNC, (Function *)0x0 }, /* Meta-. */ + { ISFUNC, (Function *)0x0 }, /* Meta-/ */ + + /* Regular digits. */ + { ISFUNC, rl_digit_argument }, /* Meta-0 */ + { ISFUNC, rl_digit_argument }, /* Meta-1 */ + { ISFUNC, rl_digit_argument }, /* Meta-2 */ + { ISFUNC, rl_digit_argument }, /* Meta-3 */ + { ISFUNC, rl_digit_argument }, /* Meta-4 */ + { ISFUNC, rl_digit_argument }, /* Meta-5 */ + { ISFUNC, rl_digit_argument }, /* Meta-6 */ + { ISFUNC, rl_digit_argument }, /* Meta-7 */ + { ISFUNC, rl_digit_argument }, /* Meta-8 */ + { ISFUNC, rl_digit_argument }, /* Meta-9 */ + + /* A little more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* Meta-: */ + { ISFUNC, (Function *)0x0 }, /* Meta-; */ + { ISFUNC, rl_beginning_of_history }, /* Meta-< */ + { ISFUNC, (Function *)0x0 }, /* Meta-= */ + { ISFUNC, rl_end_of_history }, /* Meta-> */ + { ISFUNC, rl_possible_completions }, /* Meta-? */ + { ISFUNC, (Function *)0x0 }, /* Meta-@ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-A */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-B */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-C */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-D */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-E */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-F */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-G */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-H */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-I */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-J */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-K */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-L */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-M */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-N */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-O */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-P */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-Q */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-R */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-S */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-T */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-U */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-V */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-W */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-X */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-Y */ + { ISFUNC, rl_do_lowercase_version }, /* Meta-Z */ + + /* Some more punctuation. */ + { ISFUNC, rl_arrow_keys }, /* Meta-[ */ + { ISFUNC, (Function *)0x0 }, /* Meta-\ */ + { ISFUNC, (Function *)0x0 }, /* Meta-] */ + { ISFUNC, (Function *)0x0 }, /* Meta-^ */ + { ISFUNC, (Function *)0x0 }, /* Meta-_ */ + { ISFUNC, (Function *)0x0 }, /* Meta-` */ + + /* Lowercase alphabet. */ + { ISFUNC, (Function *)0x0 }, /* Meta-a */ + { ISFUNC, rl_backward_word }, /* Meta-b */ + { ISFUNC, rl_capitalize_word }, /* Meta-c */ + { ISFUNC, rl_kill_word }, /* Meta-d */ + { ISFUNC, (Function *)0x0 }, /* Meta-e */ + { ISFUNC, rl_forward_word }, /* Meta-f */ + { ISFUNC, (Function *)0x0 }, /* Meta-g */ + { ISFUNC, (Function *)0x0 }, /* Meta-h */ + { ISFUNC, (Function *)0x0 }, /* Meta-i */ + { ISFUNC, (Function *)0x0 }, /* Meta-j */ + { ISFUNC, (Function *)0x0 }, /* Meta-k */ + { ISFUNC, rl_downcase_word }, /* Meta-l */ + { ISFUNC, (Function *)0x0 }, /* Meta-m */ + { ISFUNC, (Function *)0x0 }, /* Meta-n */ + { ISFUNC, rl_arrow_keys }, /* Meta-o */ + { ISFUNC, (Function *)0x0 }, /* Meta-p */ + { ISFUNC, (Function *)0x0 }, /* Meta-q */ + { ISFUNC, rl_revert_line }, /* Meta-r */ + { ISFUNC, (Function *)0x0 }, /* Meta-s */ + { ISFUNC, rl_transpose_words }, /* Meta-t */ + { ISFUNC, rl_upcase_word }, /* Meta-u */ + { ISFUNC, (Function *)0x0 }, /* Meta-v */ + { ISFUNC, (Function *)0x0 }, /* Meta-w */ + { ISFUNC, (Function *)0x0 }, /* Meta-x */ + { ISFUNC, rl_yank_pop }, /* Meta-y */ + { ISFUNC, (Function *)0x0 }, /* Meta-z */ + + /* Final punctuation. */ + { ISFUNC, (Function *)0x0 }, /* Meta-{ */ + { ISFUNC, (Function *)0x0 }, /* Meta-| */ + { ISFUNC, (Function *)0x0 }, /* Meta-} */ + { ISFUNC, (Function *)0x0 }, /* Meta-~ */ + { ISFUNC, rl_backward_kill_word } /* Meta-rubout */ +}; + +KEYMAP_ENTRY_ARRAY emacs_ctlx_keymap = { + + /* Control keys. */ + { ISFUNC, (Function *)0x0 }, /* Control-@ */ + { ISFUNC, (Function *)0x0 }, /* Control-a */ + { ISFUNC, (Function *)0x0 }, /* Control-b */ + { ISFUNC, (Function *)0x0 }, /* Control-c */ + { ISFUNC, (Function *)0x0 }, /* Control-d */ + { ISFUNC, (Function *)0x0 }, /* Control-e */ + { ISFUNC, (Function *)0x0 }, /* Control-f */ + { ISFUNC, rl_abort }, /* Control-g */ + { ISFUNC, (Function *)0x0 }, /* Control-h */ + { ISFUNC, (Function *)0x0 }, /* Control-i */ + { ISFUNC, (Function *)0x0 }, /* Control-j */ + { ISFUNC, (Function *)0x0 }, /* Control-k */ + { ISFUNC, (Function *)0x0 }, /* Control-l */ + { ISFUNC, (Function *)0x0 }, /* Control-m */ + { ISFUNC, (Function *)0x0 }, /* Control-n */ + { ISFUNC, (Function *)0x0 }, /* Control-o */ + { ISFUNC, (Function *)0x0 }, /* Control-p */ + { ISFUNC, (Function *)0x0 }, /* Control-q */ + { ISFUNC, rl_re_read_init_file }, /* Control-r */ + { ISFUNC, (Function *)0x0 }, /* Control-s */ + { ISFUNC, (Function *)0x0 }, /* Control-t */ + { ISFUNC, rl_undo_command }, /* Control-u */ + { ISFUNC, (Function *)0x0 }, /* Control-v */ + { ISFUNC, (Function *)0x0 }, /* Control-w */ + { ISFUNC, (Function *)0x0 }, /* Control-x */ + { ISFUNC, (Function *)0x0 }, /* Control-y */ + { ISFUNC, (Function *)0x0 }, /* Control-z */ + { ISFUNC, (Function *)0x0 }, /* Control-[ */ + { ISFUNC, (Function *)0x0 }, /* Control-\ */ + { ISFUNC, (Function *)0x0 }, /* Control-] */ + { ISFUNC, (Function *)0x0 }, /* Control-^ */ + { ISFUNC, (Function *)0x0 }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, (Function *)0x0 }, /* SPACE */ + { ISFUNC, (Function *)0x0 }, /* ! */ + { ISFUNC, (Function *)0x0 }, /* " */ + { ISFUNC, (Function *)0x0 }, /* # */ + { ISFUNC, (Function *)0x0 }, /* $ */ + { ISFUNC, (Function *)0x0 }, /* % */ + { ISFUNC, (Function *)0x0 }, /* & */ + { ISFUNC, (Function *)0x0 }, /* ' */ + { ISFUNC, rl_start_kbd_macro }, /* ( */ + { ISFUNC, rl_end_kbd_macro }, /* ) */ + { ISFUNC, (Function *)0x0 }, /* * */ + { ISFUNC, (Function *)0x0 }, /* + */ + { ISFUNC, (Function *)0x0 }, /* , */ + { ISFUNC, (Function *)0x0 }, /* - */ + { ISFUNC, (Function *)0x0 }, /* . */ + { ISFUNC, (Function *)0x0 }, /* / */ + + /* Regular digits. */ + { ISFUNC, (Function *)0x0 }, /* 0 */ + { ISFUNC, (Function *)0x0 }, /* 1 */ + { ISFUNC, (Function *)0x0 }, /* 2 */ + { ISFUNC, (Function *)0x0 }, /* 3 */ + { ISFUNC, (Function *)0x0 }, /* 4 */ + { ISFUNC, (Function *)0x0 }, /* 5 */ + { ISFUNC, (Function *)0x0 }, /* 6 */ + { ISFUNC, (Function *)0x0 }, /* 7 */ + { ISFUNC, (Function *)0x0 }, /* 8 */ + { ISFUNC, (Function *)0x0 }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* : */ + { ISFUNC, (Function *)0x0 }, /* ; */ + { ISFUNC, (Function *)0x0 }, /* < */ + { ISFUNC, (Function *)0x0 }, /* = */ + { ISFUNC, (Function *)0x0 }, /* > */ + { ISFUNC, (Function *)0x0 }, /* ? */ + { ISFUNC, (Function *)0x0 }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_do_lowercase_version }, /* A */ + { ISFUNC, rl_do_lowercase_version }, /* B */ + { ISFUNC, rl_do_lowercase_version }, /* C */ + { ISFUNC, rl_do_lowercase_version }, /* D */ + { ISFUNC, rl_do_lowercase_version }, /* E */ + { ISFUNC, rl_do_lowercase_version }, /* F */ + { ISFUNC, rl_do_lowercase_version }, /* G */ + { ISFUNC, rl_do_lowercase_version }, /* H */ + { ISFUNC, rl_do_lowercase_version }, /* I */ + { ISFUNC, rl_do_lowercase_version }, /* J */ + { ISFUNC, rl_do_lowercase_version }, /* K */ + { ISFUNC, rl_do_lowercase_version }, /* L */ + { ISFUNC, rl_do_lowercase_version }, /* M */ + { ISFUNC, rl_do_lowercase_version }, /* N */ + { ISFUNC, rl_do_lowercase_version }, /* O */ + { ISFUNC, rl_do_lowercase_version }, /* P */ + { ISFUNC, rl_do_lowercase_version }, /* Q */ + { ISFUNC, rl_do_lowercase_version }, /* R */ + { ISFUNC, rl_do_lowercase_version }, /* S */ + { ISFUNC, rl_do_lowercase_version }, /* T */ + { ISFUNC, rl_do_lowercase_version }, /* U */ + { ISFUNC, rl_do_lowercase_version }, /* V */ + { ISFUNC, rl_do_lowercase_version }, /* W */ + { ISFUNC, rl_do_lowercase_version }, /* X */ + { ISFUNC, rl_do_lowercase_version }, /* Y */ + { ISFUNC, rl_do_lowercase_version }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* [ */ + { ISFUNC, (Function *)0x0 }, /* \ */ + { ISFUNC, (Function *)0x0 }, /* ] */ + { ISFUNC, (Function *)0x0 }, /* ^ */ + { ISFUNC, (Function *)0x0 }, /* _ */ + { ISFUNC, (Function *)0x0 }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, (Function *)0x0 }, /* a */ + { ISFUNC, (Function *)0x0 }, /* b */ + { ISFUNC, (Function *)0x0 }, /* c */ + { ISFUNC, (Function *)0x0 }, /* d */ + { ISFUNC, rl_call_last_kbd_macro }, /* e */ + { ISFUNC, (Function *)0x0 }, /* f */ + { ISFUNC, (Function *)0x0 }, /* g */ + { ISFUNC, (Function *)0x0 }, /* h */ + { ISFUNC, (Function *)0x0 }, /* i */ + { ISFUNC, (Function *)0x0 }, /* j */ + { ISFUNC, (Function *)0x0 }, /* k */ + { ISFUNC, (Function *)0x0 }, /* l */ + { ISFUNC, (Function *)0x0 }, /* m */ + { ISFUNC, (Function *)0x0 }, /* n */ + { ISFUNC, (Function *)0x0 }, /* o */ + { ISFUNC, (Function *)0x0 }, /* p */ + { ISFUNC, (Function *)0x0 }, /* q */ + { ISFUNC, (Function *)0x0 }, /* r */ + { ISFUNC, (Function *)0x0 }, /* s */ + { ISFUNC, (Function *)0x0 }, /* t */ + { ISFUNC, (Function *)0x0 }, /* u */ + { ISFUNC, (Function *)0x0 }, /* v */ + { ISFUNC, (Function *)0x0 }, /* w */ + { ISFUNC, (Function *)0x0 }, /* x */ + { ISFUNC, (Function *)0x0 }, /* y */ + { ISFUNC, (Function *)0x0 }, /* z */ + + /* Final punctuation. */ + { ISFUNC, (Function *)0x0 }, /* { */ + { ISFUNC, (Function *)0x0 }, /* | */ + { ISFUNC, (Function *)0x0 }, /* } */ + { ISFUNC, (Function *)0x0 }, /* ~ */ + { ISFUNC, rl_backward_kill_line } /* RUBOUT */ +}; diff --git a/gnu/lib/libreadline/examples/Makefile b/gnu/lib/libreadline/examples/Makefile new file mode 100644 index 0000000..3d1fc52 --- /dev/null +++ b/gnu/lib/libreadline/examples/Makefile @@ -0,0 +1,12 @@ +# This is the Makefile for the examples subdirectory of readline. -*- text -*- +# + +EXECUTABLES = fileman +CFLAGS = -g -I../.. +LDFLAGS = -g -L.. + +fileman: fileman.o + $(CC) $(LDFLAGS) -o fileman fileman.o -lreadline -ltermcap + +fileman.o: fileman.c + diff --git a/gnu/lib/libreadline/examples/fileman.c b/gnu/lib/libreadline/examples/fileman.c new file mode 100644 index 0000000..92f3942 --- /dev/null +++ b/gnu/lib/libreadline/examples/fileman.c @@ -0,0 +1,392 @@ +/* fileman.c -- A tiny application which demonstrates how to use the + GNU Readline library. This application interactively allows users + to manipulate files and their modes. */ + +#include +#include +#include +#include +#include +#include +#include + +/* The names of functions that actually do the manipulation. */ +int com_list (), com_view (), com_rename (), com_stat (), com_pwd (); +int com_delete (), com_help (), com_cd (), com_quit (); + +/* A structure which contains information on the commands this program + can understand. */ + +typedef struct { + char *name; /* User printable name of the function. */ + Function *func; /* Function to call to do the job. */ + char *doc; /* Documentation for this function. */ +} COMMAND; + +COMMAND commands[] = { + { "cd", com_cd, "Change to directory DIR" }, + { "delete", com_delete, "Delete FILE" }, + { "help", com_help, "Display this text" }, + { "?", com_help, "Synonym for `help'" }, + { "list", com_list, "List files in DIR" }, + { "ls", com_list, "Synonym for `list'" }, + { "pwd", com_pwd, "Print the current working directory" }, + { "quit", com_quit, "Quit using Fileman" }, + { "rename", com_rename, "Rename FILE to NEWNAME" }, + { "stat", com_stat, "Print out statistics on FILE" }, + { "view", com_view, "View the contents of FILE" }, + { (char *)NULL, (Function *)NULL, (char *)NULL } +}; + +/* The name of this program, as taken from argv[0]. */ +char *progname; + +/* When non-zero, this global means the user is done using this program. */ +int done = 0; + +main (argc, argv) + int argc; + char **argv; +{ + progname = argv[0]; + + initialize_readline (); /* Bind our completer. */ + + /* Loop reading and executing lines until the user quits. */ + while (!done) + { + char *line; + + line = readline ("FileMan: "); + + if (!line) + { + done = 1; /* Encountered EOF at top level. */ + } + else + { + /* Remove leading and trailing whitespace from the line. + Then, if there is anything left, add it to the history list + and execute it. */ + stripwhite (line); + + if (*line) + { + add_history (line); + execute_line (line); + } + } + + if (line) + free (line); + } + exit (0); +} + +/* Execute a command line. */ +execute_line (line) + char *line; +{ + register int i; + COMMAND *find_command (), *command; + char *word; + + /* Isolate the command word. */ + i = 0; + while (line[i] && !whitespace (line[i])) + i++; + + word = line; + + if (line[i]) + line[i++] = '\0'; + + command = find_command (word); + + if (!command) + { + fprintf (stderr, "%s: No such command for FileMan.\n", word); + return; + } + + /* Get argument to command, if any. */ + while (whitespace (line[i])) + i++; + + word = line + i; + + /* Call the function. */ + (*(command->func)) (word); +} + +/* Look up NAME as the name of a command, and return a pointer to that + command. Return a NULL pointer if NAME isn't a command name. */ +COMMAND * +find_command (name) + char *name; +{ + register int i; + + for (i = 0; commands[i].name; i++) + if (strcmp (name, commands[i].name) == 0) + return (&commands[i]); + + return ((COMMAND *)NULL); +} + +/* Strip whitespace from the start and end of STRING. */ +stripwhite (string) + char *string; +{ + register int i = 0; + + while (whitespace (string[i])) + i++; + + if (i) + strcpy (string, string + i); + + i = strlen (string) - 1; + + while (i > 0 && whitespace (string[i])) + i--; + + string[++i] = '\0'; +} + +/* **************************************************************** */ +/* */ +/* Interface to Readline Completion */ +/* */ +/* **************************************************************** */ + +/* Tell the GNU Readline library how to complete. We want to try to complete + on command names if this is the first word in the line, or on filenames + if not. */ +initialize_readline () +{ + char **fileman_completion (); + + /* Allow conditional parsing of the ~/.inputrc file. */ + rl_readline_name = "FileMan"; + + /* Tell the completer that we want a crack first. */ + rl_attempted_completion_function = (Function *)fileman_completion; +} + +/* Attempt to complete on the contents of TEXT. START and END show the + region of TEXT that contains the word to complete. We can use the + entire line in case we want to do some simple parsing. Return the + array of matches, or NULL if there aren't any. */ +char ** +fileman_completion (text, start, end) + char *text; + int start, end; +{ + char **matches; + char *command_generator (); + + matches = (char **)NULL; + + /* If this word is at the start of the line, then it is a command + to complete. Otherwise it is the name of a file in the current + directory. */ + if (start == 0) + matches = completion_matches (text, command_generator); + + return (matches); +} + +/* Generator function for command completion. STATE lets us know whether + to start from scratch; without any state (i.e. STATE == 0), then we + start at the top of the list. */ +char * +command_generator (text, state) + char *text; + int state; +{ + static int list_index, len; + char *name; + + /* If this is a new word to complete, initialize now. This includes + saving the length of TEXT for efficiency, and initializing the index + variable to 0. */ + if (!state) + { + list_index = 0; + len = strlen (text); + } + + /* Return the next name which partially matches from the command list. */ + while (name = commands[list_index].name) + { + list_index++; + + if (strncmp (name, text, len) == 0) + return (name); + } + + /* If no names matched, then return NULL. */ + return ((char *)NULL); +} + +/* **************************************************************** */ +/* */ +/* FileMan Commands */ +/* */ +/* **************************************************************** */ + +/* String to pass to system (). This is for the LIST, VIEW and RENAME + commands. */ +static char syscom[1024]; + +/* List the file(s) named in arg. */ +com_list (arg) + char *arg; +{ + sprintf (syscom, "ls -FClg %s", arg); + system (syscom); +} + +com_view (arg) + char *arg; +{ + if (!valid_argument ("view", arg)) + return; + + sprintf (syscom, "cat %s | more", arg); + system (syscom); +} + +com_rename (arg) + char *arg; +{ + too_dangerous ("rename"); +} + +com_stat (arg) + char *arg; +{ + struct stat finfo; + + if (!valid_argument ("stat", arg)) + return; + + if (stat (arg, &finfo) == -1) + { + perror (arg); + return; + } + + printf ("Statistics for `%s':\n", arg); + + printf ("%s has %d link%s, and is %d bytes in length.\n", arg, + finfo.st_nlink, (finfo.st_nlink == 1) ? "" : "s", finfo.st_size); + printf (" Created on: %s", ctime (&finfo.st_ctime)); + printf (" Last access at: %s", ctime (&finfo.st_atime)); + printf ("Last modified at: %s", ctime (&finfo.st_mtime)); +} + +com_delete (arg) + char *arg; +{ + too_dangerous ("delete"); +} + +/* Print out help for ARG, or for all of the commands if ARG is + not present. */ +com_help (arg) + char *arg; +{ + register int i; + int printed = 0; + + for (i = 0; commands[i].name; i++) + { + if (!*arg || (strcmp (arg, commands[i].name) == 0)) + { + printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc); + printed++; + } + } + + if (!printed) + { + printf ("No commands match `%s'. Possibilties are:\n", arg); + + for (i = 0; commands[i].name; i++) + { + /* Print in six columns. */ + if (printed == 6) + { + printed = 0; + printf ("\n"); + } + + printf ("%s\t", commands[i].name); + printed++; + } + + if (printed) + printf ("\n"); + } +} + +/* Change to the directory ARG. */ +com_cd (arg) + char *arg; +{ + if (chdir (arg) == -1) + perror (arg); + + com_pwd (""); +} + +/* Print out the current working directory. */ +com_pwd (ignore) + char *ignore; +{ + char dir[1024]; + + (void) getwd (dir); + + printf ("Current directory is %s\n", dir); +} + +/* The user wishes to quit using this program. Just set DONE non-zero. */ +com_quit (arg) + char *arg; +{ + done = 1; +} + +/* Function which tells you that you can't do this. */ +too_dangerous (caller) + char *caller; +{ + fprintf (stderr, + "%s: Too dangerous for me to distribute. Write it yourself.\n", + caller); +} + +/* Return non-zero if ARG is a valid argument for CALLER, else print + an error message and return zero. */ +int +valid_argument (caller, arg) + char *caller, *arg; +{ + if (!arg || !*arg) + { + fprintf (stderr, "%s: Argument required.\n", caller); + return (0); + } + + return (1); +} + + +/* + * Local variables: + * compile-command: "cc -g -I../.. -L.. -o fileman fileman.c -lreadline -ltermcap" + * end: + */ diff --git a/gnu/lib/libreadline/examples/manexamp.c b/gnu/lib/libreadline/examples/manexamp.c new file mode 100644 index 0000000..b7ec96a --- /dev/null +++ b/gnu/lib/libreadline/examples/manexamp.c @@ -0,0 +1,96 @@ +/* manexamp.c -- The examples which appear in the documentation are here. */ + +#include +#include + + +/* **************************************************************** */ +/* */ +* How to Emulate gets () */ +/* */ +/* **************************************************************** */ + +/* A static variable for holding the line. */ +static char *line_read = (char *)NULL; + +/* Read a string, and return a pointer to it. Returns NULL on EOF. */ +char * +do_gets () +{ + /* If the buffer has already been allocated, return the memory + to the free pool. */ + if (line_read != (char *)NULL) + { + free (line_read); + line_read = (char *)NULL; + } + + /* Get a line from the user. */ + line_read = readline (""); + + /* If the line has any text in it, save it on the history. */ + if (line_read && *line_read) + add_history (line_read); + + return (line_read); +} + + +/* **************************************************************** */ +/* */ +/* Writing a Function to be Called by Readline. */ +/* */ +/* **************************************************************** */ + +/* Invert the case of the COUNT following characters. */ +invert_case_line (count, key) + int count, key; +{ + register int start, end; + + start = rl_point; + + if (count < 0) + { + direction = -1; + count = -count; + } + else + direction = 1; + + /* Find the end of the range to modify. */ + end = start + (count * direction); + + /* Force it to be within range. */ + if (end > rl_end) + end = rl_end; + else if (end < 0) + end = -1; + + if (start > end) + { + int temp = start; + start = end; + end = temp; + } + + if (start == end) + return; + + /* Tell readline that we are modifying the line, so save the undo + information. */ + rl_modifying (start, end); + + for (; start != end; start += direction) + { + if (uppercase_p (rl_line_buffer[start])) + rl_line_buffer[start] = to_lower (rl_line_buffer[start]); + else if (lowercase_p (rl_line_buffer[start])) + rl_line_buffer[start] = to_upper (rl_line_buffer[start]); + } + + /* Move point to on top of the last character changed. */ + rl_point = end - direction; +} + + diff --git a/gnu/lib/libreadline/funmap.c b/gnu/lib/libreadline/funmap.c new file mode 100644 index 0000000..77fad75 --- /dev/null +++ b/gnu/lib/libreadline/funmap.c @@ -0,0 +1,214 @@ +/* funmap.c -- attach names to functions. */ + +/* Copyright (C) 1988,1989 Free Software Foundation, Inc. + + This file is part of GNU Readline, a library for reading lines + of text with interactive input and history editing. + + Readline is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 1, or (at your option) any + later version. + + Readline is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline; see the file COPYING. If not, write to the Free + Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define STATIC_MALLOC +#ifndef STATIC_MALLOC +extern char *xmalloc (), *xrealloc (); +#else +static char *xmalloc (), *xrealloc (); +#endif + +#ifndef BUFSIZ +#include +#endif /* BUFSIZ */ + +#include "readline.h" + +FUNMAP **funmap = (FUNMAP **)NULL; +static int funmap_size = 0; +static int funmap_entry = 0; + +static FUNMAP default_funmap[] = { + { "arrow-key-prefix", rl_arrow_keys }, + { "redraw-current-line", rl_refresh_line}, + { "beginning-of-line", rl_beg_of_line }, + { "backward-char", rl_backward }, + { "delete-char", rl_delete }, + { "end-of-line", rl_end_of_line }, + { "forward-char", rl_forward }, + { "accept-line", rl_newline }, + { "kill-line", rl_kill_line }, + { "clear-screen", rl_clear_screen }, + { "next-history", rl_get_next_history }, + { "previous-history", rl_get_previous_history }, + { "quoted-insert", rl_quoted_insert }, + { "reverse-search-history", rl_reverse_search_history }, + { "forward-search-history", rl_forward_search_history }, + { "transpose-chars", rl_transpose_chars }, + { "unix-line-discard", rl_unix_line_discard }, + { "unix-word-rubout", rl_unix_word_rubout }, + { "yank", rl_yank }, + { "yank-pop", rl_yank_pop }, + { "yank-nth-arg", rl_yank_nth_arg }, + { "backward-delete-char", rl_rubout }, + { "backward-word", rl_backward_word }, + { "kill-word", rl_kill_word }, + { "forward-word", rl_forward_word }, + { "tab-insert", rl_tab_insert }, + { "backward-kill-word", rl_backward_kill_word }, + { "backward-kill-line", rl_backward_kill_line }, + { "transpose-words", rl_transpose_words }, + { "digit-argument", rl_digit_argument }, + { "complete", rl_complete }, + { "possible-completions", rl_possible_completions }, + { "do-lowercase-version", rl_do_lowercase_version }, + { "digit-argument", rl_digit_argument }, + { "universal-argument", rl_universal_argument }, + { "abort", rl_abort }, + { "undo", rl_undo_command }, + { "upcase-word", rl_upcase_word }, + { "downcase-word", rl_downcase_word }, + { "capitalize-word", rl_capitalize_word }, + { "revert-line", rl_revert_line }, + { "beginning-of-history", rl_beginning_of_history }, + { "end-of-history", rl_end_of_history }, + { "self-insert", rl_insert }, + { "start-kbd-macro", rl_start_kbd_macro }, + { "end-kbd-macro", rl_end_kbd_macro }, + { "re-read-init-file", rl_re_read_init_file }, +#ifdef VI_MODE + { "vi-movement-mode", rl_vi_movement_mode }, + { "vi-insertion-mode", rl_vi_insertion_mode }, + { "vi-arg-digit", rl_vi_arg_digit }, + { "vi-prev-word", rl_vi_prev_word }, + { "vi-next-word", rl_vi_next_word }, + { "vi-char-search", rl_vi_char_search }, + { "vi-editing-mode", rl_vi_editing_mode }, + { "vi-eof-maybe", rl_vi_eof_maybe }, + { "vi-append-mode", rl_vi_append_mode }, + { "vi-put", rl_vi_put }, + { "vi-append-eol", rl_vi_append_eol }, + { "vi-insert-beg", rl_vi_insert_beg }, + { "vi-delete", rl_vi_delete }, + { "vi-comment", rl_vi_comment }, + { "vi-first-print", rl_vi_first_print }, + { "vi-fword", rl_vi_fword }, + { "vi-fWord", rl_vi_fWord }, + { "vi-bword", rl_vi_bword }, + { "vi-bWord", rl_vi_bWord }, + { "vi-eword", rl_vi_eword }, + { "vi-eWord", rl_vi_eWord }, + { "vi-end-word", rl_vi_end_word }, + { "vi-change-case", rl_vi_change_case }, + { "vi-match", rl_vi_match }, + { "vi-bracktype", rl_vi_bracktype }, + { "vi-change-char", rl_vi_change_char }, + { "vi-yank-arg", rl_vi_yank_arg }, + { "vi-search", rl_vi_search }, + { "vi-search-again", rl_vi_search_again }, + { "vi-dosearch", rl_vi_dosearch }, + { "vi-subst", rl_vi_subst }, + { "vi-overstrike", rl_vi_overstrike }, + { "vi-overstrike-delete", rl_vi_overstrike_delete }, + { "vi-replace, ", rl_vi_replace }, + { "vi-column", rl_vi_column }, + { "vi-delete-to", rl_vi_delete_to }, + { "vi-change-to", rl_vi_change_to }, + { "vi-yank-to", rl_vi_yank_to }, + { "vi-complete", rl_vi_complete }, +#endif /* VI_MODE */ + + {(char *)NULL, (Function *)NULL } +}; + +rl_add_funmap_entry (name, function) + char *name; + Function *function; +{ + if (funmap_entry + 2 >= funmap_size) + if (!funmap) + funmap = (FUNMAP **)xmalloc ((funmap_size = 80) * sizeof (FUNMAP *)); + else + funmap = + (FUNMAP **)xrealloc (funmap, (funmap_size += 80) * sizeof (FUNMAP *)); + + funmap[funmap_entry] = (FUNMAP *)xmalloc (sizeof (FUNMAP)); + funmap[funmap_entry]->name = name; + funmap[funmap_entry]->function = function; + + funmap[++funmap_entry] = (FUNMAP *)NULL; +} + +static int funmap_initialized = 0; + +/* Make the funmap contain all of the default entries. */ +rl_initialize_funmap () +{ + register int i; + + if (funmap_initialized) + return; + + for (i = 0; default_funmap[i].name; i++) + rl_add_funmap_entry (default_funmap[i].name, default_funmap[i].function); + + funmap_initialized = 1; +} + +/* Things that mean `Control'. */ +char *possible_control_prefixes[] = { + "Control-", "C-", "CTRL-", (char *)NULL +}; + +char *possible_meta_prefixes[] = { + "Meta", "M-", (char *)NULL +}; + +#ifdef STATIC_MALLOC + +/* **************************************************************** */ +/* */ +/* xmalloc and xrealloc () */ +/* */ +/* **************************************************************** */ + +static void memory_error_and_abort (); + +static char * +xmalloc (bytes) + int bytes; +{ + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + char *temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static void +memory_error_and_abort () +{ + fprintf (stderr, "history: Out of virtual memory!\n"); + abort (); +} +#endif /* STATIC_MALLOC */ diff --git a/gnu/lib/libreadline/history.c b/gnu/lib/libreadline/history.c new file mode 100644 index 0000000..97df610 --- /dev/null +++ b/gnu/lib/libreadline/history.c @@ -0,0 +1,1646 @@ +/* History.c -- standalone history library */ + +/* Copyright (C) 1989 Free Software Foundation, Inc. + + This file contains the GNU History Library (the Library), a set of + routines for managing the text of previously typed lines. + + The Library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + The Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* The goal is to make the implementation transparent, so that you + don't have to know what data types are used, just what functions + you can call. I think I have done that. */ + +/* Remove these declarations when we have a complete libgnu.a. */ +#define STATIC_MALLOC +#ifndef STATIC_MALLOC +extern char *xmalloc (), *xrealloc (); +#else +static char *xmalloc (), *xrealloc (); +#endif + +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#if defined (sparc) && defined (sun) +#include +#else +extern char *alloca (); +#endif +#endif + +#include "history.h" + +#ifndef savestring +#define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x)) +#endif + +#ifndef whitespace +#define whitespace(c) (((c) == ' ') || ((c) == '\t')) +#endif + +#ifndef digit +#define digit(c) ((c) >= '0' && (c) <= '9') +#endif + +#ifndef member +#define member(c, s) ((c) ? index ((s), (c)) : 0) +#endif + +/* **************************************************************** */ +/* */ +/* History Functions */ +/* */ +/* **************************************************************** */ + +/* An array of HIST_ENTRY. This is where we store the history. */ +static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL; + +/* Non-zero means that we have enforced a limit on the amount of + history that we save. */ +int history_stifled = 0; + +/* If HISTORY_STIFLED is non-zero, then this is the maximum number of + entries to remember. */ +int max_input_history; + +/* The current location of the interactive history pointer. Just makes + life easier for outside callers. */ +static int history_offset = 0; + +/* The number of strings currently stored in the input_history list. */ +int history_length = 0; + +/* The current number of slots allocated to the input_history. */ +static int history_size = 0; + +/* The number of slots to increase the_history by. */ +#define DEFAULT_HISTORY_GROW_SIZE 50 + +/* The character that represents the start of a history expansion + request. This is usually `!'. */ +char history_expansion_char = '!'; + +/* The character that invokes word substitution if found at the start of + a line. This is usually `^'. */ +char history_subst_char = '^'; + +/* During tokenization, if this character is seen as the first character + of a word, then it, and all subsequent characters upto a newline are + ignored. For a Bourne shell, this should be '#'. Bash special cases + the interactive comment character to not be a comment delimiter. */ +char history_comment_char = '\0'; + +/* The list of characters which inhibit the expansion of text if found + immediately following history_expansion_char. */ +char *history_no_expand_chars = " \t\n\r="; + +/* The logical `base' of the history array. It defaults to 1. */ +int history_base = 1; + +/* Begin a session in which the history functions might be used. This + initializes interactive variables. */ +void +using_history () +{ + history_offset = history_length; +} + +/* Return the number of bytes that the primary history entries are using. + This just adds up the lengths of the_history->lines. */ +int +history_total_bytes () +{ + register int i, result; + + for (i = 0; the_history && the_history[i]; i++) + result += strlen (the_history[i]->line); + + return (result); +} + +/* Place STRING at the end of the history list. The data field + is set to NULL. */ +void +add_history (string) + char *string; +{ + HIST_ENTRY *temp; + + if (history_stifled && (history_length == max_input_history)) + { + register int i; + + /* If the history is stifled, and history_length is zero, + and it equals max_input_history, we don't save items. */ + if (!history_length) + return; + + /* If there is something in the slot, then remove it. */ + if (the_history[0]) + { + free (the_history[0]->line); + free (the_history[0]); + } + + for (i = 0; i < history_length; i++) + the_history[i] = the_history[i + 1]; + + history_base++; + + } + else + { + if (!history_size) + { + the_history = (HIST_ENTRY **) + xmalloc ((history_size = DEFAULT_HISTORY_GROW_SIZE) + * sizeof (HIST_ENTRY *)); + history_length = 1; + + } + else + { + if (history_length == (history_size - 1)) + { + the_history = (HIST_ENTRY **) + xrealloc (the_history, + ((history_size += DEFAULT_HISTORY_GROW_SIZE) + * sizeof (HIST_ENTRY *))); + } + history_length++; + } + } + + temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); + temp->line = savestring (string); + temp->data = (char *)NULL; + + the_history[history_length] = (HIST_ENTRY *)NULL; + the_history[history_length - 1] = temp; +} + +/* Make the history entry at WHICH have LINE and DATA. This returns + the old entry so you can dispose of the data. In the case of an + invalid WHICH, a NULL pointer is returned. */ +HIST_ENTRY * +replace_history_entry (which, line, data) + int which; + char *line; + char *data; +{ + HIST_ENTRY *temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); + HIST_ENTRY *old_value; + + if (which >= history_length) + return ((HIST_ENTRY *)NULL); + + old_value = the_history[which]; + + temp->line = savestring (line); + temp->data = data; + the_history[which] = temp; + + return (old_value); +} + +/* Returns the magic number which says what history element we are + looking at now. In this implementation, it returns history_offset. */ +int +where_history () +{ + return (history_offset); +} + +/* Search the history for STRING, starting at history_offset. + If DIRECTION < 0, then the search is through previous entries, + else through subsequent. If the string is found, then + current_history () is the history entry, and the value of this function + is the offset in the line of that history entry that the string was + found in. Otherwise, nothing is changed, and a -1 is returned. */ +int +history_search (string, direction) + char *string; + int direction; +{ + register int i = history_offset; + register int reverse = (direction < 0); + register char *line; + register int index; + int string_len = strlen (string); + + /* Take care of trivial cases first. */ + + if (!history_length || ((i == history_length) && !reverse)) + return (-1); + + if (reverse && (i == history_length)) + i--; + + while (1) + { + /* Search each line in the history list for STRING. */ + + /* At limit for direction? */ + if ((reverse && i < 0) || + (!reverse && i == history_length)) + return (-1); + + line = the_history[i]->line; + index = strlen (line); + + /* If STRING is longer than line, no match. */ + if (string_len > index) + goto next_line; + + /* Do the actual search. */ + if (reverse) + { + index -= string_len; + + while (index >= 0) + { + if (strncmp (string, line + index, string_len) == 0) + { + history_offset = i; + return (index); + } + index--; + } + } + else + { + register int limit = (string_len - index) + 1; + index = 0; + + while (index < limit) + { + if (strncmp (string, line + index, string_len) == 0) + { + history_offset = i; + return (index); + } + index++; + } + } + next_line: + if (reverse) + i--; + else + i++; + } +} + +/* Remove history element WHICH from the history. The removed + element is returned to you so you can free the line, data, + and containing structure. */ +HIST_ENTRY * +remove_history (which) + int which; +{ + HIST_ENTRY *return_value; + + if (which >= history_length || !history_length) + return_value = (HIST_ENTRY *)NULL; + else + { + register int i; + return_value = the_history[which]; + + for (i = which; i < history_length; i++) + the_history[i] = the_history[i + 1]; + + history_length--; + } + + return (return_value); +} + +/* Stifle the history list, remembering only MAX number of lines. */ +void +stifle_history (max) + int max; +{ + if (history_length > max) + { + register int i, j; + + /* This loses because we cannot free the data. */ + for (i = 0; i < (history_length - max); i++) + { + free (the_history[i]->line); + free (the_history[i]); + } + history_base = i; + for (j = 0, i = history_length - max; j < max; i++, j++) + the_history[j] = the_history[i]; + the_history[j] = (HIST_ENTRY *)NULL; + history_length = j; + } + history_stifled = 1; + max_input_history = max; +} + +/* Stop stifling the history. This returns the previous amount the history + was stifled by. The value is positive if the history was stifled, negative + if it wasn't. */ +int +unstifle_history () +{ + int result = max_input_history; + if (history_stifled) + { + result = - result; + history_stifled = 0; + } + return (result); +} + +/* Return the string that should be used in the place of this + filename. This only matters when you don't specify the + filename to read_history (), or write_history (). */ +static char * +history_filename (filename) + char *filename; +{ + char *return_val = filename ? savestring (filename) : (char *)NULL; + + if (!return_val) + { + char *home = (char *)getenv ("HOME"); + if (!home) home = "."; + return_val = (char *)xmalloc (2 + strlen (home) + strlen (".history")); + sprintf (return_val, "%s/.history", home); + } + return (return_val); +} + +/* Add the contents of FILENAME to the history list, a line at a time. + If FILENAME is NULL, then read from ~/.history. Returns 0 if + successful, or errno if not. */ +int +read_history (filename) + char *filename; +{ + return (read_history_range (filename, 0, -1)); +} + +/* Read a range of lines from FILENAME, adding them to the history list. + Start reading at the FROM'th line and end at the TO'th. If FROM + is zero, start at the beginning. If TO is less than FROM, read + until the end of the file. If FILENAME is NULL, then read from + ~/.history. Returns 0 if successful, or errno if not. */ +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, done; + struct stat finfo; + extern int errno; + + input = history_filename (filename); + file = open (input, O_RDONLY, 0666); + + if ((file < 0) || + (stat (input, &finfo) == -1)) + goto error_and_exit; + + buffer = (char *)xmalloc (finfo.st_size + 1); + + if (read (file, buffer, finfo.st_size) != finfo.st_size) + error_and_exit: + { + if (file >= 0) + close (file); + + if (buffer) + free (buffer); + + return (errno); + } + + close (file); + + /* Set TO to larger than end of file if negative. */ + if (to < 0) + to = finfo.st_size; + + /* 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 < finfo.st_size && current_line < from) + { + for (line_end = line_start; line_end < finfo.st_size; 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 < finfo.st_size; 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; + } + return (0); +} + +/* Truncate the history file FNAME, leaving only LINES trailing lines. + If FNAME is NULL, then use ~/.history. */ +history_truncate_file (fname, lines) + char *fname; + register int lines; +{ + register int i; + int file; + char *buffer = (char *)NULL, *filename; + struct stat finfo; + + filename = history_filename (fname); + if (stat (filename, &finfo) == -1) + goto truncate_exit; + + file = open (filename, O_RDONLY, 066); + + if (file == -1) + goto truncate_exit; + + buffer = (char *)xmalloc (finfo.st_size + 1); + read (file, buffer, finfo.st_size); + close (file); + + /* Count backwards from the end of buffer until we have passed + LINES lines. */ + for (i = finfo.st_size; lines && i; i--) + { + if (buffer[i] == '\n') + lines--; + } + + /* If there are fewer lines in the file than we want to truncate to, + then we are all done. */ + if (!i) + goto truncate_exit; + + /* Otherwise, write from the start of this line until the end of the + buffer. */ + for (--i; i; i--) + if (buffer[i] == '\n') + { + i++; + break; + } + + file = open (filename, O_WRONLY | O_TRUNC | O_CREAT, 0666); + if (file == -1) + goto truncate_exit; + + write (file, buffer + i, finfo.st_size - i); + close (file); + + truncate_exit: + if (buffer) + free (buffer); + + free (filename); +} + +#define HISTORY_APPEND 0 +#define HISTORY_OVERWRITE 1 + +/* Workhorse function for writing history. Writes NELEMENT entries + from the history list to FILENAME. OVERWRITE is non-zero if you + wish to replace FILENAME with the entries. */ +static int +history_do_write (filename, nelements, overwrite) + char *filename; + int nelements, overwrite; +{ + extern int errno; + register int i; + char *output = history_filename (filename); + int file, mode; + char cr = '\n'; + + if (overwrite) + mode = O_WRONLY | O_CREAT | O_TRUNC; + else + mode = O_WRONLY | O_APPEND; + + if ((file = open (output, mode, 0666)) == -1) + return (errno); + + if (nelements > history_length) + nelements = history_length; + + for (i = history_length - nelements; i < history_length; i++) + { + if (write (file, the_history[i]->line, strlen (the_history[i]->line)) < 0) + break; + if (write (file, &cr, 1) < 0) + break; + } + + close (file); + return (0); +} + +/* Append NELEMENT entries to FILENAME. The entries appended are from + the end of the list minus NELEMENTs up to the end of the list. */ +int +append_history (nelements, filename) + int nelements; + char *filename; +{ + return (history_do_write (filename, nelements, HISTORY_APPEND)); +} + +/* Overwrite FILENAME with the current history. If FILENAME is NULL, + then write the history list to ~/.history. Values returned + are as in read_history ().*/ +int +write_history (filename) + char *filename; +{ + return (history_do_write (filename, history_length, HISTORY_OVERWRITE)); +} + +/* Return the history entry at the current position, as determined by + history_offset. If there is no entry there, return a NULL pointer. */ +HIST_ENTRY * +current_history () +{ + if ((history_offset == history_length) || !the_history) + return ((HIST_ENTRY *)NULL); + else + return (the_history[history_offset]); +} + +/* Back up history_offset to the previous history entry, and return + a pointer to that entry. If there is no previous entry then return + a NULL pointer. */ +HIST_ENTRY * +previous_history () +{ + if (!history_offset) + return ((HIST_ENTRY *)NULL); + else + return (the_history[--history_offset]); +} + +/* Move history_offset forward to the next history entry, and return + a pointer to that entry. If there is no next entry then return a + NULL pointer. */ +HIST_ENTRY * +next_history () +{ + if (history_offset == history_length) + return ((HIST_ENTRY *)NULL); + else + return (the_history[++history_offset]); +} + +/* Return the current history array. The caller has to be carefull, since this + is the actual array of data, and could be bashed or made corrupt easily. + The array is terminated with a NULL pointer. */ +HIST_ENTRY ** +history_list () +{ + return (the_history); +} + +/* Return the history entry which is logically at OFFSET in the history array. + OFFSET is relative to history_base. */ +HIST_ENTRY * +history_get (offset) + int offset; +{ + int index = offset - history_base; + + if (index >= history_length || + index < 0 || + !the_history) + return ((HIST_ENTRY *)NULL); + return (the_history[index]); +} + +/* Search for STRING in the history list. DIR is < 0 for searching + backwards. POS is an absolute index into the history list at + which point to begin searching. */ +int +history_search_pos (string, dir, pos) + char *string; + int dir, pos; +{ + int ret, old = where_history (); + history_set_pos (pos); + if (history_search (string, dir) == -1) + { + history_set_pos (old); + return (-1); + } + ret = where_history (); + history_set_pos (old); + return ret; +} + +/* Make the current history item be the one at POS, an absolute index. + Returns zero if POS is out of range, else non-zero. */ +int +history_set_pos (pos) + int pos; +{ + if (pos > history_length || pos < 0 || !the_history) + return (0); + history_offset = pos; + return (1); +} + + +/* **************************************************************** */ +/* */ +/* History Expansion */ +/* */ +/* **************************************************************** */ + +/* Hairy history expansion on text, not tokens. This is of general + use, and thus belongs in this library. */ + +/* The last string searched for in a !?string? search. */ +static char *search_string = (char *)NULL; + +/* Return the event specified at TEXT + OFFSET modifying OFFSET to + point to after the event specifier. Just a pointer to the history + line is returned; NULL is returned in the event of a bad specifier. + You pass STRING with *INDEX equal to the history_expansion_char that + begins this specification. + DELIMITING_QUOTE is a character that is allowed to end the string + specification for what to search for in addition to the normal + characters `:', ` ', `\t', `\n', and sometimes `?'. + So you might call this function like: + line = get_history_event ("!echo:p", &index, 0); */ +char * +get_history_event (string, caller_index, delimiting_quote) + char *string; + int *caller_index; + int delimiting_quote; +{ + register int i = *caller_index; + int which, sign = 1; + HIST_ENTRY *entry; + + /* The event can be specified in a number of ways. + + !! the previous command + !n command line N + !-n current command-line minus N + !str the most recent command starting with STR + !?str[?] + the most recent command containing STR + + All values N are determined via HISTORY_BASE. */ + + if (string[i] != history_expansion_char) + return ((char *)NULL); + + /* Move on to the specification. */ + i++; + + /* Handle !! case. */ + if (string[i] == history_expansion_char) + { + i++; + which = history_base + (history_length - 1); + *caller_index = i; + goto get_which; + } + + /* Hack case of numeric line specification. */ + read_which: + if (string[i] == '-') + { + sign = -1; + i++; + } + + if (digit (string[i])) + { + int start = i; + + /* Get the extent of the digits. */ + for (; digit (string[i]); i++); + + /* Get the digit value. */ + sscanf (string + start, "%d", &which); + + *caller_index = i; + + if (sign < 0) + which = (history_length + history_base) - which; + + get_which: + if (entry = history_get (which)) + return (entry->line); + + return ((char *)NULL); + } + + /* This must be something to search for. If the spec begins with + a '?', then the string may be anywhere on the line. Otherwise, + the string must be found at the start of a line. */ + { + int index; + char *temp; + int substring_okay = 0; + + if (string[i] == '?') + { + substring_okay++; + i++; + } + + for (index = i; string[i]; i++) + if (whitespace (string[i]) || + string[i] == '\n' || + string[i] == ':' || + (substring_okay && string[i] == '?') || + string[i] == delimiting_quote) + break; + + temp = (char *)alloca (1 + (i - index)); + strncpy (temp, &string[index], (i - index)); + temp[i - index] = '\0'; + + if (string[i] == '?') + i++; + + *caller_index = i; + + search_again: + + index = history_search (temp, -1); + + if (index < 0) + search_lost: + { + history_offset = history_length; + return ((char *)NULL); + } + + if (index == 0 || substring_okay || + (strncmp (temp, the_history[history_offset]->line, + strlen (temp)) == 0)) + { + search_won: + entry = current_history (); + history_offset = history_length; + + /* If this was a substring search, then remember the string that + we matched for word substitution. */ + if (substring_okay) + { + if (search_string) + free (search_string); + search_string = savestring (temp); + } + + return (entry->line); + } + + if (history_offset) + history_offset--; + else + goto search_lost; + + goto search_again; + } +} + +/* Expand the string STRING, placing the result into OUTPUT, a pointer + to a string. Returns: + + 0) If no expansions took place (or, if the only change in + the text was the de-slashifying of the history expansion + character) + 1) If expansions did take place + -1) If there was an error in expansion. + + If an error ocurred in expansion, then OUTPUT contains a descriptive + error message. */ +int +history_expand (string, output) + char *string; + char **output; +{ + register int j, l = strlen (string); + int i, word_spec_error = 0; + int cc, modified = 0; + char *word_spec, *event; + int starting_index, only_printing = 0, substitute_globally = 0; + + char *get_history_word_specifier (), *rindex (); + + /* The output string, and its length. */ + int len = 0; + char *result = (char *)NULL; + + /* Used in add_string; */ + char *temp, tt[2], tbl[3]; + + /* Prepare the buffer for printing error messages. */ + result = (char *)xmalloc (len = 255); + + result[0] = tt[1] = tbl[2] = '\0'; + tbl[0] = '\\'; + tbl[1] = history_expansion_char; + + /* Grovel the string. Only backslash can quote the history escape + character. We also handle arg specifiers. */ + + /* Before we grovel forever, see if the history_expansion_char appears + anywhere within the text. */ + + /* The quick substitution character is a history expansion all right. That + is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact, + that is the substitution that we do. */ + if (string[0] == history_subst_char) + { + char *format_string = (char *)alloca (10 + strlen (string)); + + sprintf (format_string, "%c%c:s%s", + history_expansion_char, history_expansion_char, + string); + string = format_string; + l += 4; + goto grovel; + } + + /* If not quick substitution, still maybe have to do expansion. */ + + /* `!' followed by one of the characters in history_no_expand_chars + is NOT an expansion. */ + for (i = 0; string[i]; i++) + if (string[i] == history_expansion_char) + if (!string[i + 1] || member (string[i + 1], history_no_expand_chars)) + continue; + else + goto grovel; + + free (result); + *output = savestring (string); + return (0); + + grovel: + + for (i = j = 0; i < l; i++) + { + int tchar = string[i]; + if (tchar == history_expansion_char) + tchar = -3; + + switch (tchar) + { + case '\\': + if (string[i + 1] == history_expansion_char) + { + i++; + temp = tbl; + goto do_add; + } + else + goto add_char; + + /* case history_expansion_char: */ + case -3: + starting_index = i + 1; + cc = string[i + 1]; + + /* If the history_expansion_char is followed by one of the + characters in history_no_expand_chars, then it is not a + candidate for expansion of any kind. */ + if (member (cc, history_no_expand_chars)) + goto add_char; + + /* There is something that is listed as a `word specifier' in csh + documentation which means `the expanded text to this point'. + That is not a word specifier, it is an event specifier. */ + + if (cc == '#') + goto hack_pound_sign; + + /* If it is followed by something that starts a word specifier, + then !! is implied as the event specifier. */ + + if (member (cc, ":$*%^")) + { + char fake_s[3]; + int fake_i = 0; + i++; + fake_s[0] = fake_s[1] = history_expansion_char; + fake_s[2] = '\0'; + event = get_history_event (fake_s, &fake_i, 0); + } + else + { + int quoted_search_delimiter = 0; + + /* If the character before this `!' is a double or single + quote, then this expansion takes place inside of the + quoted string. If we have to search for some text ("!foo"), + allow the delimiter to end the search string. */ + if (i && (string[i - 1] == '\'' || string[i - 1] == '"')) + quoted_search_delimiter = string[i - 1]; + + event = get_history_event (string, &i, quoted_search_delimiter); + } + + if (!event) + event_not_found: + { + int l = 1 + (i - starting_index); + + temp = (char *)alloca (1 + l); + strncpy (temp, string + starting_index, l); + temp[l - 1] = 0; + sprintf (result, "%s: %s.", temp, + word_spec_error ? "Bad word specifier" : "Event not found"); + error_exit: + *output = result; + return (-1); + } + + /* If a word specifier is found, then do what that requires. */ + starting_index = i; + + word_spec = get_history_word_specifier (string, event, &i); + + /* There is no such thing as a `malformed word specifier'. However, + it is possible for a specifier that has no match. In that case, + we complain. */ + if (word_spec == (char *)-1) + bad_word_spec: + { + word_spec_error++; + goto event_not_found; + } + + /* If no word specifier, than the thing of interest was the event. */ + if (!word_spec) + temp = event; + else + { + temp = (char *)alloca (1 + strlen (word_spec)); + strcpy (temp, word_spec); + free (word_spec); + } + + /* Perhaps there are other modifiers involved. Do what they say. */ + + hack_specials: + + if (string[i] == ':') + { + char *tstr; + + switch (string[i + 1]) + { + /* :p means make this the last executed line. So we + return an error state after adding this line to the + history. */ + case 'p': + only_printing++; + goto next_special; + + /* :t discards all but the last part of the pathname. */ + case 't': + tstr = rindex (temp, '/'); + if (tstr) + temp = ++tstr; + goto next_special; + + /* :h discards the last part of a pathname. */ + case 'h': + tstr = rindex (temp, '/'); + if (tstr) + *tstr = '\0'; + goto next_special; + + /* :r discards the suffix. */ + case 'r': + tstr = rindex (temp, '.'); + if (tstr) + *tstr = '\0'; + goto next_special; + + /* :e discards everything but the suffix. */ + case 'e': + tstr = rindex (temp, '.'); + if (tstr) + temp = tstr; + goto next_special; + + /* :s/this/that substitutes `this' for `that'. */ + /* :gs/this/that substitutes `this' for `that' globally. */ + case 'g': + if (string[i + 2] == 's') + { + i++; + substitute_globally = 1; + goto substitute; + } + else + + case 's': + substitute: + { + char *this, *that, *new_event; + int delimiter = 0; + int si, l_this, l_that, l_temp = strlen (temp); + + if (i + 2 < strlen (string)) + delimiter = string[i + 2]; + + if (!delimiter) + break; + + i += 3; + + /* Get THIS. */ + for (si = i; string[si] && string[si] != delimiter; si++); + l_this = (si - i); + this = (char *)alloca (1 + l_this); + strncpy (this, string + i, l_this); + this[l_this] = '\0'; + + i = si; + if (string[si]) + i++; + + /* Get THAT. */ + for (si = i; string[si] && string[si] != delimiter; si++); + l_that = (si - i); + that = (char *)alloca (1 + l_that); + strncpy (that, string + i, l_that); + that[l_that] = '\0'; + + i = si; + if (string[si]) i++; + + /* Ignore impossible cases. */ + if (l_this > l_temp) + goto cant_substitute; + + /* Find the first occurrence of THIS in TEMP. */ + si = 0; + for (; (si + l_this) <= l_temp; si++) + if (strncmp (temp + si, this, l_this) == 0) + { + new_event = + (char *)alloca (1 + (l_that - l_this) + l_temp); + strncpy (new_event, temp, si); + strncpy (new_event + si, that, l_that); + strncpy (new_event + si + l_that, + temp + si + l_this, + l_temp - (si + l_this)); + new_event[(l_that - l_this) + l_temp] = '\0'; + temp = new_event; + + if (substitute_globally) + { + si += l_that; + l_temp = strlen (temp); + substitute_globally++; + continue; + } + + goto hack_specials; + } + + cant_substitute: + + if (substitute_globally > 1) + { + substitute_globally = 0; + goto hack_specials; + } + + goto event_not_found; + } + + /* :# is the line so far. Note that we have to + alloca () it since RESULT could be realloc ()'ed + below in add_string. */ + case '#': + hack_pound_sign: + if (result) + { + temp = (char *)alloca (1 + strlen (result)); + strcpy (temp, result); + } + else + temp = ""; + + next_special: + i += 2; + goto hack_specials; + } + + } + /* Believe it or not, we have to back the pointer up by one. */ + --i; + goto add_string; + + /* A regular character. Just add it to the output string. */ + default: + add_char: + tt[0] = string[i]; + temp = tt; + goto do_add; + + add_string: + modified++; + + do_add: + j += strlen (temp); + while (j > len) + result = (char *)xrealloc (result, (len += 255)); + + strcpy (result + (j - strlen (temp)), temp); + } + } + + *output = result; + + if (only_printing) + { + add_history (result); + return (-1); + } + + return (modified != 0); +} + +/* Return a consed string which is the word specified in SPEC, and found + in FROM. NULL is returned if there is no spec. -1 is returned if + the word specified cannot be found. CALLER_INDEX is the offset in + SPEC to start looking; it is updated to point to just after the last + character parsed. */ +char * +get_history_word_specifier (spec, from, caller_index) + char *spec, *from; + int *caller_index; +{ + register int i = *caller_index; + int first, last; + int expecting_word_spec = 0; + char *history_arg_extract (); + + /* The range of words to return doesn't exist yet. */ + first = last = 0; + + /* If we found a colon, then this *must* be a word specification. If + it isn't, then it is an error. */ + if (spec[i] == ':') + i++, expecting_word_spec++; + + /* Handle special cases first. */ + + /* `%' is the word last searched for. */ + if (spec[i] == '%') + { + *caller_index = i + 1; + if (search_string) + return (savestring (search_string)); + else + return (savestring ("")); + } + + /* `*' matches all of the arguments, but not the command. */ + if (spec[i] == '*') + { + char *star_result; + + *caller_index = i + 1; + star_result = history_arg_extract (1, '$', from); + + if (!star_result) + star_result = savestring (""); + + return (star_result); + } + + /* `$' is last arg. */ + if (spec[i] == '$') + { + *caller_index = i + 1; + return (history_arg_extract ('$', '$', from)); + } + + /* Try to get FIRST and LAST figured out. */ + if (spec[i] == '-' || spec[i] == '^') + { + first = 1; + goto get_last; + } + + get_first: + if (digit (spec[i]) && expecting_word_spec) + { + sscanf (spec + i, "%d", &first); + for (; digit (spec[i]); i++); + } + else + return ((char *)NULL); + + get_last: + if (spec[i] == '^') + { + i++; + last = 1; + goto get_args; + } + + if (spec[i] != '-') + { + last = first; + goto get_args; + } + + i++; + + if (digit (spec[i])) + { + sscanf (spec + i, "%d", &last); + for (; digit (spec[i]); i++); + } + else + if (spec[i] == '$') + { + i++; + last = '$'; + } + + get_args: + { + char *result = (char *)NULL; + + *caller_index = i; + + if (last >= first) + result = history_arg_extract (first, last, from); + + if (result) + return (result); + else + return ((char *)-1); + } +} + +/* Extract the args specified, starting at FIRST, and ending at LAST. + The args are taken from STRING. If either FIRST or LAST is < 0, + then make that arg count from the right (subtract from the number of + tokens, so that FIRST = -1 means the next to last token on the line). */ +char * +history_arg_extract (first, last, string) + int first, last; + char *string; +{ + register int i, len; + char *result = (char *)NULL; + int size = 0, offset = 0; + + char **history_tokenize (), **list; + + if (!(list = history_tokenize (string))) + return ((char *)NULL); + + for (len = 0; list[len]; len++); + + if (last < 0) + last = len + last - 1; + + if (first < 0) + first = len + first - 1; + + if (last == '$') + last = len - 1; + + if (first == '$') + first = len - 1; + + last++; + + if (first > len || last > len || first < 0 || last < 0) + result = ((char *)NULL); + else + { + for (i = first; i < last; i++) + { + int l = strlen (list[i]); + + if (!result) + result = (char *)xmalloc ((size = (2 + l))); + else + result = (char *)xrealloc (result, (size += (2 + l))); + strcpy (result + offset, list[i]); + offset += l; + if (i + 1 < last) + { + strcpy (result + offset, " "); + offset++; + } + } + } + + for (i = 0; i < len; i++) + free (list[i]); + + free (list); + + return (result); +} + +#define slashify_in_quotes "\\`\"$" + +/* Return an array of tokens, much as the shell might. The tokens are + parsed out of STRING. */ +char ** +history_tokenize (string) + char *string; +{ + char **result = (char **)NULL; + register int i, start, result_index, size; + int len; + + i = result_index = size = 0; + + /* Get a token, and stuff it into RESULT. The tokens are split + exactly where the shell would split them. */ + get_token: + + /* Skip leading whitespace. */ + for (; string[i] && whitespace(string[i]); i++); + + start = i; + + if (!string[i] || string[i] == history_comment_char) + return (result); + + if (member (string[i], "()\n")) { + i++; + goto got_token; + } + + if (member (string[i], "<>;&|")) { + int peek = string[i + 1]; + + if (peek == string[i]) { + if (peek == '<') { + if (string[1 + 2] == '-') + i++; + i += 2; + goto got_token; + } + + if (member (peek, ">:&|")) { + i += 2; + goto got_token; + } + } else { + if ((peek == '&' && + (string[i] == '>' || string[i] == '<')) || + ((peek == '>') && + (string[i] == '&'))) { + i += 2; + goto got_token; + } + } + i++; + goto got_token; + } + + /* Get word from string + i; */ + { + int delimiter = 0; + + if (member (string[i], "\"'`")) + delimiter = string[i++]; + + for (;string[i]; i++) { + + if (string[i] == '\\') { + + if (string[i + 1] == '\n') { + i++; + continue; + } else { + if (delimiter != '\'') + if ((delimiter != '"') || + (member (string[i], slashify_in_quotes))) { + i++; + continue; + } + } + } + + if (delimiter && string[i] == delimiter) { + delimiter = 0; + continue; + } + + if (!delimiter && (member (string[i], " \t\n;&()|<>"))) + goto got_token; + + if (!delimiter && member (string[i], "\"'`")) { + delimiter = string[i]; + continue; + } + } + got_token: + + len = i - start; + if (result_index + 2 >= size) { + if (!size) + result = (char **)xmalloc ((size = 10) * (sizeof (char *))); + else + result = + (char **)xrealloc (result, ((size += 10) * (sizeof (char *)))); + } + result[result_index] = (char *)xmalloc (1 + len); + strncpy (result[result_index], string + start, len); + result[result_index][len] = '\0'; + result_index++; + result[result_index] = (char *)NULL; + } + if (string[i]) + goto get_token; + + return (result); +} + +#if defined (STATIC_MALLOC) + +/* **************************************************************** */ +/* */ +/* xmalloc and xrealloc () */ +/* */ +/* **************************************************************** */ + +static void memory_error_and_abort (); + +static char * +xmalloc (bytes) + int bytes; +{ + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + char *temp; + + if (!pointer) + temp = (char *)xmalloc (bytes); + else + temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + + return (temp); +} + +static void +memory_error_and_abort () +{ + fprintf (stderr, "history: Out of virtual memory!\n"); + abort (); +} +#endif /* STATIC_MALLOC */ + + +/* **************************************************************** */ +/* */ +/* Test Code */ +/* */ +/* **************************************************************** */ +#ifdef TEST +main () +{ + char line[1024], *t; + int done = 0; + + line[0] = 0; + + while (!done) + { + fprintf (stdout, "history%% "); + t = gets (line); + + if (!t) + strcpy (line, "quit"); + + if (line[0]) + { + char *expansion; + int result; + + using_history (); + + result = history_expand (line, &expansion); + strcpy (line, expansion); + free (expansion); + if (result) + fprintf (stderr, "%s\n", line); + + if (result < 0) + continue; + + add_history (line); + } + + if (strcmp (line, "quit") == 0) done = 1; + if (strcmp (line, "save") == 0) write_history (0); + if (strcmp (line, "read") == 0) read_history (0); + if (strcmp (line, "list") == 0) + { + register HIST_ENTRY **the_list = history_list (); + register int i; + + if (the_list) + for (i = 0; the_list[i]; i++) + fprintf (stdout, "%d: %s\n", i + history_base, the_list[i]->line); + } + if (strncmp (line, "delete", strlen ("delete")) == 0) + { + int which; + if ((sscanf (line + strlen ("delete"), "%d", &which)) == 1) + { + HIST_ENTRY *entry = remove_history (which); + if (!entry) + fprintf (stderr, "No such entry %d\n", which); + else + { + free (entry->line); + free (entry); + } + } + else + { + fprintf (stderr, "non-numeric arg given to `delete'\n"); + } + } + } +} + +#endif /* TEST */ + +/* +* Local variables: +* compile-command: "gcc -g -DTEST -o history history.c" +* end: +*/ diff --git a/gnu/lib/libreadline/keymaps.c b/gnu/lib/libreadline/keymaps.c new file mode 100644 index 0000000..5e8ee24 --- /dev/null +++ b/gnu/lib/libreadline/keymaps.c @@ -0,0 +1,173 @@ +/* keymaps.c -- Functions and keymaps for the GNU Readline library. */ + +/* Copyright (C) 1988,1989 Free Software Foundation, Inc. + + This file is part of GNU Readline, a library for reading lines + of text with interactive input and history editing. + + Readline is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 1, or (at your option) any + later version. + + Readline is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline; see the file COPYING. If not, write to the Free + Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "keymaps.h" +#include "emacs_keymap.c" + +#ifdef VI_MODE +#include "vi_keymap.c" +#endif + +/* Remove these declarations when we have a complete libgnu.a. */ +#define STATIC_MALLOC +#ifndef STATIC_MALLOC +extern char *xmalloc (), *xrealloc (); +#else +static char *xmalloc (), *xrealloc (); +#endif + +/* **************************************************************** */ +/* */ +/* Functions for manipulating Keymaps. */ +/* */ +/* **************************************************************** */ + + +/* Return a new, empty keymap. + Free it with free() when you are done. */ +Keymap +rl_make_bare_keymap () +{ + register int i; + Keymap keymap = (Keymap)xmalloc (128 * sizeof (KEYMAP_ENTRY)); + + for (i = 0; i < 128; i++) + { + keymap[i].type = ISFUNC; + keymap[i].function = (Function *)NULL; + } + + for (i = 'A'; i < ('Z' + 1); i++) + { + keymap[i].type = ISFUNC; + keymap[i].function = rl_do_lowercase_version; + } + + return (keymap); +} + +/* Return a new keymap which is a copy of MAP. */ +Keymap +rl_copy_keymap (map) + Keymap map; +{ + register int i; + Keymap temp = rl_make_bare_keymap (); + + for (i = 0; i < 128; i++) + { + temp[i].type = map[i].type; + temp[i].function = map[i].function; + } + return (temp); +} + +/* Return a new keymap with the printing characters bound to rl_insert, + the uppercase Meta characters bound to run their lowercase equivalents, + and the Meta digits bound to produce numeric arguments. */ +Keymap +rl_make_keymap () +{ + extern rl_insert (), rl_rubout (), rl_do_lowercase_version (); + extern rl_digit_argument (); + register int i; + Keymap newmap; + + newmap = rl_make_bare_keymap (); + + /* All printing characters are self-inserting. */ + for (i = ' '; i < 126; i++) + newmap[i].function = rl_insert; + + newmap[TAB].function = rl_insert; + newmap[RUBOUT].function = rl_rubout; + newmap[CTRL('H')].function = rl_rubout; + + return (newmap); +} + +/* Free the storage associated with MAP. */ +rl_discard_keymap (map) + Keymap (map); +{ + int i; + + if (!map) + return; + + for (i = 0; i < 128; i++) + { + switch (map[i].type) + { + case ISFUNC: + break; + + case ISKMAP: + rl_discard_keymap ((Keymap)map[i].function); + break; + + case ISMACR: + free ((char *)map[i].function); + break; + } + } +} + +#ifdef STATIC_MALLOC + +/* **************************************************************** */ +/* */ +/* xmalloc and xrealloc () */ +/* */ +/* **************************************************************** */ + +static void memory_error_and_abort (); + +static char * +xmalloc (bytes) + int bytes; +{ + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + char *temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static void +memory_error_and_abort () +{ + fprintf (stderr, "readline: Out of virtual memory!\n"); + abort (); +} +#endif /* STATIC_MALLOC */ diff --git a/gnu/lib/libreadline/readline.c b/gnu/lib/libreadline/readline.c new file mode 100644 index 0000000..7222e37 --- /dev/null +++ b/gnu/lib/libreadline/readline.c @@ -0,0 +1,5927 @@ +/* readline.c -- a general facility for reading lines of input + with emacs style editing and completion. */ + +/* Copyright (C) 1987,1989 Free Software Foundation, Inc. + + This file contains the Readline Library (the Library), a set of + routines for providing Emacs style line input to programs that ask + for it. + + The Library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + The Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Remove these declarations when we have a complete libgnu.a. */ +#define STATIC_MALLOC +#ifndef STATIC_MALLOC +extern char *xmalloc (), *xrealloc (); +#else +static char *xmalloc (), *xrealloc (); +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#if defined (sparc) && defined (sun) +#include +#endif +#endif + +#if defined (HAVE_UNISTD_H) +#include +#endif + +#define NEW_TTY_DRIVER +#define HAVE_BSD_SIGNALS + +/* Some USG machines have BSD signal handling (sigblock, sigsetmask, etc.) */ +#if defined (USG) && !defined (hpux) +#undef HAVE_BSD_SIGNALS +#endif + +/* System V machines use termio. */ +#if !defined (_POSIX_VERSION) +#if defined (USG) || defined (hpux) || defined (Xenix) || defined (sgi) || defined (DGUX) +#undef NEW_TTY_DRIVER +#include + +#if !defined (TCOON) +#define TCOON 1 +#endif + +#endif /* USG | hpux | Xenix sgi | DUGX */ +#endif /* !_POSIX_VERSION */ + +/* Posix systems use termios. */ +#if defined (_POSIX_VERSION) +#undef NEW_TTY_DRIVER +#include +#if !defined (O_NDELAY) +#define O_NDELAY O_NONBLOCK /* Posix-style non-blocking i/o */ +#endif /* O_NDELAY */ +#endif + +/* Other (BSD) machines use sgtty. */ +#if defined (NEW_TTY_DRIVER) +#include +#endif + +#include +extern int errno; + +#include +#include + +/* These next are for filename completion. Perhaps this belongs + in a different place. */ +#include +#if defined (USG) +struct passwd *getpwuid (), *getpwent (); +#endif + +/* #define HACK_TERMCAP_MOTION */ + +#if !defined (USG) +#include +#else /* USG */ +#if defined (Xenix) +#include +#else +#ifdef hpux +#include +#else +#include +#define direct dirent +#define d_namlen d_reclen +#endif /* hpux */ +#endif /* xenix */ +#endif /* USG */ + +#if defined (USG) && defined (TIOCGWINSZ) +#include +# if defined (USGr4) || defined (USGr3) +# include +# endif /* USGr4 */ +#endif /* USG && TIOCGWINSZ */ + +/* Some standard library routines. */ +#include "readline.h" +#include "history.h" + +#ifndef digit +#define digit(c) ((c) >= '0' && (c) <= '9') +#endif + +#ifndef isletter +#define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z')) +#endif + +#ifndef digit_value +#define digit_value(c) ((c) - '0') +#endif + +#ifndef member +char *index (); +#define member(c, s) ((c) ? index ((s), (c)) : 0) +#endif + +#ifndef isident +#define isident(c) ((isletter(c) || digit(c) || c == '_')) +#endif + +#ifndef exchange +#define exchange(x, y) {int temp = x; x = y; y = temp;} +#endif + +static update_line (); +static delete_chars (); +static insert_some_chars (); + +#ifdef VOID_SIGHANDLER +#define sighandler void +#else +#define sighandler int +#endif + +/* This typedef is equivalant to the one for Function; it allows us + to say SigHandler *foo = signal (SIGKILL, SIG_IGN); */ +typedef sighandler SigHandler (); + +/* If on, then readline handles signals in a way that doesn't screw. */ +#define HANDLE_SIGNALS + + +/* **************************************************************** */ +/* */ +/* Line editing input utility */ +/* */ +/* **************************************************************** */ + +/* A pointer to the keymap that is currently in use. + By default, it is the standard emacs keymap. */ +Keymap keymap = emacs_standard_keymap; + +#define vi_mode 0 +#define emacs_mode 1 + +/* The current style of editing. */ +int rl_editing_mode = emacs_mode; + +/* Non-zero if the previous command was a kill command. */ +static int last_command_was_kill = 0; + +/* The current value of the numeric argument specified by the user. */ +int rl_numeric_arg = 1; + +/* Non-zero if an argument was typed. */ +int rl_explicit_arg = 0; + +/* Temporary value used while generating the argument. */ +static int arg_sign = 1; + +/* Non-zero means we have been called at least once before. */ +static int rl_initialized = 0; + +/* If non-zero, this program is running in an EMACS buffer. */ +static char *running_in_emacs = (char *)NULL; + +/* The current offset in the current input line. */ +int rl_point; + +/* Mark in the current input line. */ +int rl_mark; + +/* Length of the current input line. */ +int rl_end; + +/* Make this non-zero to return the current input_line. */ +int rl_done; + +/* The last function executed by readline. */ +Function *rl_last_func = (Function *)NULL; + +/* Top level environment for readline_internal (). */ +static jmp_buf readline_top_level; + +/* The streams we interact with. */ +static FILE *in_stream, *out_stream; + +/* The names of the streams that we do input and output to. */ +FILE *rl_instream = stdin, *rl_outstream = stdout; + +/* Non-zero means echo characters as they are read. */ +int readline_echoing_p = 1; + +/* Current prompt. */ +char *rl_prompt; + +/* The number of characters read in order to type this complete command. */ +int rl_key_sequence_length = 0; + +/* If non-zero, then this is the address of a function to call just + before readline_internal () prints the first prompt. */ +Function *rl_startup_hook = (Function *)NULL; + +/* If non-zero, then this is the address of a function to call when + completing on a directory name. The function is called with + the address of a string (the current directory name) as an arg. */ +Function *rl_symbolic_link_hook = (Function *)NULL; + +/* What we use internally. You should always refer to RL_LINE_BUFFER. */ +static char *the_line; + +/* The character that can generate an EOF. Really read from + the terminal driver... just defaulted here. */ +static int eof_char = CTRL ('D'); + +/* Non-zero makes this the next keystroke to read. */ +int rl_pending_input = 0; + +/* Pointer to a useful terminal name. */ +char *rl_terminal_name = (char *)NULL; + +/* Line buffer and maintenence. */ +char *rl_line_buffer = (char *)NULL; +static int rl_line_buffer_len = 0; +#define DEFAULT_BUFFER_SIZE 256 + + +/* **************************************************************** */ +/* */ +/* `Forward' declarations */ +/* */ +/* **************************************************************** */ + +/* Non-zero means do not parse any lines other than comments and + parser directives. */ +static unsigned char parsing_conditionalized_out = 0; + +/* Non-zero means to save keys that we dispatch on in a kbd macro. */ +static int defining_kbd_macro = 0; + +/* XXX this prevents to got editing mode from tcsh */ +static void wait_foreground(void) +{ + struct winsize w; + int tty = fileno (rl_instream); + + if (ioctl (tty, TIOCGWINSZ, &w) == 0) + (void) ioctl (tty, TIOCSWINSZ, &w); +} + +/* **************************************************************** */ +/* */ +/* Top Level Functions */ +/* */ +/* **************************************************************** */ + +/* Read a line of input. Prompt with PROMPT. A NULL PROMPT means + none. A return value of NULL means that EOF was encountered. */ +char * +readline (prompt) + char *prompt; +{ + static rl_prep_terminal (), rl_deprep_terminal (); + char *readline_internal (); + char *value; + + rl_prompt = prompt; + + /* If we are at EOF return a NULL string. */ + if (rl_pending_input == EOF) + { + rl_pending_input = 0; + return ((char *)NULL); + } + + rl_initialize (); + rl_prep_terminal (); + +#ifdef HANDLE_SIGNALS + rl_set_signals (); +#endif + + value = readline_internal (); + rl_deprep_terminal (); + +#ifdef HANDLE_SIGNALS + rl_clear_signals (); +#endif + + return (value); +} + +/* Read a line of input from the global rl_instream, doing output on + the global rl_outstream. + If rl_prompt is non-null, then that is our prompt. */ +char * +readline_internal () +{ + int lastc, c, eof_found; + + in_stream = rl_instream; out_stream = rl_outstream; + lastc = eof_found = 0; + + if (rl_startup_hook) + (*rl_startup_hook) (); + + if (!readline_echoing_p) + { + if (rl_prompt) + { + fprintf (out_stream, "%s", rl_prompt); + fflush (out_stream); + } + } + else + { + rl_on_new_line (); + rl_redisplay (); +#ifdef VI_MODE + if (rl_editing_mode == vi_mode) + rl_vi_insertion_mode (); +#endif /* VI_MODE */ + } + + while (!rl_done) + { + int lk = last_command_was_kill; + int code = setjmp (readline_top_level); + + if (code) + rl_redisplay (); + + if (!rl_pending_input) + { + /* Then initialize the argument and number of keys read. */ + rl_init_argument (); + rl_key_sequence_length = 0; + } + + c = rl_read_key (); + + /* EOF typed to a non-blank line is a . */ + if (c == EOF && rl_end) + c = NEWLINE; + + /* The character eof_char typed to blank line, and not as the + previous character is interpreted as EOF. */ + if (((c == eof_char && lastc != c) || c == EOF) && !rl_end) + { + eof_found = 1; + break; + } + + lastc = c; + rl_dispatch (c, keymap); + + /* If there was no change in last_command_was_kill, then no kill + has taken place. Note that if input is pending we are reading + a prefix command, so nothing has changed yet. */ + if (!rl_pending_input) + { + if (lk == last_command_was_kill) + last_command_was_kill = 0; + } + +#ifdef VI_MODE + /* In vi mode, when you exit insert mode, the cursor moves back + over the previous character. We explicitly check for that here. */ + if (rl_editing_mode == vi_mode && keymap == vi_movement_keymap) + rl_vi_check (); +#endif + + if (!rl_done) + rl_redisplay (); + } + + /* Restore the original of this history line, iff the line that we + are editing was originally in the history, AND the line has changed. */ + { + HIST_ENTRY *entry = current_history (); + + if (entry && rl_undo_list) + { + char *temp = savestring (the_line); + rl_revert_line (); + entry = replace_history_entry (where_history (), the_line, + (HIST_ENTRY *)NULL); + free_history_entry (entry); + + strcpy (the_line, temp); + free (temp); + } + } + + /* At any rate, it is highly likely that this line has an undo list. Get + rid of it now. */ + if (rl_undo_list) + free_undo_list (); + + if (eof_found) + return (char *)NULL; + else + return (savestring (the_line)); +} + + +/* **************************************************************** */ +/* */ +/* Signal Handling */ +/* */ +/* **************************************************************** */ + +#ifdef SIGWINCH +static SigHandler *old_sigwinch = (SigHandler *)NULL; + +static sighandler +rl_handle_sigwinch (sig, code, scp) + int sig, code; + struct sigcontext *scp; +{ + char *term = rl_terminal_name; + + if (readline_echoing_p) + { + if (!term) + term = getenv ("TERM"); + if (!term) + term = "dumb"; + rl_reset_terminal (term); +#ifdef NEVER + crlf (); + rl_forced_update_display (); +#endif + } + + if (old_sigwinch && + old_sigwinch != (SigHandler *)SIG_IGN && + old_sigwinch != (SigHandler *)SIG_DFL) + (*old_sigwinch)(sig, code, scp); +} +#endif /* SIGWINCH */ + +#ifdef HANDLE_SIGNALS +/* Interrupt handling. */ +static SigHandler *old_int = (SigHandler *)NULL, + *old_tstp = (SigHandler *)NULL, + *old_ttou = (SigHandler *)NULL, + *old_ttin = (SigHandler *)NULL, + *old_cont = (SigHandler *)NULL; + +/* Handle an interrupt character. */ +static sighandler +rl_signal_handler (sig, code, scp) + int sig, code; + struct sigcontext *scp; +{ + static rl_prep_terminal (), rl_deprep_terminal (); + +#if !defined (HAVE_BSD_SIGNALS) || defined (hpux) + /* Since the signal will not be blocked while we are in the signal + handler, ignore it until rl_clear_signals resets the catcher. */ + if (sig == SIGINT) + signal (sig, SIG_IGN); +#endif /* !HAVE_BSD_SIGNALS || hpux */ + + switch (sig) + { + case SIGINT: + free_undo_list (); + rl_clear_message (); + rl_init_argument (); + +#ifdef SIGTSTP + case SIGTSTP: + case SIGTTOU: + case SIGTTIN: +#endif + + rl_clean_up_for_exit (); + rl_deprep_terminal (); + rl_clear_signals (); + rl_pending_input = 0; + + kill (getpid (), sig); + +#if defined (_POSIX_VERSION) + { + sigset_t set; + + sigemptyset (&set); + sigprocmask (SIG_SETMASK, &set, (sigset_t *)NULL); + } +#else +#if defined (HAVE_BSD_SIGNALS) + sigsetmask (0); +#endif /* HAVE_BSD_SIGNALS */ +#endif /* _POSIX_VERSION */ + + rl_prep_terminal (); + rl_set_signals (); + } +} + +rl_set_signals () +{ + old_int = (SigHandler *)signal (SIGINT, rl_signal_handler); + if (old_int == (SigHandler *)SIG_IGN) + signal (SIGINT, SIG_IGN); + +#ifdef SIGTSTP + old_tstp = (SigHandler *)signal (SIGTSTP, rl_signal_handler); + if (old_tstp == (SigHandler *)SIG_IGN) + signal (SIGTSTP, SIG_IGN); +#endif +#ifdef SIGTTOU + old_ttou = (SigHandler *)signal (SIGTTOU, rl_signal_handler); + old_ttin = (SigHandler *)signal (SIGTTIN, rl_signal_handler); + + if (old_tstp == (SigHandler *)SIG_IGN) + { + signal (SIGTTOU, SIG_IGN); + signal (SIGTTIN, SIG_IGN); + } +#endif + +#ifdef SIGWINCH + old_sigwinch = (SigHandler *)signal (SIGWINCH, rl_handle_sigwinch); +#endif +} + +rl_clear_signals () +{ + signal (SIGINT, old_int); + +#ifdef SIGTSTP + signal (SIGTSTP, old_tstp); +#endif + +#ifdef SIGTTOU + signal (SIGTTOU, old_ttou); + signal (SIGTTIN, old_ttin); +#endif + +#ifdef SIGWINCH + signal (SIGWINCH, old_sigwinch); +#endif +} +#endif /* HANDLE_SIGNALS */ + + +/* **************************************************************** */ +/* */ +/* Character Input Buffering */ +/* */ +/* **************************************************************** */ + +/* If the terminal was in xoff state when we got to it, then xon_char + contains the character that is supposed to start it again. */ +static int xon_char, xoff_state; +static int pop_index = 0, push_index = 0, ibuffer_len = 511; +static unsigned char ibuffer[512]; + +/* Non-null means it is a pointer to a function to run while waiting for + character input. */ +Function *rl_event_hook = (Function *)NULL; + +#define any_typein (push_index != pop_index) + +/* Add KEY to the buffer of characters to be read. */ +rl_stuff_char (key) + int key; +{ + if (key == EOF) + { + key = NEWLINE; + rl_pending_input = EOF; + } + ibuffer[push_index++] = key; + if (push_index >= ibuffer_len) + push_index = 0; +} + +/* Return the amount of space available in the + buffer for stuffing characters. */ +int +ibuffer_space () +{ + if (pop_index > push_index) + return (pop_index - push_index); + else + return (ibuffer_len - (push_index - pop_index)); +} + +/* Get a key from the buffer of characters to be read. + Return the key in KEY. + Result is KEY if there was a key, or 0 if there wasn't. */ +int +rl_get_char (key) + int *key; +{ + if (push_index == pop_index) + return (0); + + *key = ibuffer[pop_index++]; + + if (pop_index >= ibuffer_len) + pop_index = 0; + + return (1); +} + +/* Stuff KEY into the *front* of the input buffer. + Returns non-zero if successful, zero if there is + no space left in the buffer. */ +int +rl_unget_char (key) + int key; +{ + if (ibuffer_space ()) + { + pop_index--; + if (pop_index < 0) + pop_index = ibuffer_len - 1; + ibuffer[pop_index] = key; + return (1); + } + return (0); +} + +/* If a character is available to be read, then read it + and stuff it into IBUFFER. Otherwise, just return. */ +rl_gather_tyi () +{ + int tty = fileno (in_stream); + register int tem, result = -1; + long chars_avail; + char input; + +#ifdef FIONREAD + result = ioctl (tty, FIONREAD, &chars_avail); +#endif + + if (result == -1) + { + fcntl (tty, F_SETFL, O_NDELAY); + chars_avail = read (tty, &input, 1); + fcntl (tty, F_SETFL, 0); + if (chars_avail == -1 && errno == EAGAIN) + return; + } + + /* If there's nothing available, don't waste time trying to read + something. */ + if (chars_avail == 0) + return; + + tem = ibuffer_space (); + + if (chars_avail > tem) + chars_avail = tem; + + /* One cannot read all of the available input. I can only read a single + character at a time, or else programs which require input can be + thwarted. If the buffer is larger than one character, I lose. + Damn! */ + if (tem < ibuffer_len) + chars_avail = 0; + + if (result != -1) + { + while (chars_avail--) + rl_stuff_char (rl_getc (in_stream)); + } + else + { + if (chars_avail) + rl_stuff_char (input); + } +} + +/* Read a key, including pending input. */ +int +rl_read_key () +{ + int c; + + rl_key_sequence_length++; + + if (rl_pending_input) + { + c = rl_pending_input; + rl_pending_input = 0; + } + else + { + static int next_macro_key (); + + /* If input is coming from a macro, then use that. */ + if (c = next_macro_key ()) + return (c); + + /* If the user has an event function, then call it periodically. */ + if (rl_event_hook) + { + while (rl_event_hook && !rl_get_char (&c)) + { + (*rl_event_hook) (); + rl_gather_tyi (); + } + } + else + { + if (!rl_get_char (&c)) + c = rl_getc (in_stream); + } + } + +#ifdef NEVER /* This breaks supdup to 4.0.3c machines. */ +#ifdef TIOCSTART + /* Ugh. But I can't think of a better way. */ + if (xoff_state && c == xon_char) + { + ioctl (fileno (in_stream), TIOCSTART, 0); + xoff_state = 0; + return (rl_read_key ()); + } +#endif /* TIOCSTART */ +#endif + + return (c); +} + +/* I'm beginning to hate the declaration rules for various compilers. */ +static void add_macro_char (); + +/* Do the command associated with KEY in MAP. + If the associated command is really a keymap, then read + another key, and dispatch into that map. */ +rl_dispatch (key, map) + register int key; + Keymap map; +{ + + if (defining_kbd_macro) + add_macro_char (key); + + if (key > 127 && key < 256) + { + if (map[ESC].type == ISKMAP) + { + map = (Keymap)map[ESC].function; + key -= 128; + rl_dispatch (key, map); + } + else + ding (); + return; + } + + switch (map[key].type) + { + case ISFUNC: + { + Function *func = map[key].function; + + if (func != (Function *)NULL) + { + /* Special case rl_do_lowercase_version (). */ + if (func == rl_do_lowercase_version) + { + rl_dispatch (to_lower (key), map); + return; + } + + (*map[key].function)(rl_numeric_arg * arg_sign, key); + + /* If we have input pending, then the last command was a prefix + command. Don't change the state of rl_last_func. Otherwise, + remember the last command executed in this variable. */ + if (!rl_pending_input) + rl_last_func = map[key].function; + } + else + { + rl_abort (); + return; + } + } + break; + + case ISKMAP: + if (map[key].function != (Function *)NULL) + { + int newkey; + + rl_key_sequence_length++; + newkey = rl_read_key (); + rl_dispatch (newkey, (Keymap)map[key].function); + } + else + { + rl_abort (); + return; + } + break; + + case ISMACR: + if (map[key].function != (Function *)NULL) + { + static with_macro_input (); + char *macro = savestring ((char *)map[key].function); + + with_macro_input (macro); + return; + } + break; + } +} + + +/* **************************************************************** */ +/* */ +/* Hacking Keyboard Macros */ +/* */ +/* **************************************************************** */ + +/* The currently executing macro string. If this is non-zero, + then it is a malloc ()'ed string where input is coming from. */ +static char *executing_macro = (char *)NULL; + +/* The offset in the above string to the next character to be read. */ +static int executing_macro_index = 0; + +/* The current macro string being built. Characters get stuffed + in here by add_macro_char (). */ +static char *current_macro = (char *)NULL; + +/* The size of the buffer allocated to current_macro. */ +static int current_macro_size = 0; + +/* The index at which characters are being added to current_macro. */ +static int current_macro_index = 0; + +/* A structure used to save nested macro strings. + It is a linked list of string/index for each saved macro. */ +struct saved_macro { + struct saved_macro *next; + char *string; + int index; +}; + +/* The list of saved macros. */ +struct saved_macro *macro_list = (struct saved_macro *)NULL; + +/* Forward declarations of static functions. Thank you C. */ +static void push_executing_macro (), pop_executing_macro (); + +/* This one has to be declared earlier in the file. */ +/* static void add_macro_char (); */ + +/* Set up to read subsequent input from STRING. + STRING is free ()'ed when we are done with it. */ +static +with_macro_input (string) + char *string; +{ + push_executing_macro (); + executing_macro = string; + executing_macro_index = 0; +} + +/* Return the next character available from a macro, or 0 if + there are no macro characters. */ +static int +next_macro_key () +{ + if (!executing_macro) + return (0); + + if (!executing_macro[executing_macro_index]) + { + pop_executing_macro (); + return (next_macro_key ()); + } + + return (executing_macro[executing_macro_index++]); +} + +/* Save the currently executing macro on a stack of saved macros. */ +static void +push_executing_macro () +{ + struct saved_macro *saver; + + saver = (struct saved_macro *)xmalloc (sizeof (struct saved_macro)); + saver->next = macro_list; + saver->index = executing_macro_index; + saver->string = executing_macro; + + macro_list = saver; +} + +/* Discard the current macro, replacing it with the one + on the top of the stack of saved macros. */ +static void +pop_executing_macro () +{ + if (executing_macro) + free (executing_macro); + + executing_macro = (char *)NULL; + executing_macro_index = 0; + + if (macro_list) + { + struct saved_macro *disposer = macro_list; + executing_macro = macro_list->string; + executing_macro_index = macro_list->index; + macro_list = macro_list->next; + free (disposer); + } +} + +/* Add a character to the macro being built. */ +static void +add_macro_char (c) + int c; +{ + if (current_macro_index + 1 >= current_macro_size) + { + if (!current_macro) + current_macro = (char *)xmalloc (current_macro_size = 25); + else + current_macro = + (char *)xrealloc (current_macro, current_macro_size += 25); + } + + current_macro[current_macro_index++] = c; + current_macro[current_macro_index] = '\0'; +} + +/* Begin defining a keyboard macro. + Keystrokes are recorded as they are executed. + End the definition with rl_end_kbd_macro (). + If a numeric argument was explicitly typed, then append this + definition to the end of the existing macro, and start by + re-executing the existing macro. */ +rl_start_kbd_macro (ignore1, ignore2) + int ignore1, ignore2; +{ + if (defining_kbd_macro) + rl_abort (); + + if (rl_explicit_arg) + { + if (current_macro) + with_macro_input (savestring (current_macro)); + } + else + current_macro_index = 0; + + defining_kbd_macro = 1; +} + +/* Stop defining a keyboard macro. + A numeric argument says to execute the macro right now, + that many times, counting the definition as the first time. */ +rl_end_kbd_macro (count, ignore) + int count, ignore; +{ + if (!defining_kbd_macro) + rl_abort (); + + current_macro_index -= (rl_key_sequence_length - 1); + current_macro[current_macro_index] = '\0'; + + defining_kbd_macro = 0; + + rl_call_last_kbd_macro (--count, 0); +} + +/* Execute the most recently defined keyboard macro. + COUNT says how many times to execute it. */ +rl_call_last_kbd_macro (count, ignore) + int count, ignore; +{ + if (!current_macro) + rl_abort (); + + while (count--) + with_macro_input (savestring (current_macro)); +} + + +/* **************************************************************** */ +/* */ +/* Initializations */ +/* */ +/* **************************************************************** */ + +/* Initliaze readline (and terminal if not already). */ +rl_initialize () +{ + extern char *rl_display_prompt; + + /* If we have never been called before, initialize the + terminal and data structures. */ + if (!rl_initialized) + { + readline_initialize_everything (); + rl_initialized++; + } + + /* Initalize the current line information. */ + rl_point = rl_end = 0; + the_line = rl_line_buffer; + the_line[0] = 0; + + /* We aren't done yet. We haven't even gotten started yet! */ + rl_done = 0; + + /* Tell the history routines what is going on. */ + start_using_history (); + + /* Make the display buffer match the state of the line. */ + { + extern char *rl_display_prompt; + extern int forced_display; + + rl_on_new_line (); + + rl_display_prompt = rl_prompt ? rl_prompt : ""; + forced_display = 1; + } + + /* No such function typed yet. */ + rl_last_func = (Function *)NULL; + + /* Parsing of key-bindings begins in an enabled state. */ + parsing_conditionalized_out = 0; +} + +/* Initialize the entire state of the world. */ +readline_initialize_everything () +{ + /* Find out if we are running in Emacs. */ + running_in_emacs = getenv ("EMACS"); + + /* Allocate data structures. */ + if (!rl_line_buffer) + rl_line_buffer = + (char *)xmalloc (rl_line_buffer_len = DEFAULT_BUFFER_SIZE); + + /* Initialize the terminal interface. */ + init_terminal_io ((char *)NULL); + + /* Bind tty characters to readline functions. */ + readline_default_bindings (); + + /* Initialize the function names. */ + rl_initialize_funmap (); + + /* Read in the init file. */ + rl_read_init_file ((char *)NULL); + + /* If the completion parser's default word break characters haven't + been set yet, then do so now. */ + { + extern char *rl_completer_word_break_characters; + extern char *rl_basic_word_break_characters; + + if (rl_completer_word_break_characters == (char *)NULL) + rl_completer_word_break_characters = rl_basic_word_break_characters; + } +} + +/* If this system allows us to look at the values of the regular + input editing characters, then bind them to their readline + equivalents. */ +readline_default_bindings () +{ + +#ifdef NEW_TTY_DRIVER + struct sgttyb ttybuff; + int tty = fileno (rl_instream); + + if (ioctl (tty, TIOCGETP, &ttybuff) != -1) + { + int erase = ttybuff.sg_erase, kill = ttybuff.sg_kill; + + if (erase != -1 && keymap[erase].type == ISFUNC) + keymap[erase].function = rl_rubout; + + if (kill != -1 && keymap[kill].type == ISFUNC) + keymap[kill].function = rl_unix_line_discard; + } + +#ifdef TIOCGLTC + { + struct ltchars lt; + + if (ioctl (tty, TIOCGLTC, <) != -1) + { + int erase = lt.t_werasc, nextc = lt.t_lnextc; + + if (erase != -1 && keymap[erase].type == ISFUNC) + keymap[erase].function = rl_unix_word_rubout; + + if (nextc != -1 && keymap[nextc].type == ISFUNC) + keymap[nextc].function = rl_quoted_insert; + } + } +#endif /* TIOCGLTC */ +#else /* not NEW_TTY_DRIVER */ + +#if defined (_POSIX_VERSION) + struct termios ttybuff; +#else + struct termio ttybuff; +#endif /* POSIX */ + int tty = fileno (rl_instream); + + wait_foreground (); /* XXX this prevents to got editing mode from tcsh */ +#if defined (_POSIX_VERSION) + if (tcgetattr (tty, &ttybuff) != -1) +#else + if (ioctl (tty, TCGETA, &ttybuff) != -1) +#endif /* POSIX */ + { + int erase = ttybuff.c_cc[VERASE]; + int kill = ttybuff.c_cc[VKILL]; + + if (erase != -1 && keymap[(unsigned char)erase].type == ISFUNC) + keymap[(unsigned char)erase].function = rl_rubout; + + if (kill != -1 && keymap[(unsigned char)kill].type == ISFUNC) + keymap[(unsigned char)kill].function = rl_unix_line_discard; + } +#endif /* NEW_TTY_DRIVER */ +} + + +/* **************************************************************** */ +/* */ +/* Numeric Arguments */ +/* */ +/* **************************************************************** */ + +/* Handle C-u style numeric args, as well as M--, and M-digits. */ + +/* Add the current digit to the argument in progress. */ +rl_digit_argument (ignore, key) + int ignore, key; +{ + rl_pending_input = key; + rl_digit_loop (); +} + +/* What to do when you abort reading an argument. */ +rl_discard_argument () +{ + ding (); + rl_clear_message (); + rl_init_argument (); +} + +/* Create a default argument. */ +rl_init_argument () +{ + rl_numeric_arg = arg_sign = 1; + rl_explicit_arg = 0; +} + +/* C-u, universal argument. Multiply the current argument by 4. + Read a key. If the key has nothing to do with arguments, then + dispatch on it. If the key is the abort character then abort. */ +rl_universal_argument () +{ + rl_numeric_arg *= 4; + rl_digit_loop (); +} + +rl_digit_loop () +{ + int key, c; + while (1) + { + rl_message ("(arg: %d) ", arg_sign * rl_numeric_arg); + key = c = rl_read_key (); + + if (keymap[c].type == ISFUNC && + keymap[c].function == rl_universal_argument) + { + rl_numeric_arg *= 4; + continue; + } + c = UNMETA (c); + if (numeric (c)) + { + if (rl_explicit_arg) + rl_numeric_arg = (rl_numeric_arg * 10) + (c - '0'); + else + rl_numeric_arg = (c - '0'); + rl_explicit_arg = 1; + } + else + { + if (c == '-' && !rl_explicit_arg) + { + rl_numeric_arg = 1; + arg_sign = -1; + } + else + { + rl_clear_message (); + rl_dispatch (key, keymap); + return; + } + } + } +} + + +/* **************************************************************** */ +/* */ +/* Display stuff */ +/* */ +/* **************************************************************** */ + +/* This is the stuff that is hard for me. I never seem to write good + display routines in C. Let's see how I do this time. */ + +/* (PWP) Well... Good for a simple line updater, but totally ignores + the problems of input lines longer than the screen width. + + update_line and the code that calls it makes a multiple line, + automatically wrapping line update. Carefull attention needs + to be paid to the vertical position variables. + + handling of terminals with autowrap on (incl. DEC braindamage) + could be improved a bit. Right now I just cheat and decrement + screenwidth by one. */ + +/* Keep two buffers; one which reflects the current contents of the + screen, and the other to draw what we think the new contents should + be. Then compare the buffers, and make whatever changes to the + screen itself that we should. Finally, make the buffer that we + just drew into be the one which reflects the current contents of the + screen, and place the cursor where it belongs. + + Commands that want to can fix the display themselves, and then let + this function know that the display has been fixed by setting the + RL_DISPLAY_FIXED variable. This is good for efficiency. */ + +/* Termcap variables: */ +extern char *term_up, *term_dc, *term_cr; +extern int screenheight, screenwidth, terminal_can_insert; + +/* What YOU turn on when you have handled all redisplay yourself. */ +int rl_display_fixed = 0; + +/* The visible cursor position. If you print some text, adjust this. */ +int last_c_pos = 0; +int last_v_pos = 0; + +/* The last left edge of text that was displayed. This is used when + doing horizontal scrolling. It shifts in thirds of a screenwidth. */ +static int last_lmargin = 0; + +/* The line display buffers. One is the line currently displayed on + the screen. The other is the line about to be displayed. */ +static char *visible_line = (char *)NULL; +static char *invisible_line = (char *)NULL; + +/* Number of lines currently on screen minus 1. */ +int vis_botlin = 0; + +/* A buffer for `modeline' messages. */ +char msg_buf[128]; + +/* Non-zero forces the redisplay even if we thought it was unnecessary. */ +int forced_display = 0; + +/* The stuff that gets printed out before the actual text of the line. + This is usually pointing to rl_prompt. */ +char *rl_display_prompt = (char *)NULL; + +/* Default and initial buffer size. Can grow. */ +static int line_size = 1024; + +/* Non-zero means to always use horizontal scrolling in line display. */ +static int horizontal_scroll_mode = 0; + +/* Non-zero means to display an asterisk at the starts of history lines + which have been modified. */ +static int mark_modified_lines = 0; + +/* Non-zero means to use a visible bell if one is available rather than + simply ringing the terminal bell. */ +static int prefer_visible_bell = 0; + +/* I really disagree with this, but my boss (among others) insists that we + support compilers that don't work. I don't think we are gaining by doing + so; what is the advantage in producing better code if we can't use it? */ +/* The following two declarations belong inside the + function block, not here. */ +static void move_cursor_relative (); +static void output_some_chars (); +void output_character_function (); +static int compare_strings (); + +/* Basic redisplay algorithm. */ +rl_redisplay () +{ + register int in, out, c, linenum; + register char *line = invisible_line; + char *prompt_this_line; + int c_pos = 0; + int inv_botlin = 0; /* Number of lines in newly drawn buffer. */ + + extern int readline_echoing_p; + + if (!readline_echoing_p) + return; + + if (!rl_display_prompt) + rl_display_prompt = ""; + + if (!invisible_line) + { + visible_line = (char *)xmalloc (line_size); + invisible_line = (char *)xmalloc (line_size); + line = invisible_line; + for (in = 0; in < line_size; in++) + { + visible_line[in] = 0; + invisible_line[in] = 1; + } + rl_on_new_line (); + } + + /* Draw the line into the buffer. */ + c_pos = -1; + + /* Mark the line as modified or not. We only do this for history + lines. */ + out = 0; + if (mark_modified_lines && current_history () && rl_undo_list) + { + line[out++] = '*'; + line[out] = '\0'; + } + + /* If someone thought that the redisplay was handled, but the currently + visible line has a different modification state than the one about + to become visible, then correct the callers misconception. */ + if (visible_line[0] != invisible_line[0]) + rl_display_fixed = 0; + + prompt_this_line = rindex (rl_display_prompt, '\n'); + if (!prompt_this_line) + prompt_this_line = rl_display_prompt; + else + { + prompt_this_line++; + if (forced_display) + output_some_chars (rl_display_prompt, + prompt_this_line - rl_display_prompt); + } + + strncpy (line + out, prompt_this_line, strlen (prompt_this_line)); + out += strlen (prompt_this_line); + line[out] = '\0'; + + for (in = 0; in < rl_end; in++) + { + c = the_line[in]; + + if (out + 1 >= line_size) + { + line_size *= 2; + visible_line = (char *)xrealloc (visible_line, line_size); + invisible_line = (char *)xrealloc (invisible_line, line_size); + line = invisible_line; + } + + if (in == rl_point) + c_pos = out; + + if (c > 127) + { + line[out++] = 'M'; + line[out++] = '-'; + line[out++] = c - 128; + } +#define DISPLAY_TABS +#ifdef DISPLAY_TABS + else if (c == '\t') + { + register int newout = (out | (int)7) + 1; + while (out < newout) + line[out++] = ' '; + } +#endif + else if (c < 32) + { + line[out++] = 'C'; + line[out++] = '-'; + line[out++] = c + 64; + } + else + line[out++] = c; + } + line[out] = '\0'; + if (c_pos < 0) + c_pos = out; + + /* PWP: now is when things get a bit hairy. The visible and invisible + line buffers are really multiple lines, which would wrap every + (screenwidth - 1) characters. Go through each in turn, finding + the changed region and updating it. The line order is top to bottom. */ + + /* If we can move the cursor up and down, then use multiple lines, + otherwise, let long lines display in a single terminal line, and + horizontally scroll it. */ + + if (!horizontal_scroll_mode && term_up && *term_up) + { + int total_screen_chars = (screenwidth * screenheight); + + if (!rl_display_fixed || forced_display) + { + forced_display = 0; + + /* If we have more than a screenful of material to display, then + only display a screenful. We should display the last screen, + not the first. I'll fix this in a minute. */ + if (out >= total_screen_chars) + out = total_screen_chars - 1; + + /* Number of screen lines to display. */ + inv_botlin = out / screenwidth; + + /* For each line in the buffer, do the updating display. */ + for (linenum = 0; linenum <= inv_botlin; linenum++) + update_line (linenum > vis_botlin ? "" + : &visible_line[linenum * screenwidth], + &invisible_line[linenum * screenwidth], + linenum); + + /* We may have deleted some lines. If so, clear the left over + blank ones at the bottom out. */ + if (vis_botlin > inv_botlin) + { + char *tt; + for (; linenum <= vis_botlin; linenum++) + { + tt = &visible_line[linenum * screenwidth]; + move_vert (linenum); + move_cursor_relative (0, tt); + clear_to_eol ((linenum == vis_botlin)? + strlen (tt) : screenwidth); + } + } + vis_botlin = inv_botlin; + + /* Move the cursor where it should be. */ + move_vert (c_pos / screenwidth); + move_cursor_relative (c_pos % screenwidth, + &invisible_line[(c_pos / screenwidth) * screenwidth]); + } + } + else /* Do horizontal scrolling. */ + { + int lmargin; + + /* Always at top line. */ + last_v_pos = 0; + + /* If the display position of the cursor would be off the edge + of the screen, start the display of this line at an offset that + leaves the cursor on the screen. */ + if (c_pos - last_lmargin > screenwidth - 2) + lmargin = (c_pos / (screenwidth / 3) - 2) * (screenwidth / 3); + else if (c_pos - last_lmargin < 1) + lmargin = ((c_pos - 1) / (screenwidth / 3)) * (screenwidth / 3); + else + lmargin = last_lmargin; + + /* If the first character on the screen isn't the first character + in the display line, indicate this with a special character. */ + if (lmargin > 0) + line[lmargin] = '<'; + + if (lmargin + screenwidth < out) + line[lmargin + screenwidth - 1] = '>'; + + if (!rl_display_fixed || forced_display || lmargin != last_lmargin) + { + forced_display = 0; + update_line (&visible_line[last_lmargin], + &invisible_line[lmargin], 0); + + move_cursor_relative (c_pos - lmargin, &invisible_line[lmargin]); + last_lmargin = lmargin; + } + } + fflush (out_stream); + + /* Swap visible and non-visible lines. */ + { + char *temp = visible_line; + visible_line = invisible_line; + invisible_line = temp; + rl_display_fixed = 0; + } +} + +/* PWP: update_line() is based on finding the middle difference of each + line on the screen; vis: + + /old first difference + /beginning of line | /old last same /old EOL + v v v v +old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as +new: eddie> Oh, my little buggy says to me, as lurgid as + ^ ^ ^ ^ + \beginning of line | \new last same \new end of line + \new first difference + + All are character pointers for the sake of speed. Special cases for + no differences, as well as for end of line additions must be handeled. + + Could be made even smarter, but this works well enough */ +static +update_line (old, new, current_line) + register char *old, *new; + int current_line; +{ + register char *ofd, *ols, *oe, *nfd, *nls, *ne; + int lendiff, wsatend; + + /* Find first difference. */ + for (ofd = old, nfd = new; + (ofd - old < screenwidth) && *ofd && (*ofd == *nfd); + ofd++, nfd++) + ; + + /* Move to the end of the screen line. */ + for (oe = ofd; ((oe - old) < screenwidth) && *oe; oe++); + for (ne = nfd; ((ne - new) < screenwidth) && *ne; ne++); + + /* If no difference, continue to next line. */ + if (ofd == oe && nfd == ne) + return; + + wsatend = 1; /* flag for trailing whitespace */ + ols = oe - 1; /* find last same */ + nls = ne - 1; + while ((*ols == *nls) && (ols > ofd) && (nls > nfd)) + { + if (*ols != ' ') + wsatend = 0; + ols--; + nls--; + } + + if (wsatend) + { + ols = oe; + nls = ne; + } + else if (*ols != *nls) + { + if (*ols) /* don't step past the NUL */ + ols++; + if (*nls) + nls++; + } + + move_vert (current_line); + move_cursor_relative (ofd - old, old); + + /* if (len (new) > len (old)) */ + lendiff = (nls - nfd) - (ols - ofd); + + /* Insert (diff(len(old),len(new)) ch */ + if (lendiff > 0) + { + if (terminal_can_insert) + { + extern char *term_IC; + + /* Sometimes it is cheaper to print the characters rather than + use the terminal's capabilities. */ + if ((2 * (ne - nfd)) < lendiff && !term_IC) + { + output_some_chars (nfd, (ne - nfd)); + last_c_pos += (ne - nfd); + } + else + { + if (*ols) + { + insert_some_chars (nfd, lendiff); + last_c_pos += lendiff; + } + else + { + /* At the end of a line the characters do not have to + be "inserted". They can just be placed on the screen. */ + output_some_chars (nfd, lendiff); + last_c_pos += lendiff; + } + /* Copy (new) chars to screen from first diff to last match. */ + if (((nls - nfd) - lendiff) > 0) + { + output_some_chars (&nfd[lendiff], ((nls - nfd) - lendiff)); + last_c_pos += ((nls - nfd) - lendiff); + } + } + } + else + { /* cannot insert chars, write to EOL */ + output_some_chars (nfd, (ne - nfd)); + last_c_pos += (ne - nfd); + } + } + else /* Delete characters from line. */ + { + /* If possible and inexpensive to use terminal deletion, then do so. */ + if (term_dc && (2 * (ne - nfd)) >= (-lendiff)) + { + if (lendiff) + delete_chars (-lendiff); /* delete (diff) characters */ + + /* Copy (new) chars to screen from first diff to last match */ + if ((nls - nfd) > 0) + { + output_some_chars (nfd, (nls - nfd)); + last_c_pos += (nls - nfd); + } + } + /* Otherwise, print over the existing material. */ + else + { + output_some_chars (nfd, (ne - nfd)); + last_c_pos += (ne - nfd); + clear_to_eol ((oe - old) - (ne - new)); + } + } +} + +/* (PWP) tell the update routines that we have moved onto a + new (empty) line. */ +rl_on_new_line () +{ + if (visible_line) + visible_line[0] = '\0'; + + last_c_pos = last_v_pos = 0; + vis_botlin = last_lmargin = 0; +} + +/* Actually update the display, period. */ +rl_forced_update_display () +{ + if (visible_line) + { + register char *temp = visible_line; + + while (*temp) *temp++ = '\0'; + } + rl_on_new_line (); + forced_display++; + rl_redisplay (); +} + +/* Move the cursor from last_c_pos to NEW, which are buffer indices. + DATA is the contents of the screen line of interest; i.e., where + the movement is being done. */ +static void +move_cursor_relative (new, data) + int new; + char *data; +{ + register int i; + + /* It may be faster to output a CR, and then move forwards instead + of moving backwards. */ + if (new + 1 < last_c_pos - new) + { + tputs (term_cr, 1, output_character_function); + last_c_pos = 0; + } + + if (last_c_pos == new) return; + + if (last_c_pos < new) + { + /* Move the cursor forward. We do it by printing the command + to move the cursor forward if there is one, else print that + portion of the output buffer again. Which is cheaper? */ + + /* The above comment is left here for posterity. It is faster + to print one character (non-control) than to print a control + sequence telling the terminal to move forward one character. + That kind of control is for people who don't know what the + data is underneath the cursor. */ +#ifdef HACK_TERMCAP_MOTION + extern char *term_forward_char; + + if (term_forward_char) + for (i = last_c_pos; i < new; i++) + tputs (term_forward_char, 1, output_character_function); + else + for (i = last_c_pos; i < new; i++) + putc (data[i], out_stream); +#else + for (i = last_c_pos; i < new; i++) + putc (data[i], out_stream); +#endif /* HACK_TERMCAP_MOTION */ + } + else + backspace (last_c_pos - new); + last_c_pos = new; +} + +/* PWP: move the cursor up or down. */ +move_vert (to) + int to; +{ + register int delta, i; + + if (last_v_pos == to) return; + + if (to > screenheight) + return; + + if ((delta = to - last_v_pos) > 0) + { + for (i = 0; i < delta; i++) + putc ('\n', out_stream); + tputs (term_cr, 1, output_character_function); + last_c_pos = 0; /* because crlf() will do \r\n */ + } + else + { /* delta < 0 */ + if (term_up && *term_up) + for (i = 0; i < -delta; i++) + tputs (term_up, 1, output_character_function); + } + last_v_pos = to; /* now to is here */ +} + +/* Physically print C on out_stream. This is for functions which know + how to optimize the display. */ +rl_show_char (c) + int c; +{ + if (c > 127) + { + fprintf (out_stream, "M-"); + c -= 128; + } + +#ifdef DISPLAY_TABS + if (c < 32 && c != '\t') +#else + if (c < 32) +#endif + { + + c += 64; + } + + putc (c, out_stream); + fflush (out_stream); +} + +#ifdef DISPLAY_TABS +int +rl_character_len (c, pos) + register int c, pos; +{ + if (c < ' ' || c > 126) + { + if (c == '\t') + return (((pos | (int)7) + 1) - pos); + else + return (3); + } + else + return (1); +} +#else +int +rl_character_len (c) + int c; +{ + if (c < ' ' || c > 126) + return (3); + else + return (1); +} +#endif /* DISPLAY_TAB */ + +/* How to print things in the "echo-area". The prompt is treated as a + mini-modeline. */ +rl_message (string, arg1, arg2) + char *string; +{ + sprintf (msg_buf, string, arg1, arg2); + rl_display_prompt = msg_buf; + rl_redisplay (); +} + +/* How to clear things from the "echo-area". */ +rl_clear_message () +{ + rl_display_prompt = rl_prompt; + rl_redisplay (); +} + +/* **************************************************************** */ +/* */ +/* Terminal and Termcap */ +/* */ +/* **************************************************************** */ + +static char *term_buffer = (char *)NULL; +static char *term_string_buffer = (char *)NULL; + +/* Non-zero means this terminal can't really do anything. */ +int dumb_term = 0; + +char PC; +char *BC, *UP; + +/* Some strings to control terminal actions. These are output by tputs (). */ +char *term_goto, *term_clreol, *term_cr, *term_clrpag, *term_backspace; + +int screenwidth, screenheight; + +/* Non-zero if we determine that the terminal can do character insertion. */ +int terminal_can_insert = 0; + +/* How to insert characters. */ +char *term_im, *term_ei, *term_ic, *term_ip, *term_IC; + +/* How to delete characters. */ +char *term_dc, *term_DC; + +#ifdef HACK_TERMCAP_MOTION +char *term_forward_char; +#endif /* HACK_TERMCAP_MOTION */ + +/* How to go up a line. */ +char *term_up; + +/* A visible bell, if the terminal can be made to flash the screen. */ +char *visible_bell; + +/* Re-initialize the terminal considering that the TERM/TERMCAP variable + has changed. */ +rl_reset_terminal (terminal_name) + char *terminal_name; +{ + init_terminal_io (terminal_name); +} + +init_terminal_io (terminal_name) + char *terminal_name; +{ + char *term = (terminal_name? terminal_name : getenv ("TERM")); + char *tgetstr (), *buffer; +#ifdef TIOCGWINSZ + struct winsize window_size; +#endif + int tty; + + if (!term_string_buffer) + term_string_buffer = (char *)xmalloc (2048); + + if (!term_buffer) + term_buffer = (char *)xmalloc (2048); + + buffer = term_string_buffer; + + term_clrpag = term_cr = term_clreol = (char *)NULL; + + if (!term) + term = "dumb"; + + if (tgetent (term_buffer, term) < 0) + { + dumb_term = 1; + return; + } + + BC = tgetstr ("pc", &buffer); + PC = buffer ? *buffer : 0; + + term_backspace = tgetstr ("le", &buffer); + + term_cr = tgetstr ("cr", &buffer); + term_clreol = tgetstr ("ce", &buffer); + term_clrpag = tgetstr ("cl", &buffer); + + if (!term_cr) + term_cr = "\r"; + +#ifdef HACK_TERMCAP_MOTION + term_forward_char = tgetstr ("nd", &buffer); +#endif /* HACK_TERMCAP_MOTION */ + + if (rl_instream) + tty = fileno (rl_instream); + else + tty = 0; + + screenwidth = screenheight = 0; +#ifdef TIOCGWINSZ + if (ioctl (tty, TIOCGWINSZ, &window_size) == 0) + { + screenwidth = (int) window_size.ws_col; + screenheight = (int) window_size.ws_row; + } +#endif + + if (screenwidth <= 0 || screenheight <= 0) + { + screenwidth = tgetnum ("co"); + screenheight = tgetnum ("li"); + } + + screenwidth--; + + if (screenwidth <= 0) + screenwidth = 79; + + if (screenheight <= 0) + screenheight = 24; + + term_im = tgetstr ("im", &buffer); + term_ei = tgetstr ("ei", &buffer); + term_IC = tgetstr ("IC", &buffer); + term_ic = tgetstr ("ic", &buffer); + + /* "An application program can assume that the terminal can do + character insertion if *any one of* the capabilities `IC', + `im', `ic' or `ip' is provided." But we can't do anything if + only `ip' is provided, so... */ + terminal_can_insert = (term_IC || term_im || term_ic); + + term_up = tgetstr ("up", &buffer); + term_dc = tgetstr ("dc", &buffer); + term_DC = tgetstr ("DC", &buffer); + + visible_bell = tgetstr ("vb", &buffer); +} + +/* A function for the use of tputs () */ +void +output_character_function (c) + int c; +{ + putc (c, out_stream); +} + +/* Write COUNT characters from STRING to the output stream. */ +static void +output_some_chars (string, count) + char *string; + int count; +{ + fwrite (string, 1, count, out_stream); +} + +/* Delete COUNT characters from the display line. */ +static +delete_chars (count) + int count; +{ + if (count > screenwidth) + return; + + if (term_DC && *term_DC) + { + char *tgoto (), *buffer; + buffer = tgoto (term_DC, 0, count); + tputs (buffer, 1, output_character_function); + } + else + { + if (term_dc && *term_dc) + while (count--) + tputs (term_dc, 1, output_character_function); + } +} + +/* Insert COUNT characters from STRING to the output stream. */ +static +insert_some_chars (string, count) + char *string; + int count; +{ + /* If IC is defined, then we do not have to "enter" insert mode. */ + if (term_IC) + { + char *tgoto (), *buffer; + buffer = tgoto (term_IC, 0, count); + tputs (buffer, 1, output_character_function); + output_some_chars (string, count); + } + else + { + register int i; + + /* If we have to turn on insert-mode, then do so. */ + if (term_im && *term_im) + tputs (term_im, 1, output_character_function); + + /* If there is a special command for inserting characters, then + use that first to open up the space. */ + if (term_ic && *term_ic) + { + for (i = count; i--; ) + tputs (term_ic, 1, output_character_function); + } + + /* Print the text. */ + output_some_chars (string, count); + + /* If there is a string to turn off insert mode, we had best use + it now. */ + if (term_ei && *term_ei) + tputs (term_ei, 1, output_character_function); + } +} + +/* Move the cursor back. */ +backspace (count) + int count; +{ + register int i; + + if (term_backspace) + for (i = 0; i < count; i++) + tputs (term_backspace, 1, output_character_function); + else + for (i = 0; i < count; i++) + putc ('\b', out_stream); +} + +/* Move to the start of the next line. */ +crlf () +{ + tputs (term_cr, 1, output_character_function); + putc ('\n', out_stream); +} + +/* Clear to the end of the line. COUNT is the minimum + number of character spaces to clear, */ +clear_to_eol (count) + int count; +{ + if (term_clreol) + { + tputs (term_clreol, 1, output_character_function); + } + else + { + register int i; + + /* Do one more character space. */ + count++; + + for (i = 0; i < count; i++) + putc (' ', out_stream); + + backspace (count); + } +} + + +/* **************************************************************** */ +/* */ +/* Saving and Restoring the TTY */ +/* */ +/* **************************************************************** */ + +/* Non-zero means that the terminal is in a prepped state. */ +static int terminal_prepped = 0; + +#ifdef NEW_TTY_DRIVER + +/* Standard flags, including ECHO. */ +static int original_tty_flags = 0; + +/* Local mode flags, like LPASS8. */ +static int local_mode_flags = 0; + +/* Terminal characters. This has C-s and C-q in it. */ +static struct tchars original_tchars; + +/* Local special characters. This has the interrupt characters in it. */ +static struct ltchars original_ltchars; + +/* We use this to get and set the tty_flags. */ +static struct sgttyb the_ttybuff; + +/* Put the terminal in CBREAK mode so that we can detect key presses. */ +static +rl_prep_terminal () +{ + int tty = fileno (rl_instream); + int oldmask = sigblock (sigmask (SIGINT)); + + if (!terminal_prepped) + { + /* We always get the latest tty values. Maybe stty changed them. */ + ioctl (tty, TIOCGETP, &the_ttybuff); + original_tty_flags = the_ttybuff.sg_flags; + + readline_echoing_p = (original_tty_flags & ECHO); + +#if defined (TIOCLGET) + ioctl (tty, TIOCLGET, &local_mode_flags); +#endif + +#if !defined (ANYP) +#define ANYP (EVENP | ODDP) +#endif + /* If this terminal doesn't care how the 8th bit is used, + then we can use it for the meta-key. + We check by seeing if BOTH odd and even parity are allowed. */ + if (the_ttybuff.sg_flags & ANYP) + { +#if defined (PASS8) + the_ttybuff.sg_flags |= PASS8; +#endif + + /* Hack on local mode flags if we can. */ +#if defined (TIOCLGET) && defined (LPASS8) + { + int flags; + flags = local_mode_flags | LPASS8; + ioctl (tty, TIOCLSET, &flags); + } +#endif /* TIOCLGET && LPASS8 */ + } +#ifdef TIOCGETC + { + struct tchars temp; + + ioctl (tty, TIOCGETC, &original_tchars); + temp = original_tchars; + + /* Get rid of C-s and C-q. + We remember the value of startc (C-q) so that if the terminal is in + xoff state, the user can xon it by pressing that character. */ + xon_char = temp.t_startc; + temp.t_stopc = -1; + temp.t_startc = -1; + + /* If there is an XON character, bind it to restart the output. */ + if (xon_char != -1) + rl_bind_key (xon_char, rl_restart_output); + + /* If there is an EOF char, bind eof_char to it. */ + if (temp.t_eofc != -1) + eof_char = temp.t_eofc; + +#if defined (NOTDEF) + /* Get rid of C-\ and C-c. */ + temp.t_intrc = temp.t_quitc = -1; +#endif /* NOTDEF */ + + ioctl (tty, TIOCSETC, &temp); + } +#endif /* TIOCGETC */ + +#ifdef TIOCGLTC + { + struct ltchars temp; + + ioctl (tty, TIOCGLTC, &original_ltchars); + temp = original_ltchars; + + /* Make the interrupt keys go away. Just enough to make people + happy. */ + temp.t_dsuspc = -1; /* C-y */ + temp.t_lnextc = -1; /* C-v */ + + ioctl (tty, TIOCSLTC, &temp); + } +#endif /* TIOCGLTC */ + + the_ttybuff.sg_flags &= (~ECHO|CRMOD); + the_ttybuff.sg_flags |= CBREAK; + ioctl (tty, TIOCSETN, &the_ttybuff); + + terminal_prepped = 1; + } + sigsetmask (oldmask); +} + +/* Restore the terminal to its original state. */ +static +rl_deprep_terminal () +{ + int tty = fileno (rl_instream); + int oldmask = sigblock (sigmask (SIGINT)); + + if (terminal_prepped) + { + the_ttybuff.sg_flags = original_tty_flags; + ioctl (tty, TIOCSETN, &the_ttybuff); + readline_echoing_p = 1; + +#if defined (TIOCLGET) + ioctl (tty, TIOCLSET, &local_mode_flags); +#endif + +#ifdef TIOCSLTC + ioctl (tty, TIOCSLTC, &original_ltchars); +#endif + +#ifdef TIOCSETC + ioctl (tty, TIOCSETC, &original_tchars); +#endif + terminal_prepped = 0; + } + sigsetmask (oldmask); +} + +#else /* !defined (NEW_TTY_DRIVER) */ + +#if !defined (VMIN) +#define VMIN VEOF +#endif + +#if !defined (VTIME) +#define VTIME VEOL +#endif + +#if defined (_POSIX_VERSION) +static struct termios otio; +#else +static struct termio otio; +#endif + +static +rl_prep_terminal () +{ + int tty = fileno (rl_instream); +#if defined (_POSIX_VERSION) + struct termios tio; +#else + struct termio tio; +#endif + + /* If we are on a Posix system, block the delivery of SIGINT for a while. */ +#if defined (_POSIX_VERSION) + sigset_t set, oset; + + sigemptyset (&set); + sigaddset (&set, SIGINT); + sigprocmask (SIG_BLOCK, &set, &oset); +#else +# if defined (HAVE_BSD_SIGNALS) + int oldmask = sigblock (sigmask (SIGINT)); +# endif /* HAVE_BSD_SIGNALS */ +#endif /* POSIX */ + + wait_foreground (); /* XXX this prevents to got editing mode from tcsh */ +#if defined (_POSIX_VERSION) + tcgetattr (tty, &tio); +#else + ioctl (tty, TCGETA, &tio); +#endif /* POSIX */ + + otio = tio; + + readline_echoing_p = (tio.c_lflag & ECHO); + + tio.c_lflag &= ~(ICANON|ECHO); + +#if defined (IXANY) + tio.c_iflag &= ~(IXON|IXOFF|IXANY); +#else + /* `strict' Posix systems do not define IXANY. */ + tio.c_iflag &= ~(IXON|IXOFF); +#endif /* IXANY */ + + /* Only turn this off if we are using all 8 bits. */ + /* |ISTRIP|INPCK */ + +#if !defined (HANDLE_SIGNALS) + tio.c_lflag &= ~ISIG; +#else + tio.c_lflag |= ISIG; /* shouldn't be needed, but... */ +#endif + + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + + /* Turn off characters that we need on Posix systems with job control, + just to be sure. This includes ^Y and ^V. This should not really + be necessary. */ +#if defined (_POSIX_VERSION) && defined (_POSIX_JOB_CONTROL) + +#if !defined (_POSIX_VDISABLE) +#define _POSIX_VDISABLE 0 +#endif /* POSIX_VDISABLE */ + +#if defined (VLNEXT) + tio.c_cc[VLNEXT] = _POSIX_VDISABLE; +#endif + +#if defined (VDSUSP) + tio.c_cc[VDSUSP] = _POSIX_VDISABLE; +#endif + +#endif /* POSIX && JOB_CONTROL */ + +#if defined (_POSIX_VERSION) + tcsetattr (tty, TCSADRAIN, &tio); + tcflow (tty, TCOON); /* Simulate a ^Q. */ +#else + ioctl (tty, TCSETAW, &tio); + ioctl (tty, TCXONC, 1); /* Simulate a ^Q. */ +#endif /* POSIX */ + +#if defined (_POSIX_VERSION) + sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL); +#else +# if defined (HAVE_BSD_SIGNALS) + sigsetmask (oldmask); +# endif /* HAVE_BSD_SIGNALS */ +#endif /* POSIX */ +} + +static +rl_deprep_terminal () +{ + int tty = fileno (rl_instream); + + /* If we are on a Posix system, block the delivery of SIGINT for a while. */ +#if defined (_POSIX_VERSION) + sigset_t set, oset; + + sigemptyset (&set); + sigaddset (&set, SIGINT); + sigprocmask (SIG_BLOCK, &set, &oset); +#else +# if defined (HAVE_BSD_SIGNALS) + int oldmask = sigblock (sigmask (SIGINT)); +# endif /* HAVE_BSD_SIGNALS */ +#endif /* POSIX */ + +#if defined (_POSIX_VERSION) + tcsetattr (tty, TCSADRAIN, &otio); + tcflow (tty, TCOON); /* Simulate a ^Q. */ +#else + ioctl (tty, TCSETAW, &otio); + ioctl (tty, TCXONC, 1); /* Simulate a ^Q. */ +#endif /* POSIX */ + +#if defined (_POSIX_VERSION) + sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL); +#else +# if defined (HAVE_BSD_SIGNALS) + sigsetmask (oldmask); +# endif /* HAVE_BSD_SIGNALS */ +#endif /* POSIX */ +} +#endif /* NEW_TTY_DRIVER */ + + +/* **************************************************************** */ +/* */ +/* Utility Functions */ +/* */ +/* **************************************************************** */ + +/* Return 0 if C is not a member of the class of characters that belong + in words, or 1 if it is. */ + +int allow_pathname_alphabetic_chars = 0; +char *pathname_alphabetic_chars = "/-_=~.#$"; + +int +alphabetic (c) + int c; +{ + if (pure_alphabetic (c) || (numeric (c))) + return (1); + + if (allow_pathname_alphabetic_chars) + return ((int)rindex (pathname_alphabetic_chars, c)); + else + return (0); +} + +/* Return non-zero if C is a numeric character. */ +int +numeric (c) + int c; +{ + return (c >= '0' && c <= '9'); +} + +/* Ring the terminal bell. */ +int +ding () +{ + if (readline_echoing_p) + { + if (prefer_visible_bell && visible_bell) + tputs (visible_bell, 1, output_character_function); + else + { + fprintf (stderr, "\007"); + fflush (stderr); + } + } + return (-1); +} + +/* How to abort things. */ +rl_abort () +{ + ding (); + rl_clear_message (); + rl_init_argument (); + rl_pending_input = 0; + + defining_kbd_macro = 0; + while (executing_macro) + pop_executing_macro (); + + rl_last_func = (Function *)NULL; + longjmp (readline_top_level, 1); +} + +/* Return a copy of the string between FROM and TO. + FROM is inclusive, TO is not. */ +#if defined (sun) /* Yes, that's right, some crufty function in sunview is + called rl_copy (). */ +static +#endif +char * +rl_copy (from, to) + int from, to; +{ + register int length; + char *copy; + + /* Fix it if the caller is confused. */ + if (from > to) { + int t = from; + from = to; + to = t; + } + + length = to - from; + copy = (char *)xmalloc (1 + length); + strncpy (copy, the_line + from, length); + copy[length] = '\0'; + return (copy); +} + + +/* **************************************************************** */ +/* */ +/* Insert and Delete */ +/* */ +/* **************************************************************** */ + +/* Insert a string of text into the line at point. This is the only + way that you should do insertion. rl_insert () calls this + function. */ +rl_insert_text (string) + char *string; +{ + extern int doing_an_undo; + register int i, l = strlen (string); + while (rl_end + l >= rl_line_buffer_len) + { + rl_line_buffer = + (char *)xrealloc (rl_line_buffer, + rl_line_buffer_len += DEFAULT_BUFFER_SIZE); + the_line = rl_line_buffer; + } + + for (i = rl_end; i >= rl_point; i--) + the_line[i + l] = the_line[i]; + strncpy (the_line + rl_point, string, l); + + /* Remember how to undo this if we aren't undoing something. */ + if (!doing_an_undo) + { + /* If possible and desirable, concatenate the undos. */ + if ((strlen (string) == 1) && + rl_undo_list && + (rl_undo_list->what == UNDO_INSERT) && + (rl_undo_list->end == rl_point) && + (rl_undo_list->end - rl_undo_list->start < 20)) + rl_undo_list->end++; + else + rl_add_undo (UNDO_INSERT, rl_point, rl_point + l, (char *)NULL); + } + rl_point += l; + rl_end += l; + the_line[rl_end] = '\0'; +} + +/* Delete the string between FROM and TO. FROM is + inclusive, TO is not. */ +rl_delete_text (from, to) + int from, to; +{ + extern int doing_an_undo; + register char *text; + + /* Fix it if the caller is confused. */ + if (from > to) + { + int t = from; + from = to; + to = t; + } + text = rl_copy (from, to); + strncpy (the_line + from, the_line + to, rl_end - to); + + /* Remember how to undo this delete. */ + if (!doing_an_undo) + rl_add_undo (UNDO_DELETE, from, to, text); + else + free (text); + + rl_end -= (to - from); + the_line[rl_end] = '\0'; +} + + +/* **************************************************************** */ +/* */ +/* Readline character functions */ +/* */ +/* **************************************************************** */ + +/* This is not a gap editor, just a stupid line input routine. No hair + is involved in writing any of the functions, and none should be. */ + +/* Note that: + + rl_end is the place in the string that we would place '\0'; + i.e., it is always safe to place '\0' there. + + rl_point is the place in the string where the cursor is. Sometimes + this is the same as rl_end. + + Any command that is called interactively receives two arguments. + The first is a count: the numeric arg pased to this command. + The second is the key which invoked this command. +*/ + + +/* **************************************************************** */ +/* */ +/* Movement Commands */ +/* */ +/* **************************************************************** */ + +/* Note that if you `optimize' the display for these functions, you cannot + use said functions in other functions which do not do optimizing display. + I.e., you will have to update the data base for rl_redisplay, and you + might as well let rl_redisplay do that job. */ + +/* Move forward COUNT characters. */ +rl_forward (count) + int count; +{ + if (count < 0) + rl_backward (-count); + else + while (count) + { +#ifdef VI_MODE + if (rl_point == (rl_end - (rl_editing_mode == vi_mode))) +#else + if (rl_point == rl_end) +#endif + { + ding (); + return; + } + else + rl_point++; + --count; + } +} + +/* Move backward COUNT characters. */ +rl_backward (count) + int count; +{ + if (count < 0) + rl_forward (-count); + else + while (count) + { + if (!rl_point) + { + ding (); + return; + } + else + --rl_point; + --count; + } +} + +/* Move to the beginning of the line. */ +rl_beg_of_line () +{ + rl_point = 0; +} + +/* Move to the end of the line. */ +rl_end_of_line () +{ + rl_point = rl_end; +} + +/* Move forward a word. We do what Emacs does. */ +rl_forward_word (count) + int count; +{ + int c; + + if (count < 0) + { + rl_backward_word (-count); + return; + } + + while (count) + { + if (rl_point == rl_end) + return; + + /* If we are not in a word, move forward until we are in one. + Then, move forward until we hit a non-alphabetic character. */ + c = the_line[rl_point]; + if (!alphabetic (c)) + { + while (++rl_point < rl_end) + { + c = the_line[rl_point]; + if (alphabetic (c)) break; + } + } + if (rl_point == rl_end) return; + while (++rl_point < rl_end) + { + c = the_line[rl_point]; + if (!alphabetic (c)) break; + } + --count; + } +} + +/* Move backward a word. We do what Emacs does. */ +rl_backward_word (count) + int count; +{ + int c; + + if (count < 0) + { + rl_forward_word (-count); + return; + } + + while (count) + { + if (!rl_point) + return; + + /* Like rl_forward_word (), except that we look at the characters + just before point. */ + + c = the_line[rl_point - 1]; + if (!alphabetic (c)) + { + while (--rl_point) + { + c = the_line[rl_point - 1]; + if (alphabetic (c)) break; + } + } + + while (rl_point) + { + c = the_line[rl_point - 1]; + if (!alphabetic (c)) + break; + else --rl_point; + } + --count; + } +} + +/* Clear the current line. Numeric argument to C-l does this. */ +rl_refresh_line () +{ + int curr_line = last_c_pos / screenwidth; + extern char *term_clreol; + + move_vert(curr_line); + move_cursor_relative (0, the_line); /* XXX is this right */ + + if (term_clreol) + tputs (term_clreol, 1, output_character_function); + + rl_forced_update_display (); + rl_display_fixed = 1; +} + +/* C-l typed to a line without quoting clears the screen, and then reprints + the prompt and the current input line. Given a numeric arg, redraw only + the current line. */ +rl_clear_screen () +{ + extern char *term_clrpag; + + if (rl_explicit_arg) + { + rl_refresh_line (); + return; + } + + if (term_clrpag) + tputs (term_clrpag, 1, output_character_function); + else + crlf (); + + rl_forced_update_display (); + rl_display_fixed = 1; +} + +rl_arrow_keys(count,c) + int count,c; +{ + int ch = rl_read_key(); + + switch(ch) { + case 'a': + case 'A': + rl_get_previous_history(count); + return; + case 'b': + case 'B': + rl_get_next_history(count); + return; + case 'c': + case 'C': + rl_forward(count); + return; + case 'd': + case 'D': + rl_backward(count); + return; + default: + ding(); + return; + } +} + + +/* **************************************************************** */ +/* */ +/* Text commands */ +/* */ +/* **************************************************************** */ + +/* Insert the character C at the current location, moving point forward. */ +rl_insert (count, c) + int count, c; +{ + register int i; + char *string; + + if (count <= 0) + return; + + /* If we can optimize, then do it. But don't let people crash + readline because of extra large arguments. */ + if (count > 1 && count < 1024) + { + string = (char *)alloca (1 + count); + + for (i = 0; i < count; i++) + string[i] = c; + + string[i] = '\0'; + rl_insert_text (string); + return; + } + + if (count > 1024) + { + int decreaser; + + string = (char *)alloca (1024 + 1); + + for (i = 0; i < 1024; i++) + string[i] = c; + + while (count) + { + decreaser = (count > 1024 ? 1024 : count); + string[decreaser] = '\0'; + rl_insert_text (string); + count -= decreaser; + } + return; + } + + /* We are inserting a single character. + If there is pending input, then make a string of all of the + pending characters that are bound to rl_insert, and insert + them all. */ + if (any_typein) + { + int key = 0, t; + + i = 0; + string = (char *)alloca (ibuffer_len + 1); + string[i++] = c; + + while ((t = rl_get_char (&key)) && + (keymap[key].type == ISFUNC && + keymap[key].function == rl_insert)) + string[i++] = key; + + if (t) + rl_unget_char (key); + + string[i] = '\0'; + rl_insert_text (string); + return; + } + else + { + /* Inserting a single character. */ + string = (char *)alloca (2); + + string[1] = '\0'; + string[0] = c; + rl_insert_text (string); + } +} + +/* Insert the next typed character verbatim. */ +rl_quoted_insert (count) + int count; +{ + int c = rl_read_key (); + rl_insert (count, c); +} + +/* Insert a tab character. */ +rl_tab_insert (count) + int count; +{ + rl_insert (count, '\t'); +} + +/* What to do when a NEWLINE is pressed. We accept the whole line. + KEY is the key that invoked this command. I guess it could have + meaning in the future. */ +rl_newline (count, key) + int count, key; +{ + + rl_done = 1; + +#ifdef VI_MODE + { + extern int vi_doing_insert; + if (vi_doing_insert) + { + rl_end_undo_group (); + vi_doing_insert = 0; + } + } +#endif /* VI_MODE */ + + if (readline_echoing_p) + { + move_vert (vis_botlin); + vis_botlin = 0; + crlf (); + fflush (out_stream); + rl_display_fixed++; + } +} + +rl_clean_up_for_exit () +{ + if (readline_echoing_p) + { + move_vert (vis_botlin); + vis_botlin = 0; + fflush (out_stream); + rl_restart_output (); + } +} + +/* What to do for some uppercase characters, like meta characters, + and some characters appearing in emacs_ctlx_keymap. This function + is just a stub, you bind keys to it and the code in rl_dispatch () + is special cased. */ +rl_do_lowercase_version (ignore1, ignore2) + int ignore1, ignore2; +{ +} + +/* Rubout the character behind point. */ +rl_rubout (count) + int count; +{ + if (count < 0) + { + rl_delete (-count); + return; + } + + if (!rl_point) + { + ding (); + return; + } + + if (count > 1) + { + int orig_point = rl_point; + rl_backward (count); + rl_kill_text (orig_point, rl_point); + } + else + { + int c = the_line[--rl_point]; + rl_delete_text (rl_point, rl_point + 1); + + if (rl_point == rl_end && alphabetic (c) && last_c_pos) + { + backspace (1); + putc (' ', out_stream); + backspace (1); + last_c_pos--; + visible_line[last_c_pos] = '\0'; + rl_display_fixed++; + } + } +} + +/* Delete the character under the cursor. Given a numeric argument, + kill that many characters instead. */ +rl_delete (count, invoking_key) + int count, invoking_key; +{ + if (count < 0) + { + rl_rubout (-count); + return; + } + + if (rl_point == rl_end) + { + ding (); + return; + } + + if (count > 1) + { + int orig_point = rl_point; + rl_forward (count); + rl_kill_text (orig_point, rl_point); + rl_point = orig_point; + } + else + rl_delete_text (rl_point, rl_point + 1); +} + + +/* **************************************************************** */ +/* */ +/* Kill commands */ +/* */ +/* **************************************************************** */ + +/* The next two functions mimic unix line editing behaviour, except they + save the deleted text on the kill ring. This is safer than not saving + it, and since we have a ring, nobody should get screwed. */ + +/* This does what C-w does in Unix. We can't prevent people from + using behaviour that they expect. */ +rl_unix_word_rubout () +{ + if (!rl_point) ding (); + else { + int orig_point = rl_point; + while (rl_point && whitespace (the_line[rl_point - 1])) + rl_point--; + while (rl_point && !whitespace (the_line[rl_point - 1])) + rl_point--; + rl_kill_text (rl_point, orig_point); + } +} + +/* Here is C-u doing what Unix does. You don't *have* to use these + key-bindings. We have a choice of killing the entire line, or + killing from where we are to the start of the line. We choose the + latter, because if you are a Unix weenie, then you haven't backspaced + into the line at all, and if you aren't, then you know what you are + doing. */ +rl_unix_line_discard () +{ + if (!rl_point) ding (); + else { + rl_kill_text (rl_point, 0); + rl_point = 0; + } +} + + + +/* **************************************************************** */ +/* */ +/* Commands For Typos */ +/* */ +/* **************************************************************** */ + +/* Random and interesting things in here. */ + +/* **************************************************************** */ +/* */ +/* Changing Case */ +/* */ +/* **************************************************************** */ + +/* The three kinds of things that we know how to do. */ +#define UpCase 1 +#define DownCase 2 +#define CapCase 3 + +/* Uppercase the word at point. */ +rl_upcase_word (count) + int count; +{ + rl_change_case (count, UpCase); +} + +/* Lowercase the word at point. */ +rl_downcase_word (count) + int count; +{ + rl_change_case (count, DownCase); +} + +/* Upcase the first letter, downcase the rest. */ +rl_capitalize_word (count) + int count; +{ + rl_change_case (count, CapCase); +} + +/* The meaty function. + Change the case of COUNT words, performing OP on them. + OP is one of UpCase, DownCase, or CapCase. + If a negative argument is given, leave point where it started, + otherwise, leave it where it moves to. */ +rl_change_case (count, op) + int count, op; +{ + register int start = rl_point, end; + int state = 0; + + rl_forward_word (count); + end = rl_point; + + if (count < 0) + { + int temp = start; + start = end; + end = temp; + } + + /* We are going to modify some text, so let's prepare to undo it. */ + rl_modifying (start, end); + + for (; start < end; start++) + { + switch (op) + { + case UpCase: + the_line[start] = to_upper (the_line[start]); + break; + + case DownCase: + the_line[start] = to_lower (the_line[start]); + break; + + case CapCase: + if (state == 0) + { + the_line[start] = to_upper (the_line[start]); + state = 1; + } + else + { + the_line[start] = to_lower (the_line[start]); + } + if (!pure_alphabetic (the_line[start])) + state = 0; + break; + + default: + abort (); + } + } + rl_point = end; +} + +/* **************************************************************** */ +/* */ +/* Transposition */ +/* */ +/* **************************************************************** */ + +/* Transpose the words at point. */ +rl_transpose_words (count) + int count; +{ + char *word1, *word2; + int w1_beg, w1_end, w2_beg, w2_end; + int orig_point = rl_point; + + if (!count) return; + + /* Find the two words. */ + rl_forward_word (count); + w2_end = rl_point; + rl_backward_word (1); + w2_beg = rl_point; + rl_backward_word (count); + w1_beg = rl_point; + rl_forward_word (1); + w1_end = rl_point; + + /* Do some check to make sure that there really are two words. */ + if ((w1_beg == w2_beg) || (w2_beg < w1_end)) + { + ding (); + rl_point = orig_point; + return; + } + + /* Get the text of the words. */ + word1 = rl_copy (w1_beg, w1_end); + word2 = rl_copy (w2_beg, w2_end); + + /* We are about to do many insertions and deletions. Remember them + as one operation. */ + rl_begin_undo_group (); + + /* Do the stuff at word2 first, so that we don't have to worry + about word1 moving. */ + rl_point = w2_beg; + rl_delete_text (w2_beg, w2_end); + rl_insert_text (word1); + + rl_point = w1_beg; + rl_delete_text (w1_beg, w1_end); + rl_insert_text (word2); + + /* This is exactly correct since the text before this point has not + changed in length. */ + rl_point = w2_end; + + /* I think that does it. */ + rl_end_undo_group (); + free (word1); free (word2); +} + +/* Transpose the characters at point. If point is at the end of the line, + then transpose the characters before point. */ +rl_transpose_chars (count) + int count; +{ + if (!count) + return; + + if (!rl_point || rl_end < 2) { + ding (); + return; + } + + while (count) { + if (rl_point == rl_end) { + int t = the_line[rl_point - 1]; + the_line[rl_point - 1] = the_line[rl_point - 2]; + the_line[rl_point - 2] = t; + } else { + int t = the_line[rl_point]; + the_line[rl_point] = the_line[rl_point - 1]; + the_line[rl_point - 1] = t; + if (count < 0 && rl_point) + rl_point--; + else + rl_point++; + } + if (count < 0) + count++; + else + count--; + } +} + + +/* **************************************************************** */ +/* */ +/* Bogus Flow Control */ +/* */ +/* **************************************************************** */ + +rl_restart_output (count, key) + int count, key; +{ + int fildes = fileno (stdin); +#if defined (TIOCSTART) +#if defined (apollo) + ioctl (&fildes, TIOCSTART, 0); +#else + ioctl (fildes, TIOCSTART, 0); +#endif /* apollo */ + +#else +# if defined (TCXONC) + ioctl (fildes, TCXONC, TCOON); +# endif /* TCXONC */ +#endif /* TIOCSTART */ +} + +/* **************************************************************** */ +/* */ +/* Completion matching, from readline's point of view. */ +/* */ +/* **************************************************************** */ + +/* Pointer to the generator function for completion_matches (). + NULL means to use filename_entry_function (), the default filename + completer. */ +Function *rl_completion_entry_function = (Function *)NULL; + +/* Pointer to alternative function to create matches. + Function is called with TEXT, START, and END. + START and END are indices in RL_LINE_BUFFER saying what the boundaries + of TEXT are. + If this function exists and returns NULL then call the value of + rl_completion_entry_function to try to match, otherwise use the + array of strings returned. */ +Function *rl_attempted_completion_function = (Function *)NULL; + +/* Complete the word at or before point. You have supplied the function + that does the initial simple matching selection algorithm (see + completion_matches ()). The default is to do filename completion. */ +rl_complete (ignore, invoking_key) + int ignore, invoking_key; +{ + if (rl_last_func == rl_complete) + rl_complete_internal ('?'); + else + rl_complete_internal (TAB); +} + +/* List the possible completions. See description of rl_complete (). */ +rl_possible_completions () +{ + rl_complete_internal ('?'); +} + +/* The user must press "y" or "n". Non-zero return means "y" pressed. */ +get_y_or_n () +{ + int c; + loop: + c = rl_read_key (); + if (c == 'y' || c == 'Y') return (1); + if (c == 'n' || c == 'N') return (0); + if (c == ABORT_CHAR) rl_abort (); + ding (); goto loop; +} + +/* Up to this many items will be displayed in response to a + possible-completions call. After that, we ask the user if + she is sure she wants to see them all. */ +int rl_completion_query_items = 100; + +/* The basic list of characters that signal a break between words for the + completer routine. The contents of this variable is what breaks words + in the shell, i.e. " \t\n\"\\'`@$><=" */ +char *rl_basic_word_break_characters = " \t\n\"\\'`@$><="; + +/* The list of characters that signal a break between words for + rl_complete_internal. The default list is the contents of + rl_basic_word_break_characters. */ +char *rl_completer_word_break_characters = (char *)NULL; + +/* List of characters that are word break characters, but should be left + in TEXT when it is passed to the completion function. The shell uses + this to help determine what kind of completing to do. */ +char *rl_special_prefixes = (char *)NULL; + +/* If non-zero, then disallow duplicates in the matches. */ +int rl_ignore_completion_duplicates = 1; + +/* Non-zero means that the results of the matches are to be treated + as filenames. This is ALWAYS zero on entry, and can only be changed + within a completion entry finder function. */ +int rl_filename_completion_desired = 0; + +/* This function, if defined, is called by the completer when real + filename completion is done, after all the matching names have been + generated. It is passed a (char**) known as matches in the code below. + It consists of a NULL-terminated array of pointers to potential + matching strings. The 1st element (matches[0]) is the maximal + substring that is common to all matches. This function can re-arrange + the list of matches as required, but all elements of the array must be + free()'d if they are deleted. The main intent of this function is + to implement FIGNORE a la SunOS csh. */ +Function *rl_ignore_some_completions_function = (Function *)NULL; + +/* Complete the word at or before point. + WHAT_TO_DO says what to do with the completion. + `?' means list the possible completions. + TAB means do standard completion. + `*' means insert all of the possible completions. */ +rl_complete_internal (what_to_do) + int what_to_do; +{ + char *filename_completion_function (); + char **completion_matches (), **matches; + Function *our_func; + int start, end, delimiter = 0; + char *text; + + if (rl_completion_entry_function) + our_func = rl_completion_entry_function; + else + our_func = (int (*)())filename_completion_function; + + /* Only the completion entry function can change this. */ + rl_filename_completion_desired = 0; + + /* We now look backwards for the start of a filename/variable word. */ + end = rl_point; + + if (rl_point) + { + while (--rl_point && + !rindex (rl_completer_word_break_characters, the_line[rl_point])); + + /* If we are at a word break, then advance past it. */ + if (rindex (rl_completer_word_break_characters, (the_line[rl_point]))) + { + /* If the character that caused the word break was a quoting + character, then remember it as the delimiter. */ + if (rindex ("\"'", the_line[rl_point]) && (end - rl_point) > 1) + delimiter = the_line[rl_point]; + + /* If the character isn't needed to determine something special + about what kind of completion to perform, then advance past it. */ + + if (!rl_special_prefixes || + !rindex (rl_special_prefixes, the_line[rl_point])) + rl_point++; + } + } + + start = rl_point; + rl_point = end; + text = rl_copy (start, end); + + /* If the user wants to TRY to complete, but then wants to give + up and use the default completion function, they set the + variable rl_attempted_completion_function. */ + if (rl_attempted_completion_function) + { + matches = + (char **)(*rl_attempted_completion_function) (text, start, end); + + if (matches) + { + our_func = (Function *)NULL; + goto after_usual_completion; + } + } + + matches = completion_matches (text, our_func); + + after_usual_completion: + free (text); + + if (!matches) + ding (); + else + { + register int i; + + some_matches: + + /* It seems to me that in all the cases we handle we would like + to ignore duplicate possiblilities. Scan for the text to + insert being identical to the other completions. */ + if (rl_ignore_completion_duplicates) + { + char *lowest_common; + int j, newlen = 0; + + /* Sort the items. */ + /* It is safe to sort this array, because the lowest common + denominator found in matches[0] will remain in place. */ + for (i = 0; matches[i]; i++); + qsort (matches, i, sizeof (char *), compare_strings); + + /* Remember the lowest common denominator for it may be unique. */ + lowest_common = savestring (matches[0]); + + for (i = 0; matches[i + 1]; i++) + { + if (strcmp (matches[i], matches[i + 1]) == 0) + { + free (matches[i]); + matches[i] = (char *)-1; + } + else + newlen++; + } + + /* We have marked all the dead slots with (char *)-1. + Copy all the non-dead entries into a new array. */ + { + char **temp_array = + (char **)malloc ((3 + newlen) * sizeof (char *)); + + for (i = 1, j = 1; matches[i]; i++) + { + if (matches[i] != (char *)-1) + temp_array[j++] = matches[i]; + } + + temp_array[j] = (char *)NULL; + + if (matches[0] != (char *)-1) + free (matches[0]); + + free (matches); + + matches = temp_array; + } + + /* Place the lowest common denominator back in [0]. */ + matches[0] = lowest_common; + + /* If there is one string left, and it is identical to the + lowest common denominator, then the LCD is the string to + insert. */ + if (j == 2 && strcmp (matches[0], matches[1]) == 0) + { + free (matches[1]); + matches[1] = (char *)NULL; + } + } + + switch (what_to_do) + { + case TAB: + /* If we are matching filenames, then here is our chance to + do clever processing by re-examining the list. Call the + ignore function with the array as a parameter. It can + munge the array, deleting matches as it desires */ + if (rl_ignore_some_completions_function && + our_func == (int (*)())filename_completion_function) + (void)(*rl_ignore_some_completions_function)(matches); + + if (matches[0]) + { + rl_delete_text (start, rl_point); + rl_point = start; + rl_insert_text (matches[0]); + } + + /* If there are more matches, ring the bell to indicate. + If this was the only match, and we are hacking files, + check the file to see if it was a directory. If so, + add a '/' to the name. If not, and we are at the end + of the line, then add a space. */ + if (matches[1]) + { + ding (); /* There are other matches remaining. */ + } + else + { + char temp_string[2]; + + temp_string[0] = delimiter ? delimiter : ' '; + temp_string[1] = '\0'; + + if (rl_filename_completion_desired) + { + struct stat finfo; + char *tilde_expand (); + char *filename = tilde_expand (matches[0]); + + if ((stat (filename, &finfo) == 0) && + ((finfo.st_mode & S_IFMT) == S_IFDIR)) + { + if (the_line[rl_point] != '/') + rl_insert_text ("/"); + } + else + { + if (rl_point == rl_end) + rl_insert_text (temp_string); + } + free (filename); + } + else + { + if (rl_point == rl_end) + rl_insert_text (temp_string); + } + } + break; + + case '*': + { + int i = 1; + + rl_delete_text (start, rl_point); + rl_point = start; + rl_begin_undo_group (); + if (matches[1]) + { + while (matches[i]) + { + rl_insert_text (matches[i++]); + rl_insert_text (" "); + } + } + else + { + rl_insert_text (matches[0]); + rl_insert_text (" "); + } + rl_end_undo_group (); + } + break; + + case '?': + { + int len, count, limit, max = 0; + int j, k, l; + + /* Handle simple case first. What if there is only one answer? */ + if (!matches[1]) + { + char *temp; + + if (rl_filename_completion_desired) + temp = rindex (matches[0], '/'); + else + temp = (char *)NULL; + + if (!temp) + temp = matches[0]; + else + temp++; + + crlf (); + fprintf (out_stream, "%s", temp); + crlf (); + goto restart; + } + + /* There is more than one answer. Find out how many there are, + and find out what the maximum printed length of a single entry + is. */ + for (i = 1; matches[i]; i++) + { + char *temp = (char *)NULL; + + /* If we are hacking filenames, then only count the characters + after the last slash in the pathname. */ + if (rl_filename_completion_desired) + temp = rindex (matches[i], '/'); + else + temp = (char *)NULL; + + if (!temp) + temp = matches[i]; + else + temp++; + + if (strlen (temp) > max) + max = strlen (temp); + } + + len = i; + + /* If there are many items, then ask the user if she + really wants to see them all. */ + if (len >= rl_completion_query_items) + { + crlf (); + fprintf (out_stream, + "There are %d possibilities. Do you really", len); + crlf (); + fprintf (out_stream, "wish to see them all? (y or n)"); + fflush (out_stream); + if (!get_y_or_n ()) + { + crlf (); + goto restart; + } + } + /* How many items of MAX length can we fit in the screen window? */ + max += 2; + limit = screenwidth / max; + if (limit != 1 && (limit * max == screenwidth)) + limit--; + + /* Avoid a possible floating exception. If max > screenwidth, + limit will be 0 and a divide-by-zero fault will result. */ + if (limit == 0) + limit = 1; + + /* How many iterations of the printing loop? */ + count = (len + (limit - 1)) / limit; + + /* Watch out for special case. If LEN is less than LIMIT, then + just do the inner printing loop. */ + if (len < limit) count = 1; + + /* Sort the items if they are not already sorted. */ + if (!rl_ignore_completion_duplicates) + qsort (matches, len, sizeof (char *), compare_strings); + + /* Print the sorted items, up-and-down alphabetically, like + ls might. */ + crlf (); + + for (i = 1; i < count + 1; i++) + { + for (j = 0, l = i; j < limit; j++) + { + if (l > len || !matches[l]) + { + break; + } + else + { + char *temp = (char *)NULL; + + if (rl_filename_completion_desired) + temp = rindex (matches[l], '/'); + else + temp = (char *)NULL; + + if (!temp) + temp = matches[l]; + else + temp++; + + fprintf (out_stream, "%s", temp); + for (k = 0; k < max - strlen (temp); k++) + putc (' ', out_stream); + } + l += count; + } + crlf (); + } + restart: + + rl_on_new_line (); + } + break; + + default: + abort (); + } + + for (i = 0; matches[i]; i++) + free (matches[i]); + free (matches); + } +} + +/* Stupid comparison routine for qsort () ing strings. */ +static int +compare_strings (s1, s2) + char **s1, **s2; +{ + return (strcmp (*s1, *s2)); +} + +/* A completion function for usernames. + TEXT contains a partial username preceded by a random + character (usually `~'). */ +char * +username_completion_function (text, state) + int state; + char *text; +{ + static char *username = (char *)NULL; + static struct passwd *entry; + static int namelen, first_char, first_char_loc; + + if (!state) + { + if (username) + free (username); + + first_char = *text; + + if (first_char == '~') + first_char_loc = 1; + else + first_char_loc = 0; + + username = savestring (&text[first_char_loc]); + namelen = strlen (username); + setpwent (); + } + + while (entry = getpwent ()) + { + if (strncmp (username, entry->pw_name, namelen) == 0) + break; + } + + if (!entry) + { + endpwent (); + return ((char *)NULL); + } + else + { + char *value = (char *)xmalloc (2 + strlen (entry->pw_name)); + + *value = *text; + + strcpy (value + first_char_loc, entry->pw_name); + + if (first_char == '~') + rl_filename_completion_desired = 1; + + return (value); + } +} + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +Function *rl_tilde_expander = (Function *)NULL; + +/* Expand FILENAME if it begins with a tilde. This always returns + a new string. */ +char * +tilde_expand (filename) + char *filename; +{ + char *dirname = filename ? savestring (filename) : (char *)NULL; + + if (dirname && *dirname == '~') + { + char *temp_name; + if (!dirname[1] || dirname[1] == '/') + { + /* Prepend $HOME to the rest of the string. */ + char *temp_home = getenv ("HOME"); + + temp_name = (char *)alloca (1 + strlen (&dirname[1]) + + (temp_home? strlen (temp_home) : 0)); + temp_name[0] = '\0'; + if (temp_home) + strcpy (temp_name, temp_home); + strcat (temp_name, &dirname[1]); + free (dirname); + dirname = savestring (temp_name); + } + else + { + struct passwd *getpwnam (), *user_entry; + char *username = (char *)alloca (257); + int i, c; + + for (i = 1; c = dirname[i]; i++) + { + if (c == '/') break; + else username[i - 1] = c; + } + username[i - 1] = '\0'; + + if (!(user_entry = getpwnam (username))) + { + /* If the calling program has a special syntax for + expanding tildes, and we couldn't find a standard + expansion, then let them try. */ + if (rl_tilde_expander) + { + char *expansion; + + expansion = (char *)(*rl_tilde_expander) (username); + + if (expansion) + { + temp_name = (char *)alloca (1 + strlen (expansion) + + strlen (&dirname[i])); + strcpy (temp_name, expansion); + strcat (temp_name, &dirname[i]); + free (expansion); + goto return_name; + } + } + /* + * We shouldn't report errors. + */ + } + else + { + temp_name = (char *)alloca (1 + strlen (user_entry->pw_dir) + + strlen (&dirname[i])); + strcpy (temp_name, user_entry->pw_dir); + strcat (temp_name, &dirname[i]); + return_name: + free (dirname); + dirname = savestring (temp_name); + } + endpwent (); + } + } + return (dirname); +} + + +/* **************************************************************** */ +/* */ +/* Undo, and Undoing */ +/* */ +/* **************************************************************** */ + +/* Non-zero tells rl_delete_text and rl_insert_text to not add to + the undo list. */ +int doing_an_undo = 0; + +/* The current undo list for THE_LINE. */ +UNDO_LIST *rl_undo_list = (UNDO_LIST *)NULL; + +/* Remember how to undo something. Concatenate some undos if that + seems right. */ +rl_add_undo (what, start, end, text) + enum undo_code what; + int start, end; + char *text; +{ + UNDO_LIST *temp = (UNDO_LIST *)xmalloc (sizeof (UNDO_LIST)); + temp->what = what; + temp->start = start; + temp->end = end; + temp->text = text; + temp->next = rl_undo_list; + rl_undo_list = temp; +} + +/* Free the existing undo list. */ +free_undo_list () +{ + while (rl_undo_list) { + UNDO_LIST *release = rl_undo_list; + rl_undo_list = rl_undo_list->next; + + if (release->what == UNDO_DELETE) + free (release->text); + + free (release); + } +} + +/* Undo the next thing in the list. Return 0 if there + is nothing to undo, or non-zero if there was. */ +int +rl_do_undo () +{ + UNDO_LIST *release; + int waiting_for_begin = 0; + +undo_thing: + if (!rl_undo_list) + return (0); + + doing_an_undo = 1; + + switch (rl_undo_list->what) { + + /* Undoing deletes means inserting some text. */ + case UNDO_DELETE: + rl_point = rl_undo_list->start; + rl_insert_text (rl_undo_list->text); + free (rl_undo_list->text); + break; + + /* Undoing inserts means deleting some text. */ + case UNDO_INSERT: + rl_delete_text (rl_undo_list->start, rl_undo_list->end); + rl_point = rl_undo_list->start; + break; + + /* Undoing an END means undoing everything 'til we get to + a BEGIN. */ + case UNDO_END: + waiting_for_begin++; + break; + + /* Undoing a BEGIN means that we are done with this group. */ + case UNDO_BEGIN: + if (waiting_for_begin) + waiting_for_begin--; + else + abort (); + break; + } + + doing_an_undo = 0; + + release = rl_undo_list; + rl_undo_list = rl_undo_list->next; + free (release); + + if (waiting_for_begin) + goto undo_thing; + + return (1); +} + +/* Begin a group. Subsequent undos are undone as an atomic operation. */ +rl_begin_undo_group () +{ + rl_add_undo (UNDO_BEGIN, 0, 0, 0); +} + +/* End an undo group started with rl_begin_undo_group (). */ +rl_end_undo_group () +{ + rl_add_undo (UNDO_END, 0, 0, 0); +} + +/* Save an undo entry for the text from START to END. */ +rl_modifying (start, end) + int start, end; +{ + if (start > end) + { + int t = start; + start = end; + end = t; + } + + if (start != end) + { + char *temp = rl_copy (start, end); + rl_begin_undo_group (); + rl_add_undo (UNDO_DELETE, start, end, temp); + rl_add_undo (UNDO_INSERT, start, end, (char *)NULL); + rl_end_undo_group (); + } +} + +/* Revert the current line to its previous state. */ +rl_revert_line () +{ + if (!rl_undo_list) ding (); + else { + while (rl_undo_list) + rl_do_undo (); + } +} + +/* Do some undoing of things that were done. */ +rl_undo_command (count) +{ + if (count < 0) return; /* Nothing to do. */ + + while (count) + { + if (rl_do_undo ()) + { + count--; + } + else + { + ding (); + break; + } + } +} + +/* **************************************************************** */ +/* */ +/* History Utilities */ +/* */ +/* **************************************************************** */ + +/* We already have a history library, and that is what we use to control + the history features of readline. However, this is our local interface + to the history mechanism. */ + +/* While we are editing the history, this is the saved + version of the original line. */ +HIST_ENTRY *saved_line_for_history = (HIST_ENTRY *)NULL; + +/* Set the history pointer back to the last entry in the history. */ +start_using_history () +{ + using_history (); + if (saved_line_for_history) + free_history_entry (saved_line_for_history); + + saved_line_for_history = (HIST_ENTRY *)NULL; +} + +/* Free the contents (and containing structure) of a HIST_ENTRY. */ +free_history_entry (entry) + HIST_ENTRY *entry; +{ + if (!entry) return; + if (entry->line) + free (entry->line); + free (entry); +} + +/* Perhaps put back the current line if it has changed. */ +maybe_replace_line () +{ + HIST_ENTRY *temp = current_history (); + + /* If the current line has changed, save the changes. */ + if (temp && ((UNDO_LIST *)(temp->data) != rl_undo_list)) { + temp = replace_history_entry (where_history (), the_line, rl_undo_list); + free (temp->line); + free (temp); + } +} + +/* Put back the saved_line_for_history if there is one. */ +maybe_unsave_line () +{ + if (saved_line_for_history) { + strcpy (the_line, saved_line_for_history->line); + rl_undo_list = (UNDO_LIST *)saved_line_for_history->data; + free_history_entry (saved_line_for_history); + saved_line_for_history = (HIST_ENTRY *)NULL; + rl_end = rl_point = strlen (the_line); + } else { + ding (); + } +} + +/* Save the current line in saved_line_for_history. */ +maybe_save_line () +{ + if (!saved_line_for_history) { + saved_line_for_history = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); + saved_line_for_history->line = savestring (the_line); + saved_line_for_history->data = (char *)rl_undo_list; + } +} + + +/* **************************************************************** */ +/* */ +/* History Commands */ +/* */ +/* **************************************************************** */ + +/* Meta-< goes to the start of the history. */ +rl_beginning_of_history () +{ + rl_get_previous_history (1 + where_history ()); +} + +/* Meta-> goes to the end of the history. (The current line). */ +rl_end_of_history () +{ + maybe_replace_line (); + using_history (); + maybe_unsave_line (); +} + +/* Move down to the next history line. */ +rl_get_next_history (count) + int count; +{ + HIST_ENTRY *temp = (HIST_ENTRY *)NULL; + + if (count < 0) + { + rl_get_previous_history (-count); + return; + } + + if (!count) + return; + + maybe_replace_line (); + + while (count) + { + temp = next_history (); + if (!temp) + break; + --count; + } + + if (!temp) + maybe_unsave_line (); + else + { + strcpy (the_line, temp->line); + rl_undo_list = (UNDO_LIST *)temp->data; + rl_end = rl_point = strlen (the_line); + } +} + +/* Get the previous item out of our interactive history, making it the current + line. If there is no previous history, just ding. */ +rl_get_previous_history (count) + int count; +{ + HIST_ENTRY *old_temp = (HIST_ENTRY *)NULL; + HIST_ENTRY *temp = (HIST_ENTRY *)NULL; + + if (count < 0) + { + rl_get_next_history (-count); + return; + } + + if (!count) + return; + + /* If we don't have a line saved, then save this one. */ + maybe_save_line (); + + /* If the current line has changed, save the changes. */ + maybe_replace_line (); + + while (count) + { + temp = previous_history (); + if (!temp) + break; + else + old_temp = temp; + --count; + } + + /* If there was a large argument, and we moved back to the start of the + history, that is not an error. So use the last value found. */ + if (!temp && old_temp) + temp = old_temp; + + if (!temp) + ding (); + else + { + strcpy (the_line, temp->line); + rl_undo_list = (UNDO_LIST *)temp->data; + rl_end = rl_point = strlen (the_line); +#ifdef VI_MODE + if (rl_editing_mode == vi_mode) + rl_point = 0; +#endif /* VI_MODE */ + } +} + +/* There is a command in the K*rn shell which yanks into this line, the last + word of the previous line. Here it is. We left it on M-. */ +rl_yank_previous_last_arg (ignore) + int ignore; +{ +} + + +/* **************************************************************** */ +/* */ +/* I-Search and Searching */ +/* */ +/* **************************************************************** */ + +/* Search backwards through the history looking for a string which is typed + interactively. Start with the current line. */ +rl_reverse_search_history (sign, key) + int sign; + int key; +{ + rl_search_history (-sign, key); +} + +/* Search forwards through the history looking for a string which is typed + interactively. Start with the current line. */ +rl_forward_search_history (sign, key) + int sign; + int key; +{ + rl_search_history (sign, key); +} + +/* Display the current state of the search in the echo-area. + SEARCH_STRING contains the string that is being searched for, + DIRECTION is zero for forward, or 1 for reverse, + WHERE is the history list number of the current line. If it is + -1, then this line is the starting one. */ +rl_display_search (search_string, reverse_p, where) + char *search_string; + int reverse_p, where; +{ + char *message = (char *)NULL; + + message = + (char *)alloca (1 + (search_string ? strlen (search_string) : 0) + 30); + + *message = '\0'; + +#ifdef NEVER + if (where != -1) + sprintf (message, "[%d]", where + history_base); +#endif + + strcat (message, "("); + + if (reverse_p) + strcat (message, "reverse-"); + + strcat (message, "i-search)`"); + + if (search_string) + strcat (message, search_string); + + strcat (message, "': "); + rl_message (message, 0, 0); + rl_redisplay (); +} + +/* Search through the history looking for an interactively typed string. + This is analogous to i-search. We start the search in the current line. + DIRECTION is which direction to search; > 0 means forward, < 0 means + backwards. */ +rl_search_history (direction, invoking_key) + int direction; + int invoking_key; +{ + /* The string that the user types in to search for. */ + char *search_string = (char *)alloca (128); + + /* The current length of SEARCH_STRING. */ + int search_string_index; + + /* The list of lines to search through. */ + char **lines; + + /* The length of LINES. */ + int hlen; + + /* Where we get LINES from. */ + HIST_ENTRY **hlist = history_list (); + + register int i = 0; + int orig_point = rl_point; + int orig_line = where_history (); + int last_found_line = orig_line; + int c, done = 0; + + /* The line currently being searched. */ + char *sline; + + /* Offset in that line. */ + int index; + + /* Non-zero if we are doing a reverse search. */ + int reverse = (direction < 0); + + /* Create an arrary of pointers to the lines that we want to search. */ + + maybe_replace_line (); + if (hlist) + for (i = 0; hlist[i]; i++); + + /* Allocate space for this many lines, +1 for the current input line, + and remember those lines. */ + lines = (char **)alloca ((1 + (hlen = i)) * sizeof (char *)); + for (i = 0; i < hlen; i++) + lines[i] = hlist[i]->line; + + if (saved_line_for_history) + lines[i] = saved_line_for_history->line; + else + { + /* So I have to type it in this way instead. */ + lines[i] = (char *)alloca (1 + strlen (the_line)); + strcpy (lines[i], &the_line[0]); + } + + hlen++; + + /* The line where we start the search. */ + i = orig_line; + + /* Initialize search parameters. */ + *search_string = '\0'; + search_string_index = 0; + + rl_display_search (search_string, reverse, -1); + + sline = the_line; + index = rl_point; + + while (!done) + { + c = rl_read_key (); + + /* Hack C to Do What I Mean. */ + { + Function *f = (Function *)NULL; + + if (keymap[c].type == ISFUNC) + f = keymap[c].function; + + if (f == rl_reverse_search_history) + c = reverse ? -1 : -2; + else if (f == rl_forward_search_history) + c = !reverse ? -1 : -2; + } + + switch (c) + { + case ESC: + done = 1; + continue; + + /* case invoking_key: */ + case -1: + goto search_again; + + /* switch directions */ + case -2: + direction = -direction; + reverse = (direction < 0); + + goto do_search; + + case CTRL ('G'): + strcpy (the_line, lines[orig_line]); + rl_point = orig_point; + rl_end = strlen (the_line); + rl_clear_message (); + return; + + default: + if (c < 32 || c > 126) + { + rl_execute_next (c); + done = 1; + continue; + } + else + { + search_string[search_string_index++] = c; + search_string[search_string_index] = '\0'; + goto do_search; + + search_again: + + if (!search_string_index) + continue; + else + { + if (reverse) + --index; + else + if (index != strlen (sline)) + ++index; + else + ding (); + } + do_search: + + while (1) + { + if (reverse) + { + while (index >= 0) + if (strncmp + (search_string, + sline + index, + search_string_index) == 0) + goto string_found; + else + index--; + } + else + { + register int limit = + (strlen (sline) - search_string_index) + 1; + + while (index < limit) + { + if (strncmp (search_string, + sline + index, + search_string_index) == 0) + goto string_found; + index++; + } + } + + next_line: + i += direction; + + /* At limit for direction? */ + if ((reverse && i < 0) || + (!reverse && i == hlen)) + goto search_failed; + + sline = lines[i]; + if (reverse) + index = strlen (sline); + else + index = 0; + + /* If the search string is longer than the current + line, no match. */ + if (search_string_index > strlen (sline)) + goto next_line; + + /* Start actually searching. */ + if (reverse) + index -= search_string_index; + } + + search_failed: + /* We cannot find the search string. Ding the bell. */ + ding (); + i = last_found_line; + break; + + string_found: + /* We have found the search string. Just display it. But don't + actually move there in the history list until the user accepts + the location. */ + strcpy (the_line, lines[i]); + rl_point = index; + rl_end = strlen (the_line); + last_found_line = i; + rl_display_search (search_string, reverse, + (i == orig_line) ? -1 : i); + } + } + continue; + } + + /* The searching is over. The user may have found the string that she + was looking for, or else she may have exited a failing search. If + INDEX is -1, then that shows that the string searched for was not + found. We use this to determine where to place rl_point. */ + { + int now = last_found_line; + + /* First put back the original state. */ + strcpy (the_line, lines[orig_line]); + + if (now < orig_line) + rl_get_previous_history (orig_line - now); + else + rl_get_next_history (now - orig_line); + + /* If the index of the "matched" string is less than zero, then the + final search string was never matched, so put point somewhere + reasonable. */ + if (index < 0) + index = strlen (the_line); + + rl_point = index; + rl_clear_message (); + } +} + +/* Make C be the next command to be executed. */ +rl_execute_next (c) + int c; +{ + rl_pending_input = c; +} + +/* **************************************************************** */ +/* */ +/* Killing Mechanism */ +/* */ +/* **************************************************************** */ + +/* What we assume for a max number of kills. */ +#define DEFAULT_MAX_KILLS 10 + +/* The real variable to look at to find out when to flush kills. */ +int rl_max_kills = DEFAULT_MAX_KILLS; + +/* Where to store killed text. */ +char **rl_kill_ring = (char **)NULL; + +/* Where we are in the kill ring. */ +int rl_kill_index = 0; + +/* How many slots we have in the kill ring. */ +int rl_kill_ring_length = 0; + +/* How to say that you only want to save a certain amount + of kill material. */ +rl_set_retained_kills (num) + int num; +{} + +/* The way to kill something. This appends or prepends to the last + kill, if the last command was a kill command. if FROM is less + than TO, then the text is appended, otherwise prepended. If the + last command was not a kill command, then a new slot is made for + this kill. */ +rl_kill_text (from, to) + int from, to; +{ + int slot; + char *text = rl_copy (from, to); + + /* Is there anything to kill? */ + if (from == to) + { + free (text); + last_command_was_kill++; + return; + } + + /* Delete the copied text from the line. */ + rl_delete_text (from, to); + + /* First, find the slot to work with. */ + if (!last_command_was_kill) + { + /* Get a new slot. */ + if (!rl_kill_ring) + { + /* If we don't have any defined, then make one. */ + rl_kill_ring = (char **) + xmalloc (((rl_kill_ring_length = 1) + 1) * sizeof (char *)); + slot = 1; + } + else + { + /* We have to add a new slot on the end, unless we have + exceeded the max limit for remembering kills. */ + slot = rl_kill_ring_length; + if (slot == rl_max_kills) + { + register int i; + free (rl_kill_ring[0]); + for (i = 0; i < slot; i++) + rl_kill_ring[i] = rl_kill_ring[i + 1]; + } + else + { + rl_kill_ring = + (char **) + xrealloc (rl_kill_ring, + ((slot = (rl_kill_ring_length += 1)) + 1) + * sizeof (char *)); + } + } + slot--; + } + else + { + slot = rl_kill_ring_length - 1; + } + + /* If the last command was a kill, prepend or append. */ + if (last_command_was_kill && rl_editing_mode != vi_mode) + { + char *old = rl_kill_ring[slot]; + char *new = (char *)xmalloc (1 + strlen (old) + strlen (text)); + + if (from < to) + { + strcpy (new, old); + strcat (new, text); + } + else + { + strcpy (new, text); + strcat (new, old); + } + free (old); + free (text); + rl_kill_ring[slot] = new; + } + else + { + rl_kill_ring[slot] = text; + } + rl_kill_index = slot; + last_command_was_kill++; +} + +/* Now REMEMBER! In order to do prepending or appending correctly, kill + commands always make rl_point's original position be the FROM argument, + and rl_point's extent be the TO argument. */ + +/* **************************************************************** */ +/* */ +/* Killing Commands */ +/* */ +/* **************************************************************** */ + +/* Delete the word at point, saving the text in the kill ring. */ +rl_kill_word (count) + int count; +{ + int orig_point = rl_point; + + if (count < 0) + rl_backward_kill_word (-count); + else + { + rl_forward_word (count); + + if (rl_point != orig_point) + rl_kill_text (orig_point, rl_point); + + rl_point = orig_point; + } +} + +/* Rubout the word before point, placing it on the kill ring. */ +rl_backward_kill_word (count) + int count; +{ + int orig_point = rl_point; + + if (count < 0) + rl_kill_word (-count); + else + { + rl_backward_word (count); + + if (rl_point != orig_point) + rl_kill_text (orig_point, rl_point); + } +} + +/* Kill from here to the end of the line. If DIRECTION is negative, kill + back to the line start instead. */ +rl_kill_line (direction) + int direction; +{ + int orig_point = rl_point; + + if (direction < 0) + rl_backward_kill_line (1); + else + { + rl_end_of_line (); + if (orig_point != rl_point) + rl_kill_text (orig_point, rl_point); + rl_point = orig_point; + } +} + +/* Kill backwards to the start of the line. If DIRECTION is negative, kill + forwards to the line end instead. */ +rl_backward_kill_line (direction) + int direction; +{ + int orig_point = rl_point; + + if (direction < 0) + rl_kill_line (1); + else + { + if (!rl_point) + ding (); + else + { + rl_beg_of_line (); + rl_kill_text (orig_point, rl_point); + } + } +} + +/* Yank back the last killed text. This ignores arguments. */ +rl_yank () +{ + if (!rl_kill_ring) rl_abort (); + rl_insert_text (rl_kill_ring[rl_kill_index]); +} + +/* If the last command was yank, or yank_pop, and the text just + before point is identical to the current kill item, then + delete that text from the line, rotate the index down, and + yank back some other text. */ +rl_yank_pop () +{ + int l; + + if (((rl_last_func != rl_yank_pop) && (rl_last_func != rl_yank)) || + !rl_kill_ring) + { + rl_abort (); + } + + l = strlen (rl_kill_ring[rl_kill_index]); + if (((rl_point - l) >= 0) && + (strncmp (the_line + (rl_point - l), + rl_kill_ring[rl_kill_index], l) == 0)) + { + rl_delete_text ((rl_point - l), rl_point); + rl_point -= l; + rl_kill_index--; + if (rl_kill_index < 0) + rl_kill_index = rl_kill_ring_length - 1; + rl_yank (); + } + else + rl_abort (); + +} + +/* Yank the COUNTth argument from the previous history line. */ +rl_yank_nth_arg (count, ignore) + int count; +{ + register HIST_ENTRY *entry = previous_history (); + char *arg; + + if (entry) + next_history (); + else + { + ding (); + return; + } + + arg = history_arg_extract (count, count, entry->line); + if (!arg || !*arg) + { + ding (); + return; + } + + rl_begin_undo_group (); + if (rl_point && the_line[rl_point - 1] != ' ') + rl_insert_text (" "); + rl_insert_text (arg); + free (arg); + rl_end_undo_group (); +} + +/* Vi Mode. */ +#ifdef VI_MODE +#include "vi_mode.c" +#endif /* VI_MODE */ + +/* How to toggle back and forth between editing modes. */ +rl_vi_editing_mode () +{ +#ifdef VI_MODE + rl_editing_mode = vi_mode; + rl_vi_insertion_mode (); +#endif /* VI_MODE */ +} + +rl_emacs_editing_mode () +{ + rl_editing_mode = emacs_mode; + keymap = emacs_standard_keymap; +} + + +/* **************************************************************** */ +/* */ +/* Completion */ +/* */ +/* **************************************************************** */ + +/* Non-zero means that case is not significant in completion. */ +int completion_case_fold = 0; + +/* Return an array of (char *) which is a list of completions for TEXT. + If there are no completions, return a NULL pointer. + The first entry in the returned array is the substitution for TEXT. + The remaining entries are the possible completions. + The array is terminated with a NULL pointer. + + ENTRY_FUNCTION is a function of two args, and returns a (char *). + The first argument is TEXT. + The second is a state argument; it should be zero on the first call, and + non-zero on subsequent calls. It returns a NULL pointer to the caller + when there are no more matches. + */ +char ** +completion_matches (text, entry_function) + char *text; + char *(*entry_function) (); +{ + /* Number of slots in match_list. */ + int match_list_size; + + /* The list of matches. */ + char **match_list = + (char **)xmalloc (((match_list_size = 10) + 1) * sizeof (char *)); + + /* Number of matches actually found. */ + int matches = 0; + + /* Temporary string binder. */ + char *string; + + match_list[1] = (char *)NULL; + + while (string = (*entry_function) (text, matches)) + { + if (matches + 1 == match_list_size) + match_list = + (char **)xrealloc (match_list, + ((match_list_size += 10) + 1) * sizeof (char *)); + + match_list[++matches] = string; + match_list[matches + 1] = (char *)NULL; + } + + /* If there were any matches, then look through them finding out the + lowest common denominator. That then becomes match_list[0]. */ + if (matches) + { + register int i = 1; + int low = 100000; /* Count of max-matched characters. */ + + /* If only one match, just use that. */ + if (matches == 1) + { + match_list[0] = match_list[1]; + match_list[1] = (char *)NULL; + } + else + { + /* Otherwise, compare each member of the list with + the next, finding out where they stop matching. */ + + while (i < matches) + { + register int c1, c2, si; + + if (completion_case_fold) + { + for (si = 0; + (c1 = to_lower(match_list[i][si])) && + (c2 = to_lower(match_list[i + 1][si])); + si++) + if (c1 != c2) break; + } + else + { + for (si = 0; + (c1 = match_list[i][si]) && + (c2 = match_list[i + 1][si]); + si++) + if (c1 != c2) break; + } + + if (low > si) low = si; + i++; + } + match_list[0] = (char *)xmalloc (low + 1); + strncpy (match_list[0], match_list[1], low); + match_list[0][low] = '\0'; + } + } + else /* There were no matches. */ + { + free (match_list); + match_list = (char **)NULL; + } + return (match_list); +} + +/* Okay, now we write the entry_function for filename completion. In the + general case. Note that completion in the shell is a little different + because of all the pathnames that must be followed when looking up the + completion for a command. */ +char * +filename_completion_function (text, state) + int state; + char *text; +{ + static DIR *directory; + static char *filename = (char *)NULL; + static char *dirname = (char *)NULL; + static char *users_dirname = (char *)NULL; + static int filename_len; + + struct direct *entry = (struct direct *)NULL; + + /* If we don't have any state, then do some initialization. */ + if (!state) + { + char *temp; + + if (dirname) free (dirname); + if (filename) free (filename); + if (users_dirname) free (users_dirname); + + filename = savestring (text); + if (!*text) text = "."; + dirname = savestring (text); + + temp = rindex (dirname, '/'); + + if (temp) + { + strcpy (filename, ++temp); + *temp = '\0'; + } + else + strcpy (dirname, "."); + + /* We aren't done yet. We also support the "~user" syntax. */ + + /* Save the version of the directory that the user typed. */ + users_dirname = savestring (dirname); + { + char *tilde_expand (), *temp_dirname = tilde_expand (dirname); + free (dirname); + dirname = temp_dirname; + + if (rl_symbolic_link_hook) + (*rl_symbolic_link_hook) (&dirname); + } + directory = opendir (dirname); + filename_len = strlen (filename); + + rl_filename_completion_desired = 1; + } + + /* At this point we should entertain the possibility of hacking wildcarded + filenames, like /usr/man*\/te. If the directory name contains + globbing characters, then build an array of directories to glob on, and + glob on the first one. */ + + /* Now that we have some state, we can read the directory. */ + + while (directory && (entry = readdir (directory))) + { + /* Special case for no filename. + All entries except "." and ".." match. */ + if (!filename_len) + { + if ((strcmp (entry->d_name, ".") != 0) && + (strcmp (entry->d_name, "..") != 0)) + break; + } + else + { + /* Otherwise, if these match upto the length of filename, then + it is a match. */ + if ((entry->d_namlen >= filename_len) && + (strncmp (filename, entry->d_name, filename_len) == 0)) + { + break; + } + } + } + + if (!entry) + { + if (directory) + { + closedir (directory); + directory = (DIR *)NULL; + } + return (char *)NULL; + } + else + { + char *temp; + + if (dirname && (strcmp (dirname, ".") != 0)) + { + temp = (char *)xmalloc (1 + strlen (users_dirname) + + entry->d_namlen); + strcpy (temp, users_dirname); + strcat (temp, entry->d_name); + } + else + { + temp = (savestring (entry->d_name)); + } + return (temp); + } +} + + +/* **************************************************************** */ +/* */ +/* Binding keys */ +/* */ +/* **************************************************************** */ + +/* rl_add_defun (char *name, Function *function, int key) + Add NAME to the list of named functions. Make FUNCTION + be the function that gets called. + If KEY is not -1, then bind it. */ +rl_add_defun (name, function, key) + char *name; + Function *function; + int key; +{ + if (key != -1) + rl_bind_key (key, function); + rl_add_funmap_entry (name, function); +} + +/* Bind KEY to FUNCTION. Returns non-zero if KEY is out of range. */ +int +rl_bind_key (key, function) + int key; + Function *function; +{ + if (key < 0) + return (key); + + if (key > 127 && key < 256) + { + if (keymap[ESC].type == ISKMAP) + { + Keymap escmap = (Keymap)keymap[ESC].function; + + key -= 128; + escmap[key].type = ISFUNC; + escmap[key].function = function; + return (0); + } + return (key); + } + + keymap[key].type = ISFUNC; + keymap[key].function = function; + return (0); +} + +/* Bind KEY to FUNCTION in MAP. Returns non-zero in case of invalid + KEY. */ +int +rl_bind_key_in_map (key, function, map) + int key; + Function *function; + Keymap map; +{ + int result; + Keymap oldmap = keymap; + + keymap = map; + result = rl_bind_key (key, function); + keymap = oldmap; + return (result); +} + +/* Make KEY do nothing in the currently selected keymap. + Returns non-zero in case of error. */ +int +rl_unbind_key (key) + int key; +{ + return (rl_bind_key (key, (Function *)NULL)); +} + +/* Make KEY do nothing in MAP. + Returns non-zero in case of error. */ +int +rl_unbind_key_in_map (key, map) + int key; + Keymap map; +{ + return (rl_bind_key_in_map (key, (Function *)NULL, map)); +} + +/* Bind the key sequence represented by the string KEYSEQ to + FUNCTION. This makes new keymaps as necessary. The initial + place to do bindings is in MAP. */ +rl_set_key (keyseq, function, map) + char *keyseq; + Function *function; + Keymap map; +{ + rl_generic_bind (ISFUNC, keyseq, function, map); +} + +/* Bind the key sequence represented by the string KEYSEQ to + the string of characters MACRO. This makes new keymaps as + necessary. The initial place to do bindings is in MAP. */ +rl_macro_bind (keyseq, macro, map) + char *keyseq, *macro; + Keymap map; +{ + char *macro_keys = (char *)xmalloc (2 * (strlen (macro))); + int macro_keys_len; + + if (rl_translate_keyseq (macro, macro_keys, ¯o_keys_len)) + { + free (macro_keys); + return; + } + rl_generic_bind (ISMACR, keyseq, macro_keys, map); +} + +/* Bind the key sequence represented by the string KEYSEQ to + the arbitrary pointer DATA. TYPE says what kind of data is + pointed to by DATA, right now this can be a function (ISFUNC), + a macro (ISMACR), or a keymap (ISKMAP). This makes new keymaps + as necessary. The initial place to do bindings is in MAP. */ +rl_generic_bind (type, keyseq, data, map) + int type; + char *keyseq, *data; + Keymap map; +{ + char *keys; + int keys_len; + register int i; + + /* If no keys to bind to, exit right away. */ + if (!keyseq || !*keyseq) + { + if (type == ISMACR) + free (data); + return; + } + + keys = (char *)alloca (1 + (2 * strlen (keyseq))); + + /* Translate the ASCII representation of KEYSEQ into an array + of characters. Stuff the characters into ARRAY, and the + length of ARRAY into LENGTH. */ + if (rl_translate_keyseq (keyseq, keys, &keys_len)) + return; + + /* Bind keys, making new keymaps as necessary. */ + for (i = 0; i < keys_len; i++) + { + if (i + 1 < keys_len) + { + if (map[keys[i]].type != ISKMAP) + { + if (map[i].type == ISMACR) + free ((char *)map[i].function); + + map[keys[i]].type = ISKMAP; + map[keys[i]].function = (Function *)rl_make_bare_keymap (); + } + map = (Keymap)map[keys[i]].function; + } + else + { + if (map[keys[i]].type == ISMACR) + free ((char *)map[keys[i]].function); + + map[keys[i]].function = (Function *)data; + map[keys[i]].type = type; + } + } +} + +/* Translate the ASCII representation of SEQ, stuffing the + values into ARRAY, an array of characters. LEN gets the + final length of ARRAY. Return non-zero if there was an + error parsing SEQ. */ +rl_translate_keyseq (seq, array, len) + char *seq, *array; + int *len; +{ + register int i, c, l = 0; + + for (i = 0; c = seq[i]; i++) + { + if (c == '\\') + { + c = seq[++i]; + + if (!c) + break; + + if (((c == 'C' || c == 'M') && seq[i + 1] == '-') || + (c == 'e')) + { + /* Handle special case of backwards define. */ + if (strncmp (&seq[i], "C-\\M-", 5) == 0) + { + array[l++] = ESC; + i += 5; + array[l++] = CTRL (to_upper (seq[i])); + if (!seq[i]) + i--; + continue; + } + + switch (c) + { + case 'M': + i++; + array[l++] = ESC; + break; + + case 'C': + i += 2; + array[l++] = CTRL (to_upper (seq[i])); + break; + + case 'e': + array[l++] = ESC; + } + + continue; + } + } + array[l++] = c; + } + + *len = l; + array[l] = '\0'; + return (0); +} + +/* Return a pointer to the function that STRING represents. + If STRING doesn't have a matching function, then a NULL pointer + is returned. */ +Function * +rl_named_function (string) + char *string; +{ + register int i; + + for (i = 0; funmap[i]; i++) + if (stricmp (funmap[i]->name, string) == 0) + return (funmap[i]->function); + return ((Function *)NULL); +} + +/* The last key bindings file read. */ +static char *last_readline_init_file = "~/.inputrc"; + +/* Re-read the current keybindings file. */ +rl_re_read_init_file (count, ignore) + int count, ignore; +{ + rl_read_init_file ((char *)NULL); +} + +/* Do key bindings from a file. If FILENAME is NULL it defaults + to `~/.inputrc'. If the file existed and could be opened and + read, 0 is returned, otherwise errno is returned. */ +int +rl_read_init_file (filename) + char *filename; +{ + register int i; + char *buffer, *openname, *line, *end; + struct stat finfo; + int file; + + /* Default the filename. */ + if (!filename) + filename = last_readline_init_file; + + openname = tilde_expand (filename); + + if ((stat (openname, &finfo) < 0) || + (file = open (openname, O_RDONLY, 0666)) < 0) + { + free (openname); + return (errno); + } + else + free (openname); + + last_readline_init_file = filename; + + /* Read the file into BUFFER. */ + buffer = (char *)xmalloc (finfo.st_size + 1); + i = read (file, buffer, finfo.st_size); + close (file); + + if (i != finfo.st_size) + return (errno); + + /* Loop over the lines in the file. Lines that start with `#' are + comments; all other lines are commands for readline initialization. */ + line = buffer; + end = buffer + finfo.st_size; + while (line < end) + { + /* Find the end of this line. */ + for (i = 0; line + i != end && line[i] != '\n'; i++); + + /* Mark end of line. */ + line[i] = '\0'; + + /* If the line is not a comment, then parse it. */ + if (*line != '#') + rl_parse_and_bind (line); + + /* Move to the next line. */ + line += i + 1; + } + return (0); +} + +/* **************************************************************** */ +/* */ +/* Parser Directives */ +/* */ +/* **************************************************************** */ + +/* Conditionals. */ + +/* Calling programs set this to have their argv[0]. */ +char *rl_readline_name = "other"; + +/* Stack of previous values of parsing_conditionalized_out. */ +static unsigned char *if_stack = (unsigned char *)NULL; +static int if_stack_depth = 0; +static int if_stack_size = 0; + +/* Push parsing_conditionalized_out, and set parser state based on ARGS. */ +parser_if (args) + char *args; +{ + register int i; + + /* Push parser state. */ + if (if_stack_depth + 1 >= if_stack_size) + { + if (!if_stack) + if_stack = (unsigned char *)xmalloc (if_stack_size = 20); + else + if_stack = (unsigned char *)xrealloc (if_stack, if_stack_size += 20); + } + if_stack[if_stack_depth++] = parsing_conditionalized_out; + + /* We only check to see if the first word in ARGS is the same as the + value stored in rl_readline_name. */ + + /* Isolate first argument. */ + for (i = 0; args[i] && !whitespace (args[i]); i++); + + if (args[i]) + args[i++] = '\0'; + + /* Handle "if term=foo" construct. If this isn't term=foo, then + check to see if the first word in ARGS is the same as the + value stored in rl_readline_name. */ + if (rl_terminal_name && strnicmp (args, "term=", 5) == 0) + { + char *tem, *tname; + + /* Terminals like "aaa-60" are equivalent to "aaa". */ + tname = savestring (rl_terminal_name); + tem = rindex (tname, '-'); + if (tem) + *tem = '\0'; + + if (stricmp (args + 5, tname) == 0) + parsing_conditionalized_out = 1; + else + parsing_conditionalized_out = 0; + } + else if (stricmp (args, rl_readline_name) == 0) + parsing_conditionalized_out = 0; + else + parsing_conditionalized_out = 1; +} + +/* Invert the current parser state if there is anything on the stack. */ +parser_else (args) + char *args; +{ + if (if_stack_depth) + parsing_conditionalized_out = !parsing_conditionalized_out; + else + { + /* *** What, no error message? *** */ + } +} + +/* Terminate a conditional, popping the value of + parsing_conditionalized_out from the stack. */ +parser_endif (args) + char *args; +{ + if (if_stack_depth) + parsing_conditionalized_out = if_stack[--if_stack_depth]; + else + { + /* *** What, no error message? *** */ + } +} + +/* Associate textual names with actual functions. */ +static struct { + char *name; + Function *function; +} parser_directives [] = { + { "if", parser_if }, + { "endif", parser_endif }, + { "else", parser_else }, + { (char *)0x0, (Function *)0x0 } +}; + +/* Handle a parser directive. STATEMENT is the line of the directive + without any leading `$'. */ +static int +handle_parser_directive (statement) + char *statement; +{ + register int i; + char *directive, *args; + + /* Isolate the actual directive. */ + + /* Skip whitespace. */ + for (i = 0; whitespace (statement[i]); i++); + + directive = &statement[i]; + + for (; statement[i] && !whitespace (statement[i]); i++); + + if (statement[i]) + statement[i++] = '\0'; + + for (; statement[i] && whitespace (statement[i]); i++); + + args = &statement[i]; + + /* Lookup the command, and act on it. */ + for (i = 0; parser_directives[i].name; i++) + if (stricmp (directive, parser_directives[i].name) == 0) + { + (*parser_directives[i].function) (args); + return (0); + } + + /* *** Should an error message be output? */ + return (1); +} + +/* Read the binding command from STRING and perform it. + A key binding command looks like: Keyname: function-name\0, + a variable binding command looks like: set variable value. + A new-style keybinding looks like "\C-x\C-x": exchange-point-and-mark. */ +rl_parse_and_bind (string) + char *string; +{ + extern char *possible_control_prefixes[], *possible_meta_prefixes[]; + char *funname, *kname; + static int substring_member_of_array (); + register int c; + int key, i; + + if (!string || !*string || *string == '#') + return; + + /* If this is a parser directive, act on it. */ + if (*string == '$') + { + handle_parser_directive (&string[1]); + return; + } + + /* If we are supposed to be skipping parsing right now, then do it. */ + if (parsing_conditionalized_out) + return; + + i = 0; + /* If this keyname is a complex key expression surrounded by quotes, + advance to after the matching close quote. */ + if (*string == '"') + { + for (i = 1; c = string[i]; i++) + { + if (c == '"' && string[i - 1] != '\\') + break; + } + } + + /* Advance to the colon (:) or whitespace which separates the two objects. */ + for (; (c = string[i]) && c != ':' && c != ' ' && c != '\t'; i++ ); + + /* Mark the end of the command (or keyname). */ + if (string[i]) + string[i++] = '\0'; + + /* If this is a command to set a variable, then do that. */ + if (stricmp (string, "set") == 0) + { + char *var = string + i; + char *value; + + /* Make VAR point to start of variable name. */ + while (*var && whitespace (*var)) var++; + + /* Make value point to start of value string. */ + value = var; + while (*value && !whitespace (*value)) value++; + if (*value) + *value++ = '\0'; + while (*value && whitespace (*value)) value++; + + rl_variable_bind (var, value); + return; + } + + /* Skip any whitespace between keyname and funname. */ + for (; string[i] && whitespace (string[i]); i++); + funname = &string[i]; + + /* Now isolate funname. + For straight function names just look for whitespace, since + that will signify the end of the string. But this could be a + macro definition. In that case, the string is quoted, so skip + to the matching delimiter. */ + if (*funname == '\'' || *funname == '"') + { + int delimiter = string[i++]; + + for (; c = string[i]; i++) + { + if (c == delimiter && string[i - 1] != '\\') + break; + } + if (c) + i++; + } + + /* Advance to the end of the string. */ + for (; string[i] && !whitespace (string[i]); i++); + + /* No extra whitespace at the end of the string. */ + string[i] = '\0'; + + /* If this is a new-style key-binding, then do the binding with + rl_set_key (). Otherwise, let the older code deal with it. */ + if (*string == '"') + { + char *seq = (char *)alloca (1 + strlen (string)); + register int j, k = 0; + + for (j = 1; string[j]; j++) + { + if (string[j] == '"' && string[j - 1] != '\\') + break; + + seq[k++] = string[j]; + } + seq[k] = '\0'; + + /* Binding macro? */ + if (*funname == '\'' || *funname == '"') + { + j = strlen (funname); + + if (j && funname[j - 1] == *funname) + funname[j - 1] = '\0'; + + rl_macro_bind (seq, &funname[1], keymap); + } + else + rl_set_key (seq, rl_named_function (funname), keymap); + + return; + } + + /* Get the actual character we want to deal with. */ + kname = rindex (string, '-'); + if (!kname) + kname = string; + else + kname++; + + key = glean_key_from_name (kname); + + /* Add in control and meta bits. */ + if (substring_member_of_array (string, possible_control_prefixes)) + key = CTRL (to_upper (key)); + + if (substring_member_of_array (string, possible_meta_prefixes)) + key = META (key); + + /* Temporary. Handle old-style keyname with macro-binding. */ + if (*funname == '\'' || *funname == '"') + { + char seq[2]; + int fl = strlen (funname); + + seq[0] = key; seq[1] = '\0'; + if (fl && funname[fl - 1] == *funname) + funname[fl - 1] = '\0'; + + rl_macro_bind (seq, &funname[1], keymap); + } + else + rl_bind_key (key, rl_named_function (funname)); +} + +rl_variable_bind (name, value) + char *name, *value; +{ + if (stricmp (name, "editing-mode") == 0) + { + if (strnicmp (value, "vi", 2) == 0) + { +#ifdef VI_MODE + keymap = vi_insertion_keymap; + rl_editing_mode = vi_mode; +#endif /* VI_MODE */ + } + else if (strnicmp (value, "emacs", 5) == 0) + { + keymap = emacs_standard_keymap; + rl_editing_mode = emacs_mode; + } + } + else if (stricmp (name, "horizontal-scroll-mode") == 0) + { + if (!*value || stricmp (value, "On") == 0) + horizontal_scroll_mode = 1; + else + horizontal_scroll_mode = 0; + } + else if (stricmp (name, "mark-modified-lines") == 0) + { + if (!*value || stricmp (value, "On") == 0) + mark_modified_lines = 1; + else + mark_modified_lines = 0; + } + else if (stricmp (name, "prefer-visible-bell") == 0) + { + if (!*value || stricmp (value, "On") == 0) + prefer_visible_bell = 1; + else + prefer_visible_bell = 0; + } +} + +/* Return the character which matches NAME. + For example, `Space' returns ' '. */ + +typedef struct { + char *name; + int value; +} assoc_list; + +assoc_list name_key_alist[] = { + { "Space", ' ' }, + { "SPC", ' ' }, + { "Rubout", 0x7f }, + { "DEL", 0x7f }, + { "Tab", 0x09 }, + { "Newline", '\n' }, + { "Return", '\r' }, + { "RET", '\r' }, + { "LFD", '\n' }, + { "Escape", '\033' }, + { "ESC", '\033' }, + + { (char *)0x0, 0 } +}; + +int +glean_key_from_name (name) + char *name; +{ + register int i; + + for (i = 0; name_key_alist[i].name; i++) + if (stricmp (name, name_key_alist[i].name) == 0) + return (name_key_alist[i].value); + + return (*name); +} + + +/* **************************************************************** */ +/* */ +/* String Utility Functions */ +/* */ +/* **************************************************************** */ + +/* Return non-zero if any members of ARRAY are a substring in STRING. */ +static int +substring_member_of_array (string, array) + char *string, **array; +{ + static char *strindex (); + + while (*array) + { + if (strindex (string, *array)) + return (1); + array++; + } + return (0); +} + +/* Determine if s2 occurs in s1. If so, return a pointer to the + match in s1. The compare is case insensitive. */ +static char * +strindex (s1, s2) + register char *s1, *s2; +{ + register int i, l = strlen (s2); + register int len = strlen (s1); + + for (i = 0; (len - i) >= l; i++) + if (strnicmp (&s1[i], s2, l) == 0) + return (s1 + i); + return ((char *)NULL); +} + + +/* **************************************************************** */ +/* */ +/* USG (System V) Support */ +/* */ +/* **************************************************************** */ + +/* When compiling and running in the `Posix' environment, Ultrix does + not restart system calls, so this needs to do it. */ +int +rl_getc (stream) + FILE *stream; +{ + int result; + unsigned char c; + + while (1) + { + result = read (fileno (stream), &c, sizeof (char)); + + if (result == sizeof (char)) + return (c); + + if (errno != EINTR) + return (EOF); + } +} + +#ifdef STATIC_MALLOC + +/* **************************************************************** */ +/* */ +/* xmalloc and xrealloc () */ +/* */ +/* **************************************************************** */ + +static void memory_error_and_abort (); + +static char * +xmalloc (bytes) + int bytes; +{ + char *temp = (char *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static char * +xrealloc (pointer, bytes) + char *pointer; + int bytes; +{ + char *temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static void +memory_error_and_abort () +{ + fprintf (stderr, "readline: Out of virtual memory!\n"); + abort (); +} +#endif /* STATIC_MALLOC */ + + +/* **************************************************************** */ +/* */ +/* Testing Readline */ +/* */ +/* **************************************************************** */ + +#if defined (TEST) + +main () +{ + HIST_ENTRY **history_list (); + char *temp = (char *)NULL; + char *prompt = "readline% "; + int done = 0; + + while (!done) + { + temp = readline (prompt); + + /* Test for EOF. */ + if (!temp) + exit (1); + + /* If there is anything on the line, print it and remember it. */ + if (*temp) + { + fprintf (stderr, "%s\r\n", temp); + add_history (temp); + } + + /* Check for `command' that we handle. */ + if (strcmp (temp, "quit") == 0) + done = 1; + + if (strcmp (temp, "list") == 0) { + HIST_ENTRY **list = history_list (); + register int i; + if (list) { + for (i = 0; list[i]; i++) { + fprintf (stderr, "%d: %s\r\n", i, list[i]->line); + free (list[i]->line); + } + free (list); + } + } + free (temp); + } +} + +#endif /* TEST */ + + +/* + * Local variables: + * compile-command: "gcc -g -traditional -I. -I.. -DTEST -o readline readline.c keymaps.o funmap.o history.o -ltermcap" + * end: + */ diff --git a/gnu/lib/libreadline/readline/chardefs.h b/gnu/lib/libreadline/readline/chardefs.h new file mode 100644 index 0000000..9749ae4 --- /dev/null +++ b/gnu/lib/libreadline/readline/chardefs.h @@ -0,0 +1,50 @@ +/* chardefs.h -- Character definitions for readline. */ +#ifndef _CHARDEFS_ + +#ifndef savestring +#define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x)) +#endif + +#ifndef whitespace +#define whitespace(c) (((c) == ' ') || ((c) == '\t')) +#endif + +#ifdef CTRL +#undef CTRL +#endif + +/* Some character stuff. */ +#define control_character_threshold 0x020 /* smaller than this is control */ +#define meta_character_threshold 0x07f /* larger than this is Meta. */ +#define control_character_bit 0x40 /* 0x000000, must be off. */ +#define meta_character_bit 0x080 /* x0000000, must be on. */ + +#define CTRL(c) ((c) & (~control_character_bit)) +#define META(c) ((c) | meta_character_bit) + +#define UNMETA(c) ((c) & (~meta_character_bit)) +#define UNCTRL(c) to_upper(((c)|control_character_bit)) + +#define lowercase_p(c) (((c) > ('a' - 1) && (c) < ('z' + 1))) +#define uppercase_p(c) (((c) > ('A' - 1) && (c) < ('Z' + 1))) + +#define pure_alphabetic(c) (lowercase_p(c) || uppercase_p(c)) + +#ifndef to_upper +#define to_upper(c) (lowercase_p(c) ? ((c) - 32) : (c)) +#define to_lower(c) (uppercase_p(c) ? ((c) + 32) : (c)) +#endif + +#define CTRL_P(c) ((c) < control_character_threshold) +#define META_P(c) ((c) > meta_character_threshold) + +#define NEWLINE '\n' +#define RETURN CTRL('M') +#define RUBOUT 0x07f +#define TAB '\t' +#define ABORT_CHAR CTRL('G') +#define PAGE CTRL('L') +#define SPACE 0x020 +#define ESC CTRL('[') + +#endif /* _CHARDEFS_ */ diff --git a/gnu/lib/libreadline/readline/keymaps.h b/gnu/lib/libreadline/readline/keymaps.h new file mode 100644 index 0000000..a0b5aeb --- /dev/null +++ b/gnu/lib/libreadline/readline/keymaps.h @@ -0,0 +1,51 @@ +/* keymaps.h -- Manipulation of readline keymaps. */ + +#ifndef _KEYMAPS_H_ +#define _KEYMAPS_H_ + +#include + +#ifndef __FUNCTION_DEF +typedef int Function (); +#define __FUNCTION_DEF +#endif + +/* A keymap contains one entry for each key in the ASCII set. + Each entry consists of a type and a pointer. + POINTER is the address of a function to run, or the + address of a keymap to indirect through. + TYPE says which kind of thing POINTER is. */ +typedef struct _keymap_entry { + char type; + Function *function; +} KEYMAP_ENTRY; + +/* I wanted to make the above structure contain a union of: + union { Function *function; struct _keymap_entry *keymap; } value; + but this made it impossible for me to create a static array. + Maybe I need C lessons. */ + +typedef KEYMAP_ENTRY KEYMAP_ENTRY_ARRAY[128]; +typedef KEYMAP_ENTRY *Keymap; + +/* The values that TYPE can have in a keymap entry. */ +#define ISFUNC 0 +#define ISKMAP 1 +#define ISMACR 2 + +extern KEYMAP_ENTRY_ARRAY emacs_standard_keymap, emacs_meta_keymap, emacs_ctlx_keymap; +extern KEYMAP_ENTRY_ARRAY vi_insertion_keymap, vi_movement_keymap; + +/* Return a new, empty keymap. + Free it with free() when you are done. */ +Keymap rl_make_bare_keymap (); + +/* Return a new keymap which is a copy of MAP. */ +Keymap rl_copy_keymap (); + +/* Return a new keymap with the printing characters bound to rl_insert, + the lowercase Meta characters bound to run their equivalents, and + the Meta digits bound to produce numeric arguments. */ +Keymap rl_make_keymap (); + +#endif /* _KEYMAPS_H_ */ diff --git a/gnu/lib/libreadline/readline/readline.h b/gnu/lib/libreadline/readline/readline.h new file mode 100644 index 0000000..ad1e27a --- /dev/null +++ b/gnu/lib/libreadline/readline/readline.h @@ -0,0 +1,179 @@ +/* Readline.h -- the names of functions callable from within readline. */ + +#ifndef _READLINE_H_ +#define _READLINE_H_ + +#include + +#ifndef __FUNCTION_DEF +typedef int Function (); +#define __FUNCTION_DEF +#endif + +/* The functions for manipulating the text of the line within readline. +Most of these functions are bound to keys by default. */ +extern int +rl_beg_of_line (), rl_backward (), rl_delete (), rl_end_of_line (), +rl_forward (), ding (), rl_backward (), rl_newline (), rl_kill_line (), +rl_clear_screen (), rl_get_next_history (), rl_get_previous_history (), +rl_quoted_insert (), rl_reverse_search_history (), rl_transpose_chars +(), rl_unix_line_discard (), rl_quoted_insert (), rl_unix_word_rubout +(), rl_yank (), rl_rubout (), rl_backward_word (), rl_kill_word (), +rl_forward_word (), rl_tab_insert (), rl_yank_pop (), rl_yank_nth_arg (), +rl_backward_kill_word (), rl_backward_kill_line (), rl_transpose_words +(), rl_complete (), rl_possible_completions (), rl_do_lowercase_version +(), rl_digit_argument (), rl_universal_argument (), rl_abort (), +rl_undo_command (), rl_revert_line (), rl_beginning_of_history (), +rl_end_of_history (), rl_forward_search_history (), rl_insert (), +rl_upcase_word (), rl_downcase_word (), rl_capitalize_word (), +rl_restart_output (), rl_re_read_init_file (); + +/* These are *both* defined even when VI_MODE is not. */ +extern int rl_vi_editing_mode (), rl_emacs_editing_mode (); + +#ifdef VI_MODE +/* Things for vi mode. */ +extern int rl_vi_movement_mode (), rl_vi_insertion_mode (), rl_vi_arg_digit (), +rl_vi_prev_word (), rl_vi_next_word (), rl_vi_char_search (), +rl_vi_eof_maybe (), rl_vi_append_mode (), rl_vi_put (), +rl_vi_append_eol (), rl_vi_insert_beg (), rl_vi_delete (), rl_vi_comment (), +rl_vi_first_print (), rl_vi_fword (), rl_vi_fWord (), rl_vi_bword (), +rl_vi_bWord (), rl_vi_eword (), rl_vi_eWord (), rl_vi_end_word (), +rl_vi_change_case (), rl_vi_match (), rl_vi_bracktype (), rl_vi_change_char (), +rl_vi_yank_arg (), rl_vi_search (), rl_vi_search_again (), +rl_vi_dosearch (), rl_vi_subst (), rl_vi_overstrike (), +rl_vi_overstrike_delete (), rl_vi_replace(), rl_vi_column (), +rl_vi_delete_to (), rl_vi_change_to (), rl_vi_yank_to (), rl_vi_complete (); +#endif /* VI_MODE */ + +/* Keyboard macro commands. */ +extern int +rl_start_kbd_macro (), rl_end_kbd_macro (), rl_call_last_kbd_macro (); + +extern int rl_arrow_keys(), rl_refresh_line (); + +/* Maintaining the state of undo. We remember individual deletes and inserts + on a chain of things to do. */ + +/* The actions that undo knows how to undo. Notice that UNDO_DELETE means + to insert some text, and UNDO_INSERT means to delete some text. I.e., + the code tells undo what to undo, not how to undo it. */ +enum undo_code { UNDO_DELETE, UNDO_INSERT, UNDO_BEGIN, UNDO_END }; + +/* What an element of THE_UNDO_LIST looks like. */ +typedef struct undo_list { + struct undo_list *next; + int start, end; /* Where the change took place. */ + char *text; /* The text to insert, if undoing a delete. */ + enum undo_code what; /* Delete, Insert, Begin, End. */ +} UNDO_LIST; + +/* The current undo list for RL_LINE_BUFFER. */ +extern UNDO_LIST *rl_undo_list; + +/* The data structure for mapping textual names to code addresses. */ +typedef struct { + char *name; + Function *function; +} FUNMAP; + +extern FUNMAP **funmap; + +/* **************************************************************** */ +/* */ +/* Well Published Variables */ +/* */ +/* **************************************************************** */ + +/* The name of the calling program. You should initialize this to + whatever was in argv[0]. It is used when parsing conditionals. */ +extern char *rl_readline_name; + +/* The line buffer that is in use. */ +extern char *rl_line_buffer; + +/* The location of point, and end. */ +extern int rl_point, rl_end; + +/* The name of the terminal to use. */ +extern char *rl_terminal_name; + +/* The input and output streams. */ +extern FILE *rl_instream, *rl_outstream; + +/* The basic list of characters that signal a break between words for the + completer routine. The initial contents of this variable is what + breaks words in the shell, i.e. "n\"\\'`@$>". */ +extern char *rl_basic_word_break_characters; + +/* The list of characters that signal a break between words for + rl_complete_internal. The default list is the contents of + rl_basic_word_break_characters. */ +extern char *rl_completer_word_break_characters; + +/* List of characters that are word break characters, but should be left + in TEXT when it is passed to the completion function. The shell uses + this to help determine what kind of completing to do. */ +extern char *rl_special_prefixes; + +/* Pointer to the generator function for completion_matches (). + NULL means to use filename_entry_function (), the default filename + completer. */ +extern Function *rl_completion_entry_function; + +/* If rl_ignore_some_completions_function is non-NULL it is the address + of a function to call after all of the possible matches have been + generated, but before the actual completion is done to the input line. + The function is called with one argument; a NULL terminated array + of (char *). If your function removes any of the elements, they + must be free()'ed. */ +extern Function *rl_ignore_some_completions_function; + +/* Pointer to alternative function to create matches. + Function is called with TEXT, START, and END. + START and END are indices in RL_LINE_BUFFER saying what the boundaries + of TEXT are. + If this function exists and returns NULL then call the value of + rl_completion_entry_function to try to match, otherwise use the + array of strings returned. */ +extern Function *rl_attempted_completion_function; + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +extern Function *rl_tilde_expander; + +/* If non-zero, then this is the address of a function to call just + before readline_internal () prints the first prompt. */ +extern Function *rl_startup_hook; + +/* If non-zero, then this is the address of a function to call when + completing on a directory name. The function is called with + the address of a string (the current directory name) as an arg. */ +extern Function *rl_symbolic_link_hook; + +/* Non-zero means that modified history lines are preceded + with an asterisk. */ +extern int rl_show_star; + +/* **************************************************************** */ +/* */ +/* Well Published Functions */ +/* */ +/* **************************************************************** */ + +/* Read a line of input. Prompt with PROMPT. A NULL PROMPT means none. */ +extern char *readline (); + +/* Return an array of strings which are the result of repeatadly calling + FUNC with TEXT. */ +extern char **completion_matches (); + +/* rl_add_defun (char *name, Function *function, int key) + Add NAME to the list of named functions. Make FUNCTION + be the function that gets called. + If KEY is not -1, then bind it. */ +extern int rl_add_defun (); + +#endif /* _READLINE_H_ */ diff --git a/gnu/lib/libreadline/tcsh_hack.readme b/gnu/lib/libreadline/tcsh_hack.readme new file mode 100644 index 0000000..2777512 --- /dev/null +++ b/gnu/lib/libreadline/tcsh_hack.readme @@ -0,0 +1,50 @@ +*** readline.c.orig Thu May 5 04:02:17 1994 +--- readline.c Mon May 9 00:33:44 1994 +*************** +*** 32,37 **** +--- 32,38 ---- + + #include + #include ++ #include + #include + #include + #include +*************** +*** 280,285 **** +--- 281,295 ---- + /* Non-zero means to save keys that we dispatch on in a kbd macro. */ + static int defining_kbd_macro = 0; + ++ /* XXX this prevents to got editing mode from tcsh */ ++ static void wait_foreground(void) ++ { ++ struct winsize w; ++ int tty = fileno (rl_instream); ++ ++ if (ioctl (tty, TIOCGWINSZ, &w) == 0) ++ (void) ioctl (tty, TIOCSWINSZ, &w); ++ } + + /* **************************************************************** */ + /* */ +*************** +*** 1153,1158 **** +--- 1163,1169 ---- + #endif /* POSIX */ + int tty = fileno (rl_instream); + ++ wait_foreground (); /* XXX this prevents to got editing mode from tcsh */ + #if defined (_POSIX_VERSION) + if (tcgetattr (tty, &ttybuff) != -1) + #else +*************** +*** 2307,2312 **** +--- 2318,2324 ---- + # endif /* HAVE_BSD_SIGNALS */ + #endif /* POSIX */ + ++ wait_foreground (); /* XXX this prevents to got editing mode from tcsh */ + #if defined (_POSIX_VERSION) + tcgetattr (tty, &tio); + #else diff --git a/gnu/lib/libreadline/vi_keymap.c b/gnu/lib/libreadline/vi_keymap.c new file mode 100644 index 0000000..712889c --- /dev/null +++ b/gnu/lib/libreadline/vi_keymap.c @@ -0,0 +1,474 @@ +/* vi_keymap.c -- the keymap for vi_mode in readline (). */ + +/* Copyright (C) 1988,1989 Free Software Foundation, Inc. + + This file is part of GNU Readline, a library for reading lines + of text with interactive input and history editing. + + Readline is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 1, or (at your option) any + later version. + + Readline is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline; see the file COPYING. If not, write to the Free + Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef BUFSIZ +#include +#endif /* BUFSIZ */ + +#include "readline.h" + +extern KEYMAP_ENTRY_ARRAY vi_escape_keymap; + +/* The keymap arrays for handling vi mode. */ +KEYMAP_ENTRY_ARRAY vi_movement_keymap = { + + /* The regular control keys come first. */ + { ISFUNC, (Function *)0x0 }, /* Control-@ */ + { ISFUNC, (Function *)0x0 }, /* Control-a */ + { ISFUNC, (Function *)0x0 }, /* Control-b */ + { ISFUNC, (Function *)0x0 }, /* Control-c */ + { ISFUNC, rl_vi_eof_maybe }, /* Control-d */ + { ISFUNC, rl_emacs_editing_mode }, /* Control-e */ + { ISFUNC, (Function *)0x0 }, /* Control-f */ + { ISFUNC, rl_abort }, /* Control-g */ + { ISFUNC, rl_rubout }, /* Control-h */ + { ISFUNC, (Function *)0x0 }, /* Control-i */ + { ISFUNC, rl_newline }, /* Control-j */ + { ISFUNC, rl_kill_line }, /* Control-k */ + { ISFUNC, rl_clear_screen }, /* Control-l */ + { ISFUNC, rl_newline }, /* Control-m */ + { ISFUNC, rl_get_next_history }, /* Control-n */ + { ISFUNC, (Function *)0x0 }, /* Control-o */ + { ISFUNC, rl_get_previous_history }, /* Control-p */ + { ISFUNC, rl_quoted_insert }, /* Control-q */ + { ISFUNC, rl_reverse_search_history }, /* Control-r */ + { ISFUNC, rl_forward_search_history }, /* Control-s */ + { ISFUNC, rl_transpose_chars }, /* Control-t */ + { ISFUNC, rl_unix_line_discard }, /* Control-u */ + { ISFUNC, rl_quoted_insert }, /* Control-v */ + { ISFUNC, rl_unix_word_rubout }, /* Control-w */ + { ISFUNC, (Function *)0x0 }, /* Control-x */ + { ISFUNC, rl_yank }, /* Control-y */ + { ISFUNC, (Function *)0x0 }, /* Control-z */ + + { ISKMAP, (Function *)vi_escape_keymap }, /* Control-[ */ + { ISFUNC, (Function *)0x0 }, /* Control-\ */ + { ISFUNC, (Function *)0x0 }, /* Control-] */ + { ISFUNC, (Function *)0x0 }, /* Control-^ */ + { ISFUNC, rl_undo_command }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, rl_forward }, /* SPACE */ + { ISFUNC, (Function *)0x0 }, /* ! */ + { ISFUNC, (Function *)0x0 }, /* " */ + { ISFUNC, rl_vi_comment }, /* # */ + { ISFUNC, rl_end_of_line }, /* $ */ + { ISFUNC, rl_vi_match }, /* % */ + { ISFUNC, (Function *)0x0 }, /* & */ + { ISFUNC, (Function *)0x0 }, /* ' */ + { ISFUNC, (Function *)0x0 }, /* ( */ + { ISFUNC, (Function *)0x0 }, /* ) */ + { ISFUNC, rl_vi_complete }, /* * */ + { ISFUNC, rl_get_next_history}, /* + */ + { ISFUNC, rl_vi_char_search }, /* , */ + { ISFUNC, rl_get_previous_history }, /* - */ + { ISFUNC, (Function *)0x0 }, /* . */ + { ISFUNC, rl_vi_search }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_vi_arg_digit }, /* 0 */ + { ISFUNC, rl_vi_arg_digit }, /* 1 */ + { ISFUNC, rl_vi_arg_digit }, /* 2 */ + { ISFUNC, rl_vi_arg_digit }, /* 3 */ + { ISFUNC, rl_vi_arg_digit }, /* 4 */ + { ISFUNC, rl_vi_arg_digit }, /* 5 */ + { ISFUNC, rl_vi_arg_digit }, /* 6 */ + { ISFUNC, rl_vi_arg_digit }, /* 7 */ + { ISFUNC, rl_vi_arg_digit }, /* 8 */ + { ISFUNC, rl_vi_arg_digit }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* : */ + { ISFUNC, rl_vi_char_search }, /* ; */ + { ISFUNC, (Function *)0x0 }, /* < */ + { ISFUNC, (Function *)0x0 }, /* = */ + { ISFUNC, (Function *)0x0 }, /* > */ + { ISFUNC, rl_vi_search }, /* ? */ + { ISFUNC, (Function *)0x0 }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_vi_append_eol }, /* A */ + { ISFUNC, rl_vi_prev_word}, /* B */ + { ISFUNC, rl_vi_change_to }, /* C */ + { ISFUNC, rl_vi_delete_to }, /* D */ + { ISFUNC, rl_vi_end_word }, /* E */ + { ISFUNC, rl_vi_char_search }, /* F */ + { ISFUNC, (Function *)0x0 }, /* G */ + { ISFUNC, (Function *)0x0 }, /* H */ + { ISFUNC, rl_vi_insert_beg }, /* I */ + { ISFUNC, (Function *)0x0 }, /* J */ + { ISFUNC, (Function *)0x0 }, /* K */ + { ISFUNC, (Function *)0x0 }, /* L */ + { ISFUNC, (Function *)0x0 }, /* M */ + { ISFUNC, rl_vi_search_again }, /* N */ + { ISFUNC, (Function *)0x0 }, /* O */ + { ISFUNC, rl_vi_put }, /* P */ + { ISFUNC, (Function *)0x0 }, /* Q */ + { ISFUNC, rl_vi_replace }, /* R */ + { ISFUNC, rl_vi_subst }, /* S */ + { ISFUNC, rl_vi_char_search }, /* T */ + { ISFUNC, rl_revert_line }, /* U */ + { ISFUNC, (Function *)0x0 }, /* V */ + { ISFUNC, rl_vi_next_word }, /* W */ + { ISFUNC, rl_rubout }, /* X */ + { ISFUNC, rl_vi_yank_to }, /* Y */ + { ISFUNC, (Function *)0x0 }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* [ */ + { ISFUNC, (Function *)0x0 }, /* \ */ + { ISFUNC, (Function *)0x0 }, /* ] */ + { ISFUNC, rl_vi_first_print }, /* ^ */ + { ISFUNC, rl_vi_yank_arg }, /* _ */ + { ISFUNC, (Function *)0x0 }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, rl_vi_append_mode }, /* a */ + { ISFUNC, rl_vi_prev_word }, /* b */ + { ISFUNC, rl_vi_change_to }, /* c */ + { ISFUNC, rl_vi_delete_to }, /* d */ + { ISFUNC, rl_vi_end_word }, /* e */ + { ISFUNC, rl_vi_char_search }, /* f */ + { ISFUNC, (Function *)0x0 }, /* g */ + { ISFUNC, rl_backward }, /* h */ + { ISFUNC, rl_vi_insertion_mode }, /* i */ + { ISFUNC, rl_get_next_history }, /* j */ + { ISFUNC, rl_get_previous_history }, /* k */ + { ISFUNC, rl_forward }, /* l */ + { ISFUNC, (Function *)0x0 }, /* m */ + { ISFUNC, rl_vi_search_again }, /* n */ + { ISFUNC, (Function *)0x0 }, /* o */ + { ISFUNC, rl_vi_put }, /* p */ + { ISFUNC, (Function *)0x0 }, /* q */ + { ISFUNC, rl_vi_change_char }, /* r */ + { ISFUNC, rl_vi_subst }, /* s */ + { ISFUNC, rl_vi_char_search }, /* t */ + { ISFUNC, rl_undo_command }, /* u */ + { ISFUNC, (Function *)0x0 }, /* v */ + { ISFUNC, rl_vi_next_word }, /* w */ + { ISFUNC, rl_vi_delete }, /* x */ + { ISFUNC, rl_vi_yank_to }, /* y */ + { ISFUNC, (Function *)0x0 }, /* z */ + + /* Final punctuation. */ + { ISFUNC, (Function *)0x0 }, /* { */ + { ISFUNC, rl_vi_column }, /* | */ + { ISFUNC, (Function *)0x0 }, /* } */ + { ISFUNC, rl_vi_change_case }, /* ~ */ + { ISFUNC, rl_backward } /* RUBOUT */ +}; + + +KEYMAP_ENTRY_ARRAY vi_insertion_keymap = { + + /* The regular control keys come first. */ + { ISFUNC, (Function *)0x0 }, /* Control-@ */ + { ISFUNC, rl_insert }, /* Control-a */ + { ISFUNC, rl_insert }, /* Control-b */ + { ISFUNC, rl_insert }, /* Control-c */ + { ISFUNC, rl_vi_eof_maybe }, /* Control-d */ + { ISFUNC, rl_insert }, /* Control-e */ + { ISFUNC, rl_insert }, /* Control-f */ + { ISFUNC, rl_insert }, /* Control-g */ + { ISFUNC, rl_rubout }, /* Control-h */ + { ISFUNC, rl_complete }, /* Control-i */ + { ISFUNC, rl_newline }, /* Control-j */ + { ISFUNC, rl_insert }, /* Control-k */ + { ISFUNC, rl_insert }, /* Control-l */ + { ISFUNC, rl_newline }, /* Control-m */ + { ISFUNC, rl_insert }, /* Control-n */ + { ISFUNC, rl_insert }, /* Control-o */ + { ISFUNC, rl_insert }, /* Control-p */ + { ISFUNC, rl_insert }, /* Control-q */ + { ISFUNC, rl_reverse_search_history }, /* Control-r */ + { ISFUNC, rl_forward_search_history }, /* Control-s */ + { ISFUNC, rl_transpose_chars }, /* Control-t */ + { ISFUNC, rl_unix_line_discard }, /* Control-u */ + { ISFUNC, rl_quoted_insert }, /* Control-v */ + { ISFUNC, rl_unix_word_rubout }, /* Control-w */ + { ISFUNC, rl_insert }, /* Control-x */ + { ISFUNC, rl_yank }, /* Control-y */ + { ISFUNC, rl_insert }, /* Control-z */ + + { ISFUNC, rl_vi_movement_mode }, /* Control-[ */ + { ISFUNC, rl_insert }, /* Control-\ */ + { ISFUNC, rl_insert }, /* Control-] */ + { ISFUNC, rl_insert }, /* Control-^ */ + { ISFUNC, rl_undo_command }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, rl_insert }, /* SPACE */ + { ISFUNC, rl_insert }, /* ! */ + { ISFUNC, rl_insert }, /* " */ + { ISFUNC, rl_insert }, /* # */ + { ISFUNC, rl_insert }, /* $ */ + { ISFUNC, rl_insert }, /* % */ + { ISFUNC, rl_insert }, /* & */ + { ISFUNC, rl_insert }, /* ' */ + { ISFUNC, rl_insert }, /* ( */ + { ISFUNC, rl_insert }, /* ) */ + { ISFUNC, rl_insert }, /* * */ + { ISFUNC, rl_insert }, /* + */ + { ISFUNC, rl_insert }, /* , */ + { ISFUNC, rl_insert }, /* - */ + { ISFUNC, rl_insert }, /* . */ + { ISFUNC, rl_insert }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_insert }, /* 0 */ + { ISFUNC, rl_insert }, /* 1 */ + { ISFUNC, rl_insert }, /* 2 */ + { ISFUNC, rl_insert }, /* 3 */ + { ISFUNC, rl_insert }, /* 4 */ + { ISFUNC, rl_insert }, /* 5 */ + { ISFUNC, rl_insert }, /* 6 */ + { ISFUNC, rl_insert }, /* 7 */ + { ISFUNC, rl_insert }, /* 8 */ + { ISFUNC, rl_insert }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, rl_insert }, /* : */ + { ISFUNC, rl_insert }, /* ; */ + { ISFUNC, rl_insert }, /* < */ + { ISFUNC, rl_insert }, /* = */ + { ISFUNC, rl_insert }, /* > */ + { ISFUNC, rl_insert }, /* ? */ + { ISFUNC, rl_insert }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_insert }, /* A */ + { ISFUNC, rl_insert }, /* B */ + { ISFUNC, rl_insert }, /* C */ + { ISFUNC, rl_insert }, /* D */ + { ISFUNC, rl_insert }, /* E */ + { ISFUNC, rl_insert }, /* F */ + { ISFUNC, rl_insert }, /* G */ + { ISFUNC, rl_insert }, /* H */ + { ISFUNC, rl_insert }, /* I */ + { ISFUNC, rl_insert }, /* J */ + { ISFUNC, rl_insert }, /* K */ + { ISFUNC, rl_insert }, /* L */ + { ISFUNC, rl_insert }, /* M */ + { ISFUNC, rl_insert }, /* N */ + { ISFUNC, rl_insert }, /* O */ + { ISFUNC, rl_insert }, /* P */ + { ISFUNC, rl_insert }, /* Q */ + { ISFUNC, rl_insert }, /* R */ + { ISFUNC, rl_insert }, /* S */ + { ISFUNC, rl_insert }, /* T */ + { ISFUNC, rl_insert }, /* U */ + { ISFUNC, rl_insert }, /* V */ + { ISFUNC, rl_insert }, /* W */ + { ISFUNC, rl_insert }, /* X */ + { ISFUNC, rl_insert }, /* Y */ + { ISFUNC, rl_insert }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, rl_insert }, /* [ */ + { ISFUNC, rl_insert }, /* \ */ + { ISFUNC, rl_insert }, /* ] */ + { ISFUNC, rl_insert }, /* ^ */ + { ISFUNC, rl_insert }, /* _ */ + { ISFUNC, rl_insert }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, rl_insert }, /* a */ + { ISFUNC, rl_insert }, /* b */ + { ISFUNC, rl_insert }, /* c */ + { ISFUNC, rl_insert }, /* d */ + { ISFUNC, rl_insert }, /* e */ + { ISFUNC, rl_insert }, /* f */ + { ISFUNC, rl_insert }, /* g */ + { ISFUNC, rl_insert }, /* h */ + { ISFUNC, rl_insert }, /* i */ + { ISFUNC, rl_insert }, /* j */ + { ISFUNC, rl_insert }, /* k */ + { ISFUNC, rl_insert }, /* l */ + { ISFUNC, rl_insert }, /* m */ + { ISFUNC, rl_insert }, /* n */ + { ISFUNC, rl_insert }, /* o */ + { ISFUNC, rl_insert }, /* p */ + { ISFUNC, rl_insert }, /* q */ + { ISFUNC, rl_insert }, /* r */ + { ISFUNC, rl_insert }, /* s */ + { ISFUNC, rl_insert }, /* t */ + { ISFUNC, rl_insert }, /* u */ + { ISFUNC, rl_insert }, /* v */ + { ISFUNC, rl_insert }, /* w */ + { ISFUNC, rl_insert }, /* x */ + { ISFUNC, rl_insert }, /* y */ + { ISFUNC, rl_insert }, /* z */ + + /* Final punctuation. */ + { ISFUNC, rl_insert }, /* { */ + { ISFUNC, rl_insert }, /* | */ + { ISFUNC, rl_insert }, /* } */ + { ISFUNC, rl_insert }, /* ~ */ + { ISFUNC, rl_rubout } /* RUBOUT */ +}; + +KEYMAP_ENTRY_ARRAY vi_escape_keymap = { + + /* The regular control keys come first. */ + { ISFUNC, (Function *)0x0 }, /* Control-@ */ + { ISFUNC, (Function *)0x0 }, /* Control-a */ + { ISFUNC, (Function *)0x0 }, /* Control-b */ + { ISFUNC, (Function *)0x0 }, /* Control-c */ + { ISFUNC, (Function *)0x0 }, /* Control-d */ + { ISFUNC, (Function *)0x0 }, /* Control-e */ + { ISFUNC, (Function *)0x0 }, /* Control-f */ + { ISFUNC, (Function *)0x0 }, /* Control-g */ + { ISFUNC, (Function *)0x0 }, /* Control-h */ + { ISFUNC, rl_tab_insert}, /* Control-i */ + { ISFUNC, rl_emacs_editing_mode}, /* Control-j */ + { ISFUNC, rl_kill_line }, /* Control-k */ + { ISFUNC, (Function *)0x0 }, /* Control-l */ + { ISFUNC, rl_emacs_editing_mode}, /* Control-m */ + { ISFUNC, (Function *)0x0 }, /* Control-n */ + { ISFUNC, (Function *)0x0 }, /* Control-o */ + { ISFUNC, (Function *)0x0 }, /* Control-p */ + { ISFUNC, (Function *)0x0 }, /* Control-q */ + { ISFUNC, (Function *)0x0 }, /* Control-r */ + { ISFUNC, (Function *)0x0 }, /* Control-s */ + { ISFUNC, (Function *)0x0 }, /* Control-t */ + { ISFUNC, (Function *)0x0 }, /* Control-u */ + { ISFUNC, (Function *)0x0 }, /* Control-v */ + { ISFUNC, (Function *)0x0 }, /* Control-w */ + { ISFUNC, (Function *)0x0 }, /* Control-x */ + { ISFUNC, (Function *)0x0 }, /* Control-y */ + { ISFUNC, (Function *)0x0 }, /* Control-z */ + + { ISFUNC, rl_vi_movement_mode }, /* Control-[ */ + { ISFUNC, (Function *)0x0 }, /* Control-\ */ + { ISFUNC, (Function *)0x0 }, /* Control-] */ + { ISFUNC, (Function *)0x0 }, /* Control-^ */ + { ISFUNC, rl_undo_command }, /* Control-_ */ + + /* The start of printing characters. */ + { ISFUNC, (Function *)0x0 }, /* SPACE */ + { ISFUNC, (Function *)0x0 }, /* ! */ + { ISFUNC, (Function *)0x0 }, /* " */ + { ISFUNC, (Function *)0x0 }, /* # */ + { ISFUNC, (Function *)0x0 }, /* $ */ + { ISFUNC, (Function *)0x0 }, /* % */ + { ISFUNC, (Function *)0x0 }, /* & */ + { ISFUNC, (Function *)0x0 }, /* ' */ + { ISFUNC, (Function *)0x0 }, /* ( */ + { ISFUNC, (Function *)0x0 }, /* ) */ + { ISFUNC, (Function *)0x0 }, /* * */ + { ISFUNC, (Function *)0x0 }, /* + */ + { ISFUNC, (Function *)0x0 }, /* , */ + { ISFUNC, (Function *)0x0 }, /* - */ + { ISFUNC, (Function *)0x0 }, /* . */ + { ISFUNC, (Function *)0x0 }, /* / */ + + /* Regular digits. */ + { ISFUNC, rl_vi_arg_digit }, /* 0 */ + { ISFUNC, rl_vi_arg_digit }, /* 1 */ + { ISFUNC, rl_vi_arg_digit }, /* 2 */ + { ISFUNC, rl_vi_arg_digit }, /* 3 */ + { ISFUNC, rl_vi_arg_digit }, /* 4 */ + { ISFUNC, rl_vi_arg_digit }, /* 5 */ + { ISFUNC, rl_vi_arg_digit }, /* 6 */ + { ISFUNC, rl_vi_arg_digit }, /* 7 */ + { ISFUNC, rl_vi_arg_digit }, /* 8 */ + { ISFUNC, rl_vi_arg_digit }, /* 9 */ + + /* A little more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* : */ + { ISFUNC, (Function *)0x0 }, /* ; */ + { ISFUNC, (Function *)0x0 }, /* < */ + { ISFUNC, (Function *)0x0 }, /* = */ + { ISFUNC, (Function *)0x0 }, /* > */ + { ISFUNC, (Function *)0x0 }, /* ? */ + { ISFUNC, (Function *)0x0 }, /* @ */ + + /* Uppercase alphabet. */ + { ISFUNC, rl_do_lowercase_version }, /* A */ + { ISFUNC, rl_do_lowercase_version }, /* B */ + { ISFUNC, rl_do_lowercase_version }, /* C */ + { ISFUNC, rl_do_lowercase_version }, /* D */ + { ISFUNC, rl_do_lowercase_version }, /* E */ + { ISFUNC, rl_do_lowercase_version }, /* F */ + { ISFUNC, rl_do_lowercase_version }, /* G */ + { ISFUNC, rl_do_lowercase_version }, /* H */ + { ISFUNC, rl_do_lowercase_version }, /* I */ + { ISFUNC, rl_do_lowercase_version }, /* J */ + { ISFUNC, rl_do_lowercase_version }, /* K */ + { ISFUNC, rl_do_lowercase_version }, /* L */ + { ISFUNC, rl_do_lowercase_version }, /* M */ + { ISFUNC, rl_do_lowercase_version }, /* N */ + { ISFUNC, rl_do_lowercase_version }, /* O */ + { ISFUNC, rl_do_lowercase_version }, /* P */ + { ISFUNC, rl_do_lowercase_version }, /* Q */ + { ISFUNC, rl_do_lowercase_version }, /* R */ + { ISFUNC, rl_do_lowercase_version }, /* S */ + { ISFUNC, rl_do_lowercase_version }, /* T */ + { ISFUNC, rl_do_lowercase_version }, /* U */ + { ISFUNC, rl_do_lowercase_version }, /* V */ + { ISFUNC, rl_do_lowercase_version }, /* W */ + { ISFUNC, rl_do_lowercase_version }, /* X */ + { ISFUNC, rl_do_lowercase_version }, /* Y */ + { ISFUNC, rl_do_lowercase_version }, /* Z */ + + /* Some more punctuation. */ + { ISFUNC, (Function *)0x0 }, /* [ */ + { ISFUNC, (Function *)0x0 }, /* \ */ + { ISFUNC, (Function *)0x0 }, /* ] */ + { ISFUNC, (Function *)0x0 }, /* ^ */ + { ISFUNC, (Function *)0x0 }, /* _ */ + { ISFUNC, (Function *)0x0 }, /* ` */ + + /* Lowercase alphabet. */ + { ISFUNC, (Function *)0x0 }, /* a */ + { ISFUNC, (Function *)0x0 }, /* b */ + { ISFUNC, (Function *)0x0 }, /* c */ + { ISFUNC, (Function *)0x0 }, /* d */ + { ISFUNC, (Function *)0x0 }, /* e */ + { ISFUNC, (Function *)0x0 }, /* f */ + { ISFUNC, (Function *)0x0 }, /* g */ + { ISFUNC, (Function *)0x0 }, /* h */ + { ISFUNC, (Function *)0x0 }, /* i */ + { ISFUNC, (Function *)0x0 }, /* j */ + { ISFUNC, (Function *)0x0 }, /* k */ + { ISFUNC, (Function *)0x0 }, /* l */ + { ISFUNC, (Function *)0x0 }, /* m */ + { ISFUNC, (Function *)0x0 }, /* n */ + { ISFUNC, (Function *)0x0 }, /* o */ + { ISFUNC, (Function *)0x0 }, /* p */ + { ISFUNC, (Function *)0x0 }, /* q */ + { ISFUNC, (Function *)0x0 }, /* r */ + { ISFUNC, (Function *)0x0 }, /* s */ + { ISFUNC, (Function *)0x0 }, /* t */ + { ISFUNC, (Function *)0x0 }, /* u */ + { ISFUNC, (Function *)0x0 }, /* v */ + { ISFUNC, (Function *)0x0 }, /* w */ + { ISFUNC, (Function *)0x0 }, /* x */ + { ISFUNC, (Function *)0x0 }, /* y */ + { ISFUNC, (Function *)0x0 }, /* z */ + + /* Final punctuation. */ + { ISFUNC, (Function *)0x0 }, /* { */ + { ISFUNC, (Function *)0x0 }, /* | */ + { ISFUNC, (Function *)0x0 }, /* } */ + { ISFUNC, (Function *)0x0 }, /* ~ */ + { ISFUNC, rl_backward_kill_word } /* RUBOUT */ +}; diff --git a/gnu/lib/libreadline/vi_mode.c b/gnu/lib/libreadline/vi_mode.c new file mode 100644 index 0000000..f0d9e04 --- /dev/null +++ b/gnu/lib/libreadline/vi_mode.c @@ -0,0 +1,987 @@ +/* vi_mode.c -- A vi emulation mode for Bash. + + Derived from code written by Jeff Sparkes (jeff1@????). + */ + + +/* **************************************************************** */ +/* */ +/* VI Emulation Mode */ +/* */ +/* **************************************************************** */ + +/* Last string searched for from `/' or `?'. */ +static char *vi_last_search = (char *)NULL; +static int vi_histpos; + +/* Non-zero means enter insertion mode. */ +int vi_doing_insert = 0; + +/* *** UNCLEAN *** */ +/* Command keys which do movement for xxx_to commands. */ +static char *vi_motion = " hl^$0ftFt;,%wbeWBE|"; + +/* Keymap used for vi replace characters. Created dynamically since + rarely used. */ +static Keymap vi_replace_map = (Keymap)NULL; + +/* The number of characters inserted in the last replace operation. */ +static vi_replace_count = 0; + +/* Yank the nth arg from the previous line into this line at point. */ +rl_vi_yank_arg (count) + int count; +{ + rl_yank_nth_arg (count, 0); +} + +/* Search again for the last thing searched for. */ +rl_vi_search_again (ignore, key) + int ignore, key; +{ + switch (key) + { + case 'n': + rl_vi_dosearch (vi_last_search, -1); + break; + + case 'N': + rl_vi_dosearch (vi_last_search, 1); + break; + } +} + +/* Do a vi style search. */ +rl_vi_search (count, key) + int count, key; +{ + int dir, c, save_pos; + char *p; + + switch (key) + { + case '?': + dir = 1; + break; + + case '/': + dir = -1; + break; + + default: + ding (); + return; + } + + vi_histpos = where_history (); + maybe_save_line (); + save_pos = rl_point; + + /* Reuse the line input buffer to read the search string. */ + the_line[0] = 0; + rl_end = rl_point = 0; + p = (char *)alloca (2 + (rl_prompt ? strlen (rl_prompt) : 0)); + + sprintf (p, "%s%c", rl_prompt ? rl_prompt : "", key); + + rl_message (p, 0, 0); + + while (c = rl_read_key ()) + { + switch (c) + { + case CTRL('H'): + case RUBOUT: + if (rl_point == 0) + { + maybe_unsave_line (); + rl_clear_message (); + rl_point = save_pos; + return; + } + + case CTRL('W'): + case CTRL('U'): + rl_dispatch (c, keymap); + break; + + case ESC: + case RETURN: + case NEWLINE: + goto dosearch; + break; + + case CTRL('C'): + maybe_unsave_line (); + rl_clear_message (); + rl_point = 0; + ding (); + return; + + default: + rl_insert (1, c); + break; + } + rl_redisplay (); + } + dosearch: + if (vi_last_search) + free (vi_last_search); + + vi_last_search = savestring (the_line); + rl_vi_dosearch (the_line, dir); +} + +rl_vi_dosearch (string, dir) + char *string; + int dir; +{ + int old, save = vi_histpos; + HIST_ENTRY *h; + + if (string == 0 || *string == 0 || vi_histpos < 0) + { + ding (); + return; + } + + if ((save = history_search_pos (string, dir, vi_histpos + dir)) == -1) + { + maybe_unsave_line (); + rl_clear_message (); + rl_point = 0; + ding (); + return; + } + + vi_histpos = save; + + old = where_history (); + history_set_pos (vi_histpos); + h = current_history (); + history_set_pos (old); + + strcpy (the_line, h->line); + rl_undo_list = (UNDO_LIST *)h->data; + rl_end = strlen (the_line); + rl_point = 0; + rl_clear_message (); +} + +/* Completion, from vi's point of view. */ +rl_vi_complete (ignore, key) + int ignore, key; +{ + if ((rl_point < rl_end) && (!whitespace (the_line[rl_point]))) + { + if (!whitespace (the_line[rl_point + 1])) + rl_vi_end_word (1, 'E'); + rl_point++; + } + + if (key == '*') + rl_complete_internal ('*'); + else + rl_complete (0, key); + + rl_vi_insertion_mode (); +} + +/* Previous word in vi mode. */ +rl_vi_prev_word (count, key) + int count, key; +{ + if (count < 0) + { + rl_vi_next_word (-count, key); + return; + } + + if (uppercase_p (key)) + rl_vi_bWord (count); + else + rl_vi_bword (count); +} + +/* Next word in vi mode. */ +rl_vi_next_word (count, key) + int count; +{ + if (count < 0) + { + rl_vi_prev_word (-count, key); + return; + } + + if (uppercase_p (key)) + rl_vi_fWord (count); + else + rl_vi_fword (count); +} + +/* Move to the end of the ?next? word. */ +rl_vi_end_word (count, key) + int count, key; +{ + if (count < 0) + { + ding (); + return; + } + + if (uppercase_p (key)) + rl_vi_eWord (count); + else + rl_vi_eword (count); +} + +/* Move forward a word the way that 'W' does. */ +/* Move forward a word the way that 'W' does. */ +rl_vi_fWord (count) + int count; +{ + while (count-- && rl_point < (rl_end - 1)) + { + /* Skip until whitespace. */ + while (!whitespace (the_line[rl_point]) && rl_point < rl_end) + rl_point++; + + /* Now skip whitespace. */ + while (whitespace (the_line[rl_point]) && rl_point < rl_end) + rl_point++; + } +} + +rl_vi_bWord (count) + int count; +{ + while (count-- && rl_point > 0) + { + /* If we are at the start of a word, move back to whitespace so + we will go back to the start of the previous word. */ + if (!whitespace (the_line[rl_point]) && + whitespace (the_line[rl_point - 1])) + rl_point--; + + while (rl_point > 0 && whitespace (the_line[rl_point])) + rl_point--; + + if (rl_point > 0) + { + while (--rl_point >= 0 && !whitespace (the_line[rl_point])); + rl_point++; + } + } +} + +rl_vi_eWord (count) + int count; +{ + while (count-- && rl_point < (rl_end - 1)) + { + /* Move to white space. */ + while (++rl_point < rl_end && whitespace (the_line[rl_point])) + ; + + if (rl_point && rl_point < rl_end) + { + /* Skip whitespace. */ + while (rl_point < rl_end && whitespace (the_line[rl_point])) + rl_point++; + + /* Skip until whitespace. */ + while (rl_point < rl_end && !whitespace (the_line[rl_point])) + rl_point++; + + /* Move back to the last character of the word. */ + rl_point--; + } + } +} + +rl_vi_fword (count) + int count; +{ + while (count-- && rl_point < (rl_end - 1)) + { + /* Move to white space (really non-identifer). */ + if (isident (the_line[rl_point])) + { + while (isident (the_line[rl_point]) && rl_point < rl_end) + rl_point++; + } + else /* if (!whitespace (the_line[rl_point])) */ + { + while (!isident (the_line[rl_point]) && + !whitespace (the_line[rl_point]) && rl_point < rl_end) + rl_point++; + } + + /* Move past whitespace. */ + while (whitespace (the_line[rl_point]) && rl_point < rl_end) + rl_point++; + } +} + +rl_vi_bword (count) + int count; +{ + while (count-- && rl_point > 0) + { + int last_is_ident; + + /* If we are at the start of a word, move back to a non-identifier + so we will go back to the start of the previous word. */ + if (isident (the_line[rl_point]) && !isident (the_line[rl_point - 1])) + rl_point--; + + /* If this character and the previous character are `opposite', move + back so we don't get messed up by the rl_point++ down there in + the while loop. Without this code, words like `l;' screw up the + function. */ + last_is_ident = isident (the_line[rl_point - 1]); + if ((isident (the_line[rl_point]) && !last_is_ident) || + (!isident (the_line[rl_point]) && last_is_ident)) + rl_point--; + + while (rl_point > 0 && whitespace (the_line[rl_point])) + rl_point--; + + if (rl_point > 0) + { + if (isident (the_line[rl_point])) + while (--rl_point >= 0 && isident (the_line[rl_point])); + else + while (--rl_point >= 0 && !isident (the_line[rl_point]) && + !whitespace (the_line[rl_point])); + rl_point++; + } + } +} + +rl_vi_eword (count) + int count; +{ + while (count-- && rl_point < rl_end - 1) + { + while (++rl_point < rl_end && whitespace (the_line[rl_point])) + ; + + if (rl_point < rl_end) + { + if (isident (the_line[rl_point])) + while (++rl_point < rl_end && isident (the_line[rl_point])); + else + while (++rl_point < rl_end && !isident (the_line[rl_point]) + && !whitespace (the_line[rl_point])); + rl_point--; + } + } +} + +rl_vi_insert_beg () +{ + rl_beg_of_line (); + rl_vi_insertion_mode (); + return 0; +} + +rl_vi_append_mode () +{ + if (rl_point < rl_end) + rl_point += 1; + rl_vi_insertion_mode (); + return 0; +} + +rl_vi_append_eol () +{ + rl_end_of_line (); + rl_vi_append_mode (); + return 0; +} + +/* What to do in the case of C-d. */ +rl_vi_eof_maybe (count, c) + int count, c; +{ + rl_newline (1, '\n'); +} + +/* Insertion mode stuff. */ + +/* Switching from one mode to the other really just involves + switching keymaps. */ +rl_vi_insertion_mode () +{ + keymap = vi_insertion_keymap; +} + +rl_vi_movement_mode () +{ + if (rl_point > 0) + rl_backward (1); + + keymap = vi_movement_keymap; + vi_done_inserting (); +} + +vi_done_inserting () +{ + if (vi_doing_insert) + { + rl_end_undo_group (); + vi_doing_insert = 0; + } +} + +rl_vi_arg_digit (count, c) + int count, c; +{ + if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg) + rl_beg_of_line (); + else + rl_digit_argument (count, c); +} + +/* Doesn't take an arg count in vi */ +rl_vi_change_case (ignore1, ignore2) + int ignore1, ignore2; +{ + char c = 0; + + /* Don't try this on an empty line. */ + if (rl_point >= rl_end - 1) + return; + + if (uppercase_p (the_line[rl_point])) + c = to_lower (the_line[rl_point]); + else if (lowercase_p (the_line[rl_point])) + c = to_upper (the_line[rl_point]); + + /* Vi is kind of strange here. */ + if (c) + { + rl_begin_undo_group (); + rl_delete (1, c); + rl_insert (1, c); + rl_end_undo_group (); + rl_vi_check (); + } + else + rl_forward (1); +} + +rl_vi_put (count, key) + int count, key; +{ + if (!uppercase_p (key) && (rl_point + 1 <= rl_end)) + rl_forward (1); + + rl_yank (); + rl_backward (1); +} + +rl_vi_check () +{ + if (rl_point && rl_point == rl_end) + rl_point--; +} + +rl_vi_column (count) +{ + if (count > rl_end) + rl_end_of_line (); + else + rl_point = count - 1; +} + +int +rl_vi_domove (key, nextkey) + int key, *nextkey; +{ + int c, save; + + rl_mark = rl_point; + c = rl_read_key (); + *nextkey = c; + + if (!member (c, vi_motion)) + { + if (digit (c)) + { + save = rl_numeric_arg; + rl_digit_loop1 (); + rl_numeric_arg *= save; + } + else if ((key == 'd' && c == 'd') || + (key == 'c' && c == 'c')) + { + rl_mark = rl_end; + rl_beg_of_line (); + return (0); + } + else + return (-1); + } + + rl_dispatch (c, keymap); + + /* No change in position means the command failed. */ + if (rl_mark == rl_point) + return (-1); + + if ((c == 'w' || c == 'W') && rl_point < rl_end) + rl_point--; + + if (rl_mark < rl_point) + exchange (rl_point, rl_mark); + + return (0); +} + +/* A simplified loop for vi. Don't dispatch key at end. + Don't recognize minus sign? */ +rl_digit_loop1 () +{ + int key, c; + + while (1) + { + rl_message ("(arg: %d) ", arg_sign * rl_numeric_arg, 0); + key = c = rl_read_key (); + + if (keymap[c].type == ISFUNC && + keymap[c].function == rl_universal_argument) + { + rl_numeric_arg *= 4; + continue; + } + + c = UNMETA (c); + if (numeric (c)) + { + if (rl_explicit_arg) + rl_numeric_arg = (rl_numeric_arg * 10) + (c - '0'); + else + rl_numeric_arg = (c - '0'); + rl_explicit_arg = 1; + } + else + { + rl_clear_message (); + rl_stuff_char (key); + break; + } + } +} + +rl_vi_delete_to (count, key) + int count, key; +{ + int c; + + if (uppercase_p (key)) + rl_stuff_char ('$'); + + if (rl_vi_domove (key, &c)) + { + ding (); + return; + } + + if ((c != '|') && (c != 'h') && rl_mark < rl_end) + rl_mark++; + + rl_kill_text (rl_point, rl_mark); +} + +rl_vi_change_to (count, key) + int count, key; +{ + int c; + + if (uppercase_p (key)) + rl_stuff_char ('$'); + + if (rl_vi_domove (key, &c)) + { + ding (); + return; + } + + if ((c != '|') && (c != 'h') && rl_mark < rl_end) + rl_mark++; + + rl_begin_undo_group (); + vi_doing_insert = 1; + rl_kill_text (rl_point, rl_mark); + rl_vi_insertion_mode (); +} + +rl_vi_yank_to (count, key) + int count, key; +{ + int c, save = rl_point; + + if (uppercase_p (key)) + rl_stuff_char ('$'); + + if (rl_vi_domove (key, &c)) + { + ding (); + return; + } + + rl_begin_undo_group (); + rl_kill_text (rl_point, rl_mark); + rl_end_undo_group (); + rl_do_undo (); + rl_point = save; +} + +rl_vi_delete (count) +{ + int end; + + if (rl_end == 0) + { + ding (); + return; + } + + end = rl_point + count; + + if (end >= rl_end) + end = rl_end; + + rl_kill_text (rl_point, end); + + if (rl_point > 0 && rl_point == rl_end) + rl_backward (1); +} + +/* Turn the current line into a comment in shell history. + A K*rn shell style function. */ +rl_vi_comment () +{ + rl_beg_of_line (); + rl_insert_text (": "); /* `#' doesn't work in interactive mode */ + rl_redisplay (); + rl_newline (1, '\010'); +} + +rl_vi_first_print () +{ + rl_back_to_indent (); +} + +rl_back_to_indent (ignore1, ignore2) + int ignore1, ignore2; +{ + rl_beg_of_line (); + while (rl_point < rl_end && whitespace (the_line[rl_point])) + rl_point++; +} + +/* NOTE: it is necessary that opposite directions are inverses */ +#define FTO 1 /* forward to */ +#define BTO -1 /* backward to */ +#define FFIND 2 /* forward find */ +#define BFIND -2 /* backward find */ + +rl_vi_char_search (count, key) + int count, key; +{ + static char target; + static int orig_dir, dir; + int pos; + + if (key == ';' || key == ',') + dir = (key == ';' ? orig_dir : -orig_dir); + else + { + target = rl_getc (in_stream); + + switch (key) + { + case 't': + orig_dir = dir = FTO; + break; + + case 'T': + orig_dir = dir = BTO; + break; + + case 'f': + orig_dir = dir = FFIND; + break; + + case 'F': + orig_dir = dir = BFIND; + break; + } + } + + pos = rl_point; + + if (dir < 0) + { + pos--; + do + { + if (the_line[pos] == target) + { + if (dir == BTO) + rl_point = pos + 1; + else + rl_point = pos; + return; + } + } + while (pos--); + + if (pos < 0) + { + ding (); + return; + } + } + else + { /* dir > 0 */ + pos++; + do + { + if (the_line[pos] == target) + { + if (dir == FTO) + rl_point = pos - 1; + else + rl_point = pos; + return; + } + } + while (++pos < rl_end); + + if (pos >= (rl_end - 1)) + ding (); + } +} + +/* Match brackets */ +rl_vi_match () +{ + int count = 1, brack, pos; + + pos = rl_point; + if ((brack = rl_vi_bracktype (the_line[rl_point])) == 0) + { + while ((brack = rl_vi_bracktype (the_line[rl_point])) == 0 && + rl_point < rl_end - 1) + rl_forward (1); + + if (brack <= 0) + { + rl_point = pos; + ding (); + return; + } + } + + pos = rl_point; + + if (brack < 0) + { + while (count) + { + if (--pos >= 0) + { + int b = rl_vi_bracktype (the_line[pos]); + if (b == -brack) + count--; + else if (b == brack) + count++; + } + else + { + ding (); + return; + } + } + } + else + { /* brack > 0 */ + while (count) + { + if (++pos < rl_end) + { + int b = rl_vi_bracktype (the_line[pos]); + if (b == -brack) + count--; + else if (b == brack) + count++; + } + else + { + ding (); + return; + } + } + } + rl_point = pos; +} + +int +rl_vi_bracktype (c) + int c; +{ + switch (c) + { + case '(': return 1; + case ')': return -1; + case '[': return 2; + case ']': return -2; + case '{': return 3; + case '}': return -3; + default: return 0; + } +} + +rl_vi_change_char () +{ + int c; + + c = rl_getc (in_stream); + + switch (c) + { + case '\033': + case CTRL('C'): + return; + + default: + rl_begin_undo_group (); + rl_delete (1, c); + rl_insert (1, c); + rl_end_undo_group (); + break; + } +} + +rl_vi_subst (count, key) + int count, key; +{ + rl_begin_undo_group (); + vi_doing_insert = 1; + + if (uppercase_p (key)) + { + rl_beg_of_line (); + rl_kill_line (1); + } + else + rl_delete (count, key); + + rl_vi_insertion_mode (); +} + +rl_vi_overstrike (count, key) + int count, key; +{ + int i; + + if (vi_doing_insert == 0) + { + vi_doing_insert = 1; + rl_begin_undo_group (); + } + + for (i = 0; i < count; i++) + { + vi_replace_count++; + rl_begin_undo_group (); + + if (rl_point < rl_end) + { + rl_delete (1, key); + rl_insert (1, key); + } + else + rl_insert (1, key); + + rl_end_undo_group (); + } +} + +rl_vi_overstrike_delete (count) + int count; +{ + int i, s; + + for (i = 0; i < count; i++) + { + if (vi_replace_count == 0) + { + ding (); + break; + } + s = rl_point; + + if (rl_do_undo ()) + vi_replace_count--; + + if (rl_point == s) + rl_backward (1); + } + + if (vi_replace_count == 0 && vi_doing_insert) + { + rl_end_undo_group (); + rl_do_undo (); + vi_doing_insert = 0; + } +} + +rl_vi_replace () +{ + int i; + + vi_replace_count = 0; + + vi_replace_map = rl_make_bare_keymap (); + + for (i = ' '; i < 127; i++) + vi_replace_map[i].function = rl_vi_overstrike; + + vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete; + vi_replace_map[ESC].function = rl_vi_movement_mode; + vi_replace_map[RETURN].function = rl_newline; + vi_replace_map[NEWLINE].function = rl_newline; + keymap = vi_replace_map; +} + +/* + * Try to complete the word we are standing on or the word that ends with + * the previous character. A space matches everything. + * Word delimiters are space and ;. + */ +rl_vi_possible_completions() +{ + int save_pos = rl_point; + + if (!index (" ;", the_line[rl_point])) + { + while (!index(" ;", the_line[++rl_point])) + ; + } + else if (the_line[rl_point-1] == ';') + { + ding (); + return (0); + } + + rl_possible_completions (); + rl_point = save_pos; + + return (0); +} -- cgit v1.1