pax_global_header00006660000000000000000000000064132235016260014512gustar00rootroot0000000000000052 comment=c02e757e5880b144fed789156c76daab2b5d9fdc aoeui-1.7+20160302.git4e5dee9/000077500000000000000000000000001322350162600152235ustar00rootroot00000000000000aoeui-1.7+20160302.git4e5dee9/COPYING000066400000000000000000000440011322350162600162550ustar00rootroot00000000000000The aoeui text editor is copyright 2007 Peter Klausler and released under the GNU General Public License version 2 only. Any change to a later version of the license, or to another open source license, will be explicitly stated in later editions of this file. A copy of this license is appended, whose text itself is copyright by the good folks at the Free Software Foundation. ---------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU 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. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), 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 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 show them these terms so they know 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. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. 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 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 derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 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 License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. 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. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary 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 License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 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 Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing 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 for copying, distributing or modifying the Program or works based on it. 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. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. 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 this 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 this License, you may choose any version ever published by the Free Software Foundation. 10. 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 11. 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. 12. 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 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 the public, 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) 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 2 of the License, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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) year 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 is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. aoeui-1.7+20160302.git4e5dee9/LOG000066400000000000000000000161471322350162600156000ustar00rootroot000000000000002016 03-02 pmk: rehosting on github 2015 08-06 1.7 release 2014 08-26 pmk: C++11 keywords 03-01 pmk: "find tag" now opens view as search 2013 11-25 pmk: fix ESC-'@' (ALT+'@') and ESC-RET 11-25 pmk: fix typos in man page 09-25 pmk: erase and reset Xterm alt screen when done 09-25 pmk: suppress pointer alignment warning in undo.c 06-26 pmk: Go language syntax 05-22 pmk: fix needless column erasures in window repaints 01-31 pmk: s/~0/~(size_t)0/ in several sites 2012 11-30 pmk: set colors to default before erase/insert/delete 11-10 1.6 release 10-27 pmk: automatic detection of tabbing and tab stop 10-26 pmk: fix off-by-one error in macro repetition 08-17 pmk: fix cursor position after xterm REGSCREEN 05-04 pmk: fix window destruction display error 04-11 pmk: fix line insert/delete bug 03-25 pmk: Haskell support 03-17 pmk: feature requests, line numbers 03-16 pmk: AOEUI_OVERLAP and comment highlighting 03-15 pmk: fix display issues on non-Apple Xterms, Linux console 03-11 pmk: optimize screen erasure again 02-01 pmk: fix raw hex character insertion (encode to UTF-8 unless -U) 2011 10-14 1.5 release 10-14 pmk: display debugging output, fix display errors 04-21 pmk: tweak colors, code cleanup, add display test 03-17 pmk: optimize screen erasure (background color problem) 02-16 pmk: AOEUI_WRITABLE environment variable 01-11 pmk: fix crash due to stale image pointer after resizing 01-08 pmk: work around Apple Terminal for OS X 10.5 2010 09-10 pmk: tweak ^J after { again to NOT add automatic } if one exists 08-07 pmk: tweak ^J after { to add correct automatic } more often 07-21 pmk: -o (do not save original) and -r modes (read-only mode) 06-16 pmk: ^V in search mode retains target as selection 06-10 pmk: use mode-specific background colors for selections 04-05 pmk: fix searching bug with CURSOR==0 04-05 pmk: ^P with number 04-03 pmk: -w 'p4 edit %s' (command to make file writable) 02-21 pmk: detect and support screen(1) 02-15 1.4 release 02-09 pmk: SVN check-in 01-05 pmk: auto-closing '}' for ^J after '{' 2009 12-11 pmk: multi-line code alignment 12-10 pmk: rewrite online help 12-03 1.3 release 11-19 pmk: add copyright notices 11-11 pmk: removed useless ^Space^M and ^Space^J 11-11 pmk: swap ^J and ^M again so that cut-and-paste text doesn't align 11-07 pmk: spot improvements to code alignment 10-04 1.2 release 09-09 pmk: display regular expressions during searches 09-09 pmk: swap ^J and ^M, so that Enter causes automatic alignment 09-05 pmk: fix original~ file saving and bad writes to read-only files 09-05 pmk: fix TAGS with absolute paths 09-05 pmk: fix folding, tweak motion by words and identifiers 07-04 1.1.2 release 04-18 pmk: shell mode via ptys 04-16 pmk: basic keyword highlighting for C/C++ 04-11 pmk: multiple TAGS files in directory hierarchy, multiple hits 04-07 pmk: true UP and DOWN arrow movement keys 04-04 pmk: make default ^O macro a global value, not per-view 04-04 pmk: make last search match a global string, not per-view 04-04 pmk: lots of code cleanup 04-03 1.1.1 release 04-02 pmk: snap buffers on abort (^\) 04-01 pmk: make DELETE key synonymous with BACKSPACE (for screen(1)) 02-29 pmk: -s (no_tabs) 01-21 pmk: Mac OS X port 2008 12-26 pmk: support a repeat count on macros, abort macros on failed search 10-29 pmk: fix ^ and $ in regex searches by adding REG_NEWLINE to regcomp 08-29 pmk: support output from exuberant-ctags 08-13 pmk: add -u and -U options to force and disable automatic UTF-8 detection 07-12 pmk: fix non-UTF-8 detection when UTF-8 sequence spans first page boundary 07-12 1.1.0 release 07-11 pmk: fix partial escape sequence parsing, support $ROWS/$COLUMNS 07-10 pmk: erase display after display size change 07-06 pmk: disable UTF-8 and accept DOS CR-NL line ends automatically 07-02 pmk: fix looping bug after redefined macro 07-02 pmk: fix hex literal characters 06-29 1.0.3 release 06-28 pmk: handle all Escape sequences and conversions in display.c 06-27 pmk: allow multibyte characters in macros and display input 06-27 pmk: fix buffered input bug 06-27 pmk: modularized macro facility, function key definitions 06-27 pmk: fix misspellings in documentation 06-27 pmk: remove -Werror and TAGS default build from Makefile 06-26 pmk: preserve original mark when search ends with ^V 06-26 pmk: fixes, work again on serial port 06-25 pmk: decode function keys and command responses in display.c, not mode.c 06-25 pmk: get display size from terminal as backup to ioctl 06-24 pmk: red cursor on read-only text, green cursor on dirty text 06-24 pmk: fix cursor color bug 06-23 1.0.2 release 06-22 pmk: fun animated scrolling effects 06-22 pmk: fix fg/bg colors so everything works on console 06-22 pmk: use insert/delete of spaces and lines in display 06-21 pmk: improve folding/unfolding commands 06-21 pmk: remove needless erasures and cursor motion from display 06-18 1.0.1 release 06-18 pmk: nicer background colors 06-07 pmk: "asdfg" QWERTY variant 06-06 pmk: fix spurious "file may have changed" warning after renaming 06-01 pmk: some code cleaning 05-30 1.0.0 release! yay 05-24 pmk: tweak automatic alignment 05-22 pmk: some minor BSD porting issues in buffer.c and file.c 05-16 pmk: tweak ebuild according to gentoo developer's comments 04-18 pmk: fix path name tab completion (shortest common continuation) 04-18 1.0_alpha5 04-17 pmk: rewrote alignment 04-15 pmk: generalized tab completion 04-15 pmk: don't set mark automatically with ^] 04-13 pmk: color-cue paired brackets 04-13 pmk: clean up scratch files better 04-13 pmk: ^A (macros) is now ^O, so that ^A is available to screen(1) 04-13 pmk: registers for ^B and regexs 04-12 pmk: regex searches begun 04-11 pmk: somewhat less ugly colors in xterms 04-09 1.0_alpha4 04-09 pmk: code folding 04-07 pmk: code buffing 04-06 commit 04-06 pmk: improve tag command 04-06 pmk: create new files 644, not 600 04-06 pmk: pull basic help text into program 04-05 pmk: fix cursor placement at end of full line, make it smart 04-04 commit 04-02 pmk: TAGS support (^Space.) 04-01 pmk: shell mode (^E without selection) 03-31 1.0_alpha3 03-30 pmk: generalize the asynchronous child I/O implementation 03-30 pmk: change emergency exit from ^\ to ^Space^\ 03-29 pmk: some optimization of repainting 03-29 pmk: debug behavior when xterm window size changes 03-28 pmk: support -t tabstop 03-28 pmk: special-case 'cd' in ^E 03-28 pmk: tab completion when cursor is at the end of a selection 03-28 pmk: hexadecimal arguments and Unicode ^^ literals 03-28 pmk: bookmarks 03-28 pmk: *remove* bogus DOS/Mac newline handling 03-28 pmk: ^Space # TAB sets tab stop 03-28 pmk: reap terminated children 03-28 pmk: asynchronous child I/O 03-27 1.0_alpha2 03-27 pmk: Make ^V with a selection unset it, ^Space^V exchange 03-27 pmk: Clean up makefile, write ebuild 03-26 pmk: Put newly-created texts in $HOME/.aoeui, etc. 03-25 pmk: Keep active window background set to default color 03-25 pmk: Keep file gap buffers in file# for recovery 03-24 pmk: Save original~ copy of file at write time, not open time 03-24 pmk: Memory-map clean images of files 03-23 pmk: Create new empty texts when view_next() finds no others 03-23 pmk: Make variant ^D with no selection select surrounding white space 03-22 pmk: Initial Subversion check in at sourceforge.net 2007 aoeui-1.7+20160302.git4e5dee9/Makefile000066400000000000000000000045721322350162600166730ustar00rootroot00000000000000VERSION = 1.7 PACKAGE = aoeui-$(VERSION) SRCS = main.c mem.c die.c display.c text.c file.c locus.c buffer.c \ undo.c utf8.c window.c util.c clip.c mode.c search.c \ child.c bookmark.c help.c find.c tags.c tab.c fold.c macro.c \ keyword.c HDRS = all.h buffer.h child.h mode.h text.h locus.h utf8.h display.h \ window.h util.h clip.h macro.h mem.h die.h types.h rgba.h RELS = $(SRCS:.c=.o) LIBS = -lutil INST_DIR = $(DESTDIR)/usr CFLAGS = -Wall -Wno-parentheses \ -Wpointer-arith -Wcast-align -Wwrite-strings -Wstrict-prototypes \ -Wmissing-prototypes -Wmissing-declarations # -Werror # Uncomment this line if you want to develop aoeui yourself with exuberant-ctags # CTAGS = exuberant-ctags CTAGS = ctags STRINGIFY = sed 's/\\/\\\\/g;s/"/\\"/g;s/^/"/;s/$$/\\n"/' default: optimized display-test aoeui.1 asdfg.1 aoeui: $(RELS) $(CC) $(CFLAGS) -o $@ $(RELS) $(LIBS) $(RELS): $(HDRS) help.o: aoeui.help asdfg.help aoeui.help: help.m4 m4 help.m4 | $(STRINGIFY) >$@ asdfg.help: help.m4 m4 -D ASDFG help.m4 | $(STRINGIFY) >$@ display-test: display-test.o display.o mem.o utf8.o $(CC) $(CFLAGS) -o $@ display-test.o display.o mem.o utf8.o display-test.o: types.h utf8.h display.h aoeui.1.gz: aoeui.1 gzip -9 -c aoeui.1 >$@ asdfg.1.gz: asdfg.1 gzip -9 -c asdfg.1 >$@ aoeui.1.html: aoeui.1 man2html aoeui.1 >$@ asdfg.1.html: asdfg.1 man2html asdfg.1 >$@ aoeui.1: aoeui.1.m4 m4 aoeui.1.m4 >$@ asdfg.1: aoeui.1.m4 m4 -D ASDFG aoeui.1.m4 >$@ optimized: $(MAKE) CFLAGS="-O3 $(CFLAGS)" aoeui static: $(MAKE) CFLAGS="-O3 $(CFLAGS) -static" aoeui debug: clean $(MAKE) CFLAGS="-g -O0 -DDEBUG $(CFLAGS)" aoeui profile: clean $(MAKE) CFLAGS="-pg $(CFLAGS)" aoeui TAGS: $(SRCS) $(HDRS) $(CTAGS) -x $(SRCS) $(HDRS) >$@ install: aoeui aoeui.1.gz asdfg.1.gz install -d $(INST_DIR)/bin install -d $(INST_DIR)/share/aoeui install -d $(INST_DIR)/share/man/man1 install aoeui $(INST_DIR)/bin ln -nf $(INST_DIR)/bin/aoeui $(INST_DIR)/bin/asdfg install *.txt $(INST_DIR)/share/aoeui install *.1.gz $(INST_DIR)/share/man/man1 clean: rm -f *.o *.help core gmon.out screenlog.* clobber: clean rm -f aoeui display-test unicode TAGS *.1 *.1.gz *.1.html spotless: clobber rm -f *~ *.tgz release: spotless rm -rf .tar $(PACKAGE) $(PACKAGE).tgz mkdir .tar find . | egrep -v '/\.' | egrep -v '[~#]' | cpio -o | (cd .tar; cpio -id) mv .tar $(PACKAGE) ls -CF $(PACKAGE) tar czf $(PACKAGE).tgz $(PACKAGE) rm -rf $(PACKAGE) aoeui-1.7+20160302.git4e5dee9/README000066400000000000000000000011631322350162600161040ustar00rootroot00000000000000These are the sources for the aoeui, a lightweight and unobtrusive text editing program that is optimized for fast editing by users of the Dvorak keyboard layout. It was written in March 2007 by pmklausler@gmail.com. The file COPYING contains the license under which aoeui is released; please review it so that you understand your rights and responsibilities. In particular, be aware that aoeui comes with ABSOLUTELY NO WARRANTY and your use of the program is entirely at your own risk. To build, edit first few lines of the Makefile to reflect your system. aoeui's repository is https://github.com/pklausler/aoeui Enjoy! aoeui-1.7+20160302.git4e5dee9/all.h000066400000000000000000000023331322350162600161450ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ /* All editor sources #include this file. */ /* Standard and system headers */ #define _GNU_SOURCE /* for mremap */ #define _POSIX_C_SOURCE_199309 /* for nanosleep */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined __APPLE__ || defined BSD # include #else # include #endif #ifndef NAME_MAX # define NAME_MAX 256 #endif #ifndef S_IRUSR # define S_IRUSR 0400 #endif #ifndef S_IWUSR # define S_IWUSR 0200 #endif #ifndef INLINE # ifdef __GNUC__ # define INLINE static __inline__ # else # define INLINE static # endif #endif /* Module headers */ #include "types.h" #include "utf8.h" #include "buffer.h" #include "locus.h" #include "text.h" #include "window.h" #include "util.h" #include "clip.h" #include "macro.h" #include "display.h" #include "mode.h" #include "mem.h" #include "die.h" #include "child.h" aoeui-1.7+20160302.git4e5dee9/aoeui.1.m4000066400000000000000000000610641322350162600167350ustar00rootroot00000000000000ifdef(`ASDFG',`define(`AOEUI',`asdfg')define(`cmd',`$2')',`define(`AOEUI',`aoeui')define(`cmd',`$1')')dnl .\" Man page for AOEUI .\" .\" Copyright 2007 Peter Klausler .\" Released under GPLv2. .TH AOEUI 1 "November 27, 2012" .\" .LO 1 .SH NAME AOEUI \- a lightweight visual editor optimized for the cmd(Dvorak,QWERTY) keyboard .SH SYNOPSIS .B AOEUI [ .B -k ] [ .B -o ] [ .B -r ] [ .B -s | .B -S ] [ .B -t .I "tab stop" ] [ .B -u | .B -U ] [ .B -w .I command ] .RI [ file... ] .SH DESCRIPTION .B AOEUI is an interactive display text editor optimized for users of the cmd(Dvorak,QWERTY) keyboard layout. .P When run with no file name arguments, .B AOEUI displays a short command introduction and summary. .P .B AOEUI can browse very large read-only files with quick start-up time, since the original texts are memory-mapped from files and not duplicated in memory unless they are about to be modified. .SH OPTIONS .TP .B -k Disable keyword highlighting. .TP .B -o Do not save the original contents of a modified file in .IR file ~. .TP .B -r Read-only mode: do not modify the file on disk. .TP .B -s Use spaces, not tabs, for automatic indentation, even if the file seems to currently use tabs. .TP .B -S Use tabs, not spaces, for automatic indentation, even if the file seems to currently use spaces. .TP .BI -t " 8" Set the tab stop to 8 or to some unreasonable value. This setting can be overridden on a per-text basis later. .TP .B -u Treat files as UTF-8 even if they contain invalid UTF-8 encodings. .TP .B -U Don't treat files as UTF-8 even if they look like it. .TP .BI -w " writability command" When an attempt is made to modify a read-only file, use this command (within which the string .B "%s" will be replaced with the path name of the file) to attempt to put the file into a writable state. This is useful for interacting with source code control systems (e.g., .BR "p4 edit %s" ). .SH "INTENTIONALLY MISSING FEATURES" .B AOEUI has no embedded extension language, since it is trivial to pass regions of text from the editor to any program or script that can read standard input and write standard output. The shell, .BI sed (1), .BI awk (1), .BI python (1), and .BI perl (1) are all usable for such scripting. Further, since .B AOEUI ships will full sources and the rights to modify it, users can customize it directly. .P The editor has only basic syntax highlighting of C and C++ keywords with subtle color cues that help match up parentheses, brackets, and braces. .P .B AOEUI has no mail or news reader, IRC client, or artificial intelligence psychologist mode. .P There is no X window system interface; that's what .BI xterm (1) and .BI gnome-terminal (1) are used for. .SH BASICS A .I text is a sequence of characters to be viewed or edited, such as a file. If it is not ASCII, the editor will automatically determine whether it is encoded in legal UTF-8 and do the right thing. The editor can also automatically detect DOS-style line endings. .P A .I view comprises all or part of a text. A text in the editor has at least one view, and possibly more. .P A .I window is a rectangular portion of the display, and is always associated with a single view, a contiguous portion of whose text is rendered in the window. Not every view has a window. .P Each view has a .I cursor and possibly a .IR selection , which has the cursor at one end and the .I mark at the other. The view's window, if any, always renders part of the text containing the view's cursor. .P The selection plays a critical role in .BR AOEUI . Besides highlighting regions to be cut or copied, it also serves to supply arguments to some commands, such as the path name of a file to open. .P The .I clip buffer is not visible in any window. It receives snippets of data that have been cut or copied out of texts, so that they may be moved or copied elsewhere. It also supplies the standard input to a background command launched with .B ^cmd(E,R) (below). There is one clip buffer shared by all views. .SH "COLOR CUES" AOEUI uses colors to convey information without cluttering the display with status lines or borders between windows. .P AOEUI uses distinct background colors to distinguish tiled windows. The active window is always presented in the terminal's default color scheme. Color is also used to highlight the current selection (in cyan) and folded regions (in red). .P Needless tabs and spaces are marked in violet. These include any tabs or spaces before the end of a line, as well as any spaces followed by a tab or multiple spaces that could be replaced by a tab. .P Bracketing characters are presented in alternating colors so that matching parentheses, brackets, and braces are colored identically. .P A red cursor signifies a read-only text, whereas a green cursor indicates a dirty text (meaning one that needs saving, not one unfit for young persons). .SH COMMANDS .B AOEUI understands the arrow, page up and down, and Delete keys on your keyboard, so you can actually just use it like a dumb notepad with no mouse if you don't want to read any further than the next section, which tells you how to leave the editor. .P In the following sections of the manual, commands are denoted by .B ^key to signify the use of Control, Alt, or a preceding Escape key. They all mean the same thing. .P .B Variant commands always begin with .BR ^Space , or its synonym, .BR ^@ . A few commands take a numeric argument, which is specified by .B ^Space followed by a decimal or hexadecimal number, the latter using C language syntax (0xdeadbeef). .P Many commands are sensitive to the presence or absence of a .BR selection . .SH LEAVING .TP .B ^Space^\e aborts the editor, leaving no original file modified since the last time .B ^cmd(K,W) was used. .TP .B ^cmd(Q,Q) suspends the editor and returns the terminal to the shell that invoked it. Use the shell's foreground command, probably .BR fg , to resume editing. .TP .B ^Space^cmd(Q,Q) saves all modified texts and terminates the editor. .SH NAVIGATION The "backward and forward by unit" commands treat a numeric argument, if any, as a repeat count. .TP .B ^cmd(H,G) moves the cursor backward by characters. .TP .B ^cmd(T,H) moves the cursor forward by characters. .TP .B ^Space^cmd(H,G) moves the cursor up a line on the screen. .TP .B ^Space^cmd(T,H) moves the cursor down a line on the screen. .TP .B ^cmd(N,K) moves the cursor backward by words. .TP .B ^cmd(S,L) moves the cursor forward by words. .TP .B ^Space^cmd(N,K) moves the cursor backward one sentence. .TP .B ^Space^cmd(S,L) moves the cursor forward one sentence. .TP .B ^cmd(G,T) moves the cursor back to the beginning of the line. If already there, it moves back to the beginning of the previous line. .TP .B ^cmd(C,Y) moves the cursor forward to the end of the line. If already there, it moves forward to the end of the next line. .TP .B ^Space^cmd(G,T) moves the cursor back to the beginning of the paragraph. If already there, it moves back to the beginning of the previous paragraph. .TP .B ^Space^cmd(C,Y) moves the cursor forward to the end of the paragraph. If already there, it moves forward to the end of the next paragraph. .TP .B ^cmd(R,O) moves the window backward by screenfulls. .TP .B ^cmd(L,P) moves the window forward by screenfulls. .TP .B ^Space^cmd(R,O) moves to the very beginning of the view. .TP .B ^Space^cmd(L,P) moves to the very end of the view. .TP .B ^] moves to the corresponding parenthesis, bracket, or brace, respecting nesting, if the cursor sits atop such a character. Otherwise, it moves to the nearest enclosing bracketing character. .TP .B ^cmd(Z,N) recenters the window so that the line containing the cursor lies in the middle of its portion of the display. .TP .B ^Space^cmd(Z,N) causes the current window to occupy the entire display and recenters the window. With a numeric argument, however, it simply moves the cursor to the indicated line in the view, with 1 being the number of the first line. .TP .B ^Space= (note that .B = is not a control character) sets a bookmark on the current selection or cursor position. A numeric argument may be used to manage multiple bookmarks. .TP .B ^Space- (note that .B - is not a control character) returns to a previously set bookmark, possibly identified with a numeric argument. .TP .B ^Space' (note that the single quote .B ' is not a control character) looks an identifier up the identifier in the .B TAGS files, which are sought in the same directory as the current view and then all of its parents, until one is found that contains the identifier. A new little window is opened for each of the identifier's entries in the TAGS file. .P The .B TAGS files should be generated with the .B ctags or .B exuberant-ctags utilities and their .B -x output format. If there is a selection, it is deleted from the view and its entire contents will constitute the identifier to be looked up; otherwise, the identifier that is immediately before or around the cursor is sought. .SH SELECTION These commands are sensitive to the presence or absence of a current selection. .TP .B ^cmd(V,U) begins a new selection if non exists, setting its mark at the current cursor, which is then typically navigated to its intended other end. .B ^cmd(V,U) in the presence of selection simply removes the mark. .TP .B ^Space^cmd(V,U) without a selection causes the entire current line to be selected by placing the mark at the end of the line and the cursor at its beginning. It is the same as the command sequence .B ^cmd(C,Y)^cmd(V,U)^cmd(G,T) with no selection. With a selection present, .B ^Space^cmd(V,U) exchanges its cursor with its mark. .P Note that .B ^Space^cmd(V,U) with a numeric argument unconditionally unsets the mark, which can be handy in a macro. .TP .B ^Space^cmd(D,X) with no selection causes all of the contiguous white space characters surrounding the cursor to be selected, with the cursor at the beginning so that they can be easily replaced by retyping. .SH UNDO .B AOEUI has infinite undo capabilities. .TP .B ^cmd(U,Z) reverses the effects of the last command, apart from .B ^cmd(U,Z) itself, that modified the current text in any of its views. .TP .B ^Space^cmd(U,Z) reverses the effects of the most recent undo. After .BR ^cmd(U,Z) , any .I other command that modifies the text will permanently commit the undo(s). .SH MODIFICATION In the default mode, characters typed without a command indicator are inserted at the current cursor position. Further, if the cursor is at the beginning of a selection, the selection is first cut to the clip buffer, so that the new text replaces it. .TP .B ^^ (that's Control-Shift-6, the caret character, on most keyboards, and ^6 will probably also work) inserts an otherwise untypeable control character into the text. The very next key to be pressed is either taken literally, if it is a control character, or converted to a control character if it is not, and inserted. (For example, you can press .B ^^ and then hit either Control-A or just a plain A, to get the character code 0x01 inserted.) .TP .B ^Space^^ with a numeric argument, probably in hexadecimal, inserts the specified Unicode character into the text in UTF-8 format. If the text is not UTF-8, the character code is inserted directly as a big-endian literal. .TP .B Tab (or .BR ^I ) attempts to perform tab completion; if that fails, a TAB character is inserted. If there is a selection with the cursor at its end, the editor tries to find an unambiguous continuation based on path names and words in all the views. A continuation, if found, is appended to the selection, to facilitate opening a file with .BR ^cmd(X,E) . With no selection, but the cursor immediately after one or more identifier characters, the editor searches for an unambiguous continuation using the words in the views. A continuation, if found, is inserted as the new selection with the cursor at its end. No tab completion occurs when the cursor is at the beginning of a selection; in that case, the selection is cut and replaced with a single TAB character. .TP .B ^SpaceTab (or .BR ^Space^I ) will align the current line to the indentation of the previous one. With a numeric argument of 1, it toggles the text's use of tab characters for indentation. With a numeric argument between 2 and 20, it will set the tab stop pitch. .TP .B Enter (or .BR ^M ) inserts a new line into the text without automatic indentation. .TP .B ^J (or .B ^Enter under some good terminal emulators) inserts a new line into the text with automatic indentation. If .B ^J is executed immediately after a .B { character that does not yet have a closing .BR } , .B ^J will also add a properly-indented closing brace. .TP .B Backspace (or more properly, its synonym .B ^? and sometimes, as in Mac OS X's Terminal application, .BR ^/ ), deletes the character immediately before the cursor. .TP .B ^cmd(D,X) with no selection deletes the character "under" the cursor. When a selection exists, .B ^cmd(D,X) moves it into the clip buffer, discarding any previously clipped text. .TP .B ^Space^cmd(D,X) with no selection will select surrounding white space, as described earlier. When a selection exists, .B ^Space^cmd(D,X) moves it into the clip buffer, putting it before any old text if the cursor was at its beginning and appending it to the clip buffer if the cursor was at its end. The intent is for multiple .B ^Space^cmd(D,X) commands to collect data together in the same order in which they are most likely to have been visited. .P A numeric argument to .B ^Space^cmd(D,X) places the indicated number of copies of the selection into the clip buffer. .TP .B ^cmd(F,C) requires a selection, which is copied into the clip buffer and then unmarked. .TP .B ^Space^cmd(F,C) is to .B ^cmd(F,C) what .B ^Space^cmd(D,X) is to .BR ^cmd(D,X) . It copies the selection to the clip buffer, putting it at the beginning or the end in the same way as .B ^Space^cmd(D,X) (above). A numeric argument to .B ^Space^cmd(F,C) places the indicated number of copies of the selection into the clip buffer. .TP .B ^cmd(B,V) with no selection will paste the current clip buffer's contents. But in the presence of a selection it performs a more general function: the contents of the selection and the clip buffer are exchanged. With a numeric argument, .B ^cmd(B,V) pastes or exchanges with a numbered .IR register , which is an alternate clip buffer. (The main clip buffer is the same as register 0.) Besides being a means for preserving some text for longer periods of editing, the registers also serve as a means for extracting the text that matches a parenthesized subpattern in a regular expression search. .SH SEARCHING .TP .B ^_ and its synonyms .BR ^/ , .BR ^- , and .B ^A enter search mode. The many synonyms are defined because they're often synonymous or reserved key sequences in the various window managers and the .BR screen (1) utility. .P (Specifically, .B ^/ gets mapped to .B ^_ by many X terminal emulators, while .B ^- gets mapped to .B ^_ by the Mac OS X Terminal application. .B ^A is the default escape sequence in .BR screen (1).) .P The variant version of this command .RB ( ^Space^_ and its synonyms) searches for occurrences of POSIX regular expressions. Each non-command character that is typed thereafter will be appended to the current search target string and the selection is moved to the next occurrence thereof. .P The case of alphabetic characters is .I not significant to the search. .P Most command characters will automatically take the editor out of search mode before executing, and the most recently discovered occurrence of the search target string will be its selection. .P A few commands have different meanings in search mode: .TP .B Backspace will remove the last character from the search target and move the selection back to its previous position. .TP .B ^cmd(V,U) is typically used to leave search mode with the currently highlighted search target as the selection. .TP .B ^_ (or its synonyms) with no characters in the search target string will cause the last successful search's target string to be reused. .TP .B ^cmd(H,G) and .B ^cmd(T,H) cause motion to the previous and next occurrences of the search target string, not single-character motion. .TP .B Enter (and .B ^_ and its synonyms) simply leaves search mode with the cursor at the latest hit, with the mark returned to where it was before the search (if anywhere). This is useful for using search to place the bounds of a selection. .SH TEXTS, VIEWS, and WINDOWS .TP .B ^cmd(K,W) saves .I all modified texts back to their files. .TP .B ^Space^cmd(K,W) saves just the current text. .TP .B ^cmd(X,E) with no selection inserts, as the new selection, the path name of the current text. With a selection containing a path name, possibly constructed with the assistance of tab completion (above), .B ^cmd(X,E) will raise up a window containing a view into the indicated file, creating a new text to hold it if one does not already exist. .TP .B ^Space^cmd(X,E) with a selection will rename the current text, so that it will be saved in another file. .TP .B ^cmd(W,F) finds an invisible view and associates it with the current window, making its current view invisible. Hitting .B ^cmd(W,F) repeatedly will cycle through all of the views. If there was no invisible view, .B ^cmd(W,F) creates a new scratch text, as does .B ^Space; below. .TP .B ^Space^cmd(W,F) does the same thing. but will close the window's current view, and also its text if it was the last view thereof. .TP .B ^cmd(Y,D) splits the current window horizontally, raising up an invisible or new view in the lower half of the original window. .TP .B ^Space^cmd(Y,D) splits the current window vertically, raising up an invisible or new view in the right half of the original window. .TP .B ^cmd(P,S) moves to another window. .TP .B ^cmd(P,S) with a numeric argument moves to a specific window; number 1 is in the upper left-hand corner of the display. .TP .B ^Space^cmd(P,S) moves to another window, closing the old one. .TP .B ^Space; (note that .B ; is not a control character) creates a new anonymous text. .TP .B ^Space# (note that the number sign .B ` is not a control character) displays the current position's path name and line number. .TP .B ^Space? (note that the question mark .B ? is not a control character) displays a new window with the built-in help summary of commands. .SH MACROS .TP .B ^Space^cmd(O,B) commences the recording of your keystrokes as the macro, which continues until the next .B ^cmd(O,B) or another macro recording. .TP .B ^SpaceF1-F12 commences the recording of your keystrokes as a new macro for a function key. Note that .B F1 and .B F11 are typically hijacked by window managers for their own purposes and probably will not be usable. .TP .B ^cmd(O,B) ends the recording of a macro, if one is in progress. Afterwards, .B ^cmd(O,B) replays the macro, possibly with a repeat count as the argument. Note that a failed search in a macro will terminate its execution. .SH FOLDING .B AOEUI supports the "folding" of portions of text into what appear to be single characters, and the reverse "unfolding" operation. Further, to provide outline views of texts such as source code that are heavily indented, .B AOEUI has an automatic nested folding capability. .TP .B ^Space, with a selection will fold the selection. Otherwise, it will repeatedly fold indented regions of the text to provide an outline view. A numeric value, if any, specifies the number of leading spaces or equivalent tabs at which code lines will be folded. The default is 1, causing the folding of any line that isn't left-justified. .TP .B ^Space. with a selection, or immediately atop a folded section, will unfold the topmost foldings within it. Otherwise, and if there is a numeric value, it will completely unfold the entire view. .SH SHELLS .TP .B ^cmd(E,R) with no selection will launch an interactive shell in a new scratch text. With a selection, however, .B ^cmd(E,R) will execute the shell command in the selection with the contents of the clip buffer, if any, as its standard input, and collect its output asynchronously in the background to replace the selection. This allows many helpful UNIX text processing commands to be used directly. Some handy commands to know: .TP .BR cat (1) to include another entire file, or to receive writes to a named pipe .TP .BR mkfifo (1) to create a named pipe so that commands in other windows may direct their output into a text running .B cat in the background. .TP .BI "cd " path to change the editor's current working directory (a special case command that is not actually passed to a shell) .TP .BR grep (1) to search for lines containing a pattern .TP .BR sort (1) to rearrange lines alphabetically or numerically, possibly reversed .TP .BR uniq (1) to discard duplicated lines .TP .BR sed (1) as in .B "sed 's/FROM/TO/g'" to perform unconditional search-and-replace with regular expressions .TP .BR tr (1) to convert lower to upper case with .B "a-z A-Z" and to remove DOS carriage returns with .BR "-d '[\er]'" .TP .BR fmt (1) to reformat paragraphs of natural language text .TP .B "indent -st -kr -i8 -nbbo" to reformat C language source code sensibly .TP .B "column -t" to realign data nicely into columns .TP .B "man | colcrt" to read a man page .TP .BR tailf (1) to monitor additions to a file such as a log .TP .BR make (1) to compile your code .TP .B "aspell list | sort | uniq | column" to get a list of words that may be misspelled .P .B ^Space^cmd(E,R) with no selection will terminate the output of any asynchronous child process that's still running. .SH TIPS .TP .B * To select the rest of the line after the cursor, use .B ^cmd(V,U)^cmd(C,Y) .TP .B * It is often faster to retype a bungled word than to fix it, using .B ^cmd(V,U)^cmd(N,K) and then retyping. .TP .B * Transposing multiple blocks of text is easy with .BR ^cmd(B,V) , which generalized the usual paste operation into an exchange of the clip buffer with the selection. .TP .B * Incremental search and replacement can be done with a macro or by clipping the replacement text, and on search hits that are to be replaced, using .B ^cmd(V,U)^cmd(B,V)^cmd(F,C)^/^/ to exchange the hit with the replacement text, copy it back to the clip buffer, and proceed to the next occurrence of the search pattern. But when the replacement text is short, it's sometimes easiest to just overwrite the selection by hitting .B ^cmd(V,U)^cmd(D,X) and immediately retyping the new text. .TP .B * Reconfigure your keyboards so that the key to the left of A, which is probably labeled .BR "Caps Lock" , is interpreted as a Control modifier instead. .TP .B * The .BR gnome-terminal (1) terminal emulator works well with .B AOEUI if you configure the terminal's scrollback limit to a relatively small value. .TP .B * To move backward or forward by half a screenfull, use .B ^cmd(R,O) or .B ^cmd(L,P) and then .BR ^cmd(Z,N) . (Or set the environment variable .B AOEUI_OVERLAP to 50.) .TP .B * To insert characters with a repeat count, type the characters into a new selection, cut into the clip buffer with a repeat count with .BR ^Space^cmd(D,X) , and then paste with .BR ^cmd(B,V) . .SH BUGS Inevitable; please tell me about any that you find. .SH ENVIRONMENT .TP .B SHELL is used to name the program run by the .B ^cmd(E,R) command. .TP .B ROWS and .TP .B COLUMNS may be set to override .BR AOEUI 's automatic mechanisms for determining the size of the display surface. .TP .B TERM will, when set to a string beginning with .BR xterm , cause .B AOEUI use the title bar of the terminal emulator as a status indicator that displays the path name of the active view and whether or not it has been saved since last modified. Unless the file is large, it also displays the line number of the cursor. .TP .B TERM_PROGRAM will, if set to Apple_Terminal, make .B AOEUI work around Apple's Terminal emulator's behavioral differences from standard Xterm. .TP .B AOEUI_WRITABLE Default command used in the absence of the .B -w option. .TP .B AOEUI_OVERLAP The percentage of overlap when scrolling a window up with .B ^cmd(R,O) or down with .BR ^cmd(L,P) . The default is 1, for minimal overlap. .SH FILES .TP .IB file ~ is overwritten with the original contents of .I file unless the .B -o option is used. .TP .IB file # contains the temporary image of the edited file while .B AOEUI is running, and may be useful in recovery if the editor is killed. .TP .B TAGS is found and read in by the .B ^Space' command to supply the tags that are scanned. The search for .B TAGS begins in the same directory as the current view's text and proceeds up through its parents. .TP .B $HOME/.aoeui holds any new "anonymous" texts created during editing sessions. .SH "SEE ALSO" .BR cmd(`asdfg',`aoeui') (1), .BR ctags (1), .BR exuberant-ctags (1), .BR regex (7) .P Helpful commands to use with .BR ^cmd(E,R) : .BR aspell (1), .BR cat (1), .BR colcrt (1), .BR column (1), .BR fmt (1), .BR grep (1), .BR indent (1), .BR mkfifo (1), .BR sed (1), .BR sort (1), .BR tailf (1), .BR tr (1), .BR uniq (1) .SH AUTHOR Peter Klausler wrote .BR AOEUI . aoeui-1.7+20160302.git4e5dee9/aoeui.txt000066400000000000000000000257451322350162600171030ustar00rootroot00000000000000 I have written a text editor. This is a silly thing to do, since there is certainly no shortage of interactive display editors in the world of free software, and some are excellent. But now I have an editor that works the way that I want my text editor to work. I contend that I have every capability of modern large editors that is actually useful for word processing and computer programming packaged into a small command set and tiny memory footprint. It is called aoeui because it's optimized for users, like myself, of the Dvorak keyboard layout. QWERTY key bindings could be found that make sense, I suppose, but the entire point of this project was to construct a tool for extremely fast and efficient text editing, and anybody still using QWERTY is not serious enough about their tools to want to pick up a new editor. Really. ' , . P Y F G C R L / A O E U I D H T N S - ; Q J K X B M W V Z What do I mean when I contend that aoeui is optimized for the Dvorak layout? I mean that all of the most heavily-used command keys are located under the fingers of the right hand, so that they may be accessed while the left pinky is holding down the Control or Alt key. (Control, Alt, and an Escape prefix all mean the same thing to aoeui. The user does not have to worry about using the right one or using them to alternate between, say, character and word units of motion.) In the QWERTY layout, too much of the right-hand real estate is occupied by punctuation keys that don't have Control capability, so a straightforward remapping of the command set is not obvious. The editor itself is lightweight and utterly unobtrusive. It has no mode line, minibuffer, scroll bar, or status indicators. Just a cursor, some background coloration cues, and the xterm window title. Editing with aoeui is intuitive and rather like working with a mouse- based word processing program, except of course that you don't have to take your hands off the keyboard in order to navigate and manipulate your text. New text is simply typed in place. Changes to text are typically by means of creating a selection and then copying, cutting, or replacing it. All effects are immediately visible, retractable, and repeatable. Selections are delimited by the cursor on one end and the "mark" at the other, and are highlighted in blue. Commands, as mentioned above, are control keys. The full command set fits in the available control keys because they are sensitive to the context in intuitive ways, changing their behavior in the presence or absence of a current selection, and because their behavior can be modified with a variant prefix, ^Space. This means that your thumb gets into the action too. For example, ^V is the key that creates and modifies the selection. When no selection exists, ^V starts one by placing the mark at the current cursor position. When a selection has already been started, ^V unsets the mark. The variant form ^Space^V selects the entire current line in the absence of a selection, or exchanges the cursor with the mark if one already exists. Typing new text in the presence of a selection will add the new text to the selection if the cursor is at its end, or cut the selection and begin its replacement if the cursor is at its start. Unlike Emacs, you may start a selection by dropping a mark with ^V and then define its content by typing it. This is useful because some commands take a textual argument, like a file name, from the selection. Cutting the selection, apart from the automatic cut that takes place when new text is entered with the cursor at its beginning, is done with ^D. The variant ^Space^D adds the selection to the old content of the clip buffer, which is normally replaced. ^F copies the selection to the clip buffer, with the same variation possibility. And ^B is a generalize form of pasting. It does a simple pasting of the content of the clip buffer when no selection exists, but when ^B is hit with a selection defined, it exchanges the clip buffer with the selection. This speeds up rearrangement of multiple blocks of text, and also forms the method of interactive searching and replacing. Note that ^F, ^D, and ^B constitute a column on the Dvorak keyboard. This is intentional; they are related functions, but highly unlikely to immediately follow one another. The upper right hand keys are all concerned with navigation in both directions by different units. ^H and ^T move backward and forward, respectively, in units of single characters. ^N and ^S do the same by words; their ^Space variants move by punctuated sentences. The vast majority of editing takes place with these home row commands, with ^V to create and manipulate the selection. They are combinable in many intuitive ways to accomplish tasks that constitute distinct commands in Emacs and Vi. For example, when a misspelled word has just been entered, it's often fastest to retype it than to fix it. The aoeui user does so with ^V to place the mark at the end of the bad word, ^N to put the cursor at its beginning, thereby selecting it, and then typing its correct spelling. The other navigation keys are on the upper right hand row. ^G and ^C move backward and forward, respectively, to the beginnings and endings of lines. ^R and ^L move in units of screenfuls. Their variants, ^Space^R and ^Space^L, move to the start and end of the entire view. ^] finds the closest bracket character, or the corresponding bracket character, and is the fastest way to select entire subexpressions or statement blocks. What's a view? It's usually an entire file in a buffer, but it can be just a part of a file. The aoeui editor supports multiple open files, multiple views to them, and multiple windows showing some subset of the views. ^Z recenters the cursor so that it is on the middle line of the current window. ^W finds an invisible view and puts it up in the window, making the old view invisible. (^Space^W closes the old view.) To bring new files into the editor and to rearrange the windows require some commands that are under the fingers of the left hand. First, ^X opens the file whose name is in the selection (or raises it up if it is invisible). With no selection, ^X inserts the full path name of the current file as the new selection in the current view. The variant ^Space^X replaces the current view's path name with the selection. To rename a text, one typically acquires its current name with ^X, modifies it in place within the selection, and establishes it with ^Space^X. ^Y splits the current window horizontally into two and makes another view visible in the lower half. ^Space^Y does the same thing vertically, which is quite handy in a large xterm. ^P transfers the cursor to another window; ^Space^P closes the old window afterwards. On the lower left hand row are the commands for writing files and leaving aoeui. ^K saves all modified files; its variant saves just the current view. ^Q suspends aoeui and returns you to the shell so that you can run a compiler. ^Space^Q saves all modified files and shuts the editor down. For emergencies, ^\ aborts the editor without saving anything. Now to the home row on the left hand side of the Dvorak keyboard. ^U undoes the most recent modification to the file; ^Space^U redoes it. The editor has infinite undo capability, of course. ^E brings all the power of UNIX text processing commands into your editing session, including your own programs and scripts. This is the best way to customize your editor. ^E executes the shell command pipeline that is the current selection, using the content of the clip buffer as its standard input. The selection is replaced with the command's standard output. ^Space^A begins the recording of keystrokes in the current view as a new macro. ^A ends recording, if it is active, otherwise it replays the macro as if its keystrokes had been typed again. ^I is the same as TAB. Their variants cause the current line to be aligned with the previous one. ^J is the same as ^Enter and it inserts a new line with alignment whitespace already in place. ^M is the same as Enter; their variants open a new blank line after the current one. Last is the incremental search mode, which is entered with ^/ or ^_. Case is not significant to aoeui's incremental search mode. In search mode, each character that you type is appended to a search target string and the selection is moved to its next occurrence in the view. Hitting ^/ twice restarts a search using the same target string as last time. In search mode, most commands end the search implicitly before invoking their usual behavior. The differences are: Enter simply ends the search, and ^H and ^T cause motion backwards and forwards, respectively, to other occurrences of the search target string. The aoeui editor does not have an incremental search-and-replace command, because there are at least three ways of doing that with the facilities that already exist. First, the replacement string may be cut or copied into the clip buffer, and then exchanged and recopied with the string occurrences with ^B^F^/^/. Second, the replacement string may be saved as a macro (^Space^A^Vnew text^A) the first time. And third, the entire view can be selected, cut, and then run through "sed s/OLD/NEW/g" with ^E for a global replacement. Only a few other commands exist. The aoeui editor is, of course, fully aware of UTF-8 Unicode encodings, and is capable of displaying and entering control characters. They appear in the display with a red background and a leading caret (^). To enter a control character, use ^^ (Control-^) and a valid letter or punctuation symbol. Raw Unicode code points in decimal and hexadecimal are also supported. It should go without saying that the arrow, Home/End, and Page Up/Down keys on your keyboard are also supported, as is Backspace, but that they usually are not as fast to use as the standard aoeui control character commands. And that's the end of the full story. No "line mode", interactive on-line help, mail reading client, or IRC interface. Those capabilities exist with excellent implementations elsewhere in the free software ecosystem, and with the use of ^Q and ^E are always handy to the aoeui user. Further, the source code for aoeui is freely available for your modification, and is itself smaller than many Emacs local customization files. (It's ironically about one eighth the size of the "nano" editor's source!) Let me conclude with this position statement. Free software works best when programs acknowledge each other's existence and interoperate smoothly, without cluttering themselves with capabilities that are redundant in the rich user environment that surrounds them. There is a strength that comes from having a well-written program that does a few things well that can be fully understood, appreciated, and internalized. Such programs become invisible to their users. I sincerely hope that the aoeui editor can fill that role for you as well as it has for me, and look forward to hearing whether or not it has. Thanks for giving it a look. (aoeui source code is now located at http://aoeui.sourceforge.net) Peter Klausler, 3-18-2007 aoeui-1.7+20160302.git4e5dee9/asdfg000077700000000000000000000000001322350162600172522aoeuiustar00rootroot00000000000000aoeui-1.7+20160302.git4e5dee9/bookmark.c000066400000000000000000000031041322350162600171720ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" static struct bookmark { unsigned id; struct view *view; locus_t locus[2]; struct bookmark *next; } *bookmarks; void bookmark_set(unsigned id, struct view *view, position_t cursor, position_t mark) { struct bookmark *bm = allocate0(sizeof *bm); bookmark_unset(id); bm->id = id; bm->view = view; bm->locus[0] = locus_create(view, cursor); bm->locus[1] = locus_create(view, mark); bm->next = bookmarks; bookmarks = bm; } Boolean_t bookmark_get(struct view **view, position_t *cursor, position_t *mark, unsigned id) { struct bookmark *bm; for (bm = bookmarks; bm; bm = bm->next) if (bm->id == id) { *view = bm->view; *cursor = locus_get(bm->view, bm->locus[0]); if (*cursor == UNSET) *cursor = 0; *mark = locus_get(bm->view, bm->locus[1]); return TRUE; } *view = NULL; *cursor = *mark = 0; return FALSE; } void bookmark_unset(unsigned id) { struct bookmark *bm, *prev = NULL; for (bm = bookmarks; bm; bm = bm->next) if (bm->id == id) { locus_destroy(bm->view, bm->locus[0]); locus_destroy(bm->view, bm->locus[1]); if (prev) prev->next = bm->next; else bookmarks = bm->next; return; } } void bookmark_unset_view(struct view *view) { struct bookmark *bm, *prev = NULL, *next; for (bm = bookmarks; bm; bm = next) { next = bm->next; if (bm->view != view) { prev = bm; continue; } locus_destroy(view, bm->locus[0]); locus_destroy(view, bm->locus[1]); if (prev) prev->next = next; else bookmarks = next; } } aoeui-1.7+20160302.git4e5dee9/buffer.c000066400000000000000000000145011322350162600166410ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" /* * Buffers contain payload bytes and a "gap" of unused space. * The gap is contiguous, and may appear in the midst of the * payload. Editing operations shift the gap around in order * to enlarge it (when bytes are to be deleted) or to add new * bytes to its boundaries. * * The gap comprises all of the free space in a buffer. * Since the cost of moving data does not depend on the * distance by which it is moved, there is no point to * using any gap size other than the full amount of unused * storage. * * Buffers are represented by pages mmap'ed from the file's * temporary# file, or anonymous storage for things that aren't * file texts. * * Besides being used to hold the content of files, buffers * are used for cut/copied text (the "clip buffer"), macros, * and for undo histories. */ struct buffer *buffer_create(char *path) { struct buffer *buffer = allocate0(sizeof *buffer); if (path && *path) { buffer->path = allocate(strlen(path) + 2); sprintf(buffer->path, "%s#", path); errno = 0; buffer->fd = open(buffer->path, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR); if (buffer->fd < 0) message("could not create temporary file %s", buffer->path); } else buffer->fd = -1; return buffer; } void buffer_destroy(struct buffer *buffer) { if (buffer) { munmap(buffer->data, buffer->mapped); if (buffer->fd >= 0) { close(buffer->fd); unlink(buffer->path); } RELEASE(buffer->path); RELEASE(buffer); } } static void place_gap(struct buffer *buffer, position_t offset) { size_t gapsize = buffer_gap_bytes(buffer); if (offset > buffer->payload) offset = buffer->payload; if (offset <= buffer->gap) memmove(buffer->data + offset + gapsize, buffer->data + offset, buffer->gap - offset); else memmove(buffer->data + buffer->gap, buffer->data + buffer->gap + gapsize, offset - buffer->gap); buffer->gap = offset; if (buffer->fd >= 0 && gapsize) memset(buffer->data + buffer->gap, ' ', gapsize); } static void resize(struct buffer *buffer, size_t payload_bytes) { void *p; char *old = buffer->data; fd_t fd; int mapflags = 0; size_t map_bytes = payload_bytes; static size_t pagesize; if (!pagesize) pagesize = getpagesize(); /* Whole pages, with extras as size increases */ map_bytes += pagesize-1; map_bytes /= pagesize; map_bytes *= 11; map_bytes /= 10; map_bytes *= pagesize; if (map_bytes < buffer->mapped) munmap(old + map_bytes, buffer->mapped - map_bytes); if (buffer->fd >= 0 && map_bytes != buffer->mapped) { errno = 0; if (ftruncate(buffer->fd, map_bytes)) die("could not adjust %s from %lu to %lu bytes", buffer->path, (long) buffer->mapped, (long) map_bytes); } if (map_bytes <= buffer->mapped) { buffer->mapped = map_bytes; return; } #ifdef MREMAP_MAYMOVE if (old) { /* attempt extension */ errno = 0; p = mremap(old, buffer->mapped, map_bytes, MREMAP_MAYMOVE); if (p != MAP_FAILED) goto done; #define NEED_DONE_LABEL } #endif /* new/replacement allocation */ if ((fd = buffer->fd) >= 0) { mapflags |= MAP_SHARED; if (old) { munmap(old, buffer->mapped); old = NULL; } } else { #ifdef MAP_ANONYMOUS mapflags |= MAP_ANONYMOUS; #elif defined MAP_ANON mapflags |= MAP_ANON; #else static fd_t anonymous_fd = -1; if (anonymous_fd < 0) { errno = 0; anonymous_fd = open("/dev/zero", O_RDWR); if (anonymous_fd < 0) die("could not open /dev/zero for " "anonymous mappings"); } fd = anonymous_fd; #endif mapflags |= MAP_PRIVATE; } errno = 0; p = mmap(0, map_bytes, PROT_READ|PROT_WRITE, mapflags, fd, 0); if (p == MAP_FAILED) die("mmap(%lu bytes, fd %d) failed", (long) map_bytes, fd); if (old) { memcpy(p, old, buffer->payload); munmap(old, buffer->mapped); } #ifdef NEED_DONE_LABEL done: #endif buffer->data = p; buffer->mapped = map_bytes; } size_t buffer_raw(struct buffer *buffer, char **out, position_t offset, size_t bytes) { if (!buffer) { *out = NULL; return 0; } if (offset >= buffer->payload) offset = buffer->payload; if (offset + bytes > buffer->payload) bytes = buffer->payload - offset; if (!bytes) { *out = NULL; return 0; } if (offset < buffer->gap && offset + bytes > buffer->gap) place_gap(buffer, offset + bytes); *out = buffer->data + offset; if (offset >= buffer->gap) *out += buffer_gap_bytes(buffer); return bytes; } size_t buffer_get(struct buffer *buffer, void *out, position_t offset, size_t bytes) { size_t left; if (!buffer) return 0; if (offset >= buffer->payload) offset = buffer->payload; if (offset + bytes > buffer->payload) bytes = buffer->payload - offset; if (!bytes) return 0; left = bytes; if (offset < buffer->gap) { unsigned before = buffer->gap - offset; if (before > bytes) before = bytes; memcpy(out, buffer->data + offset, before); out = (char *) out + before; offset += before; left -= before; if (!left) return bytes; } offset += buffer_gap_bytes(buffer); memcpy(out, buffer->data + offset, left); return bytes; } size_t buffer_delete(struct buffer *buffer, position_t offset, size_t bytes) { if (!buffer) return 0; if (offset > buffer->payload) offset = buffer->payload; if (offset + bytes > buffer->payload) bytes = buffer->payload - offset; place_gap(buffer, offset); buffer->payload -= bytes; return bytes; } size_t buffer_insert(struct buffer *buffer, const void *in, position_t offset, size_t bytes) { if (!buffer) return 0; if (offset > buffer->payload) offset = buffer->payload; if (bytes > buffer_gap_bytes(buffer)) { place_gap(buffer, buffer->payload); resize(buffer, buffer->payload + bytes); } place_gap(buffer, offset); if (in) memcpy(buffer->data + offset, in, bytes); else memset(buffer->data + offset, 0, bytes); buffer->gap += bytes; buffer->payload += bytes; return bytes; } size_t buffer_move(struct buffer *to, position_t to_offset, struct buffer *from, position_t from_offset, size_t bytes) { char *raw = NULL; bytes = buffer_raw(from, &raw, from_offset, bytes); buffer_insert(to, raw, to_offset, bytes); return buffer_delete(from, from_offset, bytes); } void buffer_snap(struct buffer *buffer) { if (buffer && buffer->fd >= 0) { place_gap(buffer, buffer->payload); if (ftruncate(buffer->fd, buffer->payload)) { /* don't care */ } } } aoeui-1.7+20160302.git4e5dee9/buffer.h000066400000000000000000000023161322350162600166470ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef BUFFER_H #define BUFFER_H /* Gap buffers */ struct buffer; struct buffer *buffer_create(char *path); void buffer_destroy(struct buffer *); size_t buffer_raw(struct buffer *, char **, position_t, size_t); size_t buffer_get(struct buffer *, void *, position_t, size_t); size_t buffer_delete(struct buffer *, position_t, size_t); size_t buffer_insert(struct buffer *, const void *, position_t, size_t); size_t buffer_move(struct buffer *dest, position_t, struct buffer *src, position_t, size_t); void buffer_snap(struct buffer *); /* do *not* use directly; this definition is here * just for the inline functions. */ struct buffer { char *data; size_t payload, mapped; position_t gap; fd_t fd; char *path; }; INLINE size_t buffer_bytes(struct buffer *buffer) { return buffer ? buffer->payload : 0; } INLINE size_t buffer_gap_bytes(struct buffer *buffer) { return buffer->mapped - buffer->payload; } INLINE int buffer_byte(struct buffer *buffer, size_t offset) { if (!buffer || offset >= buffer->payload) return -1; if (offset >= buffer->gap) offset += buffer_gap_bytes(buffer); return offset[(Byte_t *) buffer->data]; } #endif aoeui-1.7+20160302.git4e5dee9/child.c000066400000000000000000000247541322350162600164660ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" /* * Handle the ^E command, which runs the shell command pipeline * in the selection using the current buffer content as * the standard input, replacing the selection with the * standard output of the child process. * * This all works asynchronously. The standard output and * error streams of the child are monitored with select() * and whenever new child output is available, it's captured * and inserted into the original view. */ static struct stream *streams; typedef Boolean_t (*activity)(struct stream *, char *received, ssize_t bytes); struct stream { struct stream *next; fd_t fd; Boolean_t retain; activity activity; struct view *view; locus_t locus; const char *data; size_t bytes, writ; }; static Boolean_t insertion_activity(struct stream *stream, char *received, ssize_t bytes) { position_t offset; if (bytes <= 0) return FALSE; offset = locus_get(stream->view, stream->locus); if (offset == UNSET) return FALSE; view_insert(stream->view, received, offset, bytes); locus_set(stream->view, stream->locus, offset + bytes); return TRUE; } static Boolean_t shell_output_activity(struct stream *stream, char *received, ssize_t bytes) { position_t offset; struct view *view = stream->view; locus_t locus = view->shell_out_locus; if (bytes <= 0) return FALSE; offset = locus_get(view, locus); if (offset == UNSET) offset = view->bytes; else offset++; locus_set(view, locus, offset); while (bytes--) { char ch = *received++; if (ch != '\r' && ch != CONTROL('H')) { if (ch == '\n') offset = locus_get(view, locus); view_insert(view, &ch, offset++, 1); } } offset = locus_get(view, locus); locus_set(view, locus, offset - !!offset); return TRUE; } static Boolean_t error_activity(struct stream *stream, char *received, ssize_t bytes) { if (bytes <= 0) return FALSE; received[bytes] = '\0'; message("%s", received); return TRUE; } static Boolean_t out_activity(struct stream *stream, char *x, ssize_t bytes) { ssize_t chunk = stream->bytes - stream->writ; if (chunk <= 0) return 0; do { errno = 0; bytes = write(stream->fd, stream->data + stream->writ, chunk); } while (bytes < 0 && (errno == EAGAIN || errno == EINTR)); if (bytes < 0 && (errno == EPIPE || errno == EIO)) { message("write failed (child terminated?)"); return FALSE; } if (bytes <= 0) die("write of %d bytes failed", chunk); stream->writ += bytes; return bytes > 0; } static struct stream *stream_create(fd_t fd) { struct stream *stream = allocate0(sizeof *stream); stream->fd = fd; stream->locus = NO_LOCUS; if (!streams) streams = stream; else { struct stream *prev = streams; while (prev->next) prev = prev->next; prev->next = stream; } return stream; } static void stream_destroy(struct stream *stream, struct stream *prev) { if (!stream->retain) close(stream->fd); if (stream->view) locus_destroy(stream->view, stream->locus); if (prev) prev->next = stream->next; else streams = stream->next; RELEASE(stream->data); RELEASE(stream); } Boolean_t multiplexor(Boolean_t block) { struct timeval tv, *tvp = NULL; int j; fd_t maxfd = 0; ssize_t bytes; struct stream *stream, *prev, *next; fd_set fds[3]; char *rdbuff = NULL; for (j = 0; j < 3; j++) FD_ZERO(&fds[j]); FD_SET(0, &fds[0]); FD_SET(0, &fds[2]); for (stream = streams; stream; stream = stream->next) { FD_SET(stream->fd, &fds[!!stream->data]); FD_SET(stream->fd, &fds[2]); if (stream->fd > maxfd) maxfd = stream->fd; } if (block) tvp = NULL; else memset(tvp = &tv, 0, sizeof tv); errno = 0; if (select(maxfd + 1, &fds[0], &fds[1], &fds[2], tvp) < 0) return errno != EAGAIN && errno != EINTR; for (prev = NULL, stream = streams; stream; stream = next) { next = stream->next; if (!FD_ISSET(stream->fd, &fds[!!stream->data]) && !FD_ISSET(stream->fd, &fds[2])) { prev = stream; continue; } if (stream->data) bytes = 0; else { if (!rdbuff) rdbuff = allocate(1024); errno = 0; bytes = read(stream->fd, rdbuff, 1023); } if (stream->activity(stream, rdbuff, bytes)) prev = stream; else stream_destroy(stream, prev); } RELEASE(rdbuff); return FD_ISSET(0, &fds[0]) || FD_ISSET(0, &fds[2]); }; static void child_close(struct view *view) { if (view->shell_std_in >= 0) { close(view->shell_std_in); view->shell_std_in = -1; } if (view->shell_pg >= 0) { killpg(view->shell_pg, SIGHUP); view->shell_pg = -1; } locus_destroy(view, view->shell_out_locus); view->shell_out_locus = NO_LOCUS; } void demultiplex_view(struct view *view) { struct stream *stream, *prev = NULL, *next; for (stream = streams; stream; stream = next) { next = stream->next; if (stream->view == view) stream_destroy(stream, prev); else prev = stream; } child_close(view); } void multiplex_write(fd_t fd, const char *data, ssize_t bytes, Boolean_t retain) { struct stream *stream; if (bytes < 0) bytes = data ? strlen(data) : 0; if (!bytes) { if (!retain) close(fd); return; } stream = stream_create(fd); stream->retain = retain; stream->activity = out_activity; stream->data = data; stream->bytes = bytes; } static void single_write(fd_t fd, Unicode_t ch) { char buf[8]; size_t len = unicode_utf8(buf, ch); char *single = allocate(len); memcpy(single, buf, len); multiplex_write(fd, single, len, TRUE /*retain*/); } static Boolean_t pipes(fd_t fd[3][2], unsigned stdfds) { int j, k; static Boolean_t use_ptys = TRUE; /* pipe(2) creates two file descriptors: [0] read, [1] write; * this code transposes some to produce: [0] parent, [1] child */ if (stdfds == 2 && use_ptys) { struct termios termios = original_termios; termios.c_oflag &= ~ONLCR; termios.c_lflag &= ~(ECHO|ECHOE| ECHOKE); termios.c_lflag |= ECHOK; errno = 0; if (!openpty(&fd[0][0], &fd[0][1], NULL, &termios, NULL)) { for (j = 1; j < 3; j++) for (k = 0; k < 2; k++) fd[j][k] = dup(fd[0][k]); return TRUE; } message("could not create pty"); use_ptys = FALSE; } /* use pipes */ errno = 0; for (j = 0; j < stdfds; j++) { if (pipe(fd[j])) { message("could not create pipes"); return 0; } } j = fd[0][0], fd[0][0] = fd[0][1], fd[0][1] = j; return TRUE; } static pid_t child(fd_t stdfd[3][2], unsigned stdfds, const char *argv[]) { int j; pid_t pid; if (!pipes(stdfd, stdfds)) return -1; fflush(NULL); errno = 0; if ((pid = fork()) < 0) { message("could not fork"); return -1; } if (pid) return pid; /* parent */ /* child */ for (j = 0; j < 3; j++) { close(stdfd[j][0]); errno = 0; if (dup2(stdfd[j][1], j) != j) { fprintf(stderr, "dup2(%d,%d) failed: %s\n", stdfd[j][1], j, strerror(errno)); exit(EXIT_FAILURE); } close(stdfd[j][1]); } if (isatty(0)) { pid = setsid(); /* new session */ ioctl(0, TIOCSCTTY); /* set controlling terminal */ ioctl(0, TIOCSPGRP, &pid); /* set process group */ } setenv("TERM", "network", TRUE); unsetenv("LS_COLORS"); errno = 0; execvp(argv[0], (char *const *) argv); fprintf(stderr, "could not execute %s: %s\n", argv[0], strerror(errno)); exit(EXIT_FAILURE); } static const char *shell_name(void) { const char *shell = getenv("SHELL"); if (access(shell, X_OK)) shell = "/bin/sh"; return shell; } void mode_child(struct view *view) { char *command = view_extract_selection(view); char *wrbuff = NULL; position_t cursor; size_t to_write; fd_t stdfd[3][2]; const char *argv[4]; struct stream *std_out, *std_err; int j; if (!command) { window_beep(view); return; } if (view->shell_std_in >= 0) { locus_set(view, MARK, UNSET); multiplex_write(view->shell_std_in, command, strlen(command), TRUE /*retain*/); single_write(view->shell_std_in, '\n'); return; } view_delete_selection(view); if (command[0] == 'c' && command[1] == 'd' && (!command[2] || command[2] == ' ')) { const char *dir = command + 2; while (*dir == ' ') dir++; if (!*dir && !(dir = getenv("HOME"))) window_beep(view); else { errno = 0; if (chdir(dir)) message("%s failed", command); } return; } cursor = locus_get(view, CURSOR); to_write = clip_paste(view, cursor, 0); if (to_write) { locus_set(view, MARK, cursor); wrbuff = view_extract_selection(view); view_delete_selection(view); } argv[0] = shell_name(); argv[1] = "-c"; argv[2] = command; argv[3] = NULL; if (child(stdfd, 3, argv) < 0) return; for (j = 0; j < 3; j++) close(stdfd[j][1]); multiplex_write(stdfd[0][0], wrbuff, to_write, FALSE /*don't retain*/); std_out = stream_create(stdfd[1][0]); std_out->activity = insertion_activity; std_out->view = view; std_out->locus = locus_create(view, cursor); std_err = stream_create(stdfd[2][0]); std_err->activity = error_activity; } void mode_shell_pipe(struct view *view) { fd_t stdfd[3][2]; const char *shell, *p, *argv[8]; struct stream *output; int j, ai = 0; pid_t pg; argv[ai++] = shell = shell_name(); if ((p = strrchr(shell, '/')) && !strcmp(p+1, "bash")) argv[ai++] = "--noediting"; argv[ai++] = NULL; if ((pg = child(stdfd, 2, argv)) < 0) return; close(stdfd[2][0]); for (j = 0; j < 3; j++) close(stdfd[j][1]); child_close(view); view->shell_out_locus = locus_create(view, locus_get(view, CURSOR)); view->shell_pg = pg; output = stream_create(stdfd[1][0]); output->activity = shell_output_activity; output->view = view; view->shell_std_in = stdfd[0][0]; } void shell_command(struct view *view, Unicode_t ch) { position_t offset, linestart, cursor; char *command; if (ch >= ' ' || ch == '\t') return; cursor = locus_get(view, CURSOR); linestart = cursor ? find_line_start(view, cursor-1) : 0; offset = locus_get(view, view->shell_out_locus) + 1; if (offset < linestart || offset >= cursor) offset = linestart; command = view_extract(view, offset, cursor - offset); if (command) multiplex_write(view->shell_std_in, command, -1, TRUE /*retain*/); locus_set(view, view->shell_out_locus, view->bytes ? view->bytes-1 : UNSET); locus_set(view, CURSOR, view->bytes); } void background_command(const char *command) { int j; pid_t pg; fd_t stdfd[3][2]; const char *argv[4]; struct stream *std_out, *std_err; argv[0] = shell_name(); argv[1] = "-c"; argv[2] = command; argv[3] = NULL; if ((pg = child(stdfd, 3, argv)) < 0) return; for (j = 0; j < 3; j++) close(stdfd[j][1]); close(stdfd[0][0]); std_out = stream_create(stdfd[1][0]); std_out->activity = error_activity; std_err = stream_create(stdfd[2][0]); std_err->activity = error_activity; waitpid(pg, NULL, 0); } aoeui-1.7+20160302.git4e5dee9/child.h000066400000000000000000000006061322350162600164610ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef CHILD_H #define CHILD_H void mode_child(struct view *); void mode_shell_pipe(struct view *); void shell_command(struct view *, Unicode_t); void background_command(const char *command); Boolean_t multiplexor(Boolean_t block); void multiplex_write(fd_t fd, const char *, ssize_t bytes, Boolean_t retain); #endif aoeui-1.7+20160302.git4e5dee9/clip.c000066400000000000000000000021331322350162600163150ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" static struct buffer **clip_buffer; static unsigned clip_buffers; void clip_init(unsigned reg) { if (reg < clip_buffers) buffer_delete(clip_buffer[reg], 0, ~(size_t)0); } size_t clip(unsigned reg, struct view *view, position_t offset, size_t bytes, Boolean_t append) { char *raw; if (reg >= clip_buffers) { clip_buffer = reallocate(clip_buffer, (reg+1) * sizeof *clip_buffer); memset(clip_buffer + clip_buffers, 0, (reg + 1 - clip_buffers) * sizeof *clip_buffer); clip_buffers = reg + 1; } if (!clip_buffer[reg]) clip_buffer[reg] = buffer_create(NULL); bytes = view_raw(view, &raw, offset, bytes); return buffer_insert(clip_buffer[reg], raw, append ? buffer_bytes(clip_buffer[reg]) : 0, bytes); } size_t clip_paste(struct view *view, position_t offset, unsigned reg) { char *raw; size_t bytes; if (reg >= clip_buffers) return 0; bytes = buffer_raw(clip_buffer[reg], &raw, 0, buffer_bytes(clip_buffer[reg])); return view_insert(view, raw, offset, bytes); } aoeui-1.7+20160302.git4e5dee9/clip.h000066400000000000000000000003741322350162600163270ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ struct view; void clip_init(unsigned reg); size_t clip(unsigned reg, struct view *, position_t, size_t, Boolean_t append); size_t clip_paste(struct view *, position_t, unsigned reg); aoeui-1.7+20160302.git4e5dee9/die.c000066400000000000000000000031061322350162600161300ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" /* Error messaging */ void die(const char *msg, ...) { int err = errno; va_list ap; tcsetattr(1, TCSANOW, &original_termios); fputs("\afatal editor error: ", stderr); va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); if (err) fprintf(stderr, ": %s", strerror(err)); fputc('\n', stderr); exit(EXIT_FAILURE); } void message(const char *msg, ...) { int err = errno; va_list ap; struct view *view = view_find("* ATTENTION *"); position_t start; if (!view) view = text_create("* ATTENTION *", TEXT_EDITOR); view->text->flags &= ~TEXT_RDONLY; view_insert(view, "\n", view->bytes, 1); start = view->bytes; va_start(ap, msg); view_vprintf(view, msg, ap); va_end(ap); if (err) view_printf(view, "\n(System error code: %s)", strerror(err)); view_insert(view, " ", view->bytes, 1); view->text->flags |= TEXT_RDONLY; locus_set(view, CURSOR, start); window_below(NULL, view, 3 + !!err); } static struct view *status_view; void status(const char *msg, ...) { va_list ap; if (!status_view) status_view = text_create("* STATUS *", TEXT_EDITOR); status_view->text->flags &= ~TEXT_RDONLY; view_delete(status_view, 0, status_view->bytes); va_start(ap, msg); view_vprintf(status_view, msg, ap); va_end(ap); view_insert(status_view, " ", status_view->bytes, 1); locus_set(status_view, CURSOR, status_view->bytes-1); status_view->text->flags |= TEXT_RDONLY; window_below(NULL, status_view, 3); } void status_hide(void) { if (status_view) window_destroy(status_view->window); } aoeui-1.7+20160302.git4e5dee9/die.h000066400000000000000000000003401322350162600161320ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef DIE_H #define DIE_H void die(const char *, ...); void message(const char *, ...); void status(const char *, ...); void status_hide(void); #endif aoeui-1.7+20160302.git4e5dee9/display-test.c000066400000000000000000000074701322350162600200210ustar00rootroot00000000000000#include #include #include #include #include #include "types.h" #include "utf8.h" #include "display.h" void die(const char *, ...); Boolean_t multiplexor(Boolean_t); static struct display *D; static int rows, columns; static void dfill(int row, int rows, int col, int cols, int ch, rgba_t fgrgba, rgba_t bgrgba) { int j, k; for (j = 0; j < rows; j++) for (k = 0; k < cols; k++) display_put(D, row+j, col+k, ch, fgrgba, bgrgba); } static void dprint(int row, int col, rgba_t fgrgba, rgba_t bgrgba, const char *format, ...) { char buffer[256], *p; va_list ap; va_start(ap, format); vsnprintf(buffer, sizeof buffer, format, ap); va_end(ap); for (p = buffer; *p; p++) display_put(D, row, col++, *p, fgrgba, bgrgba); } static void dpause(void) { int ch; dfill(rows-1, 1, 0, columns, ' ', RED_RGBA, WHITE_RGBA); dprint(rows-1, 0, RED_RGBA, WHITE_RGBA, "Hit Q to quit, or any other key to continue..."); while ((ch = display_getch(D, 1)) == ERROR_CHANGED) display_get_geometry(D, &rows, &columns); if (ch == 'q' || ch == 'Q') { display_end(D); exit(EXIT_SUCCESS); } } void die(const char *msg, ...) { va_list ap; display_end(D); va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); exit(EXIT_FAILURE); } Boolean_t multiplexor(Boolean_t block) { return TRUE; } int main(void) { if (tcgetattr(1, &original_termios)) die("not running in a terminal"); D = display_init(); display_get_geometry(D, &rows, &columns); display_title(D, "display-test"); dprint(0, 0, BLACK_RGBA, WHITE_RGBA, "geometry: %d rows, %d columns", rows, columns); dprint(1, 0, DEFAULT_FGRGBA, DEFAULT_BGRGBA, "default foreground and background"); dprint(2, 0, WHITE_RGBA, BLACK_RGBA, "white foreground on black background"); dprint(3, 0, BLACK_RGBA, WHITE_RGBA, "black foreground on white background"); dprint(4, 0, RED_RGBA, BLUE_RGBA, "red foreground on blue background"); dprint(5, 0, BLUE_RGBA, RED_RGBA, "blue foreground on red background"); dpause(); dfill(0, rows, 0, columns, '.', RED_RGBA, GREEN_RGBA); dprint(0, 0, BLUE_RGBA, RED_RGBA, "filled with red dots on green"); dpause(); display_erase(D, 0, 0, rows, columns); dprint(0, 0, BLUE_RGBA, RED_RGBA, "after ERASEALL display_erase()"); dpause(); dfill(0, rows, 0, columns, '.', RED_RGBA, GREEN_RGBA); display_erase(D, rows/2, 0, rows - rows/2, columns); dprint(0, 0, BLUE_RGBA, RED_RGBA, "after ERASETOEND display_erase()"); dpause(); dfill(0, rows, 0, columns, '.', RED_RGBA, GREEN_RGBA); display_erase(D, 1, columns/2, 1, columns); dprint(0, 0, BLUE_RGBA, RED_RGBA, "after ERASELINE display_erase()"); dpause(); dfill(0, rows, 0, columns, '.', RED_RGBA, GREEN_RGBA); display_erase(D, 1, 0, 1, columns/2); dprint(0, 0, BLUE_RGBA, RED_RGBA, "after ERASECOLS display_erase()"); dpause(); dfill(0, rows, 0, columns, '.', RED_RGBA, GREEN_RGBA); display_insert_lines(D, 2, 0, rows/2, rows-2, columns); dprint(0, 0, BLUE_RGBA, RED_RGBA, "after display_insert_lines()"); dpause(); dfill(0, rows, 0, columns, '.', RED_RGBA, GREEN_RGBA); dfill(rows-1, 1, 0, columns, '*', RED_RGBA, GREEN_RGBA); display_delete_lines(D, 2, 0, rows/2, rows-2, columns); dprint(0, 0, BLUE_RGBA, RED_RGBA, "after display_delete_lines()"); dpause(); dfill(0, rows, 0, columns, '.', RED_RGBA, GREEN_RGBA); display_insert_spaces(D, 2, 0, columns/2, columns); display_insert_spaces(D, 3, columns/2, columns - (columns/2), columns); dprint(0, 0, BLUE_RGBA, RED_RGBA, "after display_insert_spaces()"); dpause(); dfill(0, rows, 0, columns, '.', RED_RGBA, GREEN_RGBA); display_delete_chars(D, 2, 0, columns/2, columns); display_delete_chars(D, 3, columns/2, columns - (columns/2), columns); dprint(0, 0, BLUE_RGBA, RED_RGBA, "after display_delete_chars()"); dpause(); display_end(D); printf("display-test done\n"); return EXIT_SUCCESS; } aoeui-1.7+20160302.git4e5dee9/display.c000066400000000000000000000617271322350162600170510ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" /* * Bandwidth-optimized terminal display management. * Maintain an image of the display surface and * update it when repainting differs from the image. * Assume lowest-common-denominator terminal emulation. * * reference: man 4 console_codes */ #define OUTBUF_SIZE 1024 #define INBUF_SIZE 64 #define MAX_COLORS 8 #define ESCCHAR '\x1b' #define ESC "\x1b" #define CSI ESC "[" #define OSC ESC "]" #define ST "\x07" #define CTL_RESET ESC "c" #define CTL_NUMLOCK ESC ">" #define CTL_CLEARLEDS ESC "[0q" #define CTL_NUMLOCKLED ESC "[2q" #define CTL_UTF8 ESC "%G" #define CTL_RESETMODES CSI "0m" #define CTL_RESETCOLORS CSI "39;49m" #define CTL_GOTO CSI "%d;%df" #define CTL_ERASEALL CSI "2J" #define CTL_ERASETOEND CSI "J" #define CTL_ERASELINE CSI "K" #define CTL_ERASECOLS CSI "%dX" #define CTL_DELCOLS CSI "%uP" #define CTL_DELLINES CSI "%uM" #define CTL_INSCOLS CSI "%u@" #define CTL_INSLINES CSI "%uL" #define CTL_RGB OSC "4;%d;rgb:%02x/%02x/%02x" ST #define CTL_CURSORRGB OSC "12;rgb:%02x/%02x/%02x" ST #define CTL_CURSORPOS CSI "6n" #define FG_COLOR 30 #define BG_COLOR 40 #define XTERM_TITLE OSC "0;%s" ST #define XTERM_ALTSCREEN CSI "?47h" #define XTERM_REGSCREEN CSI "?47l" #define XTERM_BCKISDEL CSI "?67l" #define XTERM_LOCATOR CSI "1'z" CSI "1'{" CSI "4'{" #define BAD_RGBA 0x01010100 struct cell { Unicode_t unicode; rgba_t fgrgba, bgrgba; }; enum cursor_position_knowledge { NEEDED, SOUGHT, KNOWN, INVALID }; struct display { int rows, columns; int cursor_row, cursor_column; rgba_t cursor_rgba; Boolean_t size_changed; rgba_t fgrgba, bgrgba; int at_row, at_column; enum cursor_position_knowledge get_initial_cursor_position; int initial_row, initial_column; struct cell *image; struct display *next; Byte_t inbuf[INBUF_SIZE]; char outbuf[OUTBUF_SIZE]; size_t inbuf_bytes, outbuf_bytes; Boolean_t is_xterm, is_linux, is_apple; rgba_t color[MAX_COLORS]; unsigned colors; char *title; }; struct termios original_termios; static struct display *display_list; static void (*old_sigwinch)(int, siginfo_t *, void *); static FILE *debug_file; static void emit(const char *str, size_t bytes) { size_t wrote; ssize_t chunk; if (debug_file && bytes) { fprintf(debug_file, "emit %d:", (int) bytes); fwrite(str, bytes, 1, debug_file); fputc('\n', debug_file); } for (wrote = 0; wrote < bytes; wrote += chunk) { errno = 0; chunk = write(1, str + wrote, bytes - wrote); if (chunk < 0) { if (errno == EINTR || errno == EAGAIN) continue; die("write of %d bytes failed", bytes); } } } static void flush(struct display *display) { emit(display->outbuf, display->outbuf_bytes); display->outbuf_bytes = 0; } static void out(struct display *display, const char *str, size_t bytes) { if (display->outbuf_bytes + bytes > sizeof display->outbuf) flush(display); if (bytes > sizeof display->outbuf) emit(str, bytes); else { memcpy(display->outbuf + display->outbuf_bytes, str, bytes); display->outbuf_bytes += bytes; } } static void outs(struct display *display, const char *str) { out(display, str, strlen(str)); } static void outf(struct display *display, const char *msg, ...) { char buf[128]; va_list ap; va_start(ap, msg); vsnprintf(buf, sizeof buf, msg, ap); va_end(ap); outs(display, buf); } static void force_moveto(struct display *display, int row, int column) { outf(display, CTL_GOTO, (display->at_row = row) + 1, (display->at_column = column) + 1); } static void moveto(struct display *display, int row, int column) { if (row == display->at_row && column >= display->at_column && column < display->at_column + 8) { int j, cols = column - display->at_column; struct cell *cell = &display->image[row*display->columns + display->at_column]; for (j = 0; j < cols; j++) if (cell[j].unicode != ' ' || cell[j].bgrgba != display->bgrgba) break; if (j == cols) { while (j-- > 0) outs(display, " "); display->at_column = column; return; } } else if (row == display->at_row + 1) { if (column == display->at_column) { outs(display, "\n"); display->at_row++; return; } if (!column) { outs(display, "\n\r"); display->at_row++; display->at_column = 0; return; } } force_moveto(display, row, column); } void display_sync(struct display *display) { moveto(display, display->cursor_row, display->cursor_column); flush(display); } static unsigned linux_colormap(rgba_t rgba) { unsigned bgr1; if (rgba & 0xff) return 9; /* any alpha implies "default color" */ bgr1 = !!(rgba >> 24); bgr1 |= !!(rgba >> 16 & 0xff) << 1; bgr1 |= !!(rgba >> 8 & 0xff) << 2; return bgr1; } static unsigned color_delta(rgba_t rgba1, rgba_t rgba2) { unsigned delta = 1, j; if (rgba1 >> 8 == rgba2 >> 8) return 0; for (j = 8; j < 32; j += 8) { Byte_t c1 = rgba1 >> j, c2 = rgba2 >> j; int cd = c1 - c2; delta *= (cd < 0 ? -cd : cd) + 1; } return delta; } static rgba_t color_mean(rgba_t rgba1, rgba_t rgba2) { rgba_t mean = 0; int j; for (j = 0; j < 32; j += 8) { Byte_t c1 = rgba1 >> j, c2 = rgba2 >> j; mean |= c1 + c2 >> 1 << j; } return mean; } static unsigned colormap(struct display *display, rgba_t rgba) { static rgba_t basic[] = { BLACK_RGBA, PALE_RGBA(RED_RGBA), PALE_RGBA(GREEN_RGBA), PALE_RGBA(YELLOW_RGBA), PALE_RGBA(BLUE_RGBA), PALE_RGBA(MAGENTA_RGBA), PALE_RGBA(CYAN_RGBA), PALE_RGBA(WHITE_RGBA), }; static rgba_t bright[] = { BLACK_RGBA, RED_RGBA, GREEN_RGBA, YELLOW_RGBA, BLUE_RGBA, MAGENTA_RGBA, CYAN_RGBA, WHITE_RGBA, }; unsigned idx, best, bestdelta, delta; if (rgba & 0xff) return 9; /* any alpha? use default */ for (idx = 0; idx < 8; idx++) if (rgba == basic[idx]) return idx; for (idx = 0; idx < 8; idx++) if (rgba == bright[idx]) return idx + 10; for (idx = 0; idx < display->colors; idx++) if (display->color[idx] == rgba) return idx+18; if (idx < MAX_COLORS) display->color[best = display->colors++] = rgba; else { best = 0; bestdelta = ~0; for (idx = 0; idx < display->colors; idx++) { delta = color_delta(display->color[idx], rgba); if (delta < bestdelta) { best = idx; bestdelta = delta; } } display->color[best] = color_mean(display->color[best], rgba); } best += 18; outf(display, CTL_RGB, best, rgba >> 24, rgba >> 16 & 0xff, rgba >> 8 & 0xff); return best; } static void set_color(struct display *display, rgba_t rgba, unsigned magic) { if (display->is_linux) outf(display, CSI "%dm", linux_colormap(rgba) + magic); else { unsigned c = colormap(display, rgba); if (c >= 18) outf(display, CSI "%d;5;%dm", magic+8, c); else if (c >= 10) outf(display, CSI "%dm", c - 10 + magic + 60); else outf(display, CSI "%dm", c + magic); } } static void background_color(struct display *display, rgba_t rgba) { if (rgba != display->bgrgba) { set_color(display, rgba, BG_COLOR); display->bgrgba = rgba; } } static void foreground_color(struct display *display, rgba_t rgba) { if (rgba != display->fgrgba) { set_color(display, rgba, FG_COLOR); display->fgrgba = rgba; } } static void default_colors(struct display *display) { foreground_color(display, DEFAULT_FGRGBA); background_color(display, DEFAULT_BGRGBA); } void display_put(struct display *display, int row, int column, Unicode_t unicode, rgba_t fgrgba, rgba_t bgrgba) { char buf[8]; struct cell *cell; if (row < 0 || row >= display->rows || column < 0 || column >= display->columns) return; if (unicode < ' ') unicode = ' '; cell = &display->image[row*display->columns + column]; if (cell->unicode == unicode && (cell->fgrgba == fgrgba || unicode == ' ') && cell->bgrgba == bgrgba) return; moveto(display, row, column); background_color(display, bgrgba); if (unicode != ' ') foreground_color(display, fgrgba); out(display, buf, unicode_utf8(buf, unicode)); display->at_column++; /* display->image may have moved due to resize */ cell = &display->image[row*display->columns + column]; cell->unicode = unicode; cell->bgrgba = bgrgba; cell->fgrgba = fgrgba; } static Boolean_t color_fill(struct display *display, int row, int rows, int column, int columns, rgba_t fgrgba, rgba_t bgrgba) { int r, c; Boolean_t any = FALSE; for (r = 0; r < rows; r++) { struct cell *cell = &display->image[(row+r) * display->columns + column]; for (c = 0; c < columns; c++, cell++) if (cell->fgrgba != fgrgba || cell->bgrgba != bgrgba) { cell->fgrgba = fgrgba; cell->bgrgba = bgrgba; any = TRUE; } } return any; } /* After an erase, insert, or delete command, update the state. * These commands affect an Apple Terminal differently than other * terminal emulators with respect to the background colors; * Apple fills with the default background, while a regular Xterm * and the Linux console fill with the most recent color selections. * We avoid the discrepancy by setting the colors to the default values * prior to executing erase, insert, and delete commands in the functions * that complete their work by calling this routine. * Returns TRUE if any state was modified. */ static Boolean_t space_fill(struct display *display, int row, int rows, int column, int columns) { int r, c; Boolean_t any = FALSE; rgba_t fg = display->fgrgba, bg = display->bgrgba; if (display->is_apple) { fg = DEFAULT_FGRGBA; bg = DEFAULT_BGRGBA; } for (r = 0; r < rows; r++) { struct cell *cell = &display->image[(row+r) * display->columns + column]; for (c = 0; c < columns; c++, cell++) if (cell->unicode != ' ') { cell->unicode = ' '; any = TRUE; } } if (color_fill(display, row, rows, column, columns, fg, bg)) any = TRUE; return any; } void display_erase(struct display *display, int row, int column, int rows, int columns) { int r; if (row < 0 || row >= display->rows || column < 0 || column >= display->columns) return; if (row + rows > display->rows) rows = display->rows - row; if (column + columns > display->columns) columns = display->columns - column; if (rows <= 0 || columns <= 0) return; default_colors(display); if (!space_fill(display, row, rows, column, columns)) return; if (!column && columns == display->columns && row + rows == display->rows) { if (!row) outs(display, CTL_ERASEALL); else { moveto(display, row, column); outs(display, CTL_ERASETOEND); } } else if (column + columns == display->columns) { for (r = 0; r < rows; r++) { moveto(display, row + r, column); outs(display, CTL_ERASELINE); } } else { for (r = 0; r < rows; r++) { moveto(display, row + r, column); outf(display, CTL_ERASECOLS, columns); } } } void display_insert_spaces(struct display *display, int row, int column, int spaces, int columns) { struct cell *cell; if (row < 0 || row >= display->rows || column < 0 || column >= display->columns) return; if (column + columns > display->columns) columns = display->columns - column; if (spaces > columns) spaces = columns; if (spaces <= 0) return; default_colors(display); if (column + columns != display->columns) { moveto(display, row, column + columns - spaces); outf(display, CTL_DELCOLS, spaces); } moveto(display, row, column); outf(display, CTL_INSCOLS, spaces); cell = &display->image[row*display->columns + column]; memmove(cell + spaces, cell, (columns - spaces) * sizeof *cell); space_fill(display, row, 1, column, spaces); } void display_delete_chars(struct display *display, int row, int column, int chars, int columns) { struct cell *cell; if (row < 0 || row >= display->rows || column < 0 || column >= display->columns) return; if (column + columns > display->columns) columns = display->columns - column; if (chars > columns) chars = columns; if (chars <= 0) return; default_colors(display); moveto(display, row, column); outf(display, CTL_DELCOLS, chars); moveto(display, row, column + columns - chars); outf(display, CTL_INSCOLS, chars); cell = &display->image[row*display->columns + column]; memmove(cell, cell + chars, (columns - chars) * sizeof *cell); space_fill(display, row, 1, column + columns - chars, chars); } static Boolean_t validate(struct display *display, int row, int column, int *rows, int *columns, int *lines) { if (row < 0 || row >= display->rows || column < 0 || column >= display->columns) return FALSE; if (row + *rows > display->rows) *rows = display->rows - row; if (*lines > *rows) *lines = *rows; if (column + *columns > display->columns) *columns = display->columns - column; return *lines > 0 && *columns > 0; } void display_insert_lines(struct display *display, int row, int column, int lines, int rows, int columns) { if (column || columns != display->columns || !validate(display, row, column, &rows, &columns, &lines)) return; default_colors(display); if (row + rows != display->rows) { moveto(display, row + rows - lines, 0); outf(display, CTL_DELLINES, lines); } moveto(display, row, 0); outf(display, CTL_INSLINES, lines); memmove(&display->image[(row + lines) * display->columns], &display->image[row * display->columns], (rows - lines) * display->columns * sizeof *display->image); space_fill(display, row, lines, 0, display->columns); } void display_delete_lines(struct display *display, int row, int column, int lines, int rows, int columns) { struct cell *cell; /* assigned later for safety from resizing */ if (column || columns != display->columns || !validate(display, row, column, &rows, &columns, &lines)) return; default_colors(display); moveto(display, row, 0); outf(display, CTL_DELLINES, lines); if (row + rows != display->rows) { moveto(display, row + rows - lines, 0); outf(display, CTL_INSLINES, lines); } cell = &display->image[row * display->columns]; memmove(cell, cell + lines * display->columns, (rows - lines) * display->columns * sizeof *cell); space_fill(display, row + rows - lines, lines, 0, display->columns); } static struct cell *resize(struct display *display, struct cell *old, int old_rows, int old_columns, int new_rows, int new_columns) { int cells = new_rows * new_columns; size_t bytes = cells * sizeof *old; struct cell *new = allocate(bytes); int min_rows = new_rows < old_rows ? new_rows : old_rows; int min_columns = new_columns < old_columns ? new_columns : old_columns; int j, k; if (!old) { memset(new, 0, bytes); for (j = 0; j < cells; j++) { new[j].unicode = ' '; new[j].bgrgba = display->bgrgba; new[j].fgrgba = display->fgrgba; } return new; } for (j = 0; j < min_rows; j++) { struct cell *cell = &new[j*new_columns]; for (k = 0; k < min_columns; k++) *cell++ = old[j*old_columns+k]; for (; k < new_columns; k++) { *cell = cell[-1]; cell++->unicode = ' '; } } for (; j < new_rows; j++) { struct cell *cell = &new[j*new_columns]; for (k = 0; k < new_columns; k++) { *cell = *(cell - display->columns); cell++->unicode = ' '; } } RELEASE(old); return new; } static void set_geometry(struct display *display, int rows, int columns) { if (display->image && display->rows == rows && display->columns == columns) return; display->image = resize(display, display->image, display->rows, display->columns, rows, columns); display->rows = rows; display->columns = columns; display->size_changed = TRUE; display_erase(display, 0, 0, rows, columns); display_cursor(display, display->cursor_row, display->cursor_column); if (display->get_initial_cursor_position == KNOWN) display->get_initial_cursor_position = INVALID; } static void geometry(struct display *display) { int rows = 0, columns = 0; struct winsize ws; const char *p; unsigned n; #ifdef TIOCGWINSZ if (!ioctl(1, TIOCGWINSZ, &ws)) { rows = ws.ws_row; columns = ws.ws_col; } #endif if (!rows && (p = getenv("ROWS")) && (n = atoi(p)) && n <= 100) rows = n; if (!columns && (p = getenv("COLUMNS")) && (n = atoi(p)) && n <= 200) columns = n; if (!rows || !columns) { if (!rows) rows = 24; if (!columns) columns = 80; force_moveto(display, 666, 666); outs(display, CTL_CURSORPOS); } set_geometry(display, rows, columns); } void display_get_geometry(struct display *display, int *rows, int *columns) { *rows = display->rows; *columns = display->columns; display->size_changed = FALSE; } void display_reset(struct display *display) { RELEASE(display->image); if (display->is_xterm) { if (display->get_initial_cursor_position == NEEDED) { display->get_initial_cursor_position = SOUGHT; outs(display, CTL_CURSORPOS); } outs(display, XTERM_ALTSCREEN); } else outs(display, CTL_RESET); outs(display, CTL_UTF8 CTL_RESETMODES CTL_RESETCOLORS); if (display->is_xterm) { outs(display, XTERM_BCKISDEL); /* reset character attributes (blink, etc.) */ outs(display, CSI "0;24;25;27;28;39;49m"); } if (display->is_linux) outs(display, CTL_NUMLOCK CTL_CLEARLEDS CTL_NUMLOCKLED); display->colors = 0; display->fgrgba = BAD_RGBA; display->bgrgba = BAD_RGBA; geometry(display); force_moveto(display, 0, 0); display->cursor_row = display->cursor_column = 0; display->cursor_rgba = BAD_RGBA; flush(display); } static void sigwinch(int signo, siginfo_t *info, void *data) { struct display *display; for (display = display_list; display; display = display->next) geometry(display); if (old_sigwinch) old_sigwinch(signo, info, data); } #if !(defined __linux__ || defined BSD || defined __APPLE__) void cfmakeraw(struct termios *termios) { termios->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|IXON); termios->c_oflag &= ~OPOST; termios->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); termios->c_cflag &= ~(CSIZE|PARENB); termios->c_cflag |= CS8; } #endif struct display *display_init(void) { struct display *display = allocate0(sizeof *display); struct termios termios = original_termios; const char *term, *path; if ((path = getenv("DISPLAY_DEBUG_PATH")) && !(debug_file = fopen(path, "w"))) die("could not open display debug file %s for writing", path); cfmakeraw(&termios); tcsetattr(1, TCSADRAIN, &termios); if (!(display->next = display_list)) { struct sigaction sigact, old_sigact; memset(&sigact, 0, sizeof sigact); sigact.sa_sigaction = sigwinch; sigact.sa_flags = SA_SIGINFO; sigaction(SIGWINCH, &sigact, &old_sigact); if ((void (*)(int)) old_sigact.sa_sigaction != SIG_DFL && (void (*)(int)) old_sigact.sa_sigaction != SIG_IGN && old_sigact.sa_sigaction != sigwinch) old_sigwinch = old_sigact.sa_sigaction; } display_list = display; display->inbuf_bytes = display->outbuf_bytes = 0; if (!(term = getenv("TERM"))) { } else if ((display->is_xterm = !strncmp(term, "xterm", 5))) { if ((term = getenv("COLORTERM")) && !strncmp(term, "gnome-", 6)) { /* Gnome terminal? Ignore $TERM_PROGRAM */ } else if ((term = getenv("TERM_PROGRAM"))) display->is_apple = !strcmp(term, "Apple_Terminal"); } else { display->is_linux = !strcmp(term, "linux") || !strcmp(term, "network"); } display_reset(display); return display; } void display_end(struct display *display) { struct display *d, *prev = NULL; if (!display) return; display_title(display, NULL); default_colors(display); if (display->is_xterm) { outs(display, CTL_ERASEALL CTL_RESETMODES CTL_RESETCOLORS XTERM_REGSCREEN); if (display->get_initial_cursor_position == KNOWN) force_moveto(display, display->initial_row, display->initial_column); else force_moveto(display, display->rows-1, 0); } else outs(display, CTL_RESET CTL_RESETMODES); flush(display); tcsetattr(1, TCSADRAIN, &original_termios); RELEASE(display->image); for (d = display_list; d; prev = d, d = d->next) if (d == display) { if (prev) prev->next = display->next; else display_list = display->next; break; } RELEASE(display); } Boolean_t display_title(struct display *display, const char *title) { if (!display->is_xterm) return FALSE; if (title && !*title) title = NULL; if (!title && !display->title) return TRUE; if (!title) RELEASE(display->title); else if (display->title && !strcmp(title, display->title)) return TRUE; if (title) display->title = strdup(title); else title = ""; outf(display, XTERM_TITLE, title ? title : ""); return TRUE; } void display_cursor(struct display *display, int row, int column) { if (row >= display->rows) row = display->rows - 1; if (row < 0) row = 0; if (column >= display->columns) column = display->columns - 1; if (column < 0) column = 0; display->cursor_row = row; display->cursor_column = column; } Boolean_t display_cursor_color(struct display *display, rgba_t rgba) { if (!display->is_xterm || display->is_apple || rgba & 0xff /* no alpha */) return FALSE; if (rgba != display->cursor_rgba) { outf(display, CTL_CURSORRGB, rgba >> 24, rgba >> 16 & 0xff, rgba >> 8 & 0xff); display->cursor_rgba = rgba; } return TRUE; } void display_beep(struct display *display) { emit("\a", 1); display_sync(display); } Unicode_t display_getch(struct display *display, Boolean_t block) { Byte_t *p; Unicode_t key; unsigned used, vals, val[16]; #define GOT_CURSORPOS FUNCTION_F(99) if (!display) return ERROR_EOF; again: if (display->size_changed) return ERROR_CHANGED; display_sync(display); if (display->inbuf_bytes >= sizeof display->inbuf - 1) ; else if (!multiplexor(block)) { if (!display->inbuf_bytes) return ERROR_EMPTY; } else { int n; do { errno = 0; n = read(0, display->inbuf + display->inbuf_bytes, sizeof display->inbuf - 1 - display->inbuf_bytes); } while (n < 0 && (errno == EAGAIN || errno == EINTR)); if (debug_file) { fprintf(debug_file, "read %d:", n); if (n > 0) fwrite(display->inbuf + display->inbuf_bytes, n, 1, debug_file); fputc('\n', debug_file); } if (!n) return ERROR_EOF; if (n < 0) return ERROR_INPUT; display->inbuf[display->inbuf_bytes += n] = '\0'; } p = display->inbuf; key = *p; if (key != ESCCHAR) { if (utf8_bytes[*p] > display->inbuf_bytes) { if (block) goto again; return ERROR_EMPTY; } used = utf8_length((char *) p, display->inbuf_bytes); key = utf8_unicode((char *) p, used); p += used - 1; goto done; } /* Translate Escape characters */ key = 0; vals = 0; switch (p[1]) { case '[': for (p += 2; isdigit(*p); p++) { val[vals] = 0; do { val[vals] *= 10; val[vals] += *p++ - '0'; } while (isdigit(*p)); vals++; if (*p != ';') break; if (vals == 16) vals--; } switch (*p) { case 'R': /* cursor position report from southeast corner */ if (vals >= 2) key = GOT_CURSORPOS; break; case '~': if (!val[0]) break; switch (val[0]) { case 2: key = FUNCTION_INSERT; break; case 3: key = FUNCTION_DELETE; break; case 5: key = FUNCTION_PGUP; break; case 6: key = FUNCTION_PGDOWN; break; case 15: key = FUNCTION_F(5); break; case 17: key = FUNCTION_F(6); break; case 18: key = FUNCTION_F(7); break; case 19: key = FUNCTION_F(8); break; case 20: key = FUNCTION_F(9); break; case 21: key = FUNCTION_F(10); break; /*pmk?*/ case 22: key = FUNCTION_F(11); break; case 24: key = FUNCTION_F(12); break; } break; case 'A': key = FUNCTION_UP; break; case 'B': key = FUNCTION_DOWN; break; case 'C': key = FUNCTION_RIGHT; break; case 'D': key = FUNCTION_LEFT; break; case 'F': key = FUNCTION_END; break; case 'H': key = FUNCTION_HOME; break; case '\0': /* truncated escape sequence; get more */ if (!block) return ERROR_EMPTY; goto again; } break; case 'O': switch (*(p += 2)) { case 'H': key = FUNCTION_HOME; break; case 'F': key = FUNCTION_END; break; case 'P': key = FUNCTION_F(1); break; case 'Q': key = FUNCTION_F(2); break; case 'R': key = FUNCTION_F(3); break; case 'S': key = FUNCTION_F(4); break; } break; /* Translate many Escape'd characters into Control */ case '@': case ' ': key = CONTROL('@'); p++; goto done; /* because ^@ == 0 */ case '\\': case ']': case '^': case '_': key = CONTROL(*++p); break; case '/': key = CONTROL('_'); p++; break; case '?': key = '\x7f'; p++; break; case '\r': key = '\n'; p++; break; case ESCCHAR: memmove(display->inbuf, p+2, display->inbuf_bytes -= 2); goto again; case '\0': /* truncated escape sequence; get more */ if (!block) return ERROR_EMPTY; goto again; default: if (p[1] < ' ') key = *++p; /* ignore Esc before control character */ else if (p[1] >= 'a' && p[1] <= 'z') key = CONTROL(*++p-'a'+'A'); else if (p[1] >= 'A' && p[1] <= 'Z') key = CONTROL(*++p); } /* If we couldn't translate an Escape sequence, return raw chars. */ if (!key) key = *(p = display->inbuf); done: used = ++p - display->inbuf; memmove(display->inbuf, p, display->inbuf_bytes -= used); if (key == GOT_CURSORPOS) { if (display->get_initial_cursor_position == SOUGHT && val[0] > 0 && val[1] > 0) { display->get_initial_cursor_position = KNOWN; display->initial_row = val[0] - 1; display->initial_column = val[1] - 1; } else set_geometry(display, val[0], val[1]); goto again; } return key; } aoeui-1.7+20160302.git4e5dee9/display.h000066400000000000000000000027161322350162600170470ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef DISPLAY_H #define DISPLAY_H #include "rgba.h" extern struct termios original_termios; struct display; struct display *display_init(void); void display_reset(struct display *); void display_end(struct display *); void display_get_geometry(struct display *, int *rows, int *columns); Boolean_t display_title(struct display *, const char *); void display_cursor(struct display *, int row, int column); Boolean_t display_cursor_color(struct display *, rgba_t rgba); void display_put(struct display *, int row, int column, Unicode_t unicode, rgba_t fgRGBA, rgba_t bgRGBA); void display_beep(struct display *); void display_sync(struct display *); /* display_getch() implies a display_sync(). * * Once ERROR_CHANGED is returned after a window size change, * it will continue to be returned until display_get_geometry() is called */ Unicode_t display_getch(struct display *, Boolean_t block); /* hints */ void display_erase(struct display *, int row, int column, int rows, int columns); void display_insert_spaces(struct display *, int row, int column, int spaces, int columns); void display_delete_chars(struct display *, int row, int column, int chars, int columns); void display_insert_lines(struct display *, int row, int column, int lines, int rows, int columns); void display_delete_lines(struct display *, int row, int column, int lines, int rows, int columns); #endif aoeui-1.7+20160302.git4e5dee9/file.c000066400000000000000000000273651322350162600163230ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" enum utf8_mode utf8_mode = UTF8_AUTO; const char *make_writable; Boolean_t no_save_originals; Boolean_t read_only; unsigned default_tab_stop = 8; /* the only correct value :-) */ Boolean_t default_no_tabs; Boolean_t default_tabs; const char *path_format(const char *path) { char *cwdbuf, *cwd; const char *slash; if (!path || *path != '/') return path; cwdbuf = allocate(1024); cwd = getcwd(cwdbuf, 1024); while ((slash = strchr(path, '/')) && !strncmp(cwd, path, slash - path)) { cwd += slash++ - path; if (*cwd && *cwd++ != '/') break; path = slash; } RELEASE(cwdbuf); return path; } static ssize_t old_fashioned_read(struct text *text) { char *raw; ssize_t got, total = 0; size_t max; #define CHUNK 1024 do { buffer_insert(text->buffer, NULL, total, CHUNK); max = buffer_raw(text->buffer, &raw, total, CHUNK); errno = 0; got = read(text->fd, raw, max); if (got < 0) { message("%s: can't read", path_format(text->path)); buffer_delete(text->buffer, 0, total + CHUNK); return -1; } buffer_delete(text->buffer, total + got, CHUNK - got); total += got; } while (got); return total; } static char *fix_path(const char *path) { char *fpath; const char *freepath = NULL, *home; size_t pathlen; if (!path) return NULL; while (isspace(*path)) path++; while (*path == '.' && path[1] == '/') path += 2; if (!(pathlen = strlen(path))) return NULL; if (isspace(path[pathlen-1])) { char *apath; while (pathlen && isspace(path[--pathlen])) ; if (!pathlen) return NULL; apath = allocate(pathlen+1); memcpy(apath, path, pathlen); apath[pathlen] = '\0'; freepath = path = apath; } if (!strncmp(path, "~/", 2) && (home = getenv("HOME"))) { char *apath = allocate(strlen(home) + pathlen); sprintf(apath, "%s%s", home, path + 1); RELEASE(freepath); freepath = path = apath; } else if (*path != '/') { char *cwdbuf = allocate(1024); char *cwd = getcwd(cwdbuf, 1024); char *apath = allocate(strlen(cwd) + pathlen + 2); sprintf(apath, "%s/%s", cwd, path); RELEASE(freepath); RELEASE(cwdbuf); freepath = path = apath; } fpath = strdup(path); RELEASE(freepath); return fpath; } static void clean_mmap(struct text *text, size_t bytes, int flags) { void *p; size_t pagesize = getpagesize(); unsigned pages = (bytes + pagesize - 1) / pagesize; if (text->clean) munmap(text->clean, text->clean_bytes); text->clean_bytes = bytes; text->clean = NULL; if (!pages) return; p = mmap(0, pages * pagesize, flags, MAP_SHARED, text->fd, 0); if (p != MAP_FAILED) text->clean = p; } static void grab_mtime(struct text *text) { struct stat statbuf; if (text->fd >= 0 && !fstat(text->fd, &statbuf)) text->mtime = statbuf.st_mtime; else text->mtime = 0; } static void scan(struct view *view) { char *raw, scratch[8]; position_t at; size_t bytes = view_raw(view, &raw, 0, getpagesize()); size_t chop = bytes < view->bytes ? 8 : 0; size_t chlen, check; Unicode_t ch, lastch = 0; int crnl = 0, nl = 0; Boolean_t any_tab = default_tabs; int tabstop = default_tab_stop; /* Reset state */ view->text->flags &= ~(TEXT_NO_UTF8 | TEXT_CRNL | TEXT_NO_TABS); view->text->tabstop = default_tab_stop; if (utf8_mode == UTF8_NO) view->text->flags |= TEXT_NO_UTF8; else if (utf8_mode == UTF8_AUTO) for (at = 0; at + chop < bytes; at += chlen) { chlen = utf8_length(raw + at, bytes - at); ch = utf8_unicode(raw + at, chlen); check = unicode_utf8(scratch, ch); if (chlen != check) { view->text->flags |= TEXT_NO_UTF8; break; } } for (at = 0; at + chop < bytes; lastch = ch) if ((ch = view_unicode(view, at, &at)) == '\n') { nl++; crnl += lastch == '\r'; } if (nl && crnl == nl) view->text->flags |= TEXT_CRNL; for (at = 0; at + chop < bytes; at = find_line_end(view, at) + 1) { int spaces = 0; while ((ch = view_unicode(view, at, &at)) == ' ') spaces++; if (ch == '\t') any_tab = TRUE; if (spaces > 1 && spaces < tabstop) tabstop = spaces; } if (default_no_tabs || !any_tab) { view->text->flags |= TEXT_NO_TABS; view->text->tabstop = tabstop; } } struct view *view_open(const char *path0) { struct view *view; struct text *text; struct stat statbuf; char *path = fix_path(path0); if (!path) return NULL; for (text = text_list; text; text = text->next) if (text->path && !strcmp(text->path, path)) { for (view = text->views; view; view = view->next) if (!view->window) goto done; view = view_create(text); goto done; } view = text_create(path, 0); text = view->text; errno = 0; if (stat(path, &statbuf)) { if (errno != ENOENT) { message("%s: can't stat", path_format(path)); goto fail; } if (read_only) { message("%s: can't create in read-only mode", path_format(path)); goto fail; } errno = 0; text->fd = open(path, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (text->fd < 0) { message("%s: can't create", path_format(path)); goto fail; } text->flags |= TEXT_CREATED; } else { if (!S_ISREG(statbuf.st_mode)) { message("%s: not a regular file", path_format(path)); goto fail; } if (!read_only) text->fd = open(path, O_RDWR); if (text->fd < 0) { errno = 0; text->flags |= TEXT_RDONLY; text->fd = open(path, O_RDONLY); if (text->fd < 0) { message("%s: can't open", path_format(path)); goto fail; } } clean_mmap(text, statbuf.st_size, PROT_READ); if (!text->clean) { text->buffer = buffer_create(path); if (old_fashioned_read(text) < 0) goto fail; grab_mtime(text); } view->bytes = text->buffer ? buffer_bytes(text->buffer) : text->clean_bytes; scan(view); text_forget_undo(text); } goto done; fail: view_close(view); view = NULL; done: RELEASE(path); return view; } static fd_t try_dir(char *path, const char *dir, const struct tm *gmt) { struct stat statbuf; errno = 0; if (stat(dir, &statbuf)) { if (errno != ENOENT) return -1; if (mkdir(dir, S_IRUSR|S_IWUSR|S_IXUSR)) return -1; if (stat(dir, &statbuf)) return -1; } if (!S_ISDIR(statbuf.st_mode)) return -1; sprintf(path, "%s/%02d-%02d-%02d.%02d%02d%02d", dir, gmt->tm_year+1900, gmt->tm_mon+1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec); return open(path, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR); } struct view *text_new(void) { char dir[128], path[128]; const char *me, *home; time_t now = time(NULL); struct tm *gmt = gmtime(&now); fd_t fd = -1; struct view *view; if ((home = getenv("HOME"))) { sprintf(dir, "%s/.aoeui", home); fd = try_dir(path, dir, gmt); } if (fd < 0 && (me = getenv("LOGNAME"))) { sprintf(dir, "/tmp/aoeui-%s", me); fd = try_dir(path, dir, gmt); } #if !defined __APPLE__ && !defined BSD if (fd < 0 && (me = cuserid(NULL))) { sprintf(dir, "/tmp/aoeui-%s", me); fd = try_dir(path, dir, gmt); } #endif if (fd < 0) fd = try_dir(path, "/tmp/aoeui", gmt); if (fd < 0) fd = try_dir(path, "./aoeui", gmt); if (fd < 0) view = text_create("* New *", TEXT_EDITOR); else { view = text_create(path, TEXT_CREATED | TEXT_SCRATCH); view->text->fd = fd; } return view; } Boolean_t text_rename(struct text *text, const char *path0) { char *path = fix_path(path0); struct text *b; struct view *view; fd_t fd; if (!path) return FALSE; for (b = text; b; b = b->next) if (b->path && !strcmp(b->path, path)) return FALSE; errno = 0; if ((fd = open(path, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) { message("%s: can't create", path_format(path)); RELEASE(path); return FALSE; } if (text->flags & TEXT_CREATED) { unlink(text->path); text->flags &= ~(TEXT_CREATED | TEXT_SCRATCH); } /* Do not truncate or overwrite yet. */ text->flags |= TEXT_SAVED_ORIGINAL; text->flags &= ~TEXT_RDONLY; text_dirty(text); close(text->fd); if (text->clean) { munmap(text->clean, text->clean_bytes); text->clean = NULL; } text->fd = fd; grab_mtime(text); RELEASE(text->path); text->path = path; keyword_init(text); for (view = text->views; view; view = view->next) view_name(view); return TRUE; } void text_dirty(struct text *text) { if (text->path && !text->dirties && text->flags & TEXT_RDONLY) message("%s: read-only, %s", path_format(text->path), make_writable ? "will be made writable" : "changes won't be saved here"); text->dirties++; if (!text->buffer) { text->buffer = buffer_create(text->fd >= 0 ? text->path : NULL); if (text->clean) buffer_insert(text->buffer, text->clean, 0, text->clean_bytes); grab_mtime(text); } } static void save_original(struct text *text) { char *save_path; fd_t fd; ssize_t wrote = -1; if (no_save_originals || !text->clean || !text->path || text->flags & (TEXT_SAVED_ORIGINAL | TEXT_RDONLY | TEXT_CREATED | TEXT_EDITOR)) return; save_path = allocate(strlen(text->path)+2); sprintf(save_path, "%s~", text->path); errno = 0; fd = creat(save_path, S_IRUSR|S_IWUSR); if (fd >= 0) { wrote = write(fd, text->clean, text->clean_bytes); if (close(fd)) wrote = -1; } if (wrote != text->clean_bytes) message("%s: can't save original text", path_format(save_path)); RELEASE(save_path); text->flags |= TEXT_SAVED_ORIGINAL; } Boolean_t text_is_dirty(struct text *text) { return text->preserved != text->dirties && text->fd >= 0 && text->buffer; } void text_preserve(struct text *text) { char *raw; size_t bytes; struct stat statbuf; if (text->preserved == text->dirties || text->fd < 0 || !text->buffer) return; text->preserved = ++text->dirties; if (read_only) return; text_unfold_all(text); if (text->clean) { save_original(text); bytes = buffer_raw(text->buffer, &raw, 0, ~(size_t)0); if (bytes == text->clean_bytes && !memcmp(text->clean, raw, bytes)) return; munmap(text->clean, text->clean_bytes); text->clean = NULL; } if (text->mtime && text->path && !fstat(text->fd, &statbuf) && text->mtime < statbuf.st_mtime) message("%s: modified since read into the " "editor, changes may have been overwritten.", path_format(text->path)); text->preserved = ++text->dirties; if (text->flags & TEXT_RDONLY && text->path && make_writable) { char cmd[128]; int newfd; snprintf(cmd, sizeof cmd, make_writable, text->path); background_command(cmd); newfd = open(text->path, O_RDWR); if (newfd >= 0) { close(text->fd); text->fd = newfd; text->flags &= ~TEXT_RDONLY; } } if (text->flags & TEXT_RDONLY && text->path) { int newfd; char *new_path = allocate(strlen(text->path) + 2); sprintf(new_path, "%s@", text->path); errno = 0; newfd = open(new_path, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (newfd < 0) { message("%s: can't create", path_format(new_path)); RELEASE(new_path); return; } message("%s: read-only, new version saved to %s@", text->path, text->path); text->flags &= ~TEXT_RDONLY; close(text->fd); RELEASE(text->path); text->fd = newfd; text->path = new_path; } text->flags &= ~TEXT_CREATED; bytes = buffer_raw(text->buffer, &raw, 0, ~(size_t)0); if (ftruncate(text->fd, bytes)) message("%s: truncation failed", path_format(text->path)); clean_mmap(text, bytes, PROT_READ|PROT_WRITE); if (text->clean) { memcpy(text->clean, raw, bytes); msync(text->clean, bytes, MS_SYNC); } else { ssize_t wrote; lseek(text->fd, 0, SEEK_SET); errno = 0; wrote = write(text->fd, raw, bytes); if (wrote != bytes) message("%s: write failed", path_format(text->path)); } grab_mtime(text); } void texts_preserve(void) { struct text *text; for (text = text_list; text; text = text->next) text_preserve(text); } void texts_uncreate(void) { struct text *text; for (text = text_list; text; text = text->next) if (text->flags & TEXT_CREATED) unlink(text->path); } aoeui-1.7+20160302.git4e5dee9/find.c000066400000000000000000000241451322350162600163150ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" /* Routines that scan characters in views */ position_t find_line_start(struct view *view, position_t offset) { Unicode_t ch; position_t prev; while (IS_UNICODE((ch = view_char_prior(view, offset, &prev))) && ch != '\n') offset = prev; return offset; } position_t find_line_end(struct view *view, position_t offset) { Unicode_t ch; position_t next; while (IS_UNICODE((ch = view_char(view, offset, &next))) && ch != '\n') offset = next; return offset; } position_t find_paragraph_start(struct view *view, position_t offset) { Unicode_t ch, nch = UNICODE_BAD, nnch = UNICODE_BAD; position_t prev; while (IS_UNICODE((ch = view_char_prior(view, offset, &prev)))) { if (ch == '\n' && nch == '\n' && IS_UNICODE(nnch)) return offset + 1; offset = prev, nnch = nch, nch = ch; } return offset; } position_t find_paragraph_end(struct view *view, position_t offset) { Unicode_t ch, pch = UNICODE_BAD, ppch = UNICODE_BAD; position_t next; while (IS_UNICODE((ch = view_char(view, offset, &next))) && (ch == '\n' || pch != '\n' || ppch != '\n')) offset = next, ppch = pch, pch = ch; return offset; } static void updown_goal(struct view *view, position_t at) { if (at == view->goal.cursor) ; else if (view_byte(view, at) == '\n') view->goal.row = ~0; else { view->goal.row = 0; view->goal.column = find_column(&view->goal.row, view, find_line_start(view, at), at, 0); } } static position_t same_column(struct view *view, position_t at, position_t fail) { unsigned r = 0, c = 0; unsigned tabstop = view->text->tabstop; unsigned columns = window_columns(view->window); position_t next; for (; r < view->goal.row || c < view->goal.column; at = next) { Unicode_t ch; ch = view_char(view, at, &next); if (!IS_UNICODE(ch) || ch == '\n') { at = fail; break; } if ((c += char_columns(ch, c, tabstop)) > view->goal.column && r == view->goal.row) break; if (c > columns) r++, c = char_columns(ch, 0, tabstop); } return view->goal.cursor = at; } position_t find_line_up(struct view *view, position_t at) { position_t linestart = find_line_start(view, at); updown_goal(view, at); if (!linestart) return 0; return same_column(view, find_line_start(view, linestart-1), linestart-1); } position_t find_line_down(struct view *view, position_t at) { position_t nextstart = find_line_end(view, at) + 1; updown_goal(view, at); if (nextstart >= view->bytes) return view->bytes; return same_column(view, nextstart, find_line_end(view, nextstart)); } typedef Unicode_t (*stepper_t)(struct view *, position_t, position_t *); static position_t find_not(struct view *view, position_t offset, stepper_t stepper, Boolean_t (*test)(Unicode_t)) { position_t next; while (test(stepper(view, offset, &next))) offset = next; return offset; } static Boolean_t space_test(Unicode_t ch) { return IS_CODEPOINT(ch) && isspace(ch); } static Boolean_t nonspace_test(Unicode_t ch) { return IS_UNICODE(ch) && !space_test(ch); } position_t find_space(struct view *view, position_t offset) { return find_not(view, offset, view_char, nonspace_test); } position_t find_space_prior(struct view *view, position_t offset) { return find_not(view, offset, view_char_prior, nonspace_test); } position_t find_nonspace(struct view *view, position_t offset) { return find_not(view, offset, view_char, space_test); } position_t find_nonspace_prior(struct view *view, position_t offset) { return find_not(view, offset, view_char_prior, space_test); } static position_t find_contiguous(struct view *view, position_t offset, stepper_t stepper, Boolean_t (*test)(Unicode_t, struct view *, position_t *, stepper_t)) { Unicode_t ch; position_t next; Boolean_t in_region = FALSE; for (; IS_UNICODE((ch = stepper(view, offset, &next))); offset = next) if (test(ch, view, &next, stepper)) in_region = TRUE; else if (in_region) break; return offset; } static Boolean_t word_test(Unicode_t ch, struct view *view, position_t *next, stepper_t stepper) { return is_wordch(ch); } position_t find_word_start(struct view *view, position_t offset) { return find_contiguous(view, offset, view_char_prior, word_test); } position_t find_word_end(struct view *view, position_t offset) { return find_contiguous(view, offset, view_char, word_test); } static Boolean_t id_test(Unicode_t ch, struct view *view, position_t *next, stepper_t stepper) { return is_idch(ch) || ch == ':' && stepper(view, *next, next) == ':'; } position_t find_id_start(struct view *view, position_t offset) { return find_contiguous(view, offset, view_char_prior, id_test); } position_t find_id_end(struct view *view, position_t offset) { return find_contiguous(view, offset, view_char, id_test); } position_t find_sentence_start(struct view *view, position_t offset) { position_t prev; Unicode_t ch, next = view_char_prior(view, offset, &prev); if (!IS_UNICODE(next)) return offset; while (IS_UNICODE(ch = view_char_prior(view, offset = prev, &prev)) && ch != '.' && ch != ',' && ch != ';' && ch != ':' && ch != '!' && ch != '?' && ch != '(' && ch != '[' && ch != '{' && (ch != '\n' || ch != next)) next = ch; return offset; } position_t find_sentence_end(struct view *view, position_t offset) { position_t next; Unicode_t ch, last = view_char(view, offset, &next); if (!IS_UNICODE(last)) return offset; while (IS_UNICODE(ch = view_char(view, offset = next, &next)) && ch != '.' && ch != ',' && ch != ';' && ch != ':' && ch != '!' && ch != '?' && ch != ')' && ch != ']' && ch != '}' && (ch != '\n' || ch != last)) last = ch; return offset; } sposition_t find_corresponding_bracket(struct view *view, position_t offset) { static signed char peer[256], updown[256]; const char *p = view->text->brackets; position_t next; Unicode_t ch = view_char(view, offset, &next); Byte_t stack[32]; int stackptr = 0, dir; if (!p) return -1; memset(peer, 0, sizeof peer); memset(updown, 0, sizeof updown); for (; *p; p += 2) { int L = (unsigned char) p[0], R = (unsigned char) p[1]; peer[L] = R; peer[R] = L; updown[L] = 1; updown[R] = -1; } if (ch >= sizeof updown || !(dir = updown[ch])) { position_t back = offset, ahead, next = offset; while (IS_UNICODE(ch = view_char_prior(view, back, &back))) { if (ch >= sizeof updown) continue; if (updown[ch] < 0) if (stackptr == sizeof stack) break; else stack[stackptr++] = ch; else if (updown[ch] > 0 && (!stackptr || ch != peer[stack[--stackptr]])) break; } if (!IS_UNICODE(ch)) back = offset+1; while (IS_UNICODE(ch = view_char(view, ahead = next, &next))) { if (ch >= sizeof updown) continue; if (updown[ch] > 0) if (stackptr == sizeof stack) break; else stack[stackptr++] = ch; else if (updown[ch] < 0 && (!stackptr || ch != peer[stack[--stackptr]])) break; } if (back < offset && (offset - back <= ahead - offset || !IS_UNICODE(ch))) return back; return IS_UNICODE(ch) ? ahead : -1; } stack[stackptr++] = ch; if (dir > 0) offset = next; while (stackptr) { ch = (dir > 0 ? view_char : view_char_prior) (view, offset, &next); if (ch >= sizeof updown) return -1; if (updown[ch] == dir) { if (stackptr == sizeof stack) return -1; stack[stackptr++] = ch; } else if (updown[ch] == -dir) if (ch != peer[stack[--stackptr]] || !stackptr && dir > 0) break; offset = next; } return offset; } position_t find_line_number(struct view *view, unsigned line) { position_t offset, next, next2; sposition_t fold_start = -1, fold_end = -1; if (line-- <= 1) return 0; for (offset = 0; offset < view->bytes; offset = next) { Unicode_t ch = view_unicode(view, offset, &next); if (offset >= fold_end) fold_end = -1; if (ch == '\n' && !--line) break; if (fold_end < 0 && IS_FOLDED(ch)) { size_t fbytes = FOLDED_BYTES(ch); if (view_unicode(view, next + fbytes, &next2) == FOLD_END + fbytes) { fold_start = offset; fold_end = next2; } } } if (fold_end >= 0) return fold_start; return offset + 1; } unsigned current_line_number(struct view *view, position_t offset) { int line = 1; position_t at; unsigned last = '\n'; if (offset >= view->bytes) offset = view->bytes; for (at = 0; at < offset; at++) { last = view_byte(view, at); if (last == '\n') line++; } if (at == view->bytes && last == '\n') line--; return line; } position_t find_row_bytes(struct view *view, position_t offset0, unsigned column, unsigned columns) { position_t offset = offset0, next; unsigned tabstop = view->text->tabstop; Unicode_t ch = 0; int charcols; while (column < columns) { if (!IS_UNICODE(ch = view_char(view, offset, &next))) break; if (ch == '\n') { offset = next; break; } charcols = char_columns(ch, column, tabstop); if (column+charcols > columns) break; column += charcols; offset = next; } if (column == columns && offset != locus_get(view, CURSOR) && view_char(view, offset, NULL) == '\n') offset++; return offset - offset0; } unsigned find_column(unsigned *row, struct view *view, position_t at, position_t offset, unsigned column) { unsigned tabstop = view->text->tabstop; unsigned columns = window_columns(view->window); position_t next; for (; at < offset; at = next) { Unicode_t ch = view_char(view, at, &next); if (!IS_UNICODE(ch)) break; if (ch == '\n') { if (row) ++*row; column = 0; } else { column += char_columns(ch, column, tabstop); if (column >= columns) { if (row) ++*row; column = char_columns(ch, 0, tabstop); } } } return column; } sposition_t find_string(struct view *view, const char *string, position_t offset) { const Byte_t *ustring = (const Byte_t *) string; unsigned first = *ustring, j; Unicode_t ch; for (; IS_UNICODE(ch = view_byte(view, offset)); offset++) { if (ch != first) continue; for (j = 1; (ch = ustring[j]); j++) if (ch != view_byte(view, offset+j)) break; if (!ch) return offset; } return -1; } aoeui-1.7+20160302.git4e5dee9/fold.c000066400000000000000000000067341322350162600163250ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" void view_fold(struct view *view, position_t cursor, position_t mark) { size_t bytes = mark - cursor; char buf[8]; if (mark < cursor) bytes = -bytes, cursor = mark; if (cursor > view->bytes) return; if (cursor + bytes > view->bytes) bytes = view->bytes - cursor; if (!bytes) return; view_insert(view, buf, cursor + bytes, unicode_utf8(buf, FOLD_END+bytes)); view_insert(view, buf, cursor, unicode_utf8(buf, FOLD_START+bytes)); view->text->foldings++; } sposition_t view_unfold(struct view *view, position_t offset) { position_t next, next2; size_t fbytes; Unicode_t ch = view_unicode(view, offset, &next); if (ch < FOLD_START || ch >= FOLD_END) return -1; fbytes = FOLDED_BYTES(ch); if (view_unicode(view, next + fbytes, &next2) != FOLD_END + fbytes) return -1; view_delete(view, next + fbytes, next2 - (next + fbytes)); view_delete(view, offset, next - offset); view->text->foldings--; return offset + fbytes; } void view_unfold_selection(struct view *view) { position_t offset = locus_get(view, CURSOR); position_t end = locus_get(view, MARK); if (end == UNSET) return; if (end < offset) { position_t t = end; end = offset; offset = t; } while (offset < end) { position_t next, next2; size_t fbytes; Unicode_t ch = view_unicode(view, offset, &next); if (!IS_UNICODE(ch)) break; if (ch < FOLD_START || ch >= FOLD_END) { offset = next; continue; } fbytes = FOLDED_BYTES(ch); if (view_unicode(view, next + fbytes, &next2) != FOLD_END + fbytes) { offset = next; continue; } view_delete(view, next + fbytes, next2 - (next + fbytes)); view_delete(view, offset, next - offset); view->text->foldings--; offset += fbytes; } } static int indentation(struct view *view, position_t offset) { unsigned indent = 0, tabstop = view->text->tabstop; Unicode_t ch; tabstop |= !tabstop; for (;;) if ((ch = view_char(view, offset, &offset)) == ' ') indent++; else if (ch == '\t') indent = (indent / tabstop + 1) * tabstop; else if (ch == '\n') return -1; else break; return indent; } static unsigned max_indentation(struct view *view) { position_t offset, next; int maxindent = 0; for (offset = 0; offset < view->bytes; offset = next) { int indent = indentation(view, offset); if (indent > maxindent) maxindent = indent; next = find_line_end(view, offset) + 1; } return maxindent; } void view_fold_indented(struct view *view, unsigned minindent) { unsigned maxindent; minindent |= !minindent; while ((maxindent = max_indentation(view)) >= minindent) { position_t offset, next; sposition_t start = -1; for (offset = 0; offset < view->bytes; offset = next) { next = find_line_end(view, offset) + 1; if (indentation(view, offset) < maxindent) { if (start >= 0) { view_fold(view, next = start, offset-1); start = -1; } } else if (start < 0) start = offset - !!offset; } if (start >= 0) view_fold(view, start, offset-1); } } void view_unfold_all(struct view *view) { position_t offset, next; if (!view->text->foldings) return; for (offset = 0; IS_UNICODE(view_unicode(view, offset, &next)); offset = next) if (view_unfold(view, offset) >= 0) { if (!view->text->foldings) break; next = offset; } } void text_unfold_all(struct text *text) { struct view *view; if (!text->foldings) return; view_unfold_all(view = view_create(text)); view_close(view); } aoeui-1.7+20160302.git4e5dee9/help.c000066400000000000000000000006211322350162600163160ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" static const char *help[2] = { #include "aoeui.help" , #include "asdfg.help" }; struct view *view_help(void) { struct view *view = text_create("* Help *", TEXT_EDITOR); view_insert(view, help[is_asdfg], 0, strlen(help[is_asdfg])); locus_set(view, CURSOR, 0); view->text->flags |= TEXT_RDONLY; return view; } aoeui-1.7+20160302.git4e5dee9/help.m4000066400000000000000000000076051322350162600164250ustar00rootroot00000000000000ifdef(`ASDFG',`define(`AOEUI',`asdfg')define(`cmd',`$2')',`define(`AOEUI',`aoeui')define(`cmd',`$1')')dnl Welcome to AOEUI 1.7! Here are some clues to help you use the editor. - The up/down/left/right "arrow" keys, page up/down keys, and Delete key all work fine. You can use AOEUI as a simple notepad if you like. - To insert text into a document, just type it. - In this documentation, the notation ^A means the command A, which you access by holding down Control or Alt while hitting A, or by pressing Escape and then A. All of these modifiers mean the same thing in AOEUI. - The space bar, when used as a command (^Sp), is a prefix that distinguishes command variants and numeric arguments. Command summary: ^Sp? display this help again ^Q pause the editor and return to the shell; return with "fg" ^Sp^Q save all files and quit ^Sp^\ quit immediately without saving ^cmd(U,Z) undo ^Sp^cmd(U,Z) redo ^cmd(K,W) save all files ^Sp^cmd(K,W) save one file ^cmd(H,G) backward ^cmd(T,H) forward ^Sp^cmd(H,G) up ^Sp^cmd(T,H) down ^cmd(N,K) previous word ^cmd(S,L) next word ^Sp^cmd(N,K) previous sentence ^Sp^cmd(S,L) next sentence ^cmd(G,T) previous beginning of line ^cmd(C,Y) next end of line ^Sp^cmd(G,T) paragraph start ^Sp^cmd(C,Y) paragraph end ^cmd(R,O) previous page ^cmd(L,P) next page ^Sp^cmd(R,O) go to beginning ^Sp^cmd(L,P) go to end ^] go to nearest ([{bracket}]), or to corresponding bracket ^Sp n ^cmd(Z,N) go to line number n ^cmd(V,U) begin a selection if none, forgets selection otherwise ^Sp^cmd(V,U) go to opposite end of selection, or select whole line if none ^cmd(D,X) cut, replacing clip buffer (typing new text at the start of the selection also cuts) ^Sp^cmd(D,X) with selection: cut, adding to clip buffer ^Sp^cmd(D,X) without selection: select all white space surrounding cursor ^cmd(F,C) copy, replacing clip buffer ^Sp^cmd(F,C) copy, adding to clip buffer ^cmd(B,V) exchange clip buffer with selection, if any; else paste ^J insert new line with automatic alignment (^Return may also work) Tab and ^I attempt tab completion if no selection is present ^SpTab align current line (^Sp^I also works) ^^ insert next character as raw or control character ^Sp n ^^ insert Unicode character n -- use leading 0x for hexadecimal ^_ incremental search mode (^-, ^/, and ^A may also work) ^Sp^_ incremental regular expression search mode with POSIX regexps. In search mode, use ^cmd(H,G) and ^cmd(T,H) to move from one instance of the search target to another and any other command, or Return, to resume editing. ^cmd(X,E) open file named by selection in new window insert current path as selection if none ^Sp^cmd(X,E) rename current text with path in selection ^cmd(W,F) display another open view in this window ^Sp^cmd(W,F) close this window and its text ^cmd(Y,D) split current window horizontally ^Sp^cmd(Y,D) split current window vertically ^cmd(P,S) switch to another window ^Sp^cmd(P,S) close current window ^Sp^cmd(O,B) begin recording default macro (end with ^cmd(O,B)) ^cmd(O,B) run default macro, or end macro/function key recording ^SpF1-F12 begin recording function key macro (end with ^cmd(O,B)) F1-F12 execute function key macro ^cmd(E,R) run shell command in selection with clip buffer as input, or open new shell interaction window if no selection ^Sp^cmd(E,R) cancel all pending background commands Parting words: - Many commands support a repeat count; ^Sp9^cmd(T,H) advances nine characters. - AOEUI has bookmarks, registers, tags, folding, and other features. Read the manual page for the full story and lots of useful tips. - Send me a note at pmklausler@gmail.com and say hi! aoeui-1.7+20160302.git4e5dee9/keyword.c000066400000000000000000000155521322350162600170630ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" Boolean_t no_keywords; static const char *C_keyword_list[] = { "#define", "#elif", "#else", "#endif", "#if", "#ifdef", "#ifndef", "#include", "#pragma", "#undef", "asm", "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while" }; static const char *Cpp_keyword_list[] = { "#define", "#elif", "#else", "#endif", "#if", "#ifdef", "#ifndef", "#include", "#pragma", "#undef", "asm", "auto", "bool", "break", "case", "catch", "char", "class", "const", "constexpr", "const_cast", "continue", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "final", "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", "namespace", "new", "operator", "override", "private", "protected", "public", "register", "reinterpret_cast", "return", "short", "signed", "sizeof", "static", "static_cast", "struct", "switch", "template", "this", "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while" }; static const char *go_keyword_list[] = { "break", "case", "chan", "const", "continue", "default", "defer", "else", "fallthrough", "for", "func", "go", "goto", "if", "import", "interface", "map", "package", "range", "return", "select", "struct", "switch", "type", "var" }; static const char *Haskell_keyword_list[] = { "_", "case", "class", "data", "default", "deriving", "do", "else", "foreign", "if", "import", "in", "infix", "infixl", "infixr", "instance", "let", "module", "newtype", "of", "then", "type", "where" }; static sposition_t C_comment_start(struct view *view, position_t offset) { Unicode_t ch, nch = 0; int newlines = 0; while (IS_UNICODE((ch = view_char_prior(view, offset, &offset)))) { if (ch == '\n') { if (newlines++ == 100) break; } else if (ch == '/') { if (nch == '*') return offset; if (!newlines && nch == '/') return offset; } else if (ch == '*' && nch == '/') break; nch = ch; } return -1; } static sposition_t C_comment_end(struct view *view, position_t offset) { Unicode_t ch, lch = 0; position_t next; if (view_char(view, offset, &offset) != '/') return -1; ch = view_char(view, offset, &offset); if (ch == '/') return find_line_end(view, offset); if (ch != '*') return -1; while (IS_UNICODE((ch = view_char(view, offset, &next)))) { if (lch == '*' && ch == '/') return offset; lch = ch; offset = next; } return -1; } static sposition_t C_string_end(struct view *view, position_t offset) { Unicode_t ch, lch = 0, ch0 = view_char(view, offset, &offset); position_t next; if (ch0 != '\'' && ch0 != '"') return -1; while (IS_UNICODE((ch = view_char(view, offset, &next)))) { if (ch == ch0 && lch != '\\') return offset; if (ch == '\n') break; if (ch == '\\' && lch == '\\') lch = 0; else lch = ch; offset = next; } return -1; } static sposition_t Haskell_comment_start(struct view *view, position_t offset) { Unicode_t ch, nch = 0; int newlines = 0; while (IS_UNICODE((ch = view_char_prior(view, offset, &offset)))) { if (ch == '\n') { if (newlines++ == 100) break; } else if (ch == '{' && nch == '-') return offset; else if (ch == '-') { if (!newlines && nch == '-') return offset; if (nch == '}') break; } nch = ch; } return -1; } static sposition_t Haskell_comment_end(struct view *view, position_t offset) { Unicode_t ch, lch = 0; position_t next; ch = view_char(view, offset, &offset); if (ch != '-' && ch != '{') return -1; if (view_char(view, offset, &offset) != '-') return -1; if (ch == '-') return find_line_end(view, offset); while (IS_UNICODE((ch = view_char(view, offset, &next)))) { if (lch == '-' && ch == '}') return offset; lch = ch; offset = next; } return -1; } static sposition_t Haskell_string_end(struct view *view, position_t offset) { position_t next; Unicode_t ch, lch = 0, ch0 = view_char(view, offset, &next); if (ch0 == '\'') { ch = view_char_prior(view, offset, NULL); if (isalnum(ch) || ch == '_' || ch == '\'') return -1; } else if (ch0 != '"') return -1; while (IS_UNICODE((ch = view_char(view, offset = next, &next)))) { if (ch == ch0 && lch != '\\') return offset; if (ch == '\n') break; if (ch == '\\' && lch == '\\') lch = 0; else lch = ch; } return -1; } #define KW(lang) { sizeof lang##_keyword_list / sizeof *lang##_keyword_list, \ lang##_keyword_list } static struct file_keywords { const char *suffix; struct keywords keywords; const char *brackets; sposition_t (*comment_start)(struct view *, position_t); sposition_t (*comment_end)(struct view *, position_t); sposition_t (*string_end)(struct view *, position_t); } kwmap [] = { { ".c", KW(C), "()[]{}", C_comment_start, C_comment_end, C_string_end }, { ".C", KW(C), "()[]{}", C_comment_start, C_comment_end, C_string_end }, { ".cc", KW(Cpp), "()[]{}", C_comment_start, C_comment_end, C_string_end }, { ".cpp", KW(Cpp), "()[]{}", C_comment_start, C_comment_end, C_string_end }, { ".cxx", KW(Cpp), "()[]{}", C_comment_start, C_comment_end, C_string_end }, { ".go", KW(go), "()[]{}", C_comment_start, C_comment_end, C_string_end }, { ".h", KW(Cpp), "()[]{}", C_comment_start, C_comment_end, C_string_end }, { ".hs", KW(Haskell), "()[]{}", Haskell_comment_start, Haskell_comment_end, Haskell_string_end }, { ".html", { 0, NULL }, "<>" }, { } }; void keyword_init(struct text *text) { if (text->path) { size_t pathlen = strlen(text->path); int j; for (j = 0; kwmap[j].suffix; j++) { size_t suffixlen = strlen(kwmap[j].suffix); if (pathlen > suffixlen && !strcmp(text->path + pathlen - suffixlen, kwmap[j].suffix)) { text->brackets = kwmap[j].brackets; if (!no_keywords) { text->keywords = &kwmap[j].keywords; text->comment_start = kwmap[j].comment_start; text->comment_end = kwmap[j].comment_end; text->string_end = kwmap[j].string_end; } return; } } } text->brackets = "()[]{}"; text->keywords = NULL; } Boolean_t is_keyword(struct view *view, position_t offset) { unsigned n; const char **tab; char *word; size_t bytes; if (!view->text->keywords) return FALSE; bytes = find_id_end(view, offset) - offset; if (view_raw(view, &word, offset, bytes) < bytes) return FALSE; for (n = view->text->keywords->count, tab = view->text->keywords->word; n; ) { unsigned mid = n / 2; int cmp = strncmp(word, tab[mid], bytes); if (!cmp) if (tab[mid][bytes]) cmp = -1; else return TRUE; if (cmp < 0) n = mid; else { n -= mid + 1; tab += mid + 1; } } return FALSE; } aoeui-1.7+20160302.git4e5dee9/locus.c000066400000000000000000000035361322350162600165230ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" /* * A locus is a position in a text. Insertions and * deletions in the text prior to a locus cause * automatic adjustments to the byte offset of a locus. */ #define DELETED (UNSET-1) locus_t locus_create(struct view *view, position_t offset) { locus_t locus; for (locus = 0; locus < view->loci; locus++) if (view->locus[locus] == DELETED) break; if (locus == view->loci) { view->locus = reallocate(view->locus, (locus+1) * sizeof *view->locus); view->loci++; } locus_set(view, locus, offset); return locus; } void locus_destroy(struct view *view, locus_t locus) { if (locus < view->loci) view->locus[locus] = DELETED; } position_t locus_get(struct view *view, locus_t locus) { position_t offset; if (!view || locus >= view->loci) return UNSET; offset = view->locus[locus]; if (offset == UNSET) return UNSET; if ((int) offset < 0) offset = 0; else if (offset > view->bytes) offset = view->bytes; return offset; } position_t locus_set(struct view *view, locus_t locus, position_t offset) { if (offset != UNSET && offset > view->bytes) offset = view->bytes; if (locus < view->loci) view->locus[locus] = offset; return offset; } void loci_adjust(struct view *view, position_t offset, int delta) { int j; if (delta < 0) { position_t limit = offset - delta; for (j = 0; j < view->loci; j++) { position_t locus = view->locus[j]; if (locus == DELETED || locus == UNSET) continue; if (limit <= locus) locus += delta; else if (offset < locus) locus = j == CURSOR ? offset : UNSET; view->locus[j] = locus; } } else for (j = 0; j < view->loci; j++) { position_t locus = view->locus[j]; if (locus == DELETED || locus == UNSET) continue; if (offset <= locus) view->locus[j] = locus + delta; } } aoeui-1.7+20160302.git4e5dee9/locus.h000066400000000000000000000012321322350162600165170ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef LOCUS_H #define LOCUS_H /* A locus is a fixed point in a view, an offset that get adjusted * when insertions and deletions occur before its position. */ typedef unsigned locus_t; /* loci in all views */ #define CURSOR 0 #define MARK 1 #define NO_LOCUS (~0u) #define DEFAULT_LOCI (MARK+1) #define UNSET (~0) struct view; locus_t locus_create(struct view *, position_t); void locus_destroy(struct view *, locus_t); position_t locus_get(struct view *, locus_t); position_t locus_set(struct view *, locus_t, position_t); void loci_adjust(struct view *, position_t, int delta); #endif aoeui-1.7+20160302.git4e5dee9/macro.c000066400000000000000000000050721322350162600164740ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" struct macro { position_t start, at; size_t bytes; int repeat; struct macro *next, *suspended; }; static struct buffer *macbuf; static struct macro *macros; static struct macro *recording, *playing; struct macro *macro_record(void) { struct macro *new; if (!macbuf) macbuf = buffer_create(NULL); new = allocate0(sizeof *new); new->next = macros; new->start = buffer_bytes(macbuf); return macros = recording = new; } Boolean_t macro_end_recording(Unicode_t chop) { char *raw; size_t n; if (!recording) return FALSE; n = buffer_raw(macbuf, &raw, recording->start, recording->bytes); while (n) { size_t lastlen = utf8_length_backwards(raw+n-1, n); n -= lastlen; if (utf8_unicode(raw+n, lastlen) == chop) break; } buffer_delete(macbuf, recording->start + n, recording->bytes - n); recording->bytes = n; recording->at = recording->bytes; /* not playing */ recording = NULL; return TRUE; } static Boolean_t macro_is_playing(struct macro *macro) { return macro && macro->at < macro->bytes; } Boolean_t macro_play(struct macro *macro, int repeat) { if (!macro || macro_is_playing(macro) || !macro->bytes) return FALSE; macro->suspended = playing; macro->at = 0; macro->repeat = repeat; playing = macro; return TRUE; } void macros_abort(void) { for (; playing; playing = playing->suspended) playing->at = playing->bytes; } void macro_free(struct macro *macro) { struct macro *previous = NULL, *mac, *next; if (!macro) return; if (recording) recording = NULL; else if (macro_is_playing(macro)) macros_abort(); for (mac = macros; mac != macro; previous = mac, mac = next) { next = mac->next; if (mac == macro) if (previous) previous->next = next; else macros = next; else if (mac->start > macro->start) mac->start -= macro->bytes; } buffer_delete(macbuf, macro->start, macro->bytes); RELEASE(macro); } Unicode_t macro_getch(void) { Unicode_t ch; if (playing) { char *p; size_t n = buffer_raw(macbuf, &p, playing->start + playing->at, playing->bytes - playing->at); ch = utf8_unicode(p, n = utf8_length(p, n)); if ((playing->at += n) == playing->bytes) if (playing->repeat-- > 1) playing->at = 0; else playing = playing->suspended; } else { ch = window_getch(); if (!IS_ERROR_CODE(ch) && recording) { char buf[8]; size_t n = unicode_utf8(buf, ch); buffer_insert(macbuf, buf, recording->start + recording->bytes, n); recording->bytes += n; } } return ch; } aoeui-1.7+20160302.git4e5dee9/macro.h000066400000000000000000000005101322350162600164710ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef MACRO_H #define MACRO_H struct macro *macro_record(void); Boolean_t macro_end_recording(Unicode_t chop); Boolean_t macro_play(struct macro *, int repeat); void macros_abort(void); void macro_free(struct macro *); Unicode_t macro_getch(void); #endif aoeui-1.7+20160302.git4e5dee9/main.c000066400000000000000000000052151322350162600163160ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" static void sighandler(int signo) { if (signo == SIGCHLD) { while (waitpid(-1, NULL, WNOHANG) > 0) ; } else { errno = 0; die("fatal signal %d", signo); } } static void signals(void) { static int sig[] = { SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGCHLD, #ifndef DEBUG SIGFPE, SIGSEGV, #endif #ifdef SIGPWR SIGPWR, #endif 0 }; static int sigig[] = { #ifdef SIGTTIN SIGTTIN, #endif #ifdef SIGTTOUT SIGTTOUT, #endif #ifdef SIGPIPE SIGPIPE, #endif 0 }; int j; for (j = 0; sig[j]; j++) signal(sig[j], sighandler); for (j = 0; sigig[j]; j++) signal(sigig[j], SIG_IGN); } static void save_all(void) { struct text *text; Boolean_t msg = FALSE; char *raw; for (text = text_list; text; text = text->next) { if (!text->path || !text->buffer || !text->buffer->path) continue; text_unfold_all(text); if (text->clean && buffer_raw(text->buffer, &raw, 0, ~(size_t)0) == text->clean_bytes && !memcmp(text->clean, raw, text->clean_bytes)) { unlink(text->buffer->path); continue; } if (!msg) { fprintf(stderr, "\ncheck working files for " "current unsaved data\n"); msg = TRUE; } fprintf(stderr, "\t%s\n", text->buffer->path); buffer_snap(text->buffer); } } int main(int argc, char *const *argv) { int ch, value; struct view *view; Unicode_t unicode; errno = 0; if (tcgetattr(1, &original_termios)) die("not running in a terminal"); signals(); atexit(save_all); is_asdfg = argc && argv[0] && strstr(argv[0], "asdfg"); if (!make_writable) make_writable = getenv("AOEUI_WRITABLE"); while ((ch = getopt(argc, argv, "dkoqrsSt:uUw:")) >= 0) switch (ch) { case 'd': is_asdfg = FALSE; break; case 'k': no_keywords = TRUE; break; case 'o': no_save_originals = TRUE; break; case 'q': is_asdfg = TRUE; break; case 'r': read_only = TRUE; break; case 's': default_no_tabs = TRUE; break; case 'S': default_tabs = TRUE; break; case 't': value = atoi(optarg); if (value >= 1 && value <= 20) default_tab_stop = value; else message("bad tab stop setting: %s", optarg); break; case 'u': utf8_mode = UTF8_YES; break; case 'U': utf8_mode = UTF8_NO; break; case 'w': make_writable = optarg; break; default: die("unknown flag"); } for (; optind < argc; optind++) view_open(argv[optind]); /* Main loop */ while ((view = window_current_view()) && ((unicode = macro_getch()), !IS_ERROR_CODE(unicode))) view->mode->command(view, unicode); die("error in input"); return EXIT_FAILURE; } aoeui-1.7+20160302.git4e5dee9/make-TAGS000077500000000000000000000003631322350162600166240ustar00rootroot00000000000000#!/bin/bash if [ ".$1" = . ] then set . fi for path do find -L $path -type f | egrep '\.(h|c|cc|C)$' done | xargs ctags -x \ | tee TAGS.tmp1 \ | grep '::' \ | sed 's/[^:]*:://' >TAGS.tmp2 sort TAGS.tmp[12] >TAGS rm TAGS.tmp[12] aoeui-1.7+20160302.git4e5dee9/mem.c000066400000000000000000000007531322350162600161520ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include #include #include "die.h" #include "mem.h" /* Error-checking wrappers for memory management */ void *reallocate(const void *old, size_t bytes) { void *new = realloc((void *) old, bytes); if (!new && bytes) die("could not allocate %lu bytes", (long) bytes); return new; } void *allocate0(size_t bytes) { void *new = allocate(bytes); if (new) memset(new, 0, bytes); return new; } aoeui-1.7+20160302.git4e5dee9/mem.h000066400000000000000000000004151322350162600161520ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef MEM_H #define MEM_H void *reallocate(const void *, size_t); #define allocate(sz) (reallocate(NULL, (sz))) void *allocate0(size_t); #define RELEASE(p) (reallocate((p), 0), (p) = NULL) #endif aoeui-1.7+20160302.git4e5dee9/mode.c000066400000000000000000000357561322350162600163330ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" /* * This is the default command mode. */ Boolean_t is_asdfg; static struct macro *default_macro, *function_key[FUNCTION_FKEYS+1]; struct mode_default { command command; rgba_t selection_bgrgba; Boolean_t variant, is_hex; int value; }; static void command_handler(struct view *, Unicode_t); static position_t cut(struct view *view, Boolean_t delete) { struct mode_default *mode = (struct mode_default *) view->mode; position_t offset; Boolean_t append; size_t bytes = view_get_selection(view, &offset, &append); int copies = mode->value ? mode->value : 1; if (!mode->variant || mode->value) clip_init(0); while (copies--) clip(0, view, offset, bytes, append); if (delete) view_delete(view, offset, bytes); locus_set(view, MARK, UNSET); return offset; } static void paste(struct view *view) { struct mode_default *mode = (struct mode_default *) view->mode; position_t cursor = locus_get(view, CURSOR); clip_paste(view, cursor, mode->value); locus_set(view, MARK, /*old*/ cursor); } static void forward_lines(struct view *view) { struct mode_default *mode = (struct mode_default *) view->mode; unsigned count = mode->variant ? mode->value : 1; position_t cursor = locus_get(view, CURSOR); if (!count) cursor = find_paragraph_end(view, cursor); else for (; count && cursor < view->bytes; count--) cursor = find_line_end(view, cursor+1); if (count) macros_abort(); locus_set(view, CURSOR, cursor); } static void down_lines(struct view *view) { struct mode_default *mode = (struct mode_default *) view->mode; unsigned count = mode->variant ? mode->value : 1; position_t cursor = locus_get(view, CURSOR); for (; count && cursor < view->bytes; count--) cursor = find_line_down(view, cursor); if (count) macros_abort(); locus_set(view, CURSOR, cursor); } static void backward_lines(struct view *view) { struct mode_default *mode = (struct mode_default *) view->mode; unsigned count = mode->variant ? mode->value : 1; position_t cursor = locus_get(view, CURSOR); if (!count) cursor = find_paragraph_start(view, cursor); else for (; count && cursor; count--) cursor = find_line_start(view, cursor-1); if (count) macros_abort(); locus_set(view, CURSOR, cursor); } static void up_lines(struct view *view) { struct mode_default *mode = (struct mode_default *) view->mode; unsigned count = mode->variant ? mode->value : 1; position_t cursor = locus_get(view, CURSOR); for (; count && cursor; count--) cursor = find_line_up(view, cursor); if (count && !cursor) macros_abort(); locus_set(view, CURSOR, cursor); } static void forward_chars(struct view *view) { struct mode_default *mode = (struct mode_default *) view->mode; unsigned count = mode->variant ? mode->value : 1; position_t cursor = locus_get(view, CURSOR), next; for (; count && IS_UNICODE(view_char(view, cursor, &next)); count--) cursor = next; if (count) macros_abort(); locus_set(view, CURSOR, cursor); } static void backward_chars(struct view *view) { struct mode_default *mode = (struct mode_default *) view->mode; unsigned count = mode->variant ? mode->value : 1; position_t cursor = locus_get(view, CURSOR); for (; count && cursor; count--) view_char_prior(view, cursor, &cursor); if (count) macros_abort(); locus_set(view, CURSOR, cursor); } static Boolean_t funckey(struct view *view, int Fk) { struct mode_default *mode = (struct mode_default *) view->mode; if (Fk > FUNCTION_FKEYS) return FALSE; if (mode->variant && !mode->value) { macro_end_recording(CONTROL('@')); macro_free(function_key[Fk]); function_key[Fk] = macro_record(); return TRUE; } return macro_play(function_key[Fk], mode->value); } static position_t self_insert(struct view *view, Unicode_t ch, position_t mark, position_t old_cursor) { char cbuf[16], *p = cbuf; position_t cursor = old_cursor; size_t len = 0; if (mark != UNSET && mark > cursor) { cursor = cut(view, 1); mark = UNSET; } if (ch == '\n' && view->text->flags & TEXT_CRNL) { memcpy(p, "\r\n", len = 2); } else if (view->text->flags & TEXT_NO_UTF8) { for (p = cbuf + sizeof cbuf; ch; ch >>= 8) *--p = ch, len++; if (!len) *--p = 0, len++; } else { len = unicode_utf8(p, ch); } view_insert(view, p, cursor, len); if (mark == old_cursor) locus_set(view, MARK, old_cursor); if (view->shell_std_in >= 0) shell_command(view, ch); return cursor + len; } static void command_handler(struct view *view, Unicode_t ch0) { struct mode_default *mode = (struct mode_default *) view->mode; Unicode_t ch = ch0; position_t cursor = locus_get(view, CURSOR); position_t mark = locus_get(view, MARK); position_t offset; Boolean_t ok = TRUE; struct view *new_view; char *select; status_hide(); /* Backspace always deletes the character before cursor. */ if (ch == 0x7f /*BCK*/) { delete: if (IS_UNICODE(view_char_prior(view, cursor, &mark))) view_delete(view, mark, cursor-mark); else window_beep(view); goto done; } /* Decode function-key sequences */ if (IS_FUNCTION_KEY(ch)) { /* Forget the up/down goal column if not moving up/down */ if (ch != FUNCTION_UP && ch != FUNCTION_DOWN) view->goal.cursor = UNSET; switch (ch) { case FUNCTION_DOWN: down_lines(view); break; case FUNCTION_UP: up_lines(view); break; case FUNCTION_LEFT: backward_chars(view); break; case FUNCTION_RIGHT: forward_chars(view); break; case FUNCTION_PGUP: window_page_up(view); break; case FUNCTION_PGDOWN: window_page_down(view); break; case FUNCTION_HOME: locus_set(view, CURSOR, 0); break; case FUNCTION_END: locus_set(view, CURSOR, view->bytes); break; case FUNCTION_INSERT: paste(view); break; case FUNCTION_DELETE: goto delete; default: if (ch < FUNCTION_F(1) || !funckey(view, ch - FUNCTION_F(1) + 1)) ok = FALSE; } goto done; } /* * Non-control characters are self-inserted, with a prior * automatic cut of the selection if one exists and the * cursor is at its beginning. But if we're in a variant, * some characters may contribute to the value, or be * non-Control commands. */ if (ch >= ' ' /*0x20*/) { view->goal.cursor = UNSET; if (mode->variant) { if (mode->is_hex && isxdigit(ch)) { mode->value *= 16; if (isdigit(ch)) mode->value += ch - '0'; else mode->value += tolower(ch) - 'a' + 10; return; } if (isdigit(ch)) { mode->value *= 10; mode->value += ch - '0'; return; } if (!mode->value && (ch == 'x' || ch == 'X')) { mode->is_hex = 1; return; } switch (ch) { case '=': bookmark_set(mode->value, view, cursor, mark); goto done; case '-': if (bookmark_get(&new_view, &cursor, &mark, mode->value)) { locus_set(new_view, CURSOR, cursor); if (mark != UNSET) locus_set(new_view, MARK, mark); window_activate(new_view); } else ok = FALSE; goto done; case ';': window_after(view, text_new(), -1); goto done; case '\'': find_tag(view); goto done; case ',': if (mark == UNSET) view_fold_indented(view, mode->value); else { view_fold(view, cursor, mark); locus_set(view, MARK, UNSET); } goto done; case '.': if (mode->value) view_unfold_all(view); else if (mark != UNSET) view_unfold_selection(view); else { mark = view_unfold(view, cursor); if ((signed) mark < 0) view_unfold_all(view); else locus_set(view, MARK, mark); } goto done; case '#': status("%s line %d", view->text->path, current_line_number(view, cursor)); goto done; case '?': window_after(view, view_help(), -1 /*auto*/); goto done; } } self_insert(view, ch, mark, cursor); goto done; } /* * Control character commands */ ch += '@'; if (is_asdfg && ch >= 'A' && ch <= 'Z') { static char asdfg_to_aoeui[26] = { 'A', 'O', 'F', 'Y', 'X', 'W', 'H', 'T', 'I', 'J', 'N', 'S', 'M', 'Z', 'R', 'L', 'Q', 'E', 'P', 'G', 'V', 'B', 'K', 'D', 'C', 'U' }; ch = asdfg_to_aoeui[ch-'A']; } if (ch != 'G' && ch != 'C' && (ch != 'H' && ch != 'T' || !mode->variant || mode->value)) view->goal.cursor = UNSET; switch (ch) { case '@': /* (^Space) */ if (mode->variant) break; /* unset variant */ mode->variant = 1; return; case 'A': /* synonym */ case '_': /* ^/, ^_: search */ mode_search(view, mode->variant); break; case 'B': /* exchange clip buffer and selection, if any, else paste */ if (mark != UNSET) { size_t outbytes = view_get_selection(view, &offset, NULL); unsigned reg = mode->value; size_t inbytes = clip_paste(view, offset + outbytes, reg); clip_init(reg); clip(reg, view, offset, outbytes, 0); view_delete(view, offset, outbytes); locus_set(view, CURSOR, offset); locus_set(view, MARK, offset + inbytes); } else paste(view); break; case 'C': forward_lines(view); break; case 'D': /* [select whitespace] / cut [pre/appending] */ if (mark == UNSET && mode->variant) { locus_set(view, MARK, find_nonspace(view, cursor)); locus_set(view, CURSOR, find_nonspace_prior(view, cursor)); } else cut(view, TRUE); break; case 'E': if (mark != UNSET) mode_child(view); else if (mode->variant) demultiplex_view(view); else { new_view = text_new(); window_after(view, new_view, -1); mode_shell_pipe(new_view); } break; case 'F': /* copy [pre/appending] */ cut(view, FALSE); break; case 'G': backward_lines(view); break; case 'H': if (mode->variant && !mode->value) { mode->variant = FALSE; up_lines(view); } else backward_chars(view); break; case 'I': /* (TAB) tab / tab completion [align; set tab stop] */ if (!mode->variant) { if (!tab_completion_command(view)) insert_tab(view); } else if (mode->value) if (mode->value == 1) view->text->flags ^= TEXT_NO_TABS; else if (mode->value > 1 && mode->value <= 20) view->text->tabstop = default_tab_stop = mode->value; else window_beep(view); else align(view); break; case 'J': /* line feed: new line */ insert_newline(view); break; case 'M': /* (ENTER) new line with alignment */ self_insert(view, '\n', mark, cursor); break; case 'K': /* save all [single] */ if (mode->variant) text_preserve(view->text); else texts_preserve(); break; case 'L': /* forward screen [end of view] */ if (mode->variant) locus_set(view, CURSOR, view->bytes); else window_page_down(view); break; case 'N': /* backward word(s) [sentence] */ if (mode->value) while (mode->value-- && cursor) cursor = find_word_start(view, cursor); else if (mode->variant) cursor = find_sentence_start(view, cursor); else cursor = find_word_start(view, cursor); locus_set(view, CURSOR, cursor); break; case 'O': /* macro end/execute [start] */ if (mode->variant && !mode->value) { macro_end_recording(CONTROL('@')); macro_free(default_macro); default_macro = macro_record(); } else if (!macro_end_recording(ch0) && !macro_play(default_macro, mode->value)) window_beep(view); break; case 'P': /* select other window [closing current] */ if (!mode->variant) window_next(view); else if (mode->value) window_index(mode->value); else window_destroy(view->window); break; case 'Q': /* suspend [quit] */ if (mode->variant) { windows_end(); texts_preserve(); while (text_list) view_close(text_list->views); exit(EXIT_SUCCESS); } windows_end_display(); kill(getpid(), SIGSTOP); window_recenter(view); break; case 'R': /* backward screen [beginning of view] */ if (mode->variant) locus_set(view, CURSOR, 0); else window_page_up(view); break; case 'S': /* forward word(s) [sentence] */ if (mode->value) while (mode->value--) cursor = find_word_end(view, cursor); else if (mode->variant) cursor = find_sentence_end(view, cursor); else cursor = find_word_end(view, cursor); locus_set(view, CURSOR, cursor); break; case 'T': if (mode->variant && !mode->value) { mode->variant = FALSE; down_lines(view); } else forward_chars(view); break; case 'U': /* undo [redo] */ offset = (mode->variant ? text_redo : text_undo)(view->text); if ((offset -= view->start) <= view->bytes) locus_set(view, CURSOR, offset); locus_set(view, MARK, UNSET); break; case 'V': /* set/unset mark [exchange, or select line; force unset] */ if (!mode->variant) locus_set(view, MARK, mark == UNSET ? cursor : UNSET); else if (mode->value) locus_set(view, MARK, UNSET); else if (mark == UNSET) { locus_set(view, MARK, find_line_end(view, cursor) + 1); locus_set(view, CURSOR, find_line_start(view, cursor)); } else { locus_set(view, MARK, cursor); locus_set(view, CURSOR, mark); } break; case 'W': /* select other view [closing current] */ if (mode->variant) view_close(view); else if (!window_replace(view, view_next(view))) window_beep(view); break; case 'X': /* get path / visit file [set path] */ if (mark == UNSET) { view_insert(view, view->text->path, cursor, -1); locus_set(view, MARK, /*old*/ cursor); } else if ((select = view_extract_selection(view))) { if (mode->variant) { if ((ok = text_rename(view->text, select))) window_activate(view); new_view = NULL; } else ok = !!(new_view = view_open(select)); RELEASE(select); if (ok) view_delete_selection(view); if (new_view) window_after(view, new_view, -1 /*auto*/); } break; case 'Y': /* split window [vertically] */ if (mark != UNSET) { position_t offset = mark < cursor ? mark : cursor; size_t bytes = mark < cursor ? cursor - mark : mark - cursor; new_view = view_selection(view, offset, bytes); } else new_view = view_next(view); if (new_view) window_after(view, new_view, mode->variant); else window_beep(view); break; case 'Z': /* recenter/goto */ if (mode->value) locus_set(view, CURSOR, find_line_number(view, mode->value)); else if (mode->variant) window_raise(view); window_recenter(view); break; case '\\': /* quit */ if (mode->variant) { windows_end(); texts_uncreate(); exit(EXIT_SUCCESS); } break; case ']': /* move to corresponding bracket */ cursor = find_corresponding_bracket(view, cursor); if ((signed) cursor < 0) window_beep(view); else locus_set(view, CURSOR, cursor); break; case '^': /* literal [; unicode] */ if (mode->value) self_insert(view, mode->value, mark, cursor); else if (IS_UNICODE(ch = macro_getch())) { if (ch >= '@' && ch <= '_') ch = CONTROL(ch); else if (ch >= 'a' && ch <= 'z') ch = CONTROL(ch-'a'+'A'); else if (ch == '?') ch = 0x7f; self_insert(view, ch, mark, cursor); } else ok = FALSE; break; default: ok = FALSE; break; } done: mode->variant = mode->is_hex = FALSE; mode->value = 0; if (!ok) window_beep(view); } struct mode *mode_default(void) { struct mode_default *dft = allocate0(sizeof *dft); dft->command = command_handler; dft->selection_bgrgba = SELECTION_BGRGBA; return (struct mode *) dft; } aoeui-1.7+20160302.git4e5dee9/mode.h000066400000000000000000000006011322350162600163150ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef MODE_H #define MODE_H struct view; typedef void (*command)(struct view *, Unicode_t); /* All modes start with this header. */ struct mode { command command; rgba_t selection_bgrgba; }; extern Boolean_t is_asdfg; struct mode *mode_default(void); void mode_search(struct view *, Boolean_t regex); #endif aoeui-1.7+20160302.git4e5dee9/notes.txt000066400000000000000000000132141322350162600171150ustar00rootroot00000000000000The Dvorak Simplified Keyboard layout ESC FFFFF FFFFF DEL ` 12345 67890 [] BCK TAB ',.py fgcrl /= \ CTL AoeuI dhtns - ENT SHF ;qJkx bMwvz SHF LCK ALT SPC ALT CTL arrows Commands are denoted here in these notes by ^, which signifies use of Control, Alt, or a leading Escape before a key. (Some Control keys don't work the same in a Linux console; Escape always works. Control-[ is the same as Escape.) Non-command characters are inserted or searched for. Self-insertion automatically cuts when cursor < mark. There are multiple windows, views, and texts. Every window has a view, but not every view has a window. Every view has a text and every text has at least one view. Some texts have files. The cursor, mark, and macro are local to their view. * below means "must be this character". () indicate synonyms [;] indicate variants activated by ^Space, possibly with value / indicates behavior with mark unset / set Command characters * ESC function keys and query responses, ALT * Fk global macro execute [start; repeat] ^@ (^Space) ^^ literal, control [; unicode] * ^[ (ESC or "smaller font") ^] move to corresponding or nearest bracket [AVAILABLE] * TAB tab / tab completion [align; set tab stop] ^P select another window [closing current; by index] ^Y split window [vertically] / narrow to selection ^F AVAILABLE / copy [pre/append; replicate] ^G backward to line start [paragraph start; multiple lines] ^C forward to line end [paragraph end; multiple lines] ^R backward screen(s) [beginning of view] ^L forward screen(s) [end of view] ^/ (^_) * ^? (BCK) delete character before cursor * ^+ -- larger font ^\ [quit without saving] * ^A -- reserved by screen(1), synonym for ^/(^_) ^O macro end, macro execute [macro start; repeat] ^E shell [end children] / pipe clipbuffer to command ^U undo [redo] * ^I (TAB) ^D cut char [select whitespace] / cut [pre/append; replicate] ^H backward char(s) [up line; multiple chars] ^T forward char(s) [down line; multiple chars] ^N backward word(s) [sentence; multiple words] ^S forward word(s) [sentence; multiple words] * ^- -- smaller font in some WMs, otherwise (^_) * ^_ (^/) incremental search mode [regexp] * ENT (^M) newline ^ENT (^J) ^Q suspend editor [quit] * ^J (^ENT) newline with automatic alignment ^K save all [single] ^X get path / visit file [set path] ^B paste / exchange with clip buffer [; register] * ^M (ENT) ^W select other view [closing current] ^V set/unset mark [select line / exchange mark with cursor; force unset] ^Z recenter view [single window, reset display; go to line] ^SP (^@) variant, beginning of value Non-control characters -- commands must be [variants] = [; set bookmark] - [; go to bookmark] ; [new anonymous text] ' [go to tag] , [fold view on indentation] / [fold selection] . [unfold; unfold entire view] / [unfold selection once] # [get current position] ? [help] 0-9 decimal argument x 0-9 a-f A-F hexadecimal argument unused: ` ~ ! @ $ % & ( ) { } " < > | : Missing features: multiple column characters (using wcwidth) preserving window layouts moving to prior window saving, restoring, rewriting F-key definitions Idioms - search and replace: cut replacement text, search for first occurrence, then repeat ^B^F^/^/ to exchange target with replacement, restore replacement text in the clip buffer, and proceed to the next hit - also, can pipe text through sed 's/X/Y/g' for global replacement - also, can use a macro - exchanging cursor and mark and then retyping with automatic cut is often faster than an explicit cut. - going forward or back a half screenful: use ^R/^L, then ^Z - inserting with a repeat count: cut with repeat, then paste QWERTY->Dvorak command mapping qwert yuIop Asdfg hJkl; zxcvb nM,./ navigation: G:t C:y R:o L:p H:g T:h N:k S:l Z:n selection: V:u F:c D:x B:v windows: P:s Y:d W:f files: Q:q K:w X:e others: O:b E:r U:z Unicode tips: 0x2000-206f general punctuation 0x2100-22ff math 0x2300-23ff misc. technical 0x2400-24ff printable control chars, boxed numbers and letters 0x2500-25ff block graphics 0x2600-26b2 misc. symbols 0x2700-27ff dingbats Compose key sequences for Latin-1: 0xa1 (¡) !! 0xa2 (¢) c/ 0xa3 (£) l- 0xa4 (¤) ox 0xa5 (¥) Y= 0xa6 (¦) 0xa7 (§) so 0xa8 (¨) "" 0xa9 (©) OC 0xaa (ª) a_ 0xab («) << 0xac (¬) ,- 0xad (­) --- 0xae (®) OR 0xaf (¯) ^- 0xb0 (°) 0xb1 (±) +- 0xb2 (²) ^2 0xb3 (³) ^3 0xb4 (´) '' 0xb5 (µ) u/ 0xb6 (¶) p! 0xb7 (·) ^. 0xb8 (¸) ,, 0xb9 (¹) ^1 0xba (º) o_ 0xbb (») >> 0xbc (¼) 0xbd (½) 0xbe (¾) 0xbf (¿) ?? 0xc6 (Æ) AE 0xd0 (Ð) D- 0xd7 (×) xx 0xde (Þ) TH 0xdf (ß) ss 0xe6 (æ) ae 0xf0 (ð) 0xf7 (÷) :- 0xfe (þ) th (€) C= 0x203b ※ 0x2230 ∰ 0x237e ⍾ 0x2615 ☕ 0x2620 ☠ 0x2767 ❧ ---- no mark ---- ------ mark ------ raw ^Space arg raw ^Space arg Q suspend quit U Z undo redo K W save save 1 \ AVAIL abort X E (get path) visit (set path) H G <-ch up <-chs T H ch-> down chs-> N K <-wd <-sent <-wds S L wd-> sent-> wds-> G T <-ln <-pp <-lns C Y ln-> pp-> lns-> R O <-pg home L P pg-> end ] [] Z N center reset ->line# _ search regexp J (auto-align new line) Tab (tab complete) Tab ^ (literal/ctl) unicode V U mark selline unmark unmark swap unmark F C AVAIL copy append repl D X cutch selwhite cut append repl B V paste exch register W F view close Y D split vsplit narrow P S window close ->window# O B macro macstart macros E R shell endchildren pipe = bookmk bookmk# - gotomk gotomk# ; (new anon text) ' (go to tag) , (fold view) (fold selection) . unfold unfoldall (unfold selection) # where ? help aoeui-1.7+20160302.git4e5dee9/rgba.h000066400000000000000000000017361322350162600163160ustar00rootroot00000000000000/* Copyright 2011 Peter Klausler. See COPYING for license. */ #ifndef RGBA_H #define RGBA_H typedef unsigned rgba_t; #define DEFAULT_FGRGBA 0xff #define DEFAULT_BGRGBA (~0) #define PALE_RGBA(rgba) ((rgba) & 0x7f7f7f00) #define RED_RGBA 0xff000000 #define GREEN_RGBA 0x00ff0000 #define BLUE_RGBA 0x0000ff00 #define YELLOW_RGBA 0xffff0000 #define MAGENTA_RGBA 0xff00ff00 #define CYAN_RGBA 0x00ffff00 #define WHITE_RGBA 0xffffff00 #define BLACK_RGBA 0x00000000 #define DEFAULT_CURSORRGBA GREEN_RGBA #define RDONLY_RGBA RED_RGBA #define DIRTY_RGBA MAGENTA_RGBA #define SELECTING_RGBA BLUE_RGBA #define SELECTION_FGRGBA RED_RGBA #define BRACKET_FGRGBA BLUE_RGBA #define COMMENT_FGRGBA MAGENTA_RGBA #define STRING_FGRGBA RED_RGBA #define KEYWORD_FGRGBA BLUE_RGBA #define FOLDED_FGRGBA WHITE_RGBA #define FOLDED_BGRGBA RED_RGBA #define SELECTION_BGRGBA CYAN_RGBA #define LAMESPACE_BGRGBA MAGENTA_RGBA #define BADCHAR_BGRGBA MAGENTA_RGBA #define SEARCH_BGRGBA YELLOW_RGBA #endif aoeui-1.7+20160302.git4e5dee9/search.c000066400000000000000000000172331322350162600166420ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" /* * Incremental search mode */ struct mode_search { command command; rgba_t selection_bgrgba; struct mode *previous; Byte_t *pattern; size_t bytes, alloc, last_bytes; Boolean_t backward; position_t start, mark; regex_t *regex; Boolean_t regex_ready; }; static Boolean_t match_char(Unicode_t x, Unicode_t y) { if (!IS_UNICODE(x) || !IS_UNICODE(y)) return FALSE; if (x >= 'a' && x <= 'z') x += 'A' - 'a'; if (y >= 'a' && y <= 'z') y += 'A' - 'a'; return x == y; } static size_t match_pattern(struct view *view, position_t offset) { struct mode_search *mode = (struct mode_search *) view->mode; size_t j; for (j = 0; j < mode->bytes; j++) if (!match_char(mode->pattern[j], view_byte(view, offset++))) return 0; return mode->bytes; } static int match_regex(struct view *view, position_t *offset, Boolean_t advance) { int j, err; char *raw; size_t bytes = view_raw(view, &raw, *offset, ~(size_t)0); unsigned flags = 0; regmatch_t match[10]; struct mode_search *mode = (struct mode_search *) view->mode; if (view_char_prior(view, *offset, NULL) != '\n') flags |= REG_NOTBOL; err = regexec(mode->regex, raw, 10, match, flags); if (err && err != REG_NOMATCH) window_beep(view); if (err) return 0; if (!advance && match[0].rm_so) return 0; if (match[0].rm_so >= bytes) return 0; if (match[0].rm_eo > bytes) match[0].rm_eo = bytes; for (j = 1; j < 10; j++) { if (match[j].rm_so < 0 || match[j].rm_so >= bytes) continue; if (match[j].rm_eo > bytes) match[j].rm_eo = bytes; clip_init(j); clip(j, view, *offset + match[j].rm_so, match[j].rm_eo - match[j].rm_so, 0); } *offset += match[0].rm_so; return match[0].rm_eo - match[0].rm_so; } static int scan_forward(struct view *view, size_t *length, position_t offset, position_t max_offset) { struct mode_search *mode = (struct mode_search *) view->mode; if (mode->bytes > view->bytes) return -1; if (max_offset > view->bytes - mode->bytes) max_offset = view->bytes - mode->bytes; if (offset + mode->bytes > max_offset) return -1; if (mode->regex) { if ((*length = match_regex(view, &offset, 1)) && offset < max_offset) return offset; } else for (; offset < max_offset; offset++) if ((*length = match_pattern(view, offset))) return offset; return -1; } static int scan_backward(struct view *view, size_t *length, position_t offset, position_t min_offset) { struct mode_search *mode = (struct mode_search *) view->mode; if (min_offset + mode->bytes > view->bytes) return -1; if (offset + mode->bytes > view->bytes) offset = view->bytes - mode->bytes; if (mode->regex) { for (; offset+1 > min_offset; offset--) if ((*length = match_regex(view, &offset, 0))) return offset; } else for (; offset+1 > min_offset; offset--) if ((*length = match_pattern(view, offset))) return offset; return -1; } static Boolean_t search(struct view *view, int backward, int new) { struct mode_search *mode = (struct mode_search *) view->mode; position_t mark; size_t length = 0; int at; if (!mode->bytes) { locus_set(view, CURSOR, mode->start); locus_set(view, MARK, UNSET); return TRUE; } if (mode->regex) { int err; if (new && mode->regex_ready) { regfree(mode->regex); mode->regex_ready = FALSE; } if (!mode->regex_ready) { mode->pattern[mode->bytes] = '\0'; status("regular expression: %s", mode->pattern); err = regcomp(mode->regex, (char *) mode->pattern, REG_EXTENDED | REG_ICASE | REG_NEWLINE); if (err) return FALSE; mode->regex_ready = TRUE; } } mark = locus_get(view, MARK); if (mark == UNSET) mark = locus_get(view, CURSOR); if (backward) { at = scan_backward(view, &length, mark - !new, 0); if (at < 0) at = scan_backward(view, &length, view->bytes, mark+1); } else { at = scan_forward(view, &length, mark + !new, view->bytes); if (at < 0) at = scan_forward(view, &length, 0, mark-1); } if (at < 0) { window_beep(view); macros_abort(); return 0; } /* A hit! */ locus_set(view, MARK, at); locus_set(view, CURSOR, at + length); mode->last_bytes = mode->bytes; return TRUE; } static void command_handler(struct view *view, Unicode_t ch) { struct mode_search *mode = (struct mode_search *) view->mode; static char cmdchar[][2] = { { CONTROL('H'), CONTROL('G') }, { CONTROL('T'), CONTROL('H') }, { CONTROL('V'), CONTROL('U') } }; static char *last_search; /* Backspace removes the last character from the search target and * returns to the previous hit */ if (ch == 0x7f /*BCK*/) if (!mode->bytes) { window_beep(view); goto done; } else { mode->bytes--; search(view, !mode->backward, 1); return; } /* Non-control characters are appended to the search target and * we proceed to the next hit if the current position does not * match the extended target. */ if (ch >= ' ' || ch == '\t') { size_t new; if (mode->bytes + 8 > mode->alloc) { mode->alloc = mode->bytes + 64; mode->pattern = reallocate(mode->pattern, mode->alloc); } mode->bytes += new = unicode_utf8((char *) mode->pattern + mode->bytes, ch); mode->pattern[mode->bytes] = '\0'; if (!search(view, mode->backward, new) && !mode->regex) mode->bytes -= new; return; } /* ^H moves to a hit that is earlier in the text. * ^T and ^/ (^A, ^_) proceed to a later hit. */ if (mode->last_bytes && (ch == cmdchar[0][is_asdfg] || ch == cmdchar[1][is_asdfg] || ch == CONTROL('A') || ch == CONTROL('_'))) { mode->bytes = mode->last_bytes; search(view, mode->backward = (ch == cmdchar[0][is_asdfg]), 0); return; } /* Hitting ^H/^T or ^/ (^A, ^_) with an empty search pattern causes * the last successful search target to be reused. */ if ((ch == cmdchar[0][is_asdfg] || ch == cmdchar[1][is_asdfg] || ch == CONTROL('A') || ch == CONTROL('_')) && !mode->bytes && last_search) { mode->bytes = strlen(last_search); mode->alloc = mode->bytes + 8; mode->pattern = reallocate(mode->pattern, mode->alloc); memcpy(mode->pattern, last_search, mode->bytes+1); if (ch == cmdchar[0][is_asdfg]) mode->backward = 1; else if (ch == cmdchar[1][is_asdfg]) mode->backward = 0; search(view, mode->backward, 0); return; } /* Search is done */ done: if (mode->bytes) { last_search = reallocate(last_search, mode->bytes+1); memcpy(last_search, mode->pattern, mode->bytes); last_search[mode->bytes] = '\0'; } view->mode = mode->previous; status_hide(); if (ch == cmdchar[2][is_asdfg]) { /* ^V: keep target as selection */ } else { /* restore mark, if any */ locus_set(view, MARK, mode->mark); if (ch != '\r' && ch != CONTROL('A') && ch != CONTROL('_') && ch != 0x7f /*BCK*/) view->mode->command(view, ch); } /* Release search mode resources */ RELEASE(mode->pattern); if (mode->regex_ready) regfree(mode->regex); RELEASE(mode->regex); RELEASE(mode); } void mode_search(struct view *view, Boolean_t regex) { struct mode_search *mode = allocate0(sizeof *mode); mode->previous = view->mode; mode->command = command_handler; mode->selection_bgrgba = SEARCH_BGRGBA; mode->start = locus_get(view, CURSOR); mode->mark = locus_get(view, MARK); if (mode->mark != UNSET && mode->start < mode->mark && !regex) { mode->bytes = mode->mark - mode->start; mode->alloc = mode->bytes + 8; mode->pattern = allocate(mode->alloc); view_get(view, mode->pattern, mode->start, mode->bytes); mode->last_bytes = mode->bytes; locus_set(view, MARK, mode->start); locus_set(view, CURSOR, mode->mark); } if (regex) mode->regex = allocate0(sizeof *mode->regex); view->mode = (struct mode *) mode; } aoeui-1.7+20160302.git4e5dee9/tab.c000066400000000000000000000225701322350162600161430ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" static char *path_complete(const char *string) { const char *home; size_t length; char *new = NULL, *p; DIR *dir; char *freestring = NULL; while (isspace(*string)) string++; if (!strncmp(string, "~/", 2) && (home = getenv("HOME"))) { freestring = allocate(strlen(home) + strlen(string)); sprintf(freestring, "%s%s", home, string+1); string = freestring; } length = strlen(string); new = allocate(length + NAME_MAX); memcpy(new, string, length+1); p = strrchr(new, '/'); if (p) { *p = '\0'; dir = opendir(new); *p++ = '/'; } else { dir = opendir("."); p = new; } if (dir) { struct dirent *dent; size_t prefix_len = new + length - p; size_t best_len = 0; while ((dent = readdir(dir))) { size_t dent_len = strlen(dent->d_name); if (dent_len <= prefix_len) continue; if (strncmp(p, dent->d_name, prefix_len)) continue; if (!best_len) { strcpy(new + length, dent->d_name + prefix_len); best_len = dent_len - prefix_len; } else { unsigned old_best_len = best_len; for (best_len = 0; best_len < old_best_len; best_len++) if (new[length+best_len] != dent->d_name[prefix_len+best_len]) break; if (!best_len) break; } } new[length+best_len] = '\0'; closedir(dir); if (best_len) goto done; } RELEASE(new); /* sets new = NULL */ done: RELEASE(freestring); return new; } static char *word_complete(const char *string) { struct text *text; struct view *hit_view = NULL; size_t hit_offset = 0, hit_length = 0; size_t length = strlen(string); if (!length) return NULL; for (text = text_list; text; text = text->next) { struct view *view = text->views; sposition_t offset; if (!view) continue; for (offset = 0; (offset = find_string(view, string, offset)) >= 0; offset++) { size_t old_hit_length; position_t last = offset + length - 1; size_t this_length = find_word_end(view, last) - last - 1; if (!this_length) continue; if (!(old_hit_length = hit_length)) { hit_view = view; hit_offset = offset; hit_length = this_length; } else { for (hit_length = 0; hit_length < old_hit_length; hit_length++) if (view_byte(hit_view, hit_offset + length + hit_length) != view_byte(view, offset + length + hit_length)) break; if (!hit_length) return NULL; } } } if (!hit_length) return NULL; return view_extract(hit_view, hit_offset, length + hit_length); } char *tab_complete(const char *string, Boolean_t selection) { char *new = NULL; while (isspace(*string)) string++; if (!*string) return NULL; if (selection) new = path_complete(string); if (!new) new = word_complete(string); if (new && !strcmp(new, string)) RELEASE(new); /* assigns new = NULL; */ return new; } Boolean_t tab_completion_command(struct view *view) { position_t cursor = locus_get(view, CURSOR); position_t mark = locus_get(view, MARK); char *completed = NULL, *select = NULL; Boolean_t selection = mark < cursor; Unicode_t ch; Boolean_t result = FALSE; if (selection) select = view_extract_selection(view); else if (mark == UNSET && IS_CODEPOINT((ch = view_char_prior(view, cursor, NULL))) && (isalnum(ch) || ch == '_')) { mark = find_word_start(view, cursor); if (mark < cursor) select = view_extract(view, mark, cursor-mark); } if (select && (completed = tab_complete(select, selection))) { view_delete(view, mark, cursor-mark); view_insert(view, completed, locus_get(view, CURSOR), -1); if (!selection) mark = cursor; locus_set(view, MARK, mark); RELEASE(completed); result = TRUE; } RELEASE(select); return result; } void insert_tab(struct view *view) { position_t cursor = locus_get(view, CURSOR); position_t mark = locus_get(view, MARK); if (mark != UNSET && mark > cursor) { view_delete(view, cursor, mark - cursor); locus_set(view, MARK, mark = UNSET); } if (view->text->flags & TEXT_NO_TABS) { int tabstop = view->text->tabstop; sposition_t offset = 0; position_t at = find_line_start(view, cursor); if (at) while (at != cursor) { Unicode_t ch = view_char(view, at, &at); if (ch == '\t') offset = (offset / tabstop + 1) * tabstop; else offset++; } for (offset %= tabstop; offset++ < tabstop; ) view_insert(view, " ", cursor, 1); } else view_insert(view, "\t", cursor, 1); if (mark == cursor) locus_set(view, MARK, /*old*/ cursor); } static void indent_line(struct view *view, position_t lnstart, unsigned indentation, Boolean_t no_blank_line) { char *indent; unsigned indent_bytes; position_t nonspace, next; Unicode_t ch; for (nonspace = lnstart; IS_UNICODE(ch = view_char(view, nonspace, &next)); nonspace = next) if (ch == '\n' || !IS_CODEPOINT(ch) || !isspace(ch)) break; view_delete(view, lnstart, nonspace - lnstart); if (no_blank_line && ch == '\n') return; if (view->text->flags & TEXT_NO_TABS) { indent_bytes = indentation; indent = allocate(indent_bytes); memset(indent, ' ', indentation); } else { unsigned tabstop = view->text->tabstop; tabstop |= !tabstop; indent_bytes = indentation/tabstop + indentation%tabstop; indent = allocate(indent_bytes); memset(indent, '\t', indentation / tabstop); memset(indent + indentation/tabstop, ' ', indentation % tabstop); } view_insert(view, indent, lnstart, indent_bytes); RELEASE(indent); } static int current_line_indentation(struct view *view, position_t *at) { int indent = 0, spaces = 0, chars = 0; position_t offset = *at; Unicode_t ch; unsigned tabstop = view->text->tabstop; while (IS_UNICODE(ch = view_char(view, offset, &offset)) && ch != '\n') if (ch == ' ') spaces += !chars; else if (ch == '\t') { indent = ((indent + spaces + chars) / tabstop + 1) * tabstop; spaces = chars = 0; *at = offset; } else chars++; *at += spaces; return indent + spaces; } static int line_alignment(struct view *view, position_t lnstart0) { position_t lnstart, offset, at, corr; Unicode_t ch; unsigned indent, brindent; unsigned tabstop = view->text->tabstop; Boolean_t is_nested; if (!lnstart0) return 0; lnstart = lnstart0 - 1; tabstop |= !tabstop; /* Find previous non-blank line */ again: lnstart = find_line_start(view, lnstart); while (lnstart && view_char(view, lnstart, NULL) == '\n') lnstart = find_line_start(view, lnstart-1); /* Determine its indentation */ at = lnstart; indent = current_line_indentation(view, &at); /* Adjust for nesting */ brindent = indent + 1; is_nested = FALSE; for (ch = view_char(view, at, &offset); IS_UNICODE(ch) && ch != '\n'; ch = view_char(view, at = offset, &offset), brindent++) { switch (ch) { case '(': case '[': corr = find_corresponding_bracket(view, at); if (corr >= lnstart0) { indent = brindent; is_nested = TRUE; } break; case ')': case ']': corr = find_corresponding_bracket(view, at); if (corr < lnstart) { lnstart = corr; goto again; } break; case '\t': brindent = (brindent/tabstop + 1) * tabstop; break; } } /* Adjust for clues at the end of the previous line */ if (!is_nested && lnstart0) { ch = view_char_prior(view, lnstart0 - 1, NULL); if (ch == '{' || ch == ')' || ch == ':' || ch == /*els*/'e') indent = (indent/tabstop + 1) * tabstop; else if (indent >= tabstop) { offset = lnstart; do ch = view_char_prior(view, offset, &offset); while (ch == ' ' || ch == '\t' || ch == '\n'); if (ch == ')' /* || ch == '}' */) indent = (indent/tabstop - 1) * tabstop; } } /* Adjust for leading character on current line */ if (indent) { offset = lnstart0; do ch = view_char(view, offset, &offset); while (ch == ' ' || ch == '\t'); if (ch == '}') indent = (indent/tabstop - 1) * tabstop; } return indent; } void align(struct view *view) { position_t cursor = locus_get(view, CURSOR); position_t mark = locus_get(view, MARK); if (mark == UNSET) { position_t line_start = find_line_start(view, cursor); indent_line(view, line_start, line_alignment(view, line_start), FALSE); } else { position_t top, bottom, line_start; locus_t end; if (cursor <= mark) top = cursor, bottom = mark; else top = mark, bottom = cursor; end = locus_create(view, bottom); line_start = find_line_start(view, top); do { int align = line_alignment(view, line_start); indent_line(view, line_start, align, TRUE); line_start = find_line_end(view, line_start) + 1; } while (line_start < locus_get(view, end)); locus_destroy(view, end); } } void insert_newline(struct view *view) { sposition_t cursor = locus_get(view, CURSOR); position_t mark = locus_get(view, MARK); if (mark != UNSET && mark > cursor) { view_delete(view, cursor, mark - cursor); locus_set(view, MARK, mark = UNSET); } view_insert(view, "\n", cursor, 1); locus_set(view, CURSOR, ++cursor); if (view_byte(view, cursor-2) == '{') { position_t at = cursor+1; int la, cla; if (cursor == view->bytes || (la = line_alignment(view, cursor)) > (cla = current_line_indentation(view, &at) + view->text->tabstop) || la == cla && view_byte(view, at) != '}') { view_insert(view, "}\n", cursor, 1 + (cursor == view->bytes)); align(view); view_insert(view, "\n", cursor, 1); locus_set(view, CURSOR, cursor); } } align(view); } aoeui-1.7+20160302.git4e5dee9/tags.c000066400000000000000000000116161322350162600163320ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" /* TAGS file searching */ static char *extract_id(struct view *view) { Unicode_t ch; position_t at = locus_get(view, CURSOR), next; char *id; if (is_idch((ch = view_char(view, at, &next))) || ch == ':' && view_char(view, next, NULL) == ':') locus_set(view, CURSOR, at = find_id_end(view, at)); locus_set(view, MARK, find_id_start(view, at)); id = view_extract_selection(view); locus_set(view, MARK, UNSET); return id; } static struct view *find_TAGS(struct view *view, struct view *tags) { const char *currpath = (tags ? tags : view)->text->path; char *path = allocate(strlen(currpath) + 8); char *slash; strcpy(path, currpath); if (tags) { view_close(tags); slash = strrchr(path, '/'); if (slash) *slash = '\0'; } for (; (slash = strrchr(path, '/')); *slash = '\0') { strcpy(slash+1, "TAGS"); if (!access(path, R_OK)) { tags = view_open(path); if (tags && !(tags->text->flags & TEXT_CREATED)) { RELEASE(path); return tags; } view_close(tags); } } RELEASE(path); return NULL; } static sposition_t find_id_in_TAGS(struct view *tags, const char *id) { position_t first = 0; position_t last = tags->bytes; position_t at, wordstart, wordend; sposition_t result = -1; char *this; int cmp; while (first < last) { at = find_line_start(tags, first + last >> 1); if (at < first) at = find_line_end(tags, at) + 1; if (at >= last) break; wordstart = find_nonspace(tags, at); wordend = find_space(tags, wordstart); this = view_extract(tags, wordstart, wordend - wordstart); if (!this) break; cmp = strcmp(id, this); RELEASE(this); if (!cmp) result = wordend; if (cmp <= 0) last = at; else first = find_line_end(tags, at) + 1; } return result; /* failure */ } static sposition_t find_next_id_in_TAGS(struct view *tags, const char *id, position_t prevend) { position_t wordstart = find_nonspace(tags, find_line_end(tags, prevend) + 1); position_t wordend = find_space(tags, wordstart); char *this = view_extract(tags, wordstart, wordend - wordstart); int cmp; if (!this) return -1; cmp = strcmp(id, this); RELEASE(this); return cmp ? -1 : wordend; } static struct view *show_tag(struct view *tags, sposition_t wordend, const char *id) { position_t wordstart, linestart; char *this, *path, *slash; int line; struct view *view; sposition_t at; if (wordend < 0) return NULL; wordstart = find_nonspace(tags, wordend); /* line number */ wordend = find_space(tags, wordstart); this = view_extract(tags, wordstart, wordend - wordstart); if (!isdigit(*this)) { /* exuberant-ctags puts a classifier before the line number */ RELEASE(this); wordstart = find_nonspace(tags, wordend); /* line number */ wordend = find_space(tags, wordstart); this = view_extract(tags, wordstart, wordend - wordstart); if (!isdigit(*this)) { RELEASE(this); return NULL; } } line = atoi(this); RELEASE(this); wordstart = find_nonspace(tags, wordend); /* file name */ wordend = find_space(tags, wordstart); this = view_extract(tags, wordstart, wordend - wordstart); if (*this == '/') path = this; else { path = allocate(strlen(tags->text->path) + strlen(this) + 8); strcpy(path, tags->text->path); if (!(slash = strrchr(path, '/'))) *(slash = path + strlen(path)) = '/'; strcpy(slash + 1, this); RELEASE(this); } view = view_open(path); if (!view) message("Could not open %s from TAGS", path); RELEASE(path); if (!view || view->text->flags & TEXT_CREATED) { view_close(view); return FALSE; } linestart = find_line_number(view, line); at = find_string(view, id, linestart); if (at >= 0) { locus_set(view, CURSOR, at); locus_set(view, MARK, at + strlen(id)); mode_search(view, FALSE); } else { locus_set(view, CURSOR, linestart); locus_set(view, MARK, UNSET); } return view; } static Boolean_t show_tags(struct view *tags, struct view *view, const char *id) { sposition_t wordend = find_id_in_TAGS(tags, id); struct view *new_view = show_tag(tags, wordend, id); struct view *top_view; if (!new_view) return FALSE; window_below(view, top_view = new_view, 4); while ((wordend = find_next_id_in_TAGS(tags, id, wordend)) >= 0) if ((new_view = show_tag(tags, wordend, id))) window_below(view, top_view = new_view, 4); window_activate(top_view); return TRUE; } void find_tag(struct view *view) { struct view *tags = NULL, *prior_tags; char *id; if (locus_get(view, MARK) != UNSET) { id = view_extract_selection(view); view_delete_selection(view); } else id = extract_id(view); if (!id) { window_beep(view); return; } while ((tags = find_TAGS(view, prior_tags = tags))) if (show_tags(tags, view, id)) break; if (tags) view_close(tags); else if (!prior_tags) message("No readable TAGS file found."); else { errno = 0; message("couldn't find tag %s", id); } RELEASE(id); } aoeui-1.7+20160302.git4e5dee9/text.c000066400000000000000000000147401322350162600163610ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" /* * A text is a container for the content of a file or * an internal scratch buffer. It has a buffer, which * contains the actual bytes of the text, an "undo" history * buffer, and one or more views, which each present part or * all of the text to the user for editing. The positions of * the cursor, mark, and other "loci" are all local to a view. */ struct text *text_list; struct view *view_find(const char *name) { struct view *view; struct text *text; if (!name) name = ""; for (text = text_list; text; text = text->next) for (view = text->views; view; view = view->next) if (view->name && !strcmp(view->name, name)) return view; return NULL; } void view_name(struct view *view) { int j, len; const char *name = view->text->path; char *new, *p; if (!name) name = ""; len = strlen(name); new = allocate(len + 8); if (view->text->flags & TEXT_EDITOR) { memcpy(new, name, len+1); p = new + len; } else { for (j = 0, p = new; j < len; j++) { unsigned ch = name[j]; if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9' || ch == '_' || ch == '-' || ch == '+' || ch == '.' || ch == ',' || ch >= 0x80) *p++ = ch; else if (ch == '/' && j < len-1) p = new; } *p = '\0'; } if (view_find(new)) for (j = 2; ; j++) { sprintf(p, "<%d>", j); if (!view_find(new)) break; } RELEASE(view->name); view->name = new; } struct view *view_create(struct text *text) { struct view *view = allocate0(sizeof *view); view->loci = DEFAULT_LOCI; view->locus = allocate(view->loci * sizeof *view->locus); memset(view->locus, UNSET, view->loci * sizeof *view->locus); view->locus[CURSOR] = 0; view->text = text; view->next = text->views; text->views = view; view->bytes = text->buffer ? buffer_bytes(text->buffer) : text->clean ? text->clean_bytes :0; view->mode = mode_default(); view->shell_std_in = -1; view->shell_pg = -1; view->shell_out_locus = NO_LOCUS; view_name(view); return view; } struct view *text_create(const char *path, unsigned flags) { struct text *text = allocate0(sizeof *text), *prev, *bp; text->fd = -1; text->flags = flags; text->path = strdup(path); if (utf8_mode == UTF8_NO) text->flags |= TEXT_NO_UTF8; if (default_no_tabs) text->flags |= TEXT_NO_TABS; text->tabstop = default_tab_stop; keyword_init(text); for (prev = NULL, bp = text_list; bp; prev = bp, bp = bp->next) ; if (prev) prev->next = text; else text_list = text; return view_create(text); } static void text_close(struct text *text) { struct text *prev = NULL, *bp; for (bp = text_list; bp; prev = bp, bp = bp->next) if (bp == text) { if (prev) prev->next = text->next; else text_list = text->next; break; } if (text->clean) munmap(text->clean, text->clean_bytes); buffer_destroy(text->buffer); text_forget_undo(text); if (text->fd >= 0) close(text->fd); if (text->flags & (TEXT_SCRATCH | TEXT_CREATED)) unlink(text->path); RELEASE(text->path); RELEASE(text); } void view_close(struct view *view) { struct view *vp, *prev = NULL; struct text *text; if (!view) return; text = view->text; window_destroy(view->window); demultiplex_view(view); bookmark_unset_view(view); if (text) for (vp = text->views; vp; prev = vp, vp = vp->next) if (vp == view) { if (prev) prev->next = vp->next; else if (!(text->views = vp->next)) text_close(text); break; } RELEASE(view->name); RELEASE(view); } struct view *view_selection(struct view *current, position_t offset, size_t bytes) { struct view *view; if (!current) return NULL; view = view_create(current->text); if (offset > current->bytes) offset = current->bytes; if (offset + bytes > current->bytes) bytes = current->bytes - offset; view->start = current->start + offset; view->bytes = bytes; locus_set(view, CURSOR, 0); return view; } void text_adjust_loci(struct text *text, position_t offset, int delta) { struct view *view; if (!delta) return; if (delta < 0) { position_t limit = offset - delta; for (view = text->views; view; view = view->next) if (limit < view->start) view->start += delta; else if (offset < view->start) { size_t loss = limit - view->start; if (loss > view->bytes) loss = view->bytes; view->start = offset; view->bytes -= loss; loci_adjust(view, 0, -loss); } else if (offset < view->start + view->bytes) { size_t loss = view->start + view->bytes - offset; if (loss > -delta) loss = -delta; view->bytes -= loss; loci_adjust(view, offset - view->start, -loss); } } else for (view = text->views; view; view = view->next) if (offset < view->start) view->start += delta; else if (offset <= view->start + view->bytes) { view->bytes += delta; loci_adjust(view, offset - view->start, delta); } } static size_t text_get(struct text *text, void *out, position_t offset, size_t bytes) { if (text->buffer) return buffer_get(text->buffer, out, offset, bytes); if (!text->clean || offset >= text->clean_bytes) return 0; if (offset + bytes > text->clean_bytes) bytes = text->clean_bytes - offset; memcpy(out, text->clean + offset, bytes); return bytes; } size_t view_get(struct view *view, void *out, position_t offset, size_t bytes) { if (offset >= view->bytes) return 0; if (offset + bytes > view->bytes) bytes = view->bytes - offset; return text_get(view->text, out, view->start + offset, bytes); } static size_t text_raw(struct text *text, char **out, position_t offset, size_t bytes) { if (text->buffer) return buffer_raw(text->buffer, out, offset, bytes); if (!text->clean) { *out = NULL; return 0; } if (offset + bytes > text->clean_bytes) bytes = text->clean_bytes - offset; *out = bytes ? text->clean + offset : NULL; return bytes; } size_t view_raw(struct view *view, char **out, position_t offset, size_t bytes) { if (offset >= view->bytes) { *out = NULL; return 0; } if (offset + bytes > view->bytes) bytes = view->bytes - offset; return text_raw(view->text, out, view->start + offset, bytes); } size_t view_delete(struct view *view, position_t offset, size_t bytes) { return text_delete(view->text, view->start + offset, bytes); } size_t view_insert(struct view *view, const void *in, position_t offset, ssize_t bytes) { if (bytes < 0) bytes = in ? strlen(in) : 0; return text_insert(view->text, in, view->start + offset, bytes); } aoeui-1.7+20160302.git4e5dee9/text.h000066400000000000000000000103661322350162600163660ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef TEXT_H #define TEXT_H /* Texts and views */ struct text { struct text *next; struct view *views; /* list of views into this text */ char *clean; /* unmodified content, mmap'ed */ size_t clean_bytes; fd_t fd; struct buffer *buffer; /* modified content */ struct undo *undo; /* undo/redo state */ char *path; unsigned dirties; /* number of modifications */ unsigned preserved; /* "dirties" at last save */ time_t mtime; unsigned tabstop; struct keywords *keywords; sposition_t (*comment_start)(struct view *, position_t); sposition_t (*comment_end)(struct view *, position_t); sposition_t (*string_end)(struct view *, position_t); const char *brackets; unsigned foldings; unsigned flags; #define TEXT_SAVED_ORIGINAL (1<<0) #define TEXT_RDONLY (1<<1) #define TEXT_EDITOR (1<<2) #define TEXT_CREATED (1<<3) #define TEXT_SCRATCH (1<<4) #define TEXT_NO_TABS (1<<5) #define TEXT_NO_UTF8 (1<<6) #define TEXT_CRNL (1<<7) }; struct view { struct view *next; struct text *text; struct window *window; char *name; position_t start; /* offset in text */ size_t bytes; unsigned loci; position_t *locus; struct mode *mode; fd_t shell_std_in; locus_t shell_out_locus; pid_t shell_pg; struct goal_column { position_t cursor; unsigned row, column; } goal; }; struct keywords { int count; const char **word; }; extern struct text *text_list; extern unsigned default_tab_stop; /* -t 8 */ extern Boolean_t default_no_tabs; /* -s */ extern Boolean_t default_tabs; /* -S */ extern Boolean_t no_keywords; /* -k */ extern Boolean_t no_save_originals; /* -o */ extern Boolean_t read_only; /* -r */ extern enum utf8_mode { UTF8_NO, UTF8_YES, UTF8_AUTO } utf8_mode; extern const char *make_writable; /* text.c */ struct view *view_find(const char *name); void view_name(struct view *); struct view *view_create(struct text *); struct view *text_create(const char *name, unsigned flags); struct view *text_new(void); void view_close(struct view *); struct view *view_selection(struct view *, position_t, size_t); void text_adjust_loci(struct text *, position_t, int delta); size_t view_get(struct view *, void *, position_t, size_t); size_t view_raw(struct view *, char **, position_t, size_t); size_t view_delete(struct view *, position_t, size_t); size_t view_insert(struct view *, const void *, position_t, ssize_t); /* Use only for raw bytes. See util.h for general folded and Unicode * character access with view_char[_prior](). */ INLINE Unicode_t text_byte(struct text *text, position_t offset) { if (text->buffer) return buffer_byte(text->buffer, offset); if (text->clean) return (Byte_t) text->clean[offset]; return UNICODE_BAD; } INLINE Unicode_t view_byte(struct view *view, position_t offset) { if (offset >= view->bytes) return UNICODE_BAD; return text_byte(view->text, view->start + offset); } /* file.c */ struct view *view_open(const char *path); Boolean_t text_rename(struct text *, const char *path); void text_dirty(struct text *); Boolean_t text_is_dirty(struct text *); void text_preserve(struct text *); void texts_preserve(void); void texts_uncreate(void); /* undo.c */ size_t text_delete(struct text *, position_t, size_t); size_t text_insert(struct text *, const void *, position_t, size_t); sposition_t text_undo(struct text *); sposition_t text_redo(struct text *); void text_forget_undo(struct text *); /* bookmark.c */ void bookmark_set(unsigned, struct view *, position_t cursor, position_t mark); Boolean_t bookmark_get(struct view **, position_t *cursor, position_t *mark, unsigned id); void bookmark_unset(unsigned); void bookmark_unset_view(struct view *); void demultiplex_view(struct view *); /* child.c */ struct view *view_help(void); /* help.c */ char *tab_complete(const char *, Boolean_t); /* tab.c */ Boolean_t tab_completion_command(struct view *); void insert_tab(struct view *); void insert_newline(struct view *); void align(struct view *); /* fold.c */ void view_fold(struct view *, position_t, position_t); sposition_t view_unfold(struct view *, position_t); void view_unfold_selection(struct view *); void view_fold_indented(struct view *, unsigned); void view_unfold_all(struct view *); void text_unfold_all(struct text *); /* see also util.h */ #endif aoeui-1.7+20160302.git4e5dee9/types.h000066400000000000000000000005311322350162600165370ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef TYPES_H #define TYPES_H #include /* for ssize_t */ typedef unsigned Unicode_t; typedef unsigned char Byte_t; typedef enum Boolean_t { FALSE = 0!=0, TRUE = 0==0 } Boolean_t; typedef size_t position_t; typedef ssize_t sposition_t; typedef int fd_t; #endif aoeui-1.7+20160302.git4e5dee9/undo.c000066400000000000000000000115321322350162600163360ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" struct edit { position_t offset; ssize_t bytes; /* negative means "inserted" */ }; struct undo { struct buffer *edits, *deleted; position_t redo, saved; }; static struct edit *get_raw_edit(void *raw) { /* The intermediate cast to void* suppresses an * alignment warning otherwise emitted by some * compilers. */ return raw; } static struct edit *last_edit(struct text *text) { char *raw = NULL; if (text->undo && text->undo->redo && text->undo->redo == buffer_bytes(text->undo->edits)) buffer_raw(text->undo->edits, &raw, text->undo->redo - sizeof(struct edit), sizeof(struct edit)); return get_raw_edit(raw); } static void resume_editing(struct text *text) { if (!text->undo) { text->undo = allocate0(sizeof *text->undo); text->undo->edits = buffer_create(NULL); text->undo->deleted = buffer_create(NULL); } buffer_delete(text->undo->edits, text->undo->redo, buffer_bytes(text->undo->edits) - text->undo->redo); buffer_delete(text->undo->deleted, text->undo->saved, buffer_bytes(text->undo->deleted) - text->undo->saved); } static Boolean_t in_view(struct view *view, position_t *offset, size_t *bytes) { if (*offset < view->start) { if (*offset + *bytes <= view->start) return FALSE; *bytes -= view->start - *offset; *offset = FALSE; } else { *offset -= view->start; if (*offset >= view->bytes) return FALSE; if (*offset + *bytes > view->bytes) *bytes = view->bytes - *offset; } return TRUE; } static void view_hint_deleting(struct view *view, position_t offset, size_t bytes) { if (view->window && in_view(view, &offset, &bytes)) window_hint_deleting(view->window, offset, bytes); } static void view_hint_inserted(struct view *view, position_t offset, size_t bytes) { if (view->window && in_view(view, &offset, &bytes)) window_hint_inserted(view->window, offset, bytes); } size_t text_delete(struct text *text, position_t offset, size_t bytes) { char *old; struct edit edit, *last; struct view *view; if (!bytes) return 0; text_dirty(text); edit.offset = offset; edit.bytes = buffer_raw(text->buffer, &old, offset, bytes); bytes = edit.bytes; if ((last = last_edit(text)) && last->bytes >= 0 && last->offset == offset) last->bytes += bytes; else { resume_editing(text); buffer_insert(text->undo->edits, &edit, text->undo->redo, sizeof edit); text->undo->redo += sizeof edit; } for (view = text->views; view; view = view->next) view_hint_deleting(view, offset, bytes); buffer_move(text->undo->deleted, text->undo->saved, text->buffer, offset, bytes); text->undo->saved += bytes; text_adjust_loci(text, offset, -bytes); return bytes; } size_t text_insert(struct text *text, const void *in, position_t offset, size_t bytes) { struct edit edit, *last; struct view *view; if (!bytes) return 0; text_dirty(text); bytes = buffer_insert(text->buffer, in, offset, bytes); if ((last = last_edit(text)) && last->bytes < 0 && last->offset - last->bytes == offset) last->bytes -= bytes; else { resume_editing(text); edit.offset = offset; edit.bytes = -bytes; buffer_insert(text->undo->edits, &edit, text->undo->redo, sizeof edit); text->undo->redo += sizeof edit; } text_adjust_loci(text, offset, bytes); for (view = text->views; view; view = view->next) view_hint_inserted(view, offset, bytes); return bytes; } sposition_t text_undo(struct text *text) { char *raw; struct edit *edit; if (!text->undo || !text->undo->redo) return -1; text_dirty(text); buffer_raw(text->undo->edits, &raw, text->undo->redo -= sizeof *edit, sizeof *edit); edit = get_raw_edit(raw); if (edit->bytes >= 0) buffer_move(text->buffer, edit->offset, text->undo->deleted, text->undo->saved -= edit->bytes, edit->bytes); else buffer_move(text->undo->deleted, text->undo->saved, text->buffer, edit->offset, -edit->bytes); text_adjust_loci(text, edit->offset, edit->bytes); return edit->offset; } sposition_t text_redo(struct text *text) { char *raw; struct edit *edit; if (!text->undo || text->undo->redo == buffer_bytes(text->undo->edits)) return -1; text_dirty(text); buffer_raw(text->undo->edits, &raw, text->undo->redo, sizeof *edit); edit = get_raw_edit(raw); text->undo->redo += sizeof *edit; if (edit->bytes >= 0) { buffer_move(text->undo->deleted, text->undo->saved, text->buffer, edit->offset, edit->bytes); text->undo->saved += edit->bytes; } else buffer_move(text->buffer, edit->offset, text->undo->deleted, text->undo->saved, -edit->bytes); text_adjust_loci(text, edit->offset, -edit->bytes); return edit->offset; } void text_forget_undo(struct text *text) { if (text->undo) { buffer_destroy(text->undo->edits); buffer_destroy(text->undo->deleted); RELEASE(text->undo); } } aoeui-1.7+20160302.git4e5dee9/unicode.c000066400000000000000000000011701322350162600170140ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include #include #include #include #include #include "types.h" #include "utf8.h" int main(int argc, const char *argv[]) { Unicode_t ch = 0x203b; unsigned num = 1, at = 0; char buf[8]; if (argc > 1) ch = strtoul(argv[1], NULL, 16); if (argc > 2) num = strtoul(argv[2], NULL, 0); while (num--) { if (!at) printf("0x%04x", ch); buf[unicode_utf8(buf, ch++)] = '\0'; printf("\t%s", buf); if (++at == 8) { at = 0; putchar('\n'); } } if (at) putchar('\n'); return EXIT_SUCCESS; } aoeui-1.7+20160302.git4e5dee9/utf8.c000066400000000000000000000061341322350162600162610ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include #include #include "types.h" #include "utf8.h" /* Encode a Unicode code point in UTF-8; return the encoding length in bytes. */ size_t unicode_utf8(char *out, Unicode_t unicode) { char *p = out; if (!(unicode >> 7)) *p++ = unicode; else { int n; for (n = 1; n < 5; n++) if (!(unicode >> 6 + 5*n)) break; *p++ = 0xfc << 5-n | unicode >> 6*n; while (n--) *p++ = 0x80 | unicode >> 6*n & 0x3f; } return p - out; } /* The first byte of a UTF-8 encoding reveals its length. */ Byte_t utf8_bytes[0x100] = { /* 00-7f are themselves */ /*00*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*10*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*20*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*30*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*40*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*50*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*60*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*70*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80-bf are later bytes, out-of-sync if first */ /*80*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*90*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*a0*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*b0*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c0-df are first byte of two-byte sequences (5+6=11 bits) */ /* c0-c1 are noncanonical */ /*c0*/ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /*d0*/ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* e0-ef are first byte of three-byte (4+6+6=16 bits) */ /* e0 80-9f are noncanonical */ /*e0*/ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* f0-f7 are first byte of four-byte (3+6+6+6=21 bits) */ /* f0 80-8f are noncanonical */ /*f0*/ 4, 4, 4, 4, 4, 4, 4, 4, /* f8-fb are first byte of five-byte (2+6+6+6+6=26 bits) */ /* f8 80-87 are noncanonical */ /*f8*/ 5, 5, 5, 5, /* fc-fd are first byte of six-byte (1+6+6+6+6+6=31 bits) */ /* fc 80-83 are noncanonical */ /*fc*/ 6, 6, /* fe and ff are not part of valid UTF-8 so they stand alone */ /*fe*/ 1, 1 }; /* * Validate a UTF-8 encoding and return its length. * Invalid encodings are expressed as single bytes. */ size_t utf8_length(const char *in, size_t max) { const Byte_t *p = (const Byte_t *) in; size_t n = utf8_bytes[*p]; if (max > n) max = n; if (max < n) return 1; for (n = 1; n < max; n++) if ((p[n] & 0xc0) != 0x80) return 1; return max; } /* Find the length of a UTF-8 encoding in reverse. */ size_t utf8_length_backwards(const char *in, size_t max) { int n; const Byte_t *p = (const Byte_t *) in; if ((*p & 0xc0) != 0x80) return 1; if (max > 6) max = 6; for (n = 1; n < max; n++) if ((p[-n] & 0xc0) != 0x80) break; if (utf8_bytes[p[-n]] == n+1) return n+1; return 1; } /* Decode UTF-8 to Unicode. */ Unicode_t utf8_unicode(const char *in, size_t length) { const Byte_t *p = (const Byte_t *) in; Unicode_t unicode; if (length <= 1 || length > 6) return *p; unicode = *p & (1 << 7-length)-1; while (--length) unicode <<= 6, unicode |= *++p & 0x3f; return unicode; } aoeui-1.7+20160302.git4e5dee9/utf8.h000066400000000000000000000036371322350162600162730ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef UTF8_H #define UTF8_H /* * UTF-8 encoding is used to represent actual Unicode characters * as well as the artificial values used as the delimiters of folded * blocks. One of these extended characters can be held in a Unicode_t * and passes IS_UNICODE(). True Unicode code points also satisfy * IS_CODEPOINT(). */ #define UNICODE_BAD (1u << 31) #define IS_UNICODE(u) ((u) < UNICODE_BAD) #define IS_CODEPOINT(u) ((u) < 0x10000) /* Some non-Unicode code points are used to represent function keys * and input errors. */ #define FUNCTION_KEY(x) (UNICODE_BAD + 1 + (x)) #define IS_FUNCTION_KEY(x) ((x) - FUNCTION_KEY(0) < 256) #define FUNCTION_UP FUNCTION_KEY(1) #define FUNCTION_DOWN FUNCTION_KEY(2) #define FUNCTION_RIGHT FUNCTION_KEY(3) #define FUNCTION_LEFT FUNCTION_KEY(4) #define FUNCTION_PGUP FUNCTION_KEY(5) #define FUNCTION_PGDOWN FUNCTION_KEY(6) #define FUNCTION_HOME FUNCTION_KEY(7) #define FUNCTION_END FUNCTION_KEY(8) #define FUNCTION_INSERT FUNCTION_KEY(9) #define FUNCTION_DELETE FUNCTION_KEY(10) #define FUNCTION_F(k) FUNCTION_KEY(20+(k)) #define FUNCTION_FKEYS 12 #define ERROR_CODE(e) (FUNCTION_KEY(256) + (e)) #define IS_ERROR_CODE(e) ((e) >= ERROR_CODE(0)) #define ERROR_EOF ERROR_CODE(1) #define ERROR_CHANGED ERROR_CODE(2) /* must call _get_geometry()! */ #define ERROR_INPUT ERROR_CODE(3) #define ERROR_EMPTY ERROR_CODE(4) /* no input */ size_t unicode_utf8(char *, Unicode_t); size_t utf8_length(const char *, size_t max); size_t utf8_length_backwards(const char *, size_t max); Unicode_t utf8_unicode(const char *, size_t length); #define CONTROL(x) ((x)-'@') /* Huge code point values are used to bracket folded sections. */ #define FOLD_START 0x40000000 #define FOLD_END 0x60000000 #define IS_FOLDED(ch) ((ch) - FOLD_START < FOLD_END-FOLD_START) #define FOLDED_BYTES(ch) ((ch) & 0x1fffffff) extern Byte_t utf8_bytes[0x100]; #endif aoeui-1.7+20160302.git4e5dee9/util.c000066400000000000000000000104341322350162600163460ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" ssize_t view_vprintf(struct view *view, const char *msg, va_list ap) { char buff[1024]; vsnprintf(buff, sizeof buff, msg, ap); return view_insert(view, buff, view->bytes, -1); } ssize_t view_printf(struct view *view, const char *msg, ...) { va_list ap; int result; va_start(ap, msg); result = view_vprintf(view, msg, ap); va_end(ap); return result; } size_t view_get_selection(struct view *view, position_t *offset, Boolean_t *append) { position_t cursor = locus_get(view, CURSOR); position_t mark = locus_get(view, MARK); if (mark == UNSET) if ((mark = cursor) < view->bytes) view_char(view, mark, &mark); if (append) *append = cursor >= mark; if (mark <= cursor) return cursor - (*offset = mark); return mark - (*offset = cursor); } char *view_extract(struct view *view, position_t offset, unsigned bytes) { char *str; if (!view) return NULL; if (offset > view->bytes) return NULL; if (offset + bytes > view->bytes) bytes = view->bytes - offset; if (!bytes) return NULL; str = allocate(bytes+1); str[view_get(view, str, offset, bytes)] = '\0'; return str; } char *view_extract_selection(struct view *view) { position_t offset; size_t bytes = view_get_selection(view, &offset, NULL); return view_extract(view, offset, bytes); } size_t view_delete_selection(struct view *view) { position_t offset; size_t bytes = view_get_selection(view, &offset, NULL); view_delete(view, offset, bytes); locus_set(view, MARK, UNSET); return bytes; } struct view *view_next(struct view *view) { struct view *new = view; do { if (new->next) new = new->next; else if (new->text->next) new = new->text->next->views; else new = text_list->views; } while (new != view && new->window); return new == view ? text_new() : new; } Unicode_t view_unicode(struct view *view, position_t offset, position_t *next) { Unicode_t ch = view_byte(view, offset); char *raw; size_t length; if (!IS_UNICODE(ch) || ch < 0x80 || view->text->flags & TEXT_NO_UTF8) { if (ch == '\r' && view->text->flags & TEXT_CRNL && view_byte(view, offset + 1) == '\n') { if (next) *next = offset + 2; return '\n'; } if (next) *next = offset + IS_UNICODE(ch); return ch; } length = view_raw(view, &raw, offset, 8); length = utf8_length(raw, length); if (next) *next = offset + length; return utf8_unicode(raw, length); } Unicode_t view_unicode_prior(struct view *view, position_t offset, position_t *prev) { Unicode_t ch = UNICODE_BAD; char *raw; if (offset) { ch = view_byte(view, --offset); if (IS_UNICODE(ch) && ch >= 0x80 && !(view->text->flags & TEXT_NO_UTF8)) { unsigned at = offset >= 7 ? offset-7 : 0; view_raw(view, &raw, at, offset-at+1); offset -= utf8_length_backwards(raw+offset-at, offset-at+1) - 1; ch = view_unicode(view, offset, NULL); } else if (ch == '\n' && view->text->flags & TEXT_CRNL && offset && view_byte(view, offset-1) == '\r') offset--; } if (prev) *prev = offset; return ch; } Unicode_t view_char(struct view *view, position_t offset, position_t *next) { position_t next0; Unicode_t ch = view_unicode(view, offset, &next0); if (!next) return ch; *next = next0; if (IS_FOLDED(ch)) { size_t fbytes = FOLDED_BYTES(ch); position_t next2; if (view_unicode(view, next0 + fbytes, &next2) == FOLD_END + fbytes) *next = next2; } return ch; } Unicode_t view_char_prior(struct view *view, position_t offset, position_t *prev) { Unicode_t ch = view_unicode_prior(view, offset, &offset), ch0; size_t fbytes; position_t offset0; if (ch >= FOLD_END && (fbytes = FOLDED_BYTES(ch)) <= offset && IS_FOLDED(ch0 = view_unicode_prior(view, offset - fbytes, &offset0)) && FOLDED_BYTES(ch0) == fbytes) { ch = ch0; offset = offset0; } if (prev) *prev = offset; return ch; } Boolean_t is_open_bracket(const char *brackets, Unicode_t ch) { if (ch >= 0x80) return FALSE; for (; *brackets; brackets += 2) if (*brackets == ch) return TRUE; return FALSE; } Boolean_t is_close_bracket(const char *brackets, Unicode_t ch) { if (ch >= 0x80) return FALSE; for (brackets++; *brackets; brackets += 2) if (*brackets == ch) return TRUE; return FALSE; } aoeui-1.7+20160302.git4e5dee9/util.h000066400000000000000000000055021322350162600163530ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef UTIL_H #define UTIL_H /* Utilities */ struct view; /* find.c */ position_t find_line_start(struct view *, position_t); position_t find_line_end(struct view *, position_t); position_t find_paragraph_start(struct view *, position_t); position_t find_paragraph_end(struct view *, position_t); position_t find_line_up(struct view *, position_t); position_t find_line_down(struct view *, position_t); position_t find_space(struct view *, position_t); position_t find_space_prior(struct view *, position_t); position_t find_nonspace(struct view *, position_t); position_t find_nonspace_prior(struct view *, position_t); position_t find_word_start(struct view *, position_t); position_t find_word_end(struct view *, position_t); position_t find_id_start(struct view *, position_t); position_t find_id_end(struct view *, position_t); position_t find_sentence_start(struct view *, position_t); position_t find_sentence_end(struct view *, position_t); sposition_t find_corresponding_bracket(struct view *, position_t); position_t find_line_number(struct view *, unsigned line); unsigned current_line_number(struct view *, position_t); position_t find_row_bytes(struct view *, position_t, unsigned column, unsigned columns); unsigned find_column(unsigned *row, struct view *, position_t linestart, position_t offset, unsigned start_column); sposition_t find_string(struct view *, const char *, position_t); const char *path_format(const char *); /* file.c */ void find_tag(struct view *); /* tags.c */ ssize_t view_vprintf(struct view *, const char *, va_list); ssize_t view_printf(struct view *, const char *, ...); size_t view_get_selection(struct view *, position_t *offset, Boolean_t *append); char *view_extract(struct view *, position_t, unsigned bytes); char *view_extract_selection(struct view *); size_t view_delete_selection(struct view *); struct view *view_next(struct view *); Unicode_t view_unicode(struct view *, position_t, size_t *); Unicode_t view_unicode_prior(struct view *, position_t, position_t *prev); Unicode_t view_char(struct view *, position_t, size_t *); Unicode_t view_char_prior(struct view *, position_t, position_t *prev); Boolean_t is_open_bracket(const char *, Unicode_t); Boolean_t is_close_bracket(const char *, Unicode_t); void keyword_init(struct text *); /* keyword.c */ Boolean_t is_keyword(struct view *, position_t); INLINE Boolean_t is_wordch(Unicode_t ch) { return ch > 0x100 && ch < FOLD_START || IS_CODEPOINT(ch) && isalnum(ch); } INLINE Boolean_t is_idch(Unicode_t ch) { return ch == '_' || is_wordch(ch); } INLINE unsigned char_columns(Unicode_t ch, unsigned column, unsigned tabstop) { if (ch == '\t') return tabstop - column % tabstop; if (ch < ' ' || ch == 0x7f || IS_FOLDED(ch)) return 2; /* ^X or folded <> */ return 1; } #endif aoeui-1.7+20160302.git4e5dee9/window.c000066400000000000000000000556751322350162600167200ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #include "all.h" /* * A "window" is a presentation of a view on part or all of * the display surface. One window is active, meaning that * it directs keyboard input to its view's command handler. */ struct window { struct view *view; locus_t start; int row, column; int rows, columns; int cursor_row, cursor_column; rgba_t fgrgba, bgrgba; unsigned last_dirties; Boolean_t repaint; position_t last_cursor, last_mark; struct mode *last_mode; struct window *next; }; static struct window *window_list; static struct window *active_window; static struct display *display; static int display_rows, display_columns; static Boolean_t titles = TRUE; static void title(struct window *window) { char buff[128]; struct view *view; position_t cursor; if (!titles) return; if (window != active_window) return; if (!window) { titles = display_title(display, NULL); return; } view = window->view; snprintf(buff, sizeof buff, "%s%s", view->name, view->text->flags & TEXT_CREATED ? " (new)" : view->text->flags & TEXT_RDONLY ? " (read-only)" : view->text->preserved != view->text->dirties ? " (unsaved)" : ""); cursor = locus_get(view, CURSOR); if (cursor < 65536) { int line = current_line_number(view, cursor); int len = strlen(buff); snprintf(buff + len, sizeof buff - len - 1, " [%d]", line); } titles = display_title(display, buff); } static struct window *activate(struct window *window) { if (active_window) active_window->repaint = TRUE; active_window = window; title(window); window->repaint = TRUE; return window; } static struct window *window_create(struct view *view, struct view *after) { struct window *window = view->window; if (!window) { window = allocate0(sizeof *window); window->view = view; view->window = window; window->start = locus_create(view, UNSET); if (after && after->window) { window->next = after->window->next; after->window->next = window; } else { window->next = window_list; window_list = window; } } else window->row = window->column = 0; window->rows = display_rows; window->columns = display_columns; window->last_dirties = ~0; return window; } static Boolean_t stacked(struct window *up, struct window *down) { return up && down && up->column == down->column && up->columns == down->columns && up->row + up->rows == down->row; } static Boolean_t beside(struct window *left, struct window *right) { return left && right && left->row == right->row && left->rows == right->rows && left->column + left->columns == right->column; } static Boolean_t adjacent(struct window *x, struct window *y) { if (x->column + x->columns < y->column || y->column + y->columns < x->column || x->row + x->rows < y->row || y->row + y->rows < x->row) return FALSE; if (y->column != x->column + x->columns && x->column != y->column + y->columns) return TRUE; return y->row < x->row + x->rows && x->row < y->row + y->rows; } static Boolean_t window_expand_next(struct window *window) { struct window *next = window->next; if (!next) return FALSE; if (stacked(window, next)) { next->row = window->row; next->rows += window->rows; next->repaint = TRUE; return TRUE; } if (beside(window, next)) { next->column = window->column; next->columns += window->columns; next->repaint = TRUE; return TRUE; } return FALSE; } void window_destroy(struct window *window) { struct window *previous = NULL, *wp; if (!window) return; for (wp = window_list; wp != window; previous = wp, wp = wp->next) if (wp == window) break; if (!wp) ; else if (previous) { struct window *next = previous->next = window->next; if (stacked(previous, window) && (!stacked(window, next) || previous->rows <= next->rows)) { previous->rows += window->rows; previous->repaint = TRUE; } else if (beside(previous, window) && (!beside(window, next) || previous->columns <= next->columns)) { previous->columns += window->columns; previous->repaint = TRUE; } else if (!window_expand_next(window)) window_raise(previous->view); } else if ((window_list = window->next) && !window_expand_next(window)) window_raise(window_list->view); if (window->view) { locus_destroy(window->view, window->start); window->view->window = NULL; } if (window == active_window) { active_window = NULL; wp = window_list; if (previous) { wp = previous; if (wp->next) wp = wp->next; } if (wp) activate(wp); } RELEASE(window); } struct window *window_raise(struct view *view) { if (!display) display = display_init(); display_get_geometry(display, &display_rows, &display_columns); display_erase(display, 0, 0, display_rows, display_columns); while (window_list && window_list->view != view) window_destroy(window_list); while (window_list && window_list->next) window_destroy(window_list->next); return activate(window_create(view, NULL)); } struct window *window_activate(struct view *view) { if (view->window) return activate(view->window); return window_raise(view); } struct window *window_after(struct view *before, struct view *view, int vertical) { struct window *window, *old; if (view->window) return view->window; if (!before || !(old = before->window) || old->rows < 4 || old->columns < 16) return window_raise(view); window = window_create(view, before); if (vertical < 0) vertical = old->columns > 120; if (vertical) { window->row = old->row; window->rows = old->rows; window->column = old->column + (old->columns -= (window->columns = old->columns >> 1)); } else { window->column = old->column; window->columns = old->columns; window->row = old->row + (old->rows -= (window->rows = old->rows >> 1)); } old->repaint = TRUE; return activate(window); } struct window *window_below(struct view *above, struct view *view, unsigned rows) { struct window *window, *old; if (view->window) return view->window; if (!above && active_window) above = active_window->view; if (!above || !(old = above->window) || old->rows < rows*2) return window_raise(view); window = window_create(view, above); window->column = old->column; window->columns = old->columns; window->row = old->row + (old->rows -= (window->rows = rows)); return window; } struct window *window_replace(struct view *old, struct view *new) { struct window *window = old->window; if (!new) return NULL; if (new->window) return new->window; if (!old || !window) return window_raise(new); window->view = new; old->window = NULL; locus_destroy(old, window->start); window->start = locus_create(new, locus_get(new, CURSOR)+1); new->window = window; return activate(window_recenter(new)); } struct view *window_current_view(void) { if (!active_window) { if (!text_list) view_help(); window_raise(text_list->views); } return active_window->view; } void windows_end_display(void) { struct window *window; if (display) { display_end(display); display = NULL; } for (window = window_list; window; window = window->next) window->repaint = TRUE; } void windows_end(void) { while (window_list) window_destroy(window_list); windows_end_display(); } static unsigned count_rows(struct window *window, position_t start, position_t end) { unsigned rows = 0, bytes, max_rows = window->rows + 1; for (rows = 0; start < end && rows < max_rows; rows++, start += bytes) if (!(bytes = find_row_bytes(window->view, start, 0, window->columns))) break; return rows; } static position_t find_row_start(struct window *window, position_t position, position_t start) { size_t bytes; if (position && position >= window->view->bytes) position--; while ((bytes = find_row_bytes(window->view, start, 0, window->columns))) { if (start + bytes > position) break; start += bytes; } return start; } static void new_start(struct view *view, position_t start) { if (start) view_char_prior(view, start, &start); else start = UNSET; locus_set(view, view->window->start, start); view->window->last_dirties = ~0; } static position_t screen_start(struct view *view) { position_t start = locus_get(view, view->window->start); if (start == UNSET) start = 0; else view_char(view, start, &start); return start; } static position_t focus(struct window *window) { struct view *view = window->view; position_t cursor = locus_get(view, CURSOR); position_t cursorrow = find_row_start(window, cursor, find_line_start(view, cursor)); position_t start = screen_start(view); position_t end, at; unsigned above = 0, below; size_t bytes; start = find_row_start(window, start, find_line_start(view, start)); /* Scroll by single lines when cursor is just one row out of view. */ if (cursorrow < start && start == cursorrow + find_row_bytes(view, cursorrow, 0, window->columns)) { start = cursorrow; display_insert_lines(display, window->row, window->column, 1, window->rows, window->columns); goto done; } if (cursorrow >= start && (above = count_rows(window, start, cursorrow)) == window->rows) { start += find_row_bytes(view, start, 0, window->columns); above--; display_delete_lines(display, window->row, window->column, 1, window->rows, window->columns); goto done; } if (cursorrow >= start && above < window->rows) { for (below = 1, at = cursorrow; above + below < window->rows; below++, at += bytes) if (!(bytes = find_row_bytes(view, at, 0, window->columns))) break; if (above + below == window->rows || !start) goto done; } for (start = cursorrow, above = 0; (end = start) && above < window->rows >> 1; above += count_rows(window, start, end)) start = find_line_start(view, start-1); for (; above >= window->rows >> 1; above--) start += find_row_bytes(view, start, 0, window->columns); for (below = 1, at = cursorrow; above + below < window->rows; below++, at += bytes) if (!(bytes = find_row_bytes(view, at, 0, window->columns))) break; for (; (end = start) && above + below < window->rows; above += count_rows(window, start, end)) start = find_line_start(view, start-1); for (; above + below > window->rows; above--) start += find_row_bytes(view, start, 0, window->columns); done: window->cursor_column = find_column(&above, view, cursorrow, cursor, 0); window->cursor_row = above; new_start(view, start); return start; } static Boolean_t lame_space(struct view *view, position_t start, unsigned next_tab) { unsigned distance = 0; position_t offset = start; Unicode_t ch; if (view->shell_std_in >= 0 || view->text->flags & TEXT_EDITOR) return FALSE; do { distance++; ch = view_char(view, offset, &offset); if (!IS_UNICODE(ch) || ch == '\n' || ch == '\t') return TRUE; } while (ch == ' '); return distance > next_tab && !(view->text->flags & TEXT_NO_TABS) && (next_tab || view_char_prior(view, start-1, NULL) == ' '); } static Boolean_t lame_tab(struct view *view, position_t offset) { Unicode_t ch; if (view->shell_std_in >= 0 || view->text->flags & TEXT_EDITOR) return FALSE; if (view->text->flags & TEXT_NO_TABS) return TRUE; for (; IS_UNICODE(ch = view_char(view, offset, &offset)) && ch != '\n'; ) if (ch != ' ' && ch != '\t') return FALSE; return TRUE; } static int paintch(struct window *window, Unicode_t ch, int row, int column, position_t at, position_t cursor, position_t mark, unsigned *brackets, rgba_t fgrgba) { rgba_t bgrgba = window->bgrgba; unsigned tabstop = window->view->text->tabstop; const char *brack = window->view->text->brackets; if (ch == '\n') return column; if (mark != UNSET && (at >= cursor && at < mark || at >= mark && at < cursor)) { bgrgba = window->view->mode->selection_bgrgba; fgrgba = SELECTION_FGRGBA; } else if (at == cursor) { rgba_t rgba = DEFAULT_CURSORRGBA; if (mark != UNSET) rgba = SELECTING_RGBA; else if (window->view->text->flags & TEXT_RDONLY) rgba = RDONLY_RGBA; else if (text_is_dirty(window->view->text)) rgba = DIRTY_RGBA; if (!display_cursor_color(display, rgba)) fgrgba = rgba; } if (ch == '\t') { rgba_t bg = lame_tab(window->view, at+1) ? LAMESPACE_BGRGBA : bgrgba; do { display_put(display, window->row + row, window->column + column++, ' ', fgrgba, bg); bg = bgrgba; } while (column % tabstop); return column; } if (ch < ' ' || ch == 0x7f || IS_FOLDED(ch)) { fgrgba = FOLDED_FGRGBA; bgrgba = FOLDED_BGRGBA; display_put(display, window->row + row, window->column + column++, IS_FOLDED(ch) ? '<' : '^', fgrgba, bgrgba); if (IS_FOLDED(ch)) ch = '>'; else if (ch == 0x7f) ch = '?'; else ch += '@'; } else if (ch == ' ') { if (bgrgba == window->bgrgba && lame_space(window->view, at + 1, tabstop-1 - column % tabstop)) bgrgba = LAMESPACE_BGRGBA; } else if (!IS_UNICODE(ch)) ch = ' ', bgrgba = BADCHAR_BGRGBA; else if (brackets && brack) for (; *brack; brack += 2) if (ch == brack[0] && (*brackets)++ & 1 || ch == brack[1] && --*brackets & 1) { fgrgba = BLUE_RGBA; break; } display_put(display, window->row + row, window->column + column++, ch, fgrgba, bgrgba); return column; } static Boolean_t needs_repainting(struct window *window) { struct view *view = window->view; position_t cursor = locus_get(view, CURSOR); position_t mark = locus_get(view, MARK); return window->repaint || view->text->dirties != window->last_dirties || window->last_cursor != cursor || window->last_mark != mark || window->last_mode != view->mode; } static void repainted(struct window *window, position_t cursor, position_t mark) { window->repaint = FALSE; window->last_dirties = window->view->text->dirties; window->last_cursor = cursor; window->last_mark = mark; window->last_mode = window->view->mode; } static void paint(struct window *window) { sposition_t at; int row, column; struct view *view = window->view; position_t cursor = locus_get(view, CURSOR); position_t mark = locus_get(view, MARK); sposition_t comment_end = -1; sposition_t string_end = -1; unsigned brackets = 1; Boolean_t keywords = !no_keywords && window == active_window && view->text->keywords; title(window); at = focus(window); if (keywords && view->text->comment_start) { sposition_t start = view->text->comment_start(view, at); if (start >= 0) comment_end = view->text->comment_end(view, start); } for (row = 0; row < window->rows; row++) { position_t limit = at + find_row_bytes(view, at, 0, window->columns); rgba_t fgrgba = window->fgrgba; Boolean_t look_for_keyword = keywords; position_t next; unsigned *brackets_ptr = &brackets; for (column = 0; at < limit; at = next) { Unicode_t ch = view_char(view, at, &next); if ((ch == '/' || ch == '-' || ch == '{') && at > comment_end && at > string_end && keywords && view->text->comment_end) comment_end = view->text->comment_end(view, at); if ((ch == '"' || ch == '\'') && at > comment_end && at > string_end && keywords && view->text->string_end) string_end = view->text->string_end(view, at); if (at <= comment_end) { fgrgba = COMMENT_FGRGBA; brackets_ptr = NULL; } else if (at <= string_end) { fgrgba = STRING_FGRGBA; brackets_ptr = NULL; } else if (!is_idch(ch) && ch != '#') { fgrgba = window->fgrgba; look_for_keyword = keywords; } else if (look_for_keyword && is_keyword(view, at)) fgrgba = KEYWORD_FGRGBA; else look_for_keyword = FALSE; column = paintch(window, ch, row, column, at, cursor, mark, brackets_ptr, fgrgba); if (at == comment_end || at == string_end) { fgrgba = window->fgrgba; brackets_ptr = &brackets; look_for_keyword = keywords; } } display_erase(display, window->row + row, window->column + column, 1, window->columns - column - 1); while (column < window->columns) display_put(display, window->row + row, window->column + column++, ' ', DEFAULT_FGRGBA, window->bgrgba); } repainted(window, cursor, mark); } void window_hint_deleting(struct window *window, position_t offset, size_t bytes) { struct view *view = window->view; position_t at = screen_start(view); unsigned row = 0, column; unsigned lines = 0, end_column; if (focus(window) != at || offset + bytes <= at) return; if (offset < at) { bytes -= at - offset; offset = at; } if (!bytes || view_char(view, offset, NULL) >= FOLD_START) return; column = find_column(&row, view, at, offset, 0); if (row >= window->rows) return; end_column = find_column(&lines, view, offset, offset + bytes, column); if (!lines) { unsigned old = find_row_bytes(view, offset, column, window->columns); unsigned new = find_row_bytes(view, offset + bytes, end_column, window->columns); if (new + bytes == old) { display_delete_chars(display, window->row + row, window->column + column, end_column - column, window->columns - column); return; } lines++; } if (row + lines >= window->rows) { lines = window->rows - row; end_column = 0; } display_delete_lines(display, window->row + row, window->column, lines, window->rows - row, window->columns); } void window_hint_inserted(struct window *window, position_t offset, size_t bytes) { struct view *view = window->view; position_t at = screen_start(view); position_t column, end_column; unsigned row = 0, lines = 0; if (focus(window) != at || offset + bytes <= at) return; if (offset < at) { bytes -= at - offset; offset = at; } if (!bytes || view_char(view, offset, NULL) >= FOLD_START) return; column = find_column(&row, view, at, offset, 0); if (row >= window->rows) return; end_column = find_column(&lines, view, offset, offset + bytes, column); if (!lines) { size_t old = find_row_bytes(view, offset + bytes, end_column, window->columns); size_t new = find_row_bytes(view, offset, column, window->columns); if (new == old + bytes) { display_insert_spaces(display, window->row + row, window->column + column, end_column - column, window->columns - column); return; } lines++; } if (row + lines >= window->rows) { lines = window->rows - row; end_column = 0; } display_insert_lines(display, window->row + row, window->column, lines, window->rows - row, window->columns); } void window_next(struct view *view) { if (view && view->window) activate(view->window->next ? view->window->next : window_list); } void window_index(int num) { struct window *window = window_list; while (--num > 0 && window->next) window = window->next; activate(window); } struct window *window_recenter(struct view *view) { struct window *window = view->window; position_t cursor = locus_get(view, CURSOR); position_t start, end; int row, rows, columns; if (!display) display = display_init(); display_get_geometry(display, &rows, &columns); if (rows != display_rows || columns != display_columns || !(window = view->window)) window = window_raise(view); start = find_row_start(window, cursor, find_line_start(view, cursor)); for (row = 0; (end = start) && row < window->rows >> 1; row += count_rows(window, start, end)) start = find_line_start(view, start-1); while(row-- > window->rows >> 1) start += find_row_bytes(view, start, 0, window->columns); new_start(view, start); return window; } static int page_overlap(struct window *window) { static int overlap_percent; int o; if (!overlap_percent) { const char *p = getenv("AOEUI_OVERLAP"); overlap_percent = 1; if (p) { overlap_percent = atoi(p); if (overlap_percent < 0) overlap_percent = 1; else if (overlap_percent > 100) overlap_percent = 100; } } o = overlap_percent * window->rows / 100; if (o >= window->rows) o = window->rows - 1; if (o < 1) o = 1; return o; } void window_page_up(struct view *view) { struct window *window = view->window; position_t start = screen_start(view); position_t end; int row; size_t bytes; if (start) { int overlap = page_overlap(window); for (row = 0; (end = start) && row + overlap < window->rows; row += count_rows(window, start, end)) start = find_line_start(view, start-1); while (row-- + overlap > window->rows) start += find_row_bytes(view, start, 0, window->columns); new_start(view, start); for (row = 0; row < window->rows-1; row++, start += bytes) if (!(bytes = find_row_bytes(view, start, 0, window->columns))) break; display_insert_lines(display, window->row, window->column, window->rows - overlap, window->rows, window->columns); } locus_set(view, CURSOR, start); } void window_page_down(struct view *view) { struct window *window = view->window; position_t start = screen_start(view); int row; size_t bytes; int overlap = page_overlap(window); for (row = 0; row + overlap < window->rows; row++, start += bytes) if (!(bytes = find_row_bytes(view, start, 0, window->columns))) break; display_delete_lines(display, window->row, window->column, window->rows - overlap, window->rows, window->columns); new_start(view, start); locus_set(view, CURSOR, start); } void window_beep(struct view *view) { if (display) display_beep(display); else fputc('\a', stderr); } static void window_colors(void) { int j; struct window *window, *w; static rgba_t colors[][2] = { { BLACK_RGBA, PALE_RGBA(WHITE_RGBA) }, { BLUE_RGBA, YELLOW_RGBA }, { BLUE_RGBA, PALE_RGBA(YELLOW_RGBA) }, { GREEN_RGBA, MAGENTA_RGBA }, { GREEN_RGBA, PALE_RGBA(MAGENTA_RGBA) }, { RED_RGBA, PALE_RGBA(CYAN_RGBA) }, { RED_RGBA, CYAN_RGBA }, { } }; if (active_window) { if (active_window->fgrgba != DEFAULT_FGRGBA) active_window->repaint = TRUE; active_window->fgrgba = DEFAULT_FGRGBA; active_window->bgrgba = DEFAULT_BGRGBA; } for (window = window_list; window; window = window->next) { if (window == active_window) continue; for (j = 0; colors[j][1]; j++) { for (w = window_list; w; w = w->next) if (w != window && w->bgrgba == colors[j][1] && adjacent(window, w)) break; if (!w) break; } if (window->bgrgba != colors[j][1]) { window->repaint = TRUE; window->fgrgba = colors[j][0]; window->bgrgba = colors[j][1]; } } } static void repaint(void) { struct window *window; window_colors(); for (window = window_list; window; window = window->next) if (needs_repainting(window)) paint(window); display_cursor(display, active_window->row + active_window->cursor_row, active_window->column + active_window->cursor_column); } Unicode_t window_getch(void) { Boolean_t block = FALSE; for (;;) { Unicode_t ch = display_getch(display, block); if (ch == ERROR_CHANGED) window_raise(window_current_view()); else if (ch != ERROR_EMPTY) return ch; repaint(); block = TRUE; } } unsigned window_columns(struct window *window) { return window ? window->columns : 80; } aoeui-1.7+20160302.git4e5dee9/window.h000066400000000000000000000017131322350162600167050ustar00rootroot00000000000000/* Copyright 2007, 2008 Peter Klausler. See COPYING for license. */ #ifndef WINDOW_H #define WINDOW_H /* Windows */ struct window *window_raise(struct view *); struct window *window_activate(struct view *); struct window *window_after(struct view *, struct view *, int vertical); struct window *window_below(struct view *, struct view *, unsigned rows); struct window *window_replace(struct view *, struct view *); void window_destroy(struct window *); void window_next(struct view *); void window_index(int); void window_hint_deleting(struct window *, position_t, size_t); void window_hint_inserted(struct window *, position_t, size_t); struct window *window_recenter(struct view *); void window_page_up(struct view *); void window_page_down(struct view *); void window_beep(struct view *); Unicode_t window_getch(void); struct view *window_current_view(void); unsigned window_columns(struct window *); void windows_end(void); void windows_end_display(void); #endif