diff --git a/AUTHORS b/AUTHORS index e69de29..6273b87 100644 --- a/AUTHORS +++ b/AUTHORS @@ -0,0 +1,144 @@ +(Original author based on manpage) +Chris D. Peterson, MIT X Consortium +commands.c +util.c +xedit.c +xedit.h + + +------------------------------------------------------------------------ +Paulo César Pereira de Andrade +xedit: +hook.c +hash.c +ispell.c +lisp.c +options.c +tags.c +util.h + +xedit lisp interface: +lisp/bytecode.c +lisp/bytecode.h +lisp/compile.c +lisp/core.c +lisp/core.h +lisp/debugger.c +lisp/debugger.h +lisp/format.c +lisp/format.h +lisp/internal.h +lisp/io.c +lisp/io.h +lisp/hash.c +lisp/hash.h +lisp/helper.c +lisp/helper.h +lisp/lisp.c +lisp/lisp.h +lisp/math.c +lisp/math.h +lisp/mathimp.c +lisp/package.c +lisp/package.h +lisp/pathname.c +lisp/pathname.h +lisp/private.h +lisp/read.c +lisp/read.h +lisp/regex.c +lisp/regex.h +lisp/require.c +lisp/require.h +lisp/stream.c +lisp/stream.h +lisp/string.c +lisp/string.h +lisp/struct.c +lisp/struct.h +lisp/time.c +lisp/time.h +lisp/write.c +lisp/write.h +lisp/xedit.c +lisp/xedit.h + +bignum math library: +lisp/mp/mp.h +lisp/mp/mpr.c +lisp/mp/mpi.c +lisp/mp/mp.c + +command line lisp interpreter: +lisp/lsp.c + +regex library: +lisp/re/re.c +lisp/re/re.h +lisp/re/reo.c +lisp/re/rep.h +lisp/re/rec.c +lisp/re/tests.c +lisp/re/tests.txt + +source files to test the lisp interpreter: +lisp/test/widgets.lsp +lisp/test/list.lsp +lisp/test/hello.lsp +lisp/test/regex.lsp +lisp/test/stream.lsp +lisp/test/math.lsp + +lisp binary modules: +lisp/modules/psql.c +lisp/modules/x11.c +lisp/modules/xaw.c +lisp/modules/xt.c + +lisp source modules: +lisp/modules/indent.lsp +lisp/modules/lisp.lsp +lisp/modules/syntax.lsp +lisp/modules/xedit.lsp + +lisp syntax highlight and indentation rules definitions: +lisp/modules/progmodes/c.lsp +lisp/modules/progmodes/html.lsp +lisp/modules/progmodes/imake.lsp +lisp/modules/progmodes/lisp.lsp +lisp/modules/progmodes/make.lsp +lisp/modules/progmodes/man.lsp +lisp/modules/progmodes/patch.lsp +lisp/modules/progmodes/rpm.lsp +lisp/modules/progmodes/sgml.lsp +lisp/modules/progmodes/sh.lsp +lisp/modules/progmodes/xconf.lsp +lisp/modules/progmodes/xlog.lsp +lisp/modules/progmodes/xrdb.lsp + + +------------------------------------------------------------------------ +Files required when compiling with xprint support: + +(Based on Copyright notice) +Roland Mainz +xedit: +print.c +print.h +printdialog.c +printdialog.h +printdialogprivates.h + + +------------------------------------------------------------------------ +Files required for systems that don't have the required calls in the +standard libraries: + +(Based on Copyright notice) +Jan-Simon Pendry +realpath.c + +(No author information, using CVS tag) +David Dawes +strcasecmp.c + diff --git a/COPYING b/COPYING index 7f33cbf..9771c04 100644 --- a/COPYING +++ b/COPYING @@ -1,12 +1,104 @@ -This is a stub file. This package has not yet had its complete licensing -information compiled. Please see the individual source files for details on -your rights to use and modify this software. +/* + * COPYRIGHT 1987 + * DIGITAL EQUIPMENT CORPORATION + * MAYNARD, MASSACHUSETTS + * ALL RIGHTS RESERVED. + * + * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND + * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION. + * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR + * ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. + * + * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS, + * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT + * SET FORTH ABOVE. + * + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Digital Equipment Corporation not be + * used in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. + */ -Please submit updated COPYING files to the Xorg bugzilla: -https://bugs.freedesktop.org/enter_bug.cgi?product=xorg +/* + * +Copyright 2004 Roland Mainz -All licensing questions regarding this software should be directed at the -Xorg mailing list: +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. -http://lists.freedesktop.org/mailman/listinfo/xorg +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + * + */ + + +/* + * Copyright (c) 1999-2002 by The XFree86 Project, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of the XFree86 Project shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from the + * XFree86 Project. + * + */ + +/* + * Copyright © 2007 Paulo César Pereira de Andrade + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ diff --git a/ChangeLog b/ChangeLog index 690509d..584755a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,146 @@ +2007-11-04 Paulo César Pereira de Andrade + + * AUTHORS: + Full listing of authors for xedit source files. + + * COPYING: + Lists all copyrights in source files. + + * INSTALL: + Instructs user for most important options, and also refers to + automake INSTALL file for a more complete overview of available + options. + + * Makefile.am: + Handles configure.ac changes to compile realpath.c and strcasecmp.c + if not already available in the standard libraries. + Also handles the changes to make xprint support defaults to disabled. + + * README: + Long list including description of keyboard shortcuts, mouse commands, + and examples of how to use several obscure features. + Points to other documentation files in the source tree. + Describes some of the lisp interpreter/bytecode compiler features. + Small tutorial on writing syntax highlight and indentation rules + for different file formats. + Some basic information on how to use the tags features, and some + comments for possible improvements. + + * Xedit-noxprint.ad: + Adds more help strings to the hints resource. + Add tags interface shortcut M-. action (usually Alt + Dot) + Add actions to scroll the text window using the mouse wheel. + Updates a few default ispell interface resources. + + * Xedit-xprint.ad: + Updates a few default ispell interface resources. + + * commands.c: + Reenable non multibyte text buffers. + Add a new extern function to make easier to load a filename in a new + textbuffer without using static functions. + Fix wrong assumption that the first 2 entries in a directory are + . and .. + + * configure.ac: + Don't use the XAW_CHECK_XPRINT_SUPPORT macro, instead directly + handle the --enable-xprint option, defaulting to --disable-xprint. + Add check for realpath and strcasecmp on standard libraries. + + * hook.c: + Use only one hash table implementation. Converted code. + Reenable the line-edit action to search and replace text using + regex expressions. + Fix some escaped character conversion bugs. + Escape characters in the regex pattern also, not only the text + to substitute a match. This also allows regexs that can match + nul bytes. + + * ispell.c: + Use only one hash table implementation. Converted code. + Fixed ispell interface to be usable. + Default values should use aspell and match a standard Linux install. + words, egrep, aspell, and aspell-us can/should be required packages. + Properly handle the ispell program exiting prematurely. + Add a fallback empty entry to the dictionary list, so that the + ispell program can choose the dictionary based on the environment. + + * lisp/bytecode.c: + Use only one hash table implementation. Converted code. + + * lisp/core.c: + Use only one hash table implementation. Converted code. + + * lisp/hash.c: + Remove "dumb" check that would only hash the first 32 bytes of a + string. This makes hashing of pathnames clash all the time. + Lisp object hash table has not been converted to the basic one, + still there are 2 hash table implementations in xedit, but it + is better than the at least 5 other implementations. + + * lisp/helper.c: + Use only one hash table implementation. Converted code. + + * lisp/internal.h: + Use only one hash table implementation. Converted code. + + * lisp/lisp.c: + Use only one hash table implementation. Converted code. + + * lisp/math.c: + Use only one hash table implementation. Converted code. + + * lisp/package.c: + Use only one hash table implementation. Converted code. + + * lisp/private.h: + Use only one hash table implementation. Converted code. + + * lisp/read.c: + Use only one hash table implementation. Converted code. + + * lisp/write.c: + Use only one hash table implementation. Converted code. + + * tags.c: + New file. Adds support for searching symbol definitions generated + by the command "ctags -R". The interface properly handles multiple + definitions. + + util.c: + Revert change to XeditPrintf. Before all code that interfaces it + is modified to know the first parameter is a printf format string, + it is better to keep the original implementation. Filenames with + % in the name are one example of a condition that can make it read + or write to bad addresses. + Fix the ResolveName function. It's interaction with realpath was + buggy, i.e. when compiled without optimization was working properly, + but when compiled with -O2 was generating wrong code. + Probably this was the realpath.c in the sources, but this fix + works with that file, and should work with any other realpath + implementation. + + util.h: + Definitions for data types and functions for the new hash table + implementation. + File called util.h in case other similar interfaces are added, + have a common place. + + xedit.c: + Initialize the tags interface, and check the related resources, + that can specify to not use tags. + Use the INCLUDE_XPRINT_SUPPORT to properly initialize xedit based + on it. + + xedit.h: + Add a few macros that were repeated in sources. + Adds typedef for main tags interface structure and tags resources. + Adds function definitions for the tags interface, and proper + modification to reenable old behavior. + + xedit.man: + Document tags interface related resources. + 2006-04-26 Adam Jackson * configure.ac: diff --git a/INSTALL b/INSTALL index e69de29..0405677 100644 --- a/INSTALL +++ b/INSTALL @@ -0,0 +1,32 @@ + Please refer to /usr/share/automake-1.10/INSTALL, where automake may be + a newer version for more detailed options. + + The two most important options you will need to build from sources are: + +--prefix=/pathname + + The default prefix is /usr/local, but if you are building packages, + probably you will prefer /usr. + +--enable-xprint + + Use it if you want to compile with xprint support. Note that this will + disable most of the other xedit features, like syntax highlight, regex + search, etc. Those features are outdated and use one byte Occidental + encodings. + + + The easiest method of compiling is probably to run: + +./autogen.sh --prefix=/usr + + + Depending on the amount of patching, you may need to run + +$ autoreconf -ifs + + or even worse :-) + +$ make distclean + + after having a Makefile, and restart over... diff --git a/Makefile.am b/Makefile.am index e2acfbb..3a8b7a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,7 +40,7 @@ libmp_a_SOURCES = \ # liblisp.a # -liblisp_a_CFLAGS = -I$(top_srcdir)/lisp/re -I$(top_srcdir)/lisp/mp -DLISP -DLISPDIR=\"$(xedit_lispdir)\" $(XEDIT_CFLAGS) +liblisp_a_CFLAGS = -I$(top_srcdir)/lisp/re -I$(top_srcdir)/lisp/mp -DLISP -DLISPDIR=\"$(xedit_lispdir)\" $(PKGDEPS_CFLAGS) liblisp_a_LIBADD = libmp.a liblisp_a_SOURCES = \ lisp/bytecode.c \ @@ -104,22 +104,30 @@ libre_a_SOURCES = \ # xedit # -xedit_CFLAGS = $(XEDIT_CFLAGS) -I$(top_srcdir)/lisp/re -D_BSD_SOURCE -DXEDIT -xedit_LDADD = libre.a liblisp.a libmp.a $(XEDIT_LIBS) -lm +xedit_CFLAGS = $(PKGDEPS_CFLAGS) -I$(top_srcdir)/lisp/re -D_BSD_SOURCE -DXEDIT +xedit_LDADD = libre.a liblisp.a libmp.a $(PKGDEPS_LIBS) -lm xedit_SOURCES = \ commands.c \ + hash.c \ hook.c \ ispell.c \ lisp.c \ options.c \ - realpath.c \ - strcasecmp.c \ + tags.c \ util.c \ xedit.c \ xedit.h -if XAW_USE_XPRINT +if NEED_REALPATH +xedit_SOURCES += realpath.c +endif + +if NEED_STRCASECMP +xedit_SOURCES += strcasecmp.c +endif + +if USE_XPRINT xedit_CFLAGS += -DINCLUDE_XPRINT_SUPPORT xedit_SOURCES += \ @@ -136,7 +144,7 @@ APPDEFAULTFILES = \ Xedit-color \ Xedit -if XAW_USE_XPRINT +if USE_XPRINT Xedit.ad: cp $(top_srcdir)/Xedit-xprint.ad Xedit.ad else diff --git a/README b/README index e69de29..9e20763 100644 --- a/README +++ b/README @@ -0,0 +1,604 @@ + Xedit is a simple text editor for X. + + Please check xedit(1) for information about resources and configurable + options. + + The authors file should list the original authors of all files, but + unfortunately most collaborators and people that changed portions of the + code is not listed. + + + List of default keyboard commands. + Uppercase letters means both, uppercase and lowercase. + Lowercase letter usually means Shift key is not pressed. + C- Means Control key. + M- Means Meta key. + Usually Alt key has the same effect. + S- Means Shift key. + + C-A Move cursor to beginning of line. + C-B or Left + Move cursor backwards one character. + C-C Insert CUT_BUFFER0 selection at cursor position. + C-D Delete next character. + C-E Move cursor to end of line. + C-F or Right + Move cursor forwards one character. + C-G Keyboard reset. + Use it to switch direction in the Undo stack, i.e. switch between + Undo and Redo. + It can also be used to stop the lisp interpreter if it is executing + code. + C-H Deletes character before cursor. + C-J Adds a newline and indent. + If the current buffer is the *scratch* buffer, it will work + like C-X,C-E, but print the lisp output directly to the scratch + buffer. Examples: + (list 1 2 3) C-J will print (1 2 3) and move the cursor to + the next line. + C-K Deletes text from cursor position to end of line. + C-L Redraw text window. + C-M Adds a newline. + C-N or Down + Move cursor to next line. + C-O Adds one newline at cursor position without moving cursor. + C-P or Up + Move cursor to previous line. + C-R Opens search dialog, searching backwards. + C-S Opens search dialog, searching forwards. + C-T Transpose characters. + C-U Starts multiply mode. + Multiply mode means that most keyboard commands will be repeated + by its parameter. After pressing C-U type a number, that can be + negative. Examples: + C-U 10 C-F will move the cursor 10 characters forwards + C-U -10 C-F will move the cursor 10 characters backwards + If no number is specified, or if the number zero is specified, + the default value is 4. + C-V or PageDown + Move cursor to next page. + C-W Kills current selection. + C-W is also useful when there is no active selection, to return to + the previous position the left mouse was clicked in the text window. + C-Y Inserts SECONDARY selection. + This usually means text delete with commands other than by just + pressing Backspace or Delete (that can be multiplied) + C-Z Scroll one line up. + M-B or C-Left + Move cursor backwards one word. + C-Left only moves over alphabetic or numeric characters + M-C Capitalize word under cursor position, or next word. + M-F or C-Right + Move cursor forwards one word. + C-Right only moves over alphabetic or numeric characters. + M-I Opens a dialog prompting for a file name to be inserted at the + cursor position. + (This isn't very user friendly, this is a cavemen interface, you will + love it, you will never use it). + M-K Kills text from cursor position to end of paragraph. + M-L Downcase word at cursor position or next word. + M-Q Format paragraph. + Xedit interfaces Xaw code that allows several types of text + formatting. To use it, you need to Select "Auto Fill" in the + "Edit Menu" and select values for "Break Columns". After that, + Select "Edit Menu"->"Justification" for the available options. + Text typed will usually be automatically corrected, but if you + need to change text in the middle of a previous line, use M-Q + to reformat, and C-_ to undo if required. + Examples of text: + + This text with align + left and break + columns at 20 and + 40. + + This text with align + right and break + columns at 25 and + 45. Remember that + Auto Fill must be + selected. And it may + be required to press + M-Q when going back + to edit previous + lines. + + This text with align center + and break columns at 40 and + 70. A good tip is to make sure + there are empty lines before + and after the aligned text, so + that M-Q will not format more + text than it should. + + This text with full align, + note that it will spread the + words to fully fill the + configured break columns, in + this case 15 and 45. Also note + that it doesn't remove any + extra indentation from the + first line. This may be + considered a bug or feature, + but it doesn't respect 2 + spaces after an '.' dot. And + it doesn't align the last line + of text. + + This text with Auto Fill disabled. When Auto Fill is disabled, tabs are +usually preserved, like this, and this. But it +will basically format the text to not be larger than the text widget screen +width and. But it the screen is scrolled horizontally, it will not work as +expected, the advantage over the above interface is that is considers font +characters width, while the other interface assumes fixed width characters. + + Notice that "auto formatting" with Auto Fill mode is done when + pressing enter, but information isn't saved per paragraph, so + if values of align or break columns are changed, trying to edit + a text block with different configuration will not give the expected + results. + Also notice that lines starting with non printable characters aren't + automatically formated. This is helpful for some editing modes + where symbols can be used before lines in a paragraph. If you don't + need this feature, usually pressing Space or Tab is enough to put the + cursor at the proper position. + M-U Upcase word at cursor position or next word. + M-V or PageUp + Move cursor to previous page. + M-Y Kill ring yank feature. Basically it will circulate over all + text that has been cut in the current session. Keep pressing it + until happy :-) + M-Z Scrolls one line down. + M-D Kill word at cursor position or next word. + S-M-D Delete word at cursor position or next word. + Does not go to the kill ring. + M-H or M-Delete or M-Backspace. + Kill word at cursor position or previous word. + S-M-H or S-M-Delete or S-M-Backspace + Delete word at cursor position or previous word. + Does not go to the kill ring. + M-. Find definition/declaration of symbol string of selected text, and/or + finds next tag when more than one definition exists for the symbol. + M-< or Home + Move cursor to beginning of file. + M-> or End + Move cursor to end of file. + M-] or C-Down + Move cursor to next paragraph. + M-[ or C-Up + Move cursor to previous paragraph. + C-_ or C-X,U + Undo. + If enabled in the given textwidget. Not enabled by default in the + message window and filename window, or any of the other text widgets + in the several available dialogs. + C-\ or C-Kanji + Reconnect Input Method. + In international mode (probably broken interface). + S-Insert + Insert PRIMARY selection from CUT_BUFFER0. + C-Q + Followed by any character, will insert the next typed character + useful to insert control characters. For example C-Q,C-L will + insert ^L at the cursor position. + LeftMouseButton + When pressed marks the start of a PRIMARY selection at CUTBUFFER0 + When moved while pressed extends the selection. + MiddleMouseButton + When pressed, inserts the PRIMARY selection at CUTBUFFER0 at + the cursor position. + RightMouseButton + Can be used to adjust a selection done with LeftMouseButton. + C-A,Tab + If the loaded file has a indentation rules file, C or Lisp, xedit + will reindent the line. Also, entering a fresh character on a + newline should be enough to move the cursor to the proper position. + To override it, you may need to use the C-U to multiply the action, + as it will only indent if only one character was added. + C-X,C-C or (Pressing the Quit button) + Exits xedit. + If there are unsaved files, a message will be printed asking to + "exit" again, or save the files. + C-X,C-E + Execute lisp expression before the cursor position. + C-X,C-F + Changes keyboard input focus to the filename text input window. + In this mode, pressing Tab will try to complete the filename + being typed. + When more than one match exists, pressing Tab again will display + the directory navigation window. + The initial search path usually is the basename of the file loaded + in the current textwindow, or currently directory from where + xedit was started if the *scratch* is the current "buffer". + The character ~ can be used as a shortcut for the home directory + and ~username will be replaced by the home directory of "username" + if it exists. + C-X,C-S or (Pressing the Save button) + Saves the file in the current text window. + C-X,Tab + Indents the current paragraph. + Use the C-U modifier to specify the number of spaces to insert + or remove if the C-U parameter is negative. + C-X,0 + Deletes the current window. + The file being edited is not unloaded. + C-X,1 + Deletes the other window. + The file being edited is not unloaded. + C-X,2 + Splits vertically the current window. + C-X,3 + Splits vertically the current window. + C-X,b + Switch the contents of the current window to the next file in + the list of loaded files. + C-X,d + Displays the directory listing window. + In this window, it is possible to navigate in the file system + to choose a file to edit. + Usually, this is the same as pressing C-X,C-F,Tab + See C-X,C-F for more information. + C-X,k + Unloads the file being edited. + If the file has not been saved, a message will be displayed, + asking to either press C-X,k again, or save the file + The *scratch* buffer cannot be killed and will always + prints a warning when exiting xedit. This is the expected behavior. + C-X,o + Switch input focus to the other window, making it the current one + If the editor doesn't have splited windows, this command is ignored. + Insert + Switches between insert and overwrite mode. + In overwrite mode text is typed over existing file contents, + only extending the file contents when typing at the end of + a line (or file). + Escape + Enters the line edit mode. + In this mode it is possible to use regex expressions to search and + replace the contents of the file loaded in the current text window. + Refer to the xedit(1) manpage for more information of the regex + expression format. + Note that the regex used by xedit isn't IEEE Std 1003.2 compliant, + and not compliant with most regex implementations in that it doesn't + support some complex search patterns, usually involving patterns + like ((.*)+)? that may require too much restarts or have several + correct interpretations for multiple matches. Also, it always does + minimal matching, and it is not configurable like in pcre, example: + searching "a1a1a1" with the pattern "(.*)\d" will find "a1" and + not "a1a1a1" as could be expected. + Please refer to lisp/re/README for more information on the supported + regex expressions. + C-LeftMouseButton + Displays the "File Menu" from where it is possible to select a + new file to be edited in the current window. + C-MiddleMouseButton + Displays the "Edit Menu" that usually has the following options: + +------------------+-------------+ + | Wrapping -> | Never | + | | Line | + | | Word | + | Auto Fill +-------------+ + | +-------------+ + | Justification -> | Left | + | | Right | + | | Center | + | | Full | + | +-------------+ + | Break Columns... | + | +-------------+ + | Scrollbars -> | Vertical | + | | Horizontal | + | +-------------+ + | +-------------+ + | Edit Mode -> | Plain/None | + | | C/C++ | + | | Lisp/Scheme | + | | X imake | + | | Makefile | + | | Unix shell | + | | SGML | + | | HTML | + | | Man page | + | | X resource | + | | XF86Config | + | | RPM spec | + | | XFree86 log | + | | Patch file | + +------------------+-------------+ + "Wrapping" is disabled if "Auto Fill" is enabled. + "Justification" and "Break Columns..." are enabled if "Auto Fill" + is also enabled. + "Edit Mode" lists the available syntax highlight and indentation + rules defined. + Note that most of these options don't work in "international" mode. + Several xedit interfaces works only with 8 bits encodings, and + doesn't properly handle UTF-8. + C-RightMouseButton + Displays the "Option Menu". + Currently the only option is an Ispell frontend. + FourthMouseButton (usually also moving up the mouse wheel) + Scroll one line down + FifthMouseButton (usually also moving down the mouse wheel) + Scroll one line up + + + + There is also some documentation in lisp/README, lisp/TODO, + lisp/re/README, lisp/re/tests.txt, and comments/justifications/wishlists for + the cases the interpreter fail to give the proper result for a given test. + + The lisp interpreter implements most of a standard COMMON LISP + environment, but the compiler generates only byte code and not everything + can be compiled, in those cases the interpreter does the work. Examples are + constructs involving UNWIND-PROTECT or any kind of jump outside of the + current function body. + + For more information please consult any COMMON LISP documentation or + tutorial. + + + + Simple tutorial on writing a "foolang" syntax highlight mode for xedit. + + Create a file called foolang.lsp + Add: + -- + (require "syntax") + (require "indent") + (in-package "XEDIT") + -- + to foolang.lsp + Check the available files if you want a custom property, or to know about + the available ones. One example is: + -- + (defsynprop *prop-foolang* + "foolang" + :font "fixed" + :foreground "rgb:a/b/c" + :background "rgb:1/2/3" + :underline t + :overstrike t) + -- + Check lisp/modules/xedit.lsp for the other options, like subscript, + superscript and/or combining XLFD properties. + Create a syntax definition for foolang. Check the definition of defsyntax in + lisp/modules/syntax.lsp for more details. One example for foolang is: + -- + (defsyntax *foolang-mode* :foolang nil nil nil + ;; use the sample property created for foolang whenever the string + ;; foolang is found at the toplevel + (syntoken "\\" :property *prop-foolang*) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; process C style comments + (syntoken "/*" :nospec t :begin :comment :contained t) + ;; contained is an option to not "export" this definition to child + ;; tables + + (syntable :comment *prop-comment* #'default-indent + ;; #'default-indent means a very simple indent that follows indentation + ;; of previous line. Use nil for no indentation + + ;; Don't start a new table, instead flag as an error if nested + (syntoken "/*" :nospec t :property *prop-error*) + ;; :nospec t sets the RE_NOSPEC flag for the regex, i.e. searches + ;; a literal string, and * will not be a special character + + (syntoken "XXX|TODO|FIXME" :property *prop-annotation*) + ;; just to make it easier to flag some important comment + + (syntoken "*/" :nospec t :switch -1) + ;; The :switch argument is the number of tables to "pop", in + ;; this case, we are at table :comment, and :switch -1 returns + ;; to table :foolang, that is the "root" table + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Process lisp comments that can nest + (syntable :lisp-comment *prop-comment* nil + + ;; Start a comment, possibly nested + (syntoken "#|" :nospec t :begin :lisp-comment) + + ;; Returns to previous comment in stack or to main :foolang table + (syntoken "|#" :nospec t :switch -1) + + ;; For easier flagging important comments + (syntoken "XXX|FIXME|TODO" :property *prop-annotation*) + ) + + ;; This is usually in the end of the list, but can be anywhere, + ;; just that if it isn't at the end, conflicting rules are resolved + ;; by declaration order + (synaugment :lisp-comment) + ;; Adds the :lisp-comment table to :foolang table + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Lisp like strings that don't need a \ at the end of the line + (syntable :lisp-string *prop-string* nil + ;; ignore a escaped " in the middle of the string + (syntoken "\\\\.") + ;; Note that no options are used, just keep using the current + ;; property. Unfortunately, backslashes must be escaped twice. + ;; Once for the lisp reader and once for the regex compiler. + + (syntoken "\"" :nospec: t :switch -1) + ;; :nospec is used just to create a faster regex. switch -1 + ;; returns to the previous syntax table. It isn't an error to + ;; try to go down the "root" table, but if that happens, it + ;; probably means either wrong syntax definition of malformed input. + ) + + (synaugment :lisp-string) + ;; Adds the :lisp-string table to :foolang table + ;; Note that since there isn't a rule to start a string in the + ;; :lisp-string table, it cannot nest, maybe because the ending + ;; character is the starting character ? :-)) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; C style string rules + (syntable :string *prop-string* nil + ;; Ignore escaped characters + (syntoken "\\\\.") + + ;; Match, most, printf arguments. + (syntoken "%%|%([+-]?\\d+)?(l?[deEfgiouxX]|[cdeEfgiopsuxX])" + :property *prop-format*) + ;; Just for fun. This makes easier to see printf formats in strings + ;; *prop-format* is *prop-string* with the :underline option + + (syntoken "\\\\$") + ;; a backslash extends the string to the next line + + ;; A nonscaped " inside a string finishes it, since this table doesn't + ;; have sub tables, and cannot nest, should return to :foolang table + ;; from here + (syntoken "\"" :nospec t :switch -1) + + ;; This token rule starts a new table called :error because end of line + ;; has been matched. Note that it is defined last, so that a line + ;; ending with " or \ will be processed first. + (syntoken ".?$" :begin :error) + + (synaugment :string) + ;; Adds the :string table to :foolang table + + ;; This table is used by :string, but could be shared for other patterns + ;; like characters constants, etc. + ;; It uses :switch -2 because it is started inside the :string table, + ;; but it is also a table, so, pops two tables from the table stack + (syntable :error *prop-error* nil + (syntoken "^.*$" :switch -2) + ) + ) + -- + + Indentation rules are significantly more complex. I suggest looking at + lisp/modules/indent.lsp for the macros and function definitions; + and lisp/modules/progmodes/lisp.lsp and lisp/modules/progmodes/c.lsp for two + sample implementations of indentation for Lisp/Scheme and C/C++ + respectively. + + Note also that indentation is parsed backwards, what can cause some + confusion, and make "visualization" of order or precedence of evaluation for + rules harder to understand. + + A simple indentation rules definition for foolang could be: + + -- + (defindent *foolang-mode-indent* :foolang + ;; This must be the first token and usually the only token matching + ;; BOL (Beginning of Line) + (indtoken "^\\s*" :indent + :code (or *offset* (setq *offset* (+ *ind-offset* *ind-length*)))) + ;; the keyword :indent is a pattern put on a list when there is + ;; a match, so that later patterns can be "reduced" by some other + ;; rule, i.e.: (:indent :indent) could be reduced to (:indent) + + (indtoken "//.*$" nil) + ;; C++ style comment. Returning nil instead of a token basically + ;; ignores the token, as it should not enter the reduction pattern + ;; list + + ;; Sample C comment pattern + (indtoken "*/" :ccomment :nospec t :begin :comment) + ;; Note that the indaugment macro doesn't need to be used, and actually + ;; would be an error. In this example, the indentation compiler checks + ;; the :begin :comment and handles it internally, as it already needs + ;; to check for things like typos, unreachable labels, detectable + ;; non resolving rules, etc. There is runtime check also, so it should + ;; never enter an infinite loop when trying to resolve reduction rules. + ;; Check lisp/modules/indent.lsp:(compile-indent-table) for the + ;; implementation and more comments. + + ;; Indentation rules also have stacked tables + (indtable :comment + (indtoken "/*" :ocomment :nospec t :switch -1)) + ;; Note that the name is :ocomment (open comment), but that the + ;; the table is finished when matching the open comment pattern + + ;; A simple initialization of a variable used by the indentation rules + (indinit (parens 0)) + ;; This variable can be declared anywhere in the body of defindent, + ;; It will be properly moved to a "variables declaration section" + ;; when expanding and compiling the table. + + (indtoken "(" :oparen :nospec t :code (incf parens)) + (indtoken ")" :cparen :nospec t :code (decf parens)) + ;; These two tokes add the patterns :oparen and :cparen to the + ;; "pattern list", and also have code to remember the balancing + ;; of parenthesis. + + + ;; One of the simplest reduction rules :-) + (indreduce nil + t + ((:comment))) + ;; Once the boundings of a comment are found, just ignore it, like + ;; what was done with the // pattern, but in that case, the boundings + ;; were readily available. + ;; The t (True) parameter means that this rule is always evaluated, + ;; but conditional code may be used, and implicit code may be added + ;; to the end of the indreduce macro. + ;; Since it is a macro, code can be compiled to optimized bytecode + ;; after the macro is expanded. + + (indinit (indent 0)) + ;; Note that there is a special *indent* variable that should hold the + ;; proper indentation value, but it may be required to "overwrite" + ;; without forgetting that value, for things like: + ;; + ;; foo(bar(baz(blah + ;; ^ ^ + ;; | | + ;; indent | + ;; effective indentation to be used + ;; + ;; where it is desirable to align the code in the next line with the + ;; last open parenthesis. + + + Since the interface is, unfortunately, complex enough to not expect + casual users to have something like a $HOME/.xedit file, if you want to add + new modes, please check lisp/modules/xedit.lsp:*auto-modes* + + There is some documentation about the variable. It should be possible to + change/update the variable from the lisp interface in the *scratch* buffer, + but for safety, it is suggested that if you add new rules, you should + restart xedit after it. Also note that there is compiled code in + lisp/xedit.c that expects that variable to follow an specific format. + + You may notice that several .lsp files aren't "properly indented"; this + is because the lisp indentation rules file was made long after most of the + other files were done, and it was considered a bad practice to gratuitously + reindent all files. + + + + At the time of this writing, the ispell interface should be again + functional, but it may be required to use some old ispell program to handle + non utf8 text. But it should work properly for the english language, and in + international mode, should work with any language. But there are problems in + international mode not fixed. + + I (Paulo César) considered several times to extend the normal + textwidget to handle utf8, but this is probably a lot of work/time badly + spent and I prefer to work on other personal projects, but still xedit is + the editor I use for programming. The XPRINT support unfortunately is broken + due to it. Note that xedit, like pretty much any other Xaw application can + be linked against Xaw with XPRINT support. In the case of xedit, a flag + could be used, like the "international" variable to, at runtime, disable all + the related code, if required. + + Also at the time of this writing, the tags interface was added, as well + as several other bug fixes. The tags interface is documented in the manpage, + but the action shortcut may not be clear. The default shortcut is M-. . + There is no support for more than one tags file, or changing the tags file + at runtime. But the code should be easy to adapt for this case, as all + related functions receive pointers to the appropriate structures. One option + could be to descend from the directory of the loaded file searching for a + tags file, and then associate the file with the tags definition, if it isn't + already loaded. Shouldn't be hard to implement. For "inter project" files, + it should be better to have one xedit window per "project", i.e. searching + the definition of something like a libc symbol should probably be done in + another xedit window. diff --git a/Xedit-noxprint.ad b/Xedit-noxprint.ad index 16334a3..b626bf3 100644 --- a/Xedit-noxprint.ad +++ b/Xedit-noxprint.ad @@ -5,7 +5,7 @@ *enableBackups: True *backupNameSuffix: ~ *changedBitmap: xlogo11 -*international: True +*international: False *hints:\ Use Control-S and Control-R to Search.\n\ @@ -39,7 +39,9 @@ Use Control-X,k to close file being edited.\n\ Use Control-X,o to switch to another splitted window.\n\ Use Control-X,u to undo. Control-G to switch between Undo and Redo.\n\ Use Insert to toggle Overwrite mode.\n\ -Use Control-G to interrupt the lisp subprocess +Use Control-G to interrupt the lisp subprocess.\n\ +Use Escape to enter or leave regex search and replace mode.\n\ +Use Alt-. to search tags for the selected symbol or find the next match. *formWindow*defaultDistance: 2 *formWindow.?.borderWidth: 0 @@ -201,6 +203,7 @@ mI: no-op(r) X,!u:undo()\n\ G: xedit-keyboard-reset()\n\ J: xedit-print-lisp-eval()\n\ +:m.: tags()\n\ Tab: insert-char()\n\ !l @Num_Lockb:insert-char()\n\ !l b: insert-char()\n\ @@ -235,7 +238,9 @@ c l @Num_Lock:xedit-focus() popup-menu(optionsMenu)\n\ c @Num_Lock:xedit-focus() popup-menu(optionsMenu)\n\ c l:xedit-focus() popup-menu(optionsMenu)\n\ c: xedit-focus() popup-menu(optionsMenu)\n\ -: xedit-focus() select-start() +: xedit-focus() select-start()\n\ +: scroll-one-line-down()\n\ +: scroll-one-line-up() *filename.?.pieceSize: 256 *filename.translations: #override \ @@ -257,7 +262,8 @@ cG: cancel-find-file()\n\ !*ispell.dictionary: br !*ispell.dictionaries: br american americanmed+ english !*ispell*br.wordChars: áéíóúçÁÉÍÓÚÇàÀâêôÂÊÔüÜãõÃÕ- -!*ispell.ispellCommand: /usr/local/bin/ispell -B -m +*ispell.ispellCommand: /usr/bin/aspell -B -m +*ispell.lookCommand: /bin/egrep !*ispell*text.skipLines: .# *ispell.geometry: 0x0 diff --git a/Xedit-xprint.ad b/Xedit-xprint.ad index cb8445f..88830c8 100644 --- a/Xedit-xprint.ad +++ b/Xedit-xprint.ad @@ -262,6 +262,8 @@ cG: cancel-find-file()\n\ !*ispell.dictionaries: br american americanmed+ english !*ispell*br.wordChars: áéíóúçÁÉÍÓÚÇàÀâêôÂÊÔüÜãõÃÕ- !*ispell.ispellCommand: /usr/local/bin/ispell -B -m +*ispell.ispellCommand: /usr/bin/aspell -B -m +*ispell.lookCommand: /bin/egrep !*ispell*text.skipLines: .# *ispell.geometry: 0x0 diff --git a/commands.c b/commands.c index 7fa14d1..ffe98ad 100644 --- a/commands.c +++ b/commands.c @@ -357,8 +357,9 @@ DoSave(Widget w, XtPointer client_data, XtPointer call_data) XtRemoveCallback(scratch, XtNcallback, SourceChanged, (XtPointer)item); item->source = scratch = - XtVaCreateWidget("textSource", - multiSrcObjectClass, + XtVaCreateWidget("textSource", international ? + multiSrcObjectClass : + asciiSrcObjectClass, topwindow, XtNtype, XawAsciiFile, XtNeditType, XawtextEdit, @@ -401,6 +402,12 @@ DoLoad(Widget w, XtPointer client_data, XtPointer call_data) } } +Bool +LoadFileInTextwindow(char *name, char *resolved_name) +{ + return (ReallyDoLoad(name, resolved_name)); +} + static Bool ReallyDoLoad(char *name, char *filename) { @@ -495,8 +502,9 @@ ReallyDoLoad(char *name, char *filename) XtSetArg(args[num_args], XtNstring, NULL); num_args++; } - source = XtVaCreateWidget("textSource", - multiSrcObjectClass, + source = XtVaCreateWidget("textSource", international ? + multiSrcObjectClass : + asciiSrcObjectClass, topwindow, XtNtype, XawAsciiFile, XtNeditType, XawtextEdit, @@ -935,11 +943,12 @@ FileCompletion(Widget w, XEvent *event, String *params, Cardinal *num_params) mlen = 0; match[0] = '\0'; - (void)readdir(dir); /* "." */ - (void)readdir(dir); /* ".." */ while ((ent = readdir(dir)) != NULL) { unsigned d_namlen = strlen(ent->d_name); + if (strncmp(ent->d_name, ".", 1) == 0 || + strncmp(ent->d_name, "..", 2) == 0) + continue; if (d_namlen >= len && strncmp(ent->d_name, file_name, len) == 0) { char *tmp = &(ent->d_name[len]), *mat = match; struct stat st; diff --git a/configure.ac b/configure.ac index c421a79..787154e 100644 --- a/configure.ac +++ b/configure.ac @@ -29,20 +29,29 @@ AM_MAINTAINER_MODE AM_CONFIG_HEADER(config.h) AC_PROG_CC +AM_PROG_CC_C_O AC_PROG_INSTALL AC_PROG_RANLIB - -# Checks for pkg-config packages -XAW_CHECK_XPRINT_SUPPORT(XEDIT) -if test "x$xaw_use_xprint" = "xyes" ; then - PKG_CHECK_MODULES(XPRINT_UTIL, xprintutil xp) - - XEDIT_CFLAGS="$XEDIT_CFLAGS $XPRINT_UTIL_CFLAGS" - XEDIT_LIBS="$XEDIT_LIBS $XPRINT_UTIL_LIBS" +PKG_PROG_PKG_CONFIG + +AC_ARG_ENABLE(xprint, + AS_HELP_STRING([--enable-xprint], + [Compile with xprint support (default: disabled)]), + [enable_xprint=$enableval], [enable_xprint=no]) +AM_CONDITIONAL(USE_XPRINT, test x$enable_xprint = xyes) +if test x$enable_xprint = xyes; then + PKG_CHECK_MODULES(PKGDEPS, xprintutil xp xaw8) +else + PKG_CHECK_MODULES(PKGDEPS, xaw7) fi -AC_SUBST(XEDIT_CFLAGS) -AC_SUBST(XEDIT_LIBS) +AC_CHECK_FUNC(realpath, [], [have_realpath=yes]) +AM_CONDITIONAL(NEED_REALPATH, test x$have_realpath = xyes) +AC_CHECK_FUNC(strcasecmp, [], [have_strcasecmp=yes]) +AM_CONDITIONAL(NEED_STRCASECMP, test x$have_strcasecmp = xyes) + +AC_SUBST(PKGDEPS_CFLAGS) +AC_SUBST(PKGDEPS_LIBS) PKG_CHECK_MODULES(APPDEFS, xt) appdefaultdir=$(pkg-config --variable=appdefaultdir xt) diff --git a/hook.c b/hook.c index 13a8b94..871ea1b 100644 --- a/hook.c +++ b/hook.c @@ -39,6 +39,7 @@ #include "xedit.h" #include "re.h" +#include "util.h" #include #include #include @@ -46,11 +47,11 @@ /* * Types */ -typedef struct _ReplaceList { - char *word; +typedef struct _ReplaceEntry { + hash_key *word; + struct _ReplaceEntry *next; char *replace; - struct _ReplaceList *next; -} ReplaceList; +} ReplaceEntry; typedef enum { SubstituteDisabled, @@ -108,7 +109,7 @@ static void SubstituteCallback(Widget, XtPointer, XtPointer); * Initialization */ #define STRTBLSZ 11 -static ReplaceList *replace_list[STRTBLSZ]; +static hash_table *replace_hash; static EditInfo einfo; extern Widget scratch; @@ -191,6 +192,8 @@ StartAutoReplace(void) if (!replace || !*replace) return (False); + replace_hash = hash_new(STRTBLSZ, NULL); + left = XtMalloc(llen = 256); right = XtMalloc(rlen = 256); while (*replace) { @@ -247,34 +250,26 @@ StartAutoReplace(void) static char * ReplacedWord(char *word, char *replace) { - ReplaceList *list; - int ii = 0; - char *pp = word; - - while (*pp) - ii = (ii << 1) ^ *pp++; - if (ii < 0) - ii = -ii; - ii %= STRTBLSZ; - for (list = replace_list[ii]; list; list = list->next) - if (strcmp(list->word, word) == 0) { - if (replace) { - XtFree(list->replace); - list->replace = XtNewString(replace); - } - return (list->replace); - } - - if (!replace) - return (NULL); - - list = XtNew(ReplaceList); - list->word = XtNewString(word); - list->replace = XtNewString(replace); - list->next = replace_list[ii]; - replace_list[ii] = list; + int length; + ReplaceEntry *entry; + + length = strlen(word); + entry = (ReplaceEntry *)hash_check(replace_hash, word, length); + if (entry == NULL && replace != NULL) { + entry = XtNew(ReplaceEntry); + entry->word = XtNew(hash_key); + entry->word->value = XtNewString(word); + entry->word->length = length; + entry->next = NULL; + entry->replace = XtNewString(replace); + hash_put(replace_hash, (hash_entry *)entry); + } + else if (replace) { + XtFree(entry->replace); + entry->replace = XtNewString(replace); + } - return (list->replace); + return (entry ? entry->replace : NULL); } static void @@ -388,7 +383,7 @@ LineEditAction(Widget w, XEvent *event, String *params, Cardinal *num_params) { XawTextBlock block; - if (True) { + if (international) { /* XXX FIXME */ fprintf(stderr, "LineEditAction: Not working in international mode.\n"); return; @@ -405,10 +400,6 @@ LineEditAction(Widget w, XEvent *event, String *params, Cardinal *num_params) line_edit = True; } -#define LSCAN(from, count, include) \ - XawTextSourceScan(source, from, XawstEOL, XawsdLeft, count, include) -#define RSCAN(from, count, include) \ - XawTextSourceScan(source, from, XawstEOL, XawsdRight, count, include) void LineEdit(Widget w) { @@ -810,7 +801,7 @@ LineEdit(Widget w) csubst = -1; switch (einfo.subst[i + 1]) { case '0': csubst = '\0'; break; - case 'a': csubst = '\b'; break; + case 'a': csubst = '\a'; break; case 'b': csubst = '\b'; break; case 'f': csubst = '\f'; break; case 'n': csubst = '\n'; break; @@ -820,9 +811,8 @@ LineEdit(Widget w) case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - ++i; - if (einfo.subst[i] - '0' > einfo.sref) - einfo.sref = einfo.subst[i] - '0'; + if (einfo.subst[i + 1] - '0' > einfo.sref) + einfo.sref = einfo.subst[i + 1] - '0'; break; default: csubst = einfo.subst[i + 1]; @@ -833,7 +823,6 @@ LineEdit(Widget w) einfo.slen - i); einfo.subst[i] = csubst; --einfo.slen; - ++i; csubst = -1; } } @@ -847,8 +836,39 @@ LineEdit(Widget w) /* Compile pattern if required */ if (compile) { + int ch; + char *pattern; + char *eptr, *pptr; + + /* Parse backslashes */ + pptr = pattern = XtMalloc(strlen(einfo.pattern)); + for (eptr = einfo.pattern, ch = *eptr; ch; eptr++, ch = *eptr) { + if (ch == '\\') { + ++eptr; + ch = *eptr; + switch (ch) { + case '0': ch = '\0'; einfo.flags |= RE_PEND; break; + case 'a': ch = '\a'; break; + case 'b': ch = '\b'; break; + case 'f': ch = '\f'; break; + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + break; + default: break; + } + *pptr++ = ch; + } + } + *pptr = '\0'; + refree(&einfo.regex); - if ((ecode = recomp(&einfo.regex, einfo.pattern, einfo.flags)) != 0) + /* Allow nuls in search regex */ + einfo.regex.re_endp = pptr; + ecode = recomp(&einfo.regex, einfo.pattern, einfo.flags); + XtFree(pattern); + if (ecode) goto print; } @@ -917,7 +937,7 @@ confirm_label: } memcpy(einfo.line, block.ptr, block.length); length = block.length; - for (position += length; position < to; position += length) { + for (position += length; position < to; position += block.length) { XawTextSourceRead(source, position, &block, to - position); memcpy(einfo.line + length, block.ptr, block.length); length += block.length; diff --git a/ispell.c b/ispell.c index 1e7d38a..01fc87b 100644 --- a/ispell.c +++ b/ispell.c @@ -31,11 +31,13 @@ /* $XFree86: xc/programs/xedit/ispell.c,v 1.19 2002/10/19 20:04:20 herrb Exp $ */ #include "xedit.h" +#include "util.h" #include #include #include #include #include +#include #include #include #include @@ -133,17 +135,19 @@ struct _ispell { struct _ispell_format *format_info; }; -typedef struct _ReplaceList { - char *word; - char *replace; - struct _ReplaceList *next; -} ReplaceList; +typedef struct _ReplaceEntry ReplaceEntry; +struct _ReplaceEntry { + hash_key *word; + ReplaceEntry*next; + char *replace; +}; -typedef struct _IgnoreList { - char *word; - int add; - struct _IgnoreList *next; -} IgnoreList; +typedef struct _IgnoreEntry IgnoreEntry; +struct _IgnoreEntry { + hash_key *word; + IgnoreEntry *next; + int add; +}; /* * Prototypes @@ -169,6 +173,7 @@ static void IspellSetSensitive(Bool); static void IspellSetStatus(char*); static void IspellSetTerseMode(Bool); static Bool IspellStartProcess(void); +static Bool IspellCheckProcess(void); static Bool IspellEndProcess(Bool, Bool); static void LookIspell(Widget, XtPointer, XtPointer); static void PopdownIspell(Widget, XtPointer, XtPointer); @@ -194,8 +199,8 @@ static struct _ispell ispell; #define RSTRTBLSZ 23 #define ISTRTBLSZ 71 -static ReplaceList *replace_list[RSTRTBLSZ]; -static IgnoreList *ignore_list[ISTRTBLSZ]; +static hash_table *replace_hash; +static hash_table *ignore_hash; #ifndef XtCStatus #define XtCStatus "Status" @@ -441,71 +446,59 @@ IspellCheckUndo(void) static char * IspellReplacedWord(char *word, char *replace) { - ReplaceList *list; - int ii = 0; - char *pp = word; - - while (*pp) - ii = (ii << 1) ^ *pp++; - if (ii < 0) - ii = -ii; - ii %= RSTRTBLSZ; - for (list = replace_list[ii]; list; list = list->next) - if (strcmp(list->word, word) == 0) { - if (replace) { - XtFree(list->replace); - list->replace = XtNewString(replace); - } - return (list->replace); - } - - if (!replace) - return (NULL); + int word_len; + hash_key *word_key; + ReplaceEntry *entry; + + word_len = strlen(word); + entry = (ReplaceEntry *)hash_check(replace_hash, word, word_len); + if (entry == NULL) { + word_key = XtNew(hash_key); + word_key->value = XtNewString(word); + word_key->length = word_len; + entry = XtNew(ReplaceEntry); + entry->word = word_key; + entry->replace = NULL; + entry->next = NULL; + hash_put(replace_hash, (hash_entry *)entry); + } - list = XtNew(ReplaceList); - list->word = XtNewString(word); - list->replace = XtNewString(replace); - list->next = replace_list[ii]; - replace_list[ii] = list; + if (replace) { + XtFree(entry->replace); + entry->replace = XtNewString(replace); + } - return (list->replace); + return (entry->replace); } static Bool IspellDoIgnoredWord(char *word, int cmd, int add) { - IgnoreList *list, *prev; - int ii = 0; - char *pp = word; - - while (*pp) - ii = (ii << 1) ^ *pp++; - if (ii < 0) - ii = -ii; - ii %= ISTRTBLSZ; - for (prev = list = ignore_list[ii]; list; prev = list, list = list->next) - if (strcmp(list->word, word) == 0) { - if (cmd == REMOVE) { - XtFree(list->word); - prev->next = list->next; - XtFree((char*)list); - if (prev == list) - ignore_list[ii] = NULL; - return (True); - } - return (cmd == CHECK); - } + int word_len; + hash_key *word_key; + IgnoreEntry *entry; + + word_len = strlen(word); + entry = (IgnoreEntry *)hash_check(ignore_hash, word, word_len); + if (entry == NULL) { + if (cmd != ADD) + return (False); - if (cmd != ADD) - return (False); + word_key = XtNew(hash_key); + word_key->value = XtNewString(word); + word_key->length = word_len; + entry = XtNew(IgnoreEntry); + entry->word = word_key; + entry->add = add; + entry->next = NULL; + hash_put(ignore_hash, (hash_entry *)entry); - list = XtNew(IgnoreList); - list->word = XtNewString(word); - list->add = add; - list->next = ignore_list[ii]; - ignore_list[ii] = list; + return (True); + } + else if (cmd == REMOVE) + hash_rem(ignore_hash, (hash_entry *)entry); - return (True); + return (cmd == CHECK); } static Bool @@ -938,7 +931,10 @@ IspellSend(void) return (-1); } for (i = 0; i < block.length; i++) { - wctomb(mb, ((wchar_t*)block.ptr)[i]); + if (international) + wctomb(mb, ((wchar_t*)block.ptr)[i]); + else + mb[0] = block.ptr[i]; if (amplen) { if (amplen + 2 >= sizeof(ampbuf)) { if (!ispell.terse_mode) @@ -1036,7 +1032,10 @@ IspellSend(void) return (-1); } for (i = 0; i < block.length; i++) { - wctomb(mb, ((wchar_t*)block.ptr)[i]); + if (international) + wctomb(mb, ((wchar_t*)block.ptr)[i]); + else + mb[0] = block.ptr[i]; if (amplen) { if (amplen + 2 >= sizeof(ampbuf)) { if (!ispell.terse_mode) @@ -1322,14 +1321,26 @@ IspellStartProcess(void) { if (!ispell.pid) { int len; - char *command; + char format[32]; + static char *command; ispell.source = XawTextGetSource(ispell.ascii); - len = strlen(ispell.cmd) + strlen(ispell.dictionary) + - strlen(ispell.wchars) + 16; + if (command) + XtFree(command); + + strcpy(format, "%s -a"); + len = strlen(ispell.cmd) + 4; + if (ispell.dictionary && *ispell.dictionary) { + len += strlen(ispell.dictionary) + 6; + strcat(format, " -d '%s'"); + if (ispell.wchars && *ispell.wchars) { + len += strlen(ispell.wchars + 6); + strcat(format, " -w '%s'"); + } + } command = XtMalloc(len); - XmuSnprintf(command, len, "%s -a -d '%s' -w '%s'", + XmuSnprintf(command, len, format, ispell.cmd, ispell.dictionary, ispell.wchars); pipe(ispell.ifd); @@ -1343,7 +1354,9 @@ IspellStartProcess(void) close(ispell.ofd[1]); close(ispell.ifd[0]); close(ispell.ifd[1]); - execl("/bin/sh", "sh", "-c", command, (void *)NULL); + if (!international) + setlocale(LC_ALL, "ISO-8859-1"); + execl("/bin/sh", "sh", "-c", command, NULL); exit(-127); } else if (ispell.pid < 0) { @@ -1373,44 +1386,48 @@ PopdownIspell(Widget w, XtPointer client_data, XtPointer call_data) } static Bool +IspellCheckProcess(void) +{ + int status; + + if (ispell.pid) { + waitpid(ispell.pid, &status, WNOHANG); + if (WIFEXITED(status)) { + ispell.pid = 0; + } + else + return (True); + } + + return (False); +} + +static Bool IspellEndProcess(Bool killit, Bool killundo) { ispell.source = NULL; if (ispell.pid) { - IgnoreList *il, *pil, *nil; - int i; + IgnoreEntry *ientry; + ReplaceEntry *rentry; /* insert added words in private dictionary */ - for (i = 0; i < ISTRTBLSZ; i++) { - pil = il = ignore_list[i]; - while (il) { - if (il->add) { - nil = il->next; - if (il == pil) - ignore_list[i] = nil; - else - pil->next = nil; - if (il->add == UNCAP) - write(ispell.ofd[1], "&", 1); - else - write(ispell.ofd[1], "*", 1); - write(ispell.ofd[1], il->word, strlen(il->word)); - write(ispell.ofd[1], "\n", 1); - XtFree(il->word); - XtFree((char*)il); - il = nil; - } + for (ientry = (IgnoreEntry *)hash_iter_first(ignore_hash); + ientry; + ientry = (IgnoreEntry *)hash_iter_next(ignore_hash)) { + if (ientry->add) { + if (ientry->add == UNCAP) + write(ispell.ofd[1], "&", 1); else - il = il->next; - pil = il; + write(ispell.ofd[1], "*", 1); + write(ispell.ofd[1], ientry->word->value, ientry->word->length); + write(ispell.ofd[1], "\n", 1); } } write(ispell.ofd[1], "#\n", 2); /* save dictionary */ + hash_clr(ignore_hash); if (killit) { - ReplaceList *rl, *prl; - XtRemoveInput(ispell.id); close(ispell.ofd[0]); @@ -1430,27 +1447,13 @@ IspellEndProcess(Bool killit, Bool killundo) XtFree(ispell.buf); ispell.buf = NULL; - for (i = 0; i < RSTRTBLSZ; i++) { - prl = rl = replace_list[i]; - while (prl) { - rl = rl->next; - XtFree(prl->word); - XtFree(prl->replace); - XtFree((char*)prl); - prl = rl; - } - replace_list[i] = NULL; - } - for (i = 0; i < ISTRTBLSZ; i++) { - pil = il = ignore_list[i]; - while (pil) { - il = il->next; - XtFree(pil->word); - XtFree((char*)pil); - pil = il; - } - ignore_list[i] = NULL; + /* forget about replace matches */ + for (rentry = (ReplaceEntry *)hash_iter_first(replace_hash); + rentry; + rentry = (ReplaceEntry *)hash_iter_next(replace_hash)) { + XtFree(rentry->replace); } + hash_clr(replace_hash); } if (killundo) @@ -1560,12 +1563,18 @@ ReplaceIspell(Widget w, XtPointer client_data, XtPointer call_data) char mb[sizeof(wchar_t)]; if (XawTextSourceRead(ispell.source, pos - 1, &check, 1) > 0) { - wctomb(mb, *(wchar_t*)check.ptr); + if (international) + wctomb(mb, *(wchar_t*)check.ptr); + else + mb[0] = check.ptr[0]; do_replace = !isalpha(*mb) && *mb && !strchr(ispell.wchars, *mb); } if (do_replace && XawTextSourceRead(ispell.source, pos + search.length, &check, 1) > 0) { - wctomb(mb, *(wchar_t*)check.ptr); + if (international) + wctomb(mb, *(wchar_t*)check.ptr); + else + mb[0] = check.ptr[0]; do_replace = !isalpha(*mb) && *mb && !strchr(ispell.wchars, *mb); } if (do_replace) { @@ -1957,8 +1966,10 @@ ChangeDictionaryIspell(Widget w, XtPointer client_data, XtPointer call_data) return; if (!ispell.lock) { - Feep(); - return; + if (IspellCheckProcess()) { + Feep(); + return; + } } for (tmp = ispell.dict_info; tmp; tmp = tmp->next) @@ -2024,6 +2035,7 @@ InitIspell(void) Atom delete_window; char *str, *list; XtResource dict_res; + ispell_dict *dict, *prev_dict; int i; static XtResource text_res[] = { {"skipLines", "Skip", XtRString, sizeof(char*), @@ -2033,6 +2045,9 @@ InitIspell(void) if (ispell.shell) return (False); + replace_hash = hash_new(RSTRTBLSZ, NULL); + ignore_hash = hash_new(ISTRTBLSZ, NULL); + ispell.shell = XtCreatePopupShell("ispell", transientShellWidgetClass, topwindow, NULL, 0); @@ -2169,36 +2184,43 @@ InitIspell(void) dict_res.default_addr = ""; list = XtNewString(ispell.dict_list); - for (str = strtok(list, " \t,"); str; str = strtok(NULL, " \t,")) { - ispell_dict *dic = XtNew(ispell_dict); - dic->sme = XtCreateManagedWidget(str, smeBSBObjectClass, - ispell.dictMenu, NULL, 0); - XtGetApplicationResources(dic->sme, (XtPointer)dic, &dict_res, + /* Create first empty entry */ + dict = XtNew(ispell_dict); + dict->sme = XtCreateManagedWidget("", smeBSBObjectClass, + ispell.dictMenu, NULL, 0); + dict->wchars = ""; + XtAddCallback(dict->sme, XtNcallback, ChangeDictionaryIspell, + (XtPointer)dict); + ispell.dict_info = prev_dict = dict; + + for (str = strtok(list, " \t,"); str; str = strtok(NULL, " \t,")) { + dict = XtNew(ispell_dict); + dict->sme = XtCreateManagedWidget(str, smeBSBObjectClass, + ispell.dictMenu, NULL, 0); + XtGetApplicationResources(dict->sme, (XtPointer)dict, &dict_res, 1, NULL, 0); - XtAddCallback(dic->sme, XtNcallback, ChangeDictionaryIspell, - (XtPointer)dic); - dic->next = NULL; - if (!ispell.dict_info) - ispell.dict_info = dic; - else { - ispell_dict *tmp = ispell.dict_info; - - for (; tmp->next; tmp = tmp->next) - ; - tmp->next = dic; - } - if (strcmp(str, ispell.dictionary) == 0) { + XtAddCallback(dict->sme, XtNcallback, ChangeDictionaryIspell, + (XtPointer)dict); + prev_dict->next = dict; + prev_dict = dict; + dict->next = NULL; + } + XtFree(list); + + for (dict = ispell.dict_info; dict; dict = dict->next) { + if (strcmp(XtName(dict->sme), ispell.dictionary) == 0) { Arg args[1]; XtSetArg(args[0], XtNleftBitmap, flist.pixmap); - XtSetValues(dic->sme, args, 1); - XtSetArg(args[0], XtNlabel, str); + XtSetValues(dict->sme, args, 1); + XtSetArg(args[0], XtNlabel, XtName(dict->sme)); XtSetValues(ispell.dict, args, 1); - ispell.wchars = dic->wchars; + ispell.wchars = dict->wchars; + break; } } - XtFree(list); + delete_window = XInternAtom(XtDisplay(ispell.shell), "WM_DELETE_WINDOW", False); XSetWMProtocols(XtDisplay(ispell.shell), XtWindow(ispell.shell), &delete_window, 1); diff --git a/lisp/bytecode.c b/lisp/bytecode.c index 3e824d2..e1e5158 100644 --- a/lisp/bytecode.c +++ b/lisp/bytecode.c @@ -627,8 +627,8 @@ Lisp_Disassemble(LispBuiltin *builtin) for (i = 0; i < alist->normals.num_symbols; i++) { LispWriteChar(NIL, i ? ',' : ':'); LispWriteChar(NIL, ' '); - LispWriteStr(NIL, ATOMID(alist->normals.symbols[i]), - strlen(ATOMID(alist->normals.symbols[i]))); + LispWriteStr(NIL, ATOMID(alist->normals.symbols[i])->value, + ATOMID(alist->normals.symbols[i])->length); } LispWriteChar(NIL, '\n'); @@ -639,8 +639,8 @@ Lisp_Disassemble(LispBuiltin *builtin) for (i = 0; i < alist->optionals.num_symbols; i++) { LispWriteChar(NIL, i ? ',' : ':'); LispWriteChar(NIL, ' '); - LispWriteStr(NIL, ATOMID(alist->optionals.symbols[i]), - strlen(ATOMID(alist->optionals.symbols[i]))); + LispWriteStr(NIL, ATOMID(alist->optionals.symbols[i])->value, + ATOMID(alist->optionals.symbols[i])->length); } LispWriteChar(NIL, '\n'); @@ -657,8 +657,8 @@ Lisp_Disassemble(LispBuiltin *builtin) if (alist->rest) { LispWriteStr(NIL, "Rest argument: ", 15); - LispWriteStr(NIL, ATOMID(alist->rest), - strlen(ATOMID(alist->rest))); + LispWriteStr(NIL, ATOMID(alist->rest)->value, + ATOMID(alist->rest)->length); LispWriteChar(NIL, '\n'); } else @@ -666,6 +666,7 @@ Lisp_Disassemble(LispBuiltin *builtin) } if (bytecode) { + Atom_id id; char *ptr; int *offsets[4]; int i, done, j, sym0, sym1, con0, con1, bui0, byt0, strd, strf; @@ -724,11 +725,11 @@ Lisp_Disassemble(LispBuiltin *builtin) * and called as XSTRING(atom->object) * it would also print the package name were the symbol was first defined, * but for local variables, only the symbol string is important. */ -#define XSTRING(string) string ? string : "#" +#define XSTRING(string) string ? string : (unsigned char *)"#" for (i = 0; i < num_symbols; i++) { sprintf(buffer, "Symbol %d = %s\n", - i, XSTRING(symbols[i]->string)); + i, XSTRING(symbols[i]->string->value)); LispWriteStr(NIL, buffer, strlen(buffer)); } for (i = 0; i < num_builtins; i++) { @@ -756,24 +757,24 @@ Lisp_Disassemble(LispBuiltin *builtin) for (i = 0; i < alist->normals.num_symbols; i++, j++) { sprintf(buffer, "%d = ", j); LispWriteStr(NIL, buffer, strlen(buffer)); - ptr = alist->normals.symbols[i]->data.atom->string; - LispWriteStr(NIL, ptr, strlen(ptr)); + id = alist->normals.symbols[i]->data.atom->string; + LispWriteStr(NIL, id->value, id->length); LispWriteChar(NIL, '\n'); } for (i = 0; i < alist->optionals.num_symbols; i++, j++) { sprintf(buffer, "%d = ", j); LispWriteStr(NIL, buffer, strlen(buffer)); - ptr = alist->optionals.symbols[i]->data.atom->string; - LispWriteStr(NIL, ptr, strlen(ptr)); + id = alist->optionals.symbols[i]->data.atom->string; + LispWriteStr(NIL, id->value, id->length); LispWriteChar(NIL, '\n'); if (alist->optionals.sforms[i]) { sprintf(buffer, "%d = ", j); len1 = strlen(buffer); LispWriteStr(NIL, buffer, len1); - ptr = alist->optionals.sforms[i]->data.atom->string; - len2 = strlen(ptr); - LispWriteStr(NIL, ptr, len2); + id = alist->optionals.sforms[i]->data.atom->string; + len2 = id->length; + LispWriteStr(NIL, id->value, len2); LispWriteChars(NIL, ' ', 28 - (len1 + len2)); LispWriteStr(NIL, "; sform\n", 9); j++; @@ -785,24 +786,24 @@ Lisp_Disassemble(LispBuiltin *builtin) len1 = strlen(buffer); LispWriteStr(NIL, buffer, len1); if (alist->keys.keys[i]) { - ptr = alist->keys.keys[i]->data.atom->string; - len2 = strlen(ptr); - LispWriteStr(NIL, ptr, strlen(ptr)); + id = alist->keys.keys[i]->data.atom->string; + len2 = id->length; + LispWriteStr(NIL, id->value, id->length); LispWriteChars(NIL, ' ', 28 - (len1 + len2)); LispWriteStr(NIL, "; special key", 14); } else { - ptr = alist->keys.symbols[i]->data.atom->string; - LispWriteStr(NIL, ptr, strlen(ptr)); + id = alist->keys.symbols[i]->data.atom->string; + LispWriteStr(NIL, id->value, id->length); } LispWriteChar(NIL, '\n'); if (alist->keys.sforms[i]) { sprintf(buffer, "%d = ", j); len1 = strlen(buffer); LispWriteStr(NIL, buffer, len1); - ptr = alist->keys.sforms[i]->data.atom->string; - len2 = strlen(ptr); - LispWriteStr(NIL, ptr, len2); + id = alist->keys.sforms[i]->data.atom->string; + len2 = id->length; + LispWriteStr(NIL, id->value, len2); LispWriteChars(NIL, ' ', 28 - (len1 + len2)); LispWriteStr(NIL, "; sform\n", 9); j++; @@ -813,9 +814,9 @@ Lisp_Disassemble(LispBuiltin *builtin) sprintf(buffer, "%d = ", j); len1 = strlen(buffer); LispWriteStr(NIL, buffer, len1); - ptr = alist->rest->data.atom->string; - len2 = strlen(ptr); - LispWriteStr(NIL, ptr, len2); + id = alist->rest->data.atom->string; + len2 = id->length; + LispWriteStr(NIL, id->value, len2); LispWriteChar(NIL, '\n'); j++; } @@ -824,9 +825,9 @@ Lisp_Disassemble(LispBuiltin *builtin) sprintf(buffer, "%d = ", j); len1 = strlen(buffer); LispWriteStr(NIL, buffer, len1); - ptr = alist->auxs.symbols[i]->data.atom->string; - len2 = strlen(ptr); - LispWriteStr(NIL, ptr, len2); + id = alist->auxs.symbols[i]->data.atom->string; + len2 = id->length; + LispWriteStr(NIL, id->value, len2); LispWriteChars(NIL, ' ', 28 - (len1 + len2)); LispWriteStr(NIL, "; aux\n", 7); } @@ -1116,7 +1117,7 @@ integer: for (; strf >= 0; strf--) fields = CDR(fields); strcpy(ptr, " "); ptr += 2; - strcpy(ptr, CAR(fields)->data.atom->string); + strcpy(ptr, CAR(fields)->data.atom->string->value); ptr += strlen(ptr); } if (strd >= 0) { @@ -1154,11 +1155,11 @@ integer: /* Symbols */ if (sym0 >= 0) { strcpy(ptr, " "); ptr += 2; - strcpy(ptr, XSTRING(symbols[sym0]->string)); + strcpy(ptr, XSTRING(symbols[sym0]->string->value)); ptr += strlen(ptr); if (sym1 >= 0) { strcpy(ptr, " "); ptr += 2; - strcpy(ptr, XSTRING(symbols[sym1]->string)); + strcpy(ptr, XSTRING(symbols[sym1]->string->value)); ptr += strlen(ptr); } } @@ -1658,7 +1659,7 @@ LinkWarnUnused(LispCom *com, CodeBlock *block) if (!(block->variables.flags[i] & (VARIABLE_USED | VARIABLE_ARGUMENT))) { ++com->warnings; LispWarning("the variable %s is unused", - block->variables.symbols[i]->string); + block->variables.symbols[i]->string->value); } } @@ -3631,12 +3632,12 @@ OPCODE_LABEL(XBC_STRUCT): offset = *stream++; reg1 = constants[*stream++]; if (!STRUCTP(reg0) || reg0->data.struc.def != reg1) { - char *name = ATOMID(CAR(reg1)); + char *name = ATOMID(CAR(reg1))->value; for (reg1 = CDR(reg1); offset; offset--) reg1 = CDR(reg1); LispDestroy("%s-%s: %s is not a %s", - name, ATOMID(CAR(reg1)), STROBJ(reg0), name); + name, ATOMID(CAR(reg1))->value, STROBJ(reg0), name); } for (reg0 = reg0->data.struc.fields; offset; offset--) reg0 = CDR(reg0); diff --git a/lisp/compile.c b/lisp/compile.c index 6058c67..4d2f3f0 100644 --- a/lisp/compile.c +++ b/lisp/compile.c @@ -1645,7 +1645,7 @@ rest_label: constantp = 0; } - string = builtin ? ATOMID(name) : NULL; + string = builtin ? ATOMID(name)->value : NULL; /* XXX FIXME should have a flag indicating if function call * change the &REST arguments even if it is a constant list * (or if the returned value may be changed). */ diff --git a/lisp/core.c b/lisp/core.c index 684081a..60e6994 100644 --- a/lisp/core.c +++ b/lisp/core.c @@ -1070,7 +1070,7 @@ Lisp_Defmacro(LispBuiltin *builtin) name = ARGUMENT(0); CHECK_SYMBOL(name); - alist = LispCheckArguments(LispMacro, lambda_list, ATOMID(name), 0); + alist = LispCheckArguments(LispMacro, lambda_list, ATOMID(name)->value, 0); if (CONSP(body) && STRINGP(CAR(body))) { LispAddDocumentation(name, CAR(body), LispDocFunction); @@ -1112,7 +1112,7 @@ Lisp_Defun(LispBuiltin *builtin) name = ARGUMENT(0); CHECK_SYMBOL(name); - alist = LispCheckArguments(LispFunction, lambda_list, ATOMID(name), 0); + alist = LispCheckArguments(LispFunction, lambda_list, ATOMID(name)->value, 0); if (CONSP(body) && STRINGP(CAR(body))) { LispAddDocumentation(name, CAR(body), LispDocFunction); @@ -1166,7 +1166,7 @@ Lisp_Defsetf(LispBuiltin *builtin) return (function); } - alist = LispCheckArguments(LispSetf, lambda_list, ATOMID(function), 0); + alist = LispCheckArguments(LispSetf, lambda_list, ATOMID(function)->value, 0); store = CAR(body); if (!CONSP(store)) @@ -2050,7 +2050,7 @@ Lisp_Lambda(LispBuiltin *builtin) body = ARGUMENT(1); lambda_list = ARGUMENT(0); - alist = LispCheckArguments(LispLambda, lambda_list, Snil, 0); + alist = LispCheckArguments(LispLambda, lambda_list, Snil->value, 0); name = OPAQUE(alist, LispArgList_t); lambda_list = LispListProtectedArguments(alist); @@ -3942,7 +3942,7 @@ Lisp_Proclaim(LispBuiltin *builtin) object = CAR(arguments); CHECK_SYMBOL(object); - operation = ATOMID(object); + operation = ATOMID(object)->value; if (strcmp(operation, "SPECIAL") == 0) { for (arguments = CDR(arguments); CONSP(arguments); arguments = CDR(arguments)) { diff --git a/lisp/format.c b/lisp/format.c index abbb49d..ab85565 100644 --- a/lisp/format.c +++ b/lisp/format.c @@ -556,7 +556,7 @@ format_ascii(LispObj *stream, LispObj *object, FmtArgs *args) if (collon) LispWriteStr(stream, "()", 2); else - LispWriteStr(stream, Snil, 3); + LispWriteStr(stream, Snil->value, 3); } else { /* if string is not NIL, atsign was specified diff --git a/lisp/hash.c b/lisp/hash.c index 5959330..a6b91ec 100644 --- a/lisp/hash.c +++ b/lisp/hash.c @@ -153,8 +153,6 @@ LispHashKey(LispObj *object, int function) case LispString_t: string = THESTR(object); length = STRLEN(object); - if (length > 32) - length = 32; for (i = 0, key = 0; i < length; i++) key = (key << 1) ^ string[i]; break; diff --git a/lisp/helper.c b/lisp/helper.c index 100ed2e..8dba59b 100644 --- a/lisp/helper.c +++ b/lisp/helper.c @@ -343,8 +343,8 @@ LispCharacterCoerce(LispBuiltin *builtin, LispObj *object) return (object); else if (STRINGP(object) && STRLEN(object) == 1) return (SCHAR(THESTR(object)[0])); - else if (SYMBOLP(object) && ATOMID(object)[1] == '\0') - return (SCHAR(ATOMID(object)[0])); + else if (SYMBOLP(object) && ATOMID(object)->value[1] == '\0') + return (SCHAR(ATOMID(object)->value[0])); else if (INDEXP(object)) { int c = FIXNUM_VALUE(object); @@ -374,9 +374,9 @@ LispStringCoerce(LispBuiltin *builtin, LispObj *object) return (LSTRING(string, 1)); } else if (object == NIL) - return (LSTRING(Snil, 3)); + return (LSTRING(Snil->value, 3)); else if (object == T) - return (LSTRING(St, 1)); + return (LSTRING(St->value, 1)); else LispDestroy("%s: cannot convert %s to string", STRFUN(builtin), STROBJ(object)); diff --git a/lisp/internal.h b/lisp/internal.h index b00db3e..f0c95a2 100644 --- a/lisp/internal.h +++ b/lisp/internal.h @@ -39,6 +39,8 @@ #include "mp.h" #include "re.h" +#include "util.h" + /* * Defines */ @@ -110,7 +112,7 @@ typedef struct _LispMac LispMac; #define UPROTECT(key, list) LispUProtect(key, list) /* create a new unique static atom string */ -#define GETATOMID(string) LispGetAtomString(string, 1) +#define GETATOMID(string) LispGetAtomKey(string, 1) #define GCDisable() ++gcpro #define GCEnable() --gcpro @@ -432,7 +434,7 @@ typedef struct _LispMac LispMac; #define LispFileno(file) ((file)->descriptor) -#define STRFUN(builtin) ATOMID(builtin->symbol) +#define STRFUN(builtin) ATOMID(builtin->symbol)->value #define STROBJ(obj) LispStrObj(obj) /* fetch builtin function/macro argument value @@ -489,7 +491,7 @@ typedef struct _LispHashTable LispHashTable; /* Bytecode compiler data */ typedef struct _LispCom LispCom; -typedef char *Atom_id; +typedef hash_key *Atom_id; typedef enum _LispType { /* objects encoded in the LispObj pointer */ diff --git a/lisp/lisp.c b/lisp/lisp.c index 87bf2cf..61689aa 100644 --- a/lisp/lisp.c +++ b/lisp/lisp.c @@ -935,23 +935,22 @@ Lisp__GC(LispObj *car, LispObj *cdr) /* Traverse atom list, protecting properties, and function/structure * definitions if lisp__data.gc.immutablebits set */ - for (i = 0; i < STRTBLSZ; i++) { - atom = pack->atoms[i]; - while (atom) { - if (atom->property != NOPROPERTY) { - if (atom->a_property) - LispMark(atom->property->properties); - if (lisp__data.gc.immutablebits) { - if (atom->a_function || atom->a_compiled) - LispProt(atom->property->fun.function); - if (atom->a_defsetf) - LispProt(atom->property->setf); - if (atom->a_defstruct) - LispProt(atom->property->structure.definition); - } + for (atom = (LispAtom *)hash_iter_first(pack->atoms); + atom; + atom = (LispAtom *)hash_iter_next(pack->atoms)) { + if (atom->property != NOPROPERTY) { + if (atom->a_property) + LispMark(atom->property->properties); + if (lisp__data.gc.immutablebits) { + if (atom->a_function || atom->a_compiled) + LispProt(atom->property->fun.function); + if (atom->a_defsetf) + LispProt(atom->property->setf); + if (atom->a_defstruct) + LispProt(atom->property->structure.definition); } - atom = atom->next; } + atom = atom->next; } } @@ -1285,20 +1284,25 @@ LispSetVariable(LispObj *var, LispObj *val, char *fname, int eval) int LispRegisterOpaqueType(char *desc) { + int length; LispOpaque *opaque; - int ii = STRHASH(desc); - for (opaque = lisp__data.opqs[ii]; opaque; opaque = opaque->next) - if (strcmp(opaque->desc, desc) == 0) - return (opaque->type); - opaque = (LispOpaque*)LispMalloc(sizeof(LispOpaque)); - opaque->desc = LispStrdup(desc); - opaque->next = lisp__data.opqs[ii]; - lisp__data.opqs[ii] = opaque; - LispMused(opaque->desc); - LispMused(opaque); + length = strlen(desc); + opaque = (LispOpaque *)hash_check(lisp__data.opqs, desc, length); + + if (opaque == NULL) { + opaque = (LispOpaque*)LispMalloc(sizeof(LispOpaque)); + opaque->desc = (hash_key*)LispCalloc(1, sizeof(hash_key)); + opaque->desc->value = LispStrdup(desc); + opaque->desc->length = length; + hash_put(lisp__data.opqs, (hash_entry *)opaque); + LispMused(opaque->desc->value); + LispMused(opaque->desc); + LispMused(opaque); + opaque->type = ++lisp__data.opaque; + } - return (opaque->type = ++lisp__data.opaque); + return (opaque->type); } char * @@ -1308,74 +1312,61 @@ LispIntToOpaqueType(int type) LispOpaque *opaque; if (type) { - for (i = 0; i < STRTBLSZ; i++) { - opaque = lisp__data.opqs[i]; - while (opaque) { - if (opaque->type == type) - return (opaque->desc); - opaque = opaque->next; - } + for (opaque = (LispOpaque *)hash_iter_first(lisp__data.opqs); + opaque; + opaque = (LispOpaque *)hash_iter_next(lisp__data.opqs)) { + if (opaque->type == type) + return (opaque->desc->value); } LispDestroy("Opaque type %d not registered", type); } - return (Snil); + return (Snil->value); } -int -LispDoHashString(char *string) +hash_key * +LispGetAtomKey(char *string, int perm) { - char *pp; - int ii, count; - - for (pp = string, ii = count = 0; *pp && count < 32; pp++, count++) - ii = (ii << 1) ^ *pp; - if (ii < 0) - ii = -ii; - - return (ii % STRTBLSZ); -} - -char * -LispGetAtomString(char *string, int perm) -{ - LispStringHash *entry; - int ii = STRHASH(string); - - for (entry = lisp__data.strings[ii]; entry != NULL; entry = entry->next) - if (strcmp(entry->string, string) == 0) - return (entry->string); - - entry = (LispStringHash*)LispCalloc(1, sizeof(LispStringHash)); - if (perm) - entry->string = string; - else - entry->string = LispStrdup(string); - LispMused(entry); - if (!perm) - LispMused(entry->string); - entry->next = lisp__data.strings[ii]; - lisp__data.strings[ii] = entry; + int length; + hash_entry *entry; + + length = strlen(string); + entry = hash_check(lisp__data.strings, string, length); + if (entry == NULL) { + entry = LispCalloc(1, sizeof(hash_entry)); + entry->key = LispCalloc(1, sizeof(hash_key)); + if (perm) + entry->key->value = string; + else + entry->key->value = LispStrdup(string); + entry->key->length = length; + + hash_put(lisp__data.strings, entry); + if (!perm) + LispMused(entry->key->value); + LispMused(entry->key); + LispMused(entry); + } - return (entry->string); + return (entry->key); } LispAtom * LispDoGetAtom(char *str, int perm) { + int length; LispAtom *atom; - int ii = STRHASH(str); - for (atom = lisp__data.pack->atoms[ii]; atom; atom = atom->next) - if (strcmp(atom->string, str) == 0) - return (atom); + length = strlen(str); + atom = (LispAtom *)hash_check(lisp__data.pack->atoms, str, length); - atom = (LispAtom*)LispCalloc(1, sizeof(LispAtom)); - atom->string = LispGetAtomString(str, perm); - LispMused(atom); - atom->next = lisp__data.pack->atoms[ii]; - lisp__data.pack->atoms[ii] = atom; - atom->property = NOPROPERTY; + if (atom == NULL) { + atom = (LispAtom*)LispCalloc(1, sizeof(LispAtom)); + atom->string = LispGetAtomKey(str, perm); + hash_put(lisp__data.pack->atoms, (hash_entry *)atom); + atom->property = NOPROPERTY; + LispMused(atom); + } return (atom); } @@ -1752,7 +1743,7 @@ LispCheckKeyword(LispObj *keyword) if (KEYWORDP(keyword)) return (keyword); - return (KEYWORD(ATOMID(keyword))); + return (KEYWORD(ATOMID(keyword)->value)); } void @@ -1904,7 +1895,7 @@ LispCheckArguments(LispFunType type, LispObj *list, char *name, int builtin) if (list != NIL) LispDestroy("%s %s: %s cannot be a %s argument list", fnames[type], name, STROBJ(list), types[type]); - alist->description = GETATOMID(""); + alist->description = GETATOMID("")->value; return (alist); } @@ -2048,14 +2039,14 @@ LispCheckArguments(LispFunType type, LispObj *list, char *name, int builtin) else { Atom_id atom = ATOMID(spec); - if (atom[0] == '&') { + if (atom->value[0] == '&') { if (atom == Srest) { if (rest || aux || CDR(list) == NIL || !SYMBOLP(CADR(list)) /* only &aux allowed after &rest */ || (CDDR(list) != NIL && !SYMBOLP(CAR(CDDR(list))) && ATOMID(CAR(CDDR(list))) != Saux)) LispDestroy("%s %s: syntax error parsing %s", - fnames[type], name, ATOMID(spec)); + fnames[type], name, ATOMID(spec)->value); if (key) LispDestroy("%s %s: %s not allowed after %s", fnames[type], name, keys[IREST], keys[IKEY]); @@ -2066,7 +2057,7 @@ LispCheckArguments(LispFunType type, LispObj *list, char *name, int builtin) else if (atom == Skey) { if (rest || aux) LispDestroy("%s %s: %s not allowed after %s", - fnames[type], name, ATOMID(spec), + fnames[type], name, ATOMID(spec)->value, rest ? keys[IREST] : keys[IAUX]); key = 1; continue; @@ -2075,7 +2066,7 @@ LispCheckArguments(LispFunType type, LispObj *list, char *name, int builtin) else if (atom == Soptional) { if (rest || optional || aux || key) LispDestroy("%s %s: %s not allowed after %s", - fnames[type], name, ATOMID(spec), + fnames[type], name, ATOMID(spec)->value, rest ? keys[IREST] : optional ? keys[IOPTIONAL] : @@ -2088,7 +2079,7 @@ LispCheckArguments(LispFunType type, LispObj *list, char *name, int builtin) /* &AUX must be the last keyword parameter */ if (aux) LispDestroy("%s %s: syntax error parsing %s", - fnames[type], name, ATOMID(spec)); + fnames[type], name, ATOMID(spec)->value); else if (builtin) LispDestroy("builtin function cannot have &AUX arguments"); aux = 1; @@ -2099,7 +2090,7 @@ LispCheckArguments(LispFunType type, LispObj *list, char *name, int builtin) * argument names starting with the '&' character */ else LispDestroy("%s %s: %s not allowed/implemented", - fnames[type], name, ATOMID(spec)); + fnames[type], name, ATOMID(spec)->value); } /* Add argument to alist */ @@ -2170,7 +2161,7 @@ LispCheckArguments(LispFunType type, LispObj *list, char *name, int builtin) fnames[type], name, STROBJ(list), types[type]); *desc = '\0'; - alist->description = LispGetAtomString(description, 0); + alist->description = LispGetAtomKey(description, 0)->value; return (alist); } @@ -2214,7 +2205,7 @@ LispAddBuiltinFunction(LispBuiltin *builtin) LispPopInput(&stream); atom = name->data.atom; - alist = LispCheckArguments(builtin->type, CDR(list), atom->string, 1); + alist = LispCheckArguments(builtin->type, CDR(list), atom->string->value, 1); builtin->symbol = CAR(list); LispSetAtomBuiltinProperty(atom, builtin, alist); LispUseArgList(alist); @@ -2758,8 +2749,8 @@ LispSymbolName(LispObj *symbol) --atomseg.nfree; name->type = LispString_t; - THESTR(name) = atom->string; - STRLEN(name) = strlen(atom->string); + THESTR(name) = atom->string->value; + STRLEN(name) = atom->string->length; name->data.string.writable = 0; atom->name = name; @@ -3156,6 +3147,8 @@ LispNewPackage(LispObj *name, LispObj *nicknames) package->data.package.nicknames = nicknames; package->data.package.package = pack; + package->data.package.package->atoms = hash_new(STRTBLSZ, NULL); + LispMused(pack); return (package); @@ -3185,30 +3178,18 @@ LispSymbolFunction(LispObj *symbol) static INLINE LispObj * LispGetVarPack(LispObj *symbol) { - int ii; - char *string; LispAtom *atom; - string = ATOMID(symbol); - ii = STRHASH(string); + atom = (LispAtom *)hash_get(lisp__data.pack->atoms, + symbol->data.atom->string); - atom = lisp__data.pack->atoms[ii]; - while (atom) { - if (strcmp(atom->string, string) == 0) - return (atom->object); - - atom = atom->next; - } - - /* Symbol not found, just import it */ - return (NULL); + return (atom ? atom->object : NULL); } /* package must be of type LispPackage_t */ void LispUsePackage(LispObj *package) { - unsigned i; LispAtom *atom; LispPackage *pack; LispObj **pentry, **eentry; @@ -3242,13 +3223,11 @@ LispUsePackage(LispObj *package) pack = package->data.package.package; /* Traverse atom list, searching for extern symbols */ - for (i = 0; i < STRTBLSZ; i++) { - atom = pack->atoms[i]; - while (atom) { - if (atom->ext) - LispImportSymbol(atom->object); - atom = atom->next; - } + for (atom = (LispAtom *)hash_iter_first(pack->atoms); + atom; + atom = (LispAtom *)hash_iter_next(pack->atoms)) { + if (atom->ext) + LispImportSymbol(atom->object); } } @@ -3272,7 +3251,7 @@ LispImportSymbol(LispObj *symbol) } /* Create copy of atom in current package */ - atom = LispDoGetAtom(ATOMID(symbol), 0); + atom = LispDoGetAtom(ATOMID(symbol)->value, 0); /* Need to create a copy because if anything new is atached to the * property, the current package is the owner, not the previous one. */ @@ -5192,6 +5171,9 @@ LispBegin(void) pagesize = LispGetPageSize(); segsize = pagesize / sizeof(LispObj); + lisp__data.strings = hash_new(STRTBLSZ, NULL); + lisp__data.opqs = hash_new(STRTBLSZ, NULL); + /* Initialize memory management */ lisp__data.mem.mem = (void**)calloc(lisp__data.mem.space = 16, sizeof(void*)); @@ -5275,7 +5257,7 @@ LispBegin(void) /* Create the KEYWORD package */ Skeyword = GETATOMID("KEYWORD"); - object = LispNewPackage(STRING(Skeyword), + object = LispNewPackage(STRING(Skeyword->value), CONS(STRING(""), NIL)); /* Update list of packages */ diff --git a/lisp/math.c b/lisp/math.c index f9b6952..bdca034 100644 --- a/lisp/math.c +++ b/lisp/math.c @@ -71,7 +71,7 @@ LispMathInit(void) obj_one = FIXNUM(1); Oequal_ = STATIC_ATOM("="); - Ocomplex = STATIC_ATOM(Scomplex); + Ocomplex = STATIC_ATOM(Scomplex->value); Oshort_float = STATIC_ATOM("SHORT-FLOAT"); LispExportSymbol(Oshort_float); Osingle_float = STATIC_ATOM("SINGLE-FLOAT"); diff --git a/lisp/package.c b/lisp/package.c index 6ba23c9..8b941ec 100644 --- a/lisp/package.c +++ b/lisp/package.c @@ -87,7 +87,7 @@ LispFindPackage(LispObj *name) return (name); if (SYMBOLP(name)) - string = ATOMID(name); + string = ATOMID(name)->value; else if (STRINGP(name)) string = THESTR(name); else @@ -169,22 +169,18 @@ LispDoExport(LispBuiltin *builtin, if (package == PACKAGE) symbol->data.atom->ext = export ? 1 : 0; else { - int i; - char *string; + Atom_id string; LispAtom *atom; LispPackage *pack; string = ATOMID(symbol); pack = package->data.package.package; - i = STRHASH(string); - atom = pack->atoms[i]; - while (atom) { - if (strcmp(atom->string, string) == 0) { - atom->ext = export ? 1 : 0; - return; - } + atom = (LispAtom *)hash_check(pack->atoms, + string->value, string->length); - atom = atom->next; + if (atom) { + atom->ext = export ? 1 : 0; + return; } LispDestroy("%s: the symbol %s is not available in package %s", @@ -203,9 +199,9 @@ LispDoImport(LispBuiltin *builtin, LispObj *symbol) static LispObj * LispReallyDoSymbols(LispBuiltin *builtin, int only_externs, int all_symbols) { - int i, head = lisp__data.env.length; + int head = lisp__data.env.length; LispPackage *pack = NULL; - LispAtom *atom, *next_atom; + LispAtom *atom; LispObj *variable, *package = NULL, *list, *code, *result_form; LispObj *init, *body; @@ -251,21 +247,17 @@ LispReallyDoSymbols(LispBuiltin *builtin, int only_externs, int all_symbols) } /* Traverse the symbol list, executing body */ - for (i = 0; i < STRTBLSZ; i++) { - atom = pack->atoms[i]; - while (atom) { + for (atom = (LispAtom *)hash_iter_first(pack->atoms); + atom; + atom = (LispAtom *)hash_iter_next(pack->atoms)) { /* Save pointer to next atom. If variable is removed, * predicatable result is only guaranteed if the bound * variable is removed. */ - next_atom = atom->next; - - if (LispDoSymbol(package, atom, only_externs, all_symbols)) { - LispSetVar(variable, atom->object); - for (code = body; CONSP(code); code = CDR(code)) - EVAL(CAR(code)); - } - atom = next_atom; + if (LispDoSymbol(package, atom, only_externs, all_symbols)) { + LispSetVar(variable, atom->object); + for (code = body; CONSP(code); code = CDR(code)) + EVAL(CAR(code)); } } @@ -306,7 +298,6 @@ LispDoSymbols(LispBuiltin *builtin, int only_externs, int all_symbols) LispObj * LispFindSymbol(LispBuiltin *builtin, int intern) { - int i; char *ptr; LispAtom *atom; LispObj *symbol; @@ -342,15 +333,9 @@ LispFindSymbol(LispBuiltin *builtin, int intern) return (symbol); } - i = STRHASH(ptr); - atom = pack->atoms[i]; - while (atom) { - if (strcmp(atom->string, ptr) == 0) { - symbol = atom->object; - break; - } - atom = atom->next; - } + atom = (LispAtom *)hash_check(pack->atoms, ptr, strlen(ptr)); + if (atom) + symbol = atom->object; if (symbol == NULL || symbol->data.atom->package == NULL) { RETURN(0) = NIL; @@ -436,46 +421,45 @@ Lisp_FindAllSymbols(LispBuiltin *builtin) LispAtom *atom; LispPackage *pack; LispObj *list, *package, *result; - int i; + int length = 0; LispObj *string_or_symbol; string_or_symbol = ARGUMENT(0); - if (STRINGP(string_or_symbol)) + if (STRINGP(string_or_symbol)) { string = THESTR(string_or_symbol); - else if (SYMBOLP(string_or_symbol)) - string = ATOMID(string_or_symbol); + length = STRLEN(string_or_symbol); + } + else if (SYMBOLP(string_or_symbol)) { + string = ATOMID(string_or_symbol)->value; + length = ATOMID(string_or_symbol)->length; + } else LispDestroy("%s: %s is not a string or symbol", STRFUN(builtin), STROBJ(string_or_symbol)); result = NIL; - i = STRHASH(string); /* Traverse all packages, searching for symbols matching specified string */ for (list = PACK; CONSP(list); list = CDR(list)) { package = CAR(list); pack = package->data.package.package; - atom = pack->atoms[i]; - while (atom) { - if (strcmp(atom->string, string) == 0 && - LispDoSymbol(package, atom, 0, 1)) { - /* Return only one pointer to a matching symbol */ + atom = (LispAtom *)hash_check(pack->atoms, string, length); + if (atom && LispDoSymbol(package, atom, 0, 1)) { + /* Return only one pointer to a matching symbol */ - if (result == NIL) { - result = CONS(atom->object, NIL); - GC_PROTECT(result); - } - else { - /* Put symbols defined first in the - * beginning of the result list */ - RPLACD(result, CONS(CAR(result), CDR(result))); - RPLACA(result, atom->object); - } + if (result == NIL) { + result = CONS(atom->object, NIL); + GC_PROTECT(result); + } + else { + /* Put symbols defined first in the + * beginning of the result list */ + RPLACD(result, CONS(CAR(result), CDR(result))); + RPLACA(result, atom->object); } - atom = atom->next; } } GC_LEAVE(); @@ -651,7 +635,7 @@ Lisp_MakePackage(LispBuiltin *builtin) /* Error checks done, package_name is either a symbol or string */ if (!XSTRINGP(package_name)) - package_name = STRING(ATOMID(package_name)); + package_name = STRING(ATOMID(package_name)->value); GC_PROTECT(package_name); @@ -667,7 +651,7 @@ Lisp_MakePackage(LispBuiltin *builtin) /* Store all nicknames as strings */ package = CAR(list); if (!XSTRINGP(package)) - package = STRING(ATOMID(package)); + package = STRING(ATOMID(package)->value); if (nicks == NIL) { nicks = cons = CONS(package, NIL); GC_PROTECT(nicks); diff --git a/lisp/private.h b/lisp/private.h index 08d3bec..6655492 100644 --- a/lisp/private.h +++ b/lisp/private.h @@ -192,6 +192,9 @@ struct _LispProperty { }; struct _LispAtom { + hash_key *string; + struct _LispAtom *next; + /* hint: dynamically binded variable */ unsigned int dyn : 1; @@ -222,14 +225,12 @@ struct _LispAtom { /* Symbol value is constant, cannot be changed */ unsigned int constant : 1; - char *string; LispObj *object; /* backpointer to object ATOM */ int offset; /* in the environment list */ LispObj *package; /* package home of symbol */ LispObj *function; /* symbol function */ LispObj *name; /* symbol string */ LispProperty *property; - struct _LispAtom *next; LispObj *documentation[5]; }; @@ -243,20 +244,13 @@ struct _LispObjList { struct _LispPackage { LispObjList glb; /* global symbols in package */ LispObjList use; /* inherited packages */ - LispAtom *atoms[STRTBLSZ]; /* atoms in this package */ + hash_table *atoms; /* atoms in this package */ }; struct _LispOpaque { - int type; - char *desc; + hash_key *desc; LispOpaque *next; -}; - -/* These strings are never released, they are used to avoid - * the need of strcmp() on two symbol names, just compare pointers */ -struct _LispStringHash { - char *string; - LispStringHash *next; + int type; }; typedef enum _LispBlockType { @@ -357,8 +351,8 @@ struct _LispMac { int average; /* of cells freed after gc calls */ } gc; - LispStringHash *strings[STRTBLSZ]; - LispOpaque *opqs[STRTBLSZ]; + hash_table *strings; + hash_table *opqs; int opaque; LispObj *standard_input, *input, *input_list; @@ -452,6 +446,7 @@ void LispExportSymbol(LispObj*); void LispImportSymbol(LispObj*); /* always returns the same string */ +hash_key *LispGetAtomKey(char*, int); char *LispGetAtomString(char*, int); /* destructive fast reverse, note that don't receive a LispMac* argument */ @@ -474,8 +469,6 @@ void LispBlockUnwind(LispBlock*); void LispUpdateResults(LispObj*, LispObj*); void LispTopLevel(void); -#define STRHASH(string) LispDoHashString(string) -int LispDoHashString(char*); LispAtom *LispDoGetAtom(char *str, int); /* get value from atom's property list */ LispObj *LispGetAtomProperty(LispAtom*, LispObj*); diff --git a/lisp/read.c b/lisp/read.c index 3c5df3e..005a7b3 100644 --- a/lisp/read.c +++ b/lisp/read.c @@ -1322,7 +1322,7 @@ LispParseAtom(char *package, char *symbol, int intern, int unreadable, /* Get the object pointer */ if (pack == lisp__data.key) - object = KEYWORD(LispDoGetAtom(symbol, 0)->string); + object = KEYWORD(LispDoGetAtom(symbol, 0)->string->value); else object = ATOM(symbol); if (unreadable) @@ -1336,19 +1336,11 @@ LispParseAtom(char *package, char *symbol, int intern, int unreadable, else { /* Symbol must exist (and be extern) in the specified package */ - int i; LispAtom *atom; - i = STRHASH(symbol); - atom = pack->atoms[i]; - while (atom) { - if (strcmp(atom->string, symbol) == 0) { - object = atom->object; - break; - } - - atom = atom->next; - } + atom = (LispAtom *)hash_check(pack->atoms, symbol, strlen(symbol)); + if (atom) + object = atom->object; /* No object found */ if (object == NULL || object->data.atom->ext == 0) @@ -1875,13 +1867,13 @@ LispReadStruct(read_info *info) GC_PROTECT(fields); - len = strlen(ATOMID(CAR(fields))); + len = ATOMID(CAR(fields))->length; /* MAKE- */ if (len + 6 > sizeof(stk)) str = LispMalloc(len + 6); else str = stk; - sprintf(str, "MAKE-%s", ATOMID(CAR(fields))); + sprintf(str, "MAKE-%s", ATOMID(CAR(fields))->value); RPLACA(fields, ATOM(str)); if (str != stk) LispFree(str); diff --git a/lisp/struct.c b/lisp/struct.c index 7fbb486..366b459 100644 --- a/lisp/struct.c +++ b/lisp/struct.c @@ -65,8 +65,8 @@ Lisp_Defstruct(LispBuiltin *builtin) CHECK_SYMBOL(oname); - strname = ATOMID(oname); - length = strlen(strname); + strname = ATOMID(oname)->value; + length = ATOMID(oname)->length; /* MAKE- */ size = length + 6; @@ -101,13 +101,13 @@ Lisp_Defstruct(LispBuiltin *builtin) cons = object; object = CAR(object); } - if (!SYMBOLP(object) || strcmp(ATOMID(object), "P") == 0) + if (!SYMBOLP(object) || strcmp(ATOMID(object)->value, "P") == 0) /* p is invalid as a field name due to `type'-p */ LispDestroy("%s: %s cannot be a field for %s", STRFUN(builtin), STROBJ(object), ATOMID(oname)); if (!KEYWORDP(object)) - CAR(cons) = KEYWORD(ATOMID(object)); + CAR(cons) = KEYWORD(ATOMID(object)->value); /* check for repeated field names */ for (object = description; object != list; object = CDR(object)) { @@ -143,16 +143,18 @@ Lisp_Defstruct(LispBuiltin *builtin) LispExportSymbol(object); for (i = 0, list = description; CONSP(list); i++, list = CDR(list)) { + Atom_id id; + if (CONSP(CAR(list))) - sname = ATOMID(CAR(CAR(list))); + id = ATOMID(CAR(CAR(list))); else - sname = ATOMID(CAR(list)); - slength = strlen(sname); + id = ATOMID(CAR(list)); + slength = id->length; if (length + slength + 2 > size) { size = length + slength + 2; name = LispRealloc(name, size); } - sprintf(name, "%s-%s", strname, sname); + sprintf(name, "%s-%s", strname, id->value); atom = (object = ATOM(name))->data.atom; LispSetAtomStructProperty(atom, definition, i); if (!intern) diff --git a/lisp/write.c b/lisp/write.c index c5d7f24..1798b68 100644 --- a/lisp/write.c +++ b/lisp/write.c @@ -522,15 +522,15 @@ LispPrintCircle(LispObj *stream, LispObj *object, long circle, static int LispWriteAlist(LispObj *stream, LispArgList *alist, write_info *info) { - char *name; + Atom_id name; int i, length = 0, need_space = 0; #define WRITE_ATOM(object) \ name = ATOMID(object); \ - length += LispDoWriteAtom(stream, name, strlen(name), \ + length += LispDoWriteAtom(stream, name->value, name->length, \ info->print_case) -#define WRITE_STRING(string) \ - length += LispDoWriteAtom(stream, string, strlen(string), \ +#define WRITE_ATOMID(atomid) \ + length += LispDoWriteAtom(stream, atomid->value, atomid->length, \ info->print_case) #define WRITE_OBJECT(object) \ length += LispDoWriteObject(stream, object, info, 1) @@ -552,7 +552,7 @@ LispWriteAlist(LispObj *stream, LispArgList *alist, write_info *info) if (alist->optionals.num_symbols) { if (need_space) WRITE_SPACE(); - WRITE_STRING(Soptional); + WRITE_ATOMID(Soptional); WRITE_SPACE(); for (i = 0; i < alist->optionals.num_symbols; i++) { WRITE_OPAREN(); @@ -572,7 +572,7 @@ LispWriteAlist(LispObj *stream, LispArgList *alist, write_info *info) if (alist->keys.num_symbols) { if (need_space) WRITE_SPACE(); - length += LispDoWriteAtom(stream, Skey, 4, info->print_case); + length += LispDoWriteAtom(stream, Skey->value, 4, info->print_case); WRITE_SPACE(); for (i = 0; i < alist->keys.num_symbols; i++) { WRITE_OPAREN(); @@ -599,7 +599,7 @@ LispWriteAlist(LispObj *stream, LispArgList *alist, write_info *info) if (alist->rest) { if (need_space) WRITE_SPACE(); - WRITE_STRING(Srest); + WRITE_ATOMID(Srest); WRITE_SPACE(); WRITE_ATOM(alist->rest); need_space = 1; @@ -607,7 +607,7 @@ LispWriteAlist(LispObj *stream, LispArgList *alist, write_info *info) if (alist->auxs.num_symbols) { if (need_space) WRITE_SPACE(); - WRITE_STRING(Saux); + WRITE_ATOMID(Saux); WRITE_SPACE(); for (i = 0; i < alist->auxs.num_symbols; i++) { WRITE_OPAREN(); @@ -622,7 +622,7 @@ LispWriteAlist(LispObj *stream, LispArgList *alist, write_info *info) WRITE_CPAREN(); #undef WRITE_ATOM -#undef WRITE_STRING +#undef WRITE_ATOMID #undef WRITE_OBJECT #undef WRITE_OPAREN #undef WRITE_SPACE @@ -867,9 +867,9 @@ write_again: switch (OBJECT_TYPE(object)) { case LispNil_t: if (object == NIL) - string = Snil; + string = Snil->value; else if (object == T) - string = St; + string = St->value; else if (object == DOT) string = "#"; else if (object == UNSPEC) @@ -1017,7 +1017,7 @@ write_again: ->data.atom->property->alist, info); } else { - length += LispDoWriteAtom(stream, Snil, 3, info->print_case); + length += LispDoWriteAtom(stream, "NIL", 3, info->print_case); length += LispWriteChar(stream, ' '); length += LispWriteAlist(stream, (LispArgList*)object-> data.lambda.name->data.opaque.data, @@ -1299,7 +1299,7 @@ LispWriteAtom(LispObj *stream, LispObj *object, write_info *info) } if (atom->unreadable) length += LispWriteChar(stream, '|'); - length += LispDoWriteAtom(stream, id, strlen(id), + length += LispDoWriteAtom(stream, id->value, id->length, atom->unreadable ? UPCASE : info->print_case); if (atom->unreadable) length += LispWriteChar(stream, '|'); diff --git a/tags.c b/tags.c new file mode 100644 index 0000000..89be2ed --- /dev/null +++ b/tags.c @@ -0,0 +1,451 @@ +/* + * Copyright © 2007 Paulo César Pereira de Andrade + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Paulo César Pereira de Andrade + */ + +#include "xedit.h" +#include "util.h" +#include "re.h" + +/* + * Types + */ +typedef struct _TagsEntry TagsEntry; +typedef struct _RegexEntry RegexEntry; + +struct _TagsEntry { + hash_key *symbol; + TagsEntry *next; + + int nentries; + hash_entry **filenames; + char **patterns; +}; + +struct _RegexEntry { + hash_key *pattern; + RegexEntry *next; + + re_cod regex; +}; + +struct _XeditTagsInfo { + char *pathname; + hash_table *entries; + hash_table *filenames; + hash_table *patterns; + + /* Cache information for circulating over multiple definitions */ + TagsEntry *entry; + int offset; + Widget textwindow; + XawTextPosition position; +}; + +/* + * Prototypes + */ +static void FindTagFirst(XeditTagsInfo *tags, char *symbol); +static void FindTagNext(XeditTagsInfo *tags, + Widget window, XawTextPosition position); +static void FindTag(XeditTagsInfo *tags); + +/* + * Initialization + */ +extern Widget texts[3]; +XeditTagsInfo *tags; + +/* + * Implementation + */ +XeditTagsInfo * +LoadTagsFile(char *tagsfile) +{ + char *ptr; + int length; + FILE *file; + XeditTagsInfo *tags; + TagsEntry *entry; + hash_entry *file_entry; + char buffer[BUFSIZ]; + char *symbol, *filename, *pattern; + + + file = fopen(tagsfile, "r"); + if (file) { + tags = XtNew(XeditTagsInfo); + + /* Tags information */ + tags->pathname = XtNewString(tagsfile); + tags->entries = hash_new(809, NULL); + tags->filenames = hash_new(31, NULL); + tags->patterns = hash_new(47, NULL); + + /* Cache information */ + tags->entry = NULL; + tags->offset = 0; + tags->textwindow = NULL; + tags->position = 0; + + while (fgets(buffer, sizeof(buffer) - 1, file)) { + /* XXX Ignore malformed lines and tags file format information */ + if (isspace(buffer[0]) || buffer[0] == '!') + continue; + + /* Symbol name */ + symbol = ptr = buffer; + while (*ptr && !isspace(*ptr)) + ptr++; + *ptr++ = '\0'; + while (isspace(*ptr)) + ptr++; + + /* Filename with basename of tagsfile for symbol definition */ + filename = ptr; + while (*ptr && !isspace(*ptr)) + ptr++; + *ptr++ = '\0'; + while (isspace(*ptr)) + ptr++; + + pattern = ptr; + /* Check for regex */ + if (*pattern == '/' || *pattern == '?') { + ptr++; + while (*ptr && *ptr != *pattern) { + if (*ptr == '\\') { + if (ptr[1] == *pattern) { + /* XXX tags will escape pattern end, not sure + * about other special characters */ + memmove(ptr, ptr + 1, strlen(ptr)); + } + else { + ++ptr; + if (!*ptr) + break; + } + } + ptr++; + } + + if (*ptr != *pattern) + continue; + ++pattern; + if (*pattern == '^' && ptr[-1] == '$') { + ++pattern; + --ptr; + } + } + /* Check for line number */ + else if (isdigit(*ptr)) { + while (isdigit(*ptr)) + ptr++; + } + /* Format not understood */ + else + continue; + + *ptr = '\0'; + + length = strlen(symbol); + entry = (TagsEntry *)hash_check(tags->entries, + symbol, length); + if (entry == NULL) { + entry = XtNew(TagsEntry); + entry->symbol = XtNew(hash_key); + entry->symbol->value = XtNewString(symbol); + entry->symbol->length = length; + entry->next = NULL; + entry->nentries = 0; + entry->filenames = NULL; + entry->patterns = NULL; + hash_put(tags->entries, (hash_entry *)entry); + } + + length = strlen(filename); + file_entry = hash_check(tags->filenames, filename, length); + if (file_entry == NULL) { + file_entry = XtNew(hash_entry); + file_entry->key = XtNew(hash_key); + file_entry->key->value = XtNewString(filename); + file_entry->key->length = length; + file_entry->next = NULL; + hash_put(tags->filenames, file_entry); + } + + if ((entry->nentries % 4) == 0) { + entry->filenames = (hash_entry **) + XtRealloc((char *)entry->filenames, + sizeof(hash_entry *) * + (entry->nentries + 4)); + entry->patterns = (char **) + XtRealloc((char *)entry->patterns, + sizeof(char *) * + (entry->nentries + 4)); + } + entry->filenames[entry->nentries] = file_entry; + entry->patterns[entry->nentries] = XtNewString(pattern); + ++entry->nentries; + } + fclose(file); + + ptr = strrchr(tags->pathname, '/'); + if (ptr) + ptr[1] = '\0'; + } + else { + XmuSnprintf(buffer, sizeof(buffer), + "Failed to load tags file %s.\n" + "Run \"ctags -R\" to build a tags file.\n", tagsfile); + XeditPrintf(buffer); + tags = NULL; + } + + + return (tags); +} + +void +TagsAction(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + char buffer[1024]; + XawTextPosition position, left, right; + XawTextBlock block; + int length; + Widget source; + + if (tags) { + source = XawTextGetSource(w); + position = XawTextGetInsertionPoint(w); + XawTextGetSelectionPos(w, &left, &right); + if (right > left) { + XawTextSourceRead(source, left, &block, right - left); + length = block.length + 1; + if (length >= sizeof(buffer)) + length = sizeof(buffer); + XmuSnprintf(buffer, length, "%s", block.ptr); + tags->textwindow = w; + tags->position = position; + FindTagFirst(tags, buffer); + } + else + FindTagNext(tags, w, position); + } + else + Feep(); +} + +static void +FindTagFirst(XeditTagsInfo *tags, char *symbol) +{ + char *ptr; + TagsEntry *entry; + char buffer[1024]; + + /* Check for malformed parameters */ + ptr = symbol; + while (*ptr) { + if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n' || *ptr == '\r' || + *ptr == '(' || *ptr == ')') { + Feep(); + return; + } + ptr++; + } + + entry = (TagsEntry *)hash_check(tags->entries, symbol, strlen(symbol)); + if (entry == NULL) { + XmuSnprintf(buffer, sizeof(buffer), "Symbol %s not in tags\n", symbol); + XeditPrintf(buffer); + Feep(); + return; + } + + tags->entry = entry; + tags->offset = 0; + + FindTag(tags); +} + +static void +FindTagNext(XeditTagsInfo *tags, Widget window, XawTextPosition position) +{ + if (window != tags->textwindow || position != tags->position) + Feep(); + else { + if (tags->entry->nentries > 1) { + if (++tags->offset >= tags->entry->nentries) + tags->offset = 0; + FindTag(tags); + } + else + Feep(); + } +} + +static void +FindTag(XeditTagsInfo *tags) +{ + static String params[] = { "vertical", NULL }; + + char buffer[BUFSIZ]; + char *pattern; + int length; + char *line; + char *text; + RegexEntry *regex; + re_mat match; + XawTextPosition position, left, right, last; + Widget source; + XawTextBlock block; + int size; + int lineno; + Boolean found; + xedit_flist_item *item; + Widget otherwindow; + + XmuSnprintf(buffer, sizeof(buffer), "%s%s", tags->pathname, + tags->entry->filenames[tags->offset]->key->value); + + pattern = tags->entry->patterns[tags->offset]; + if (isdigit(*pattern)) { + lineno = atoi(pattern); + regex = NULL; + } + else { + lineno = 0; + length = strlen(pattern); + regex = (RegexEntry *)hash_check(tags->patterns, pattern, length); + if (regex == NULL) { + regex = XtNew(RegexEntry); + regex->pattern = XtNew(hash_key); + regex->pattern->value = XtNewString(pattern); + regex->pattern->length = length; + regex->next = NULL; + if (recomp(®ex->regex, pattern, RE_NOSUB | RE_NOSPEC)) { + XmuSnprintf(buffer, sizeof(buffer), + "Failed to compile regex %s\n", pattern); + XeditPrintf(buffer); + Feep(); + return; + } + } + } + + /* Short circuit to know if split horizontally */ + if (!XtIsManaged(texts[1])) + XtCallActionProc(textwindow, "split-window", NULL, params, 1); + + /* Switch to "other" buffer */ + XtCallActionProc(textwindow, "other-window", NULL, NULL, 0); + + /* This should print a message if file cannot be opened for reading */ + if (!LoadFileInTextwindow(tags->entry->filenames[tags->offset]->key->value, + buffer)) + return; + + otherwindow = textwindow; + + item = FindTextSource(XawTextGetSource(textwindow), NULL); + source = item->source; + left = XawTextSourceScan(source, 0, XawstAll, XawsdLeft, 1, True); + + found = False; + + if (lineno) { + right = RSCAN(left, lineno, False); + left = LSCAN(right, 1, False); + found = True; + } + else { + right = RSCAN(left, 1, True); + last = XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True); + text = buffer; + + size = sizeof(buffer); + for (;;) { + length = right - left; + match.rm_so = 0; + match.rm_eo = length; + XawTextSourceRead(source, left, &block, right - left); + if (block.length >= length) + line = block.ptr; + else { + if (length > size) { + if (text == buffer) + text = XtMalloc(length); + else + text = XtRealloc(text, length); + size = length; + } + line = text; + memcpy(line, block.ptr, block.length); + length = block.length; + for (position = left + length; + position < right; + position += block.length) { + XawTextSourceRead(source, position, &block, right - position); + memcpy(line + length, block.ptr, block.length); + length += block.length; + } + } + + /* If not last line or if it ends in a newline */ + if (right < last || line[match.rm_eo] == '\n') + --match.rm_eo; + + if (reexec(®ex->regex, line, 1, &match, RE_STARTEND) == 0 && + match.rm_eo > match.rm_so) { + right = left + match.rm_so + (match.rm_eo - match.rm_so); + found = True; + break; + } + else if (right >= last) { + XmuSnprintf(buffer, sizeof(buffer), + "Failed to match regex %s\n", pattern); + XeditPrintf(buffer); + Feep(); + break; + } + else { + left = LSCAN(right + 1, 1, False); + right = RSCAN(left, 1, True); + } + } + + if (text != buffer) + XtFree(text); + } + + /* Switch back to editing buffer */ + XtCallActionProc(otherwindow, "other-window", NULL, NULL, 0); + + if (found) { + if (source != XawTextGetSource(tags->textwindow) || + right < tags->position || left > tags->position) { + XawTextSetInsertionPoint(otherwindow, left); + XawTextSetSelection(otherwindow, left, right); + } + } +} diff --git a/util.c b/util.c index 1d7c5f3..455f8e0 100644 --- a/util.c +++ b/util.c @@ -27,8 +27,13 @@ /* $XFree86: xc/programs/xedit/util.c,v 1.26 2003/05/07 20:54:43 herrb Exp $ */ #include +#if 0 /* FIXME XeditPrintf */ #include +#endif + +#include /* POSIX basename() */ #include /* for realpath() */ +#include /* for ENOENT */ #include "xedit.h" #include @@ -58,6 +63,7 @@ extern Widget scratch; extern Widget vpanes[2], labels[3], texts[3], forms[3]; extern XawTextWrapMode wrapmodes[3]; +#if 0 /* FIXME XeditPrintf */ #ifndef va_copy # ifdef __va_copy # define va_copy __va_copy @@ -65,20 +71,30 @@ extern XawTextWrapMode wrapmodes[3]; # error "no working va_copy was found" # endif #endif +#endif /* FIXME XeditPrintf */ /* * Implementation */ void -XeditPrintf(const char *format, ...) +XeditPrintf( +#if 0 /* FIXME */ + const char *format, ... +#else + const char *str +#endif + ) { +#if 0 char *str; size_t size; va_list va, va2; +#endif XawTextBlock text; XawTextPosition pos; - + +#if 0 va_start(va, format); va_copy(va2, va); @@ -86,27 +102,32 @@ XeditPrintf(const char *format, ...) va_end(va2); str = (char *)malloc(size + 1); +#endif if (str == NULL) return; +#if 0 vsnprintf(str, size + 1, format, va); str[size] = 0; va_end(va); +#endif pos = XawTextSourceScan(XawTextGetSource(messwidget), 0, XawstAll, XawsdRight, 1, True); text.length = strlen(str); - text.ptr = str; + text.ptr = (char *)str; text.firstPos = 0; text.format = FMT8BIT; XawTextReplace(messwidget, pos, pos, &text); XawTextSetInsertionPoint(messwidget, pos + text.length); - + +#if 0 free(str); +#endif } Widget @@ -456,16 +477,38 @@ SwitchTextSource(xedit_flist_item *item) XtSetValues(filenamewindow, args, num_args); } -/* XXX sizeof(name) must match argument size for realpath */ -static char name[BUFSIZ]; char * ResolveName(char *filename) { +#ifndef __UNIXOS2__ + static char *name; + char *result, *tmp = name; +#endif + if (filename == NULL) filename = GetString(filenamewindow); #ifndef __UNIXOS2__ - return (realpath(filename, name)); + name = XtMalloc(BUFSIZ); + /* Ensure not passing the same pointer again to realpath as weird + * results may happen with glibc due to some "optmization" apparently + * not doing the expected behaviour when compiling with -O2 but working + * without optimization... */ + XtFree(tmp); + result = realpath(filename, name); + + if (result == NULL && errno == ENOENT) { + char *dir = dirname(filename); + + /* Again something that may break elsewhere, if the path does not + * exist, glibc realpath will return NULL but should have done the + * expected/proper work of canonicallizing the path, i.e. correcting + * ../ and removing ./ or correcting // */ + if (dir && access(dir, F_OK) == 0) + result = name; + } + + return (result); #else return filename; #endif diff --git a/util.h b/util.h new file mode 100644 index 0000000..c50f543 --- /dev/null +++ b/util.h @@ -0,0 +1,80 @@ +/* + * Copyright © 2007 Paulo César Pereira de Andrade + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Paulo César Pereira de Andrade + */ + +/* Generic hash table */ + +#ifndef _util_h +#define _util_h + +/* + * Types + */ +typedef struct _hash_key hash_key; +typedef struct _hash_entry hash_entry; +typedef struct _hash_table hash_table; +typedef int (*hash_compare)(hash_key *left, hash_key *right); + +struct _hash_key { + unsigned char *value; + unsigned int length; +}; + +struct _hash_entry { + hash_key *key; + hash_entry *next; +}; + +struct _hash_table { + hash_entry **entries; + unsigned int count; /* length of entries */ + unsigned int length; /* sum of entries */ + hash_compare compare; + + struct { + int offset; + hash_entry *entry; + } iter; +}; + +/* + * Prototypes + */ +hash_table *hash_new(unsigned int length, hash_compare compare); +hash_entry *hash_put(hash_table *hash, hash_entry *entry); +hash_entry *hash_get(hash_table *hash, hash_key *name); +hash_entry * hash_check(hash_table *hash, unsigned char *name, unsigned int length); +void hash_rem(hash_table *hash, hash_entry *entry); +/* Removes from hash table but doesn't release any memory */ +hash_entry *hash_rem_no_free(hash_table *hash, hash_entry *entry); +void hash_rehash(hash_table *hash, unsigned int length); +hash_entry *hash_iter_first(hash_table *hash); +hash_entry *hash_iter_next(hash_table *hash); + +/* Frees all data. When casting to another type, use the + * iterator to free extra data */ +void hash_clr(hash_table *hash); +void hash_del(hash_table *hash); + +#endif /* _util_h */ diff --git a/xedit.c b/xedit.c index 8d96258..ae873df 100644 --- a/xedit.c +++ b/xedit.c @@ -60,7 +60,8 @@ static XtActionsRec actions[] = { {"xedit-keyboard-reset",XeditKeyboardReset}, #endif {"ispell", IspellAction}, -{"line-edit", LineEditAction} +{"line-edit", LineEditAction}, +{"tags", TagsAction} }; #define DEF_HINT_INTERVAL 300 /* in seconds, 5 minutes */ @@ -73,6 +74,7 @@ static XawTextPositionInfo infos[3]; Widget topwindow, textwindow, messwidget, labelwindow, filenamewindow; Widget scratch, hpane, vpanes[2], labels[3], texts[3], forms[3], positions[3]; Widget options_popup, dirlabel, dirwindow; +Boolean international; Boolean line_edit; XawTextWrapMode wrapmodes[3]; @@ -89,6 +91,8 @@ Display *CurDpy; struct _app_resources app_resources; struct _xedit_flist flist; +extern XeditTagsInfo *tags; + #define Offset(field) XtOffsetOf(struct _app_resources, field) static XtResource resources[] = { @@ -108,23 +112,38 @@ static XtResource resources[] = { Offset(position_format), XtRString, "L%l"}, {"autoReplace", "Replace", XtRString, sizeof(char*), Offset(auto_replace), XtRImmediate, NULL}, + {"tagsName", "TagsName", XtRString, sizeof(char *), + Offset(tagsName), XtRString, "tags"}, + {"loadTags", "LoadTags", XtRBoolean, sizeof(Boolean), + Offset(loadTags), XtRImmediate, (XtPointer)TRUE}, }; #undef Offset +#ifdef INCLUDE_XPRINT_SUPPORT String fallback_resources[] = { "*international: True", /* set this globally for ALL widgets to avoid wiered crashes */ NULL }; +#endif int main(int argc, char *argv[]) { + Boolean exists; + char *filename; + FileAccess file_access; XtAppContext appcon; unsigned num_loaded = 0; XtSetLanguageProc(NULL, NULL, NULL); - topwindow = XtAppInitialize(&appcon, "Xedit", NULL, 0, &argc, argv, fallback_resources, NULL, 0); + topwindow = XtAppInitialize(&appcon, "Xedit", NULL, 0, &argc, argv, +#ifdef INCLUDE_XPRINT_SUPPORT + fallback_resources, +#else + NULL, +#endif + NULL, 0); XtAppAddActions(appcon, actions, XtNumber(actions)); XtOverrideTranslations @@ -173,10 +192,7 @@ main(int argc, char *argv[]) UpdateTextProperties(0); if (argc > 1) { - Boolean exists; xedit_flist_item *item; - FileAccess file_access; - char *filename; Widget source; Arg args[2]; unsigned i, num_args; @@ -246,8 +262,9 @@ main(int argc, char *argv[]) flags = 0; XtSetArg(args[num_args], XtNstring, NULL); num_args++; } - source = XtVaCreateWidget("textSource", - multiSrcObjectClass, topwindow, + source = XtVaCreateWidget("textSource", international ? + multiSrcObjectClass + : asciiSrcObjectClass, topwindow, XtNtype, XawAsciiFile, XtNeditType, XawtextEdit, NULL, NULL); @@ -285,6 +302,13 @@ main(int argc, char *argv[]) else XtSetKeyboardFocus(topwindow, textwindow); + if (app_resources.loadTags) { + filename = ResolveName(app_resources.tagsName); + file_access = CheckFilePermissions(filename, &exists); + if (file_access == READ_OK || file_access == WRITE_OK) + tags = LoadTagsFile(filename); + } + XtAppMainLoop(appcon); return EXIT_SUCCESS; } @@ -354,11 +378,22 @@ makeButtonsAndBoxes(Widget parent) XtSetArg(arglist[num_args], XtNeditType, XawtextEdit); ++num_args; textwindow = XtCreateManagedWidget(editWindow, asciiTextWidgetClass, vpanes[0], arglist, num_args); + +#ifdef INCLUDE_XPRINT_SUPPORT + international = True; +#else + /* Get international resource value form the textwindow */ + num_args = 0; + XtSetArg(arglist[num_args], XtNinternational, &international); ++num_args; + XtGetValues(textwindow, arglist, num_args); +#endif + num_args = 0; XtSetArg(arglist[num_args], XtNtype, XawAsciiFile); ++num_args; XtSetArg(arglist[num_args], XtNeditType, XawtextEdit); ++num_args; - scratch = XtVaCreateWidget("textSource", - multiSrcObjectClass, topwindow, + scratch = XtVaCreateWidget("textSource", international ? + multiSrcObjectClass : + asciiSrcObjectClass, topwindow, XtNtype, XawAsciiFile, XtNeditType, XawtextEdit, NULL, NULL); diff --git a/xedit.h b/xedit.h index eeb7ca3..4ce792b 100644 --- a/xedit.h +++ b/xedit.h @@ -49,6 +49,11 @@ #include +#define LSCAN(from, count, include) \ + XawTextSourceScan(source, from, XawstEOL, XawsdLeft, count, include) +#define RSCAN(from, count, include) \ + XawTextSourceScan(source, from, XawstEOL, XawsdRight, count, include) + typedef struct _xedit_hints { char *resource; unsigned long interval; @@ -61,6 +66,7 @@ typedef struct _xedit_hints { typedef enum {NO_READ, READ_OK, WRITE_OK} FileAccess; typedef struct _XeditLispData XeditLispData; +typedef struct _XeditTagsInfo XeditTagsInfo; #define CHANGED_BIT 0x01 #define EXISTS_BIT 0x02 @@ -96,17 +102,27 @@ extern struct _app_resources { char *changed_pixmap_name; char *position_format; char *auto_replace; + char *tagsName; + Boolean loadTags; } app_resources; extern Widget topwindow, textwindow, labelwindow, filenamewindow, messwidget; extern Widget dirlabel, dirwindow; +extern Boolean international; extern Boolean line_edit; /* externals in xedit.c */ void Feep(void); /* externals in util.c */ +/* FIXME Unfortunatelly this won't work if there are % characters in format + * string due to all other code assuming it is really a "XeditPuts" + * without adding an ending newline */ +#if 0 void XeditPrintf(const char *format, ...); +#else +void XeditPrintf(const char *str); +#endif Widget MakeCommandButton(Widget, char*, XtCallbackProc); Widget MakeStringBox(Widget, String, String); String GetString(Widget); @@ -137,6 +153,7 @@ void DoPrint(Widget, XtPointer, XtPointer); void CancelFindFile(Widget, XEvent*, String*, Cardinal*); void FindFile(Widget, XEvent*, String*, Cardinal*); void LoadFile(Widget, XEvent*, String*, Cardinal*); +Bool LoadFileInTextwindow(char *name, char *resolved_name); #ifdef INCLUDE_XPRINT_SUPPORT void PrintFile(Widget, XEvent*, String*, Cardinal*); #endif /* INCLUDE_XPRINT_SUPPORT */ @@ -170,6 +187,10 @@ void UnsetTextProperties(xedit_flist_item*); void CreateEditModePopup(Widget); void SetEditModeMenu(void); +/* tags.c */ +XeditTagsInfo *LoadTagsFile(char *tagsfile); +void TagsAction(Widget, XEvent*, String*, Cardinal*); + /* externs for system replacement functions */ #ifdef NEED_STRCASECMP int strcasecmp(const char *s1, const char *s2); diff --git a/xedit.man b/xedit.man index 0c2f540..582a92f 100644 --- a/xedit.man +++ b/xedit.man @@ -245,6 +245,13 @@ of the this button and displays it in the Edit window. .SH RESOURCES For \fIxedit\fP the available resources are: .TP 8 +.B tagsName (\fPClass\fB TagsName) +Specifies the name of the tags file to search at startup. Default value +is \fItags\fP. +.TP 8 +.B loadTags (\fPClass\fB LoadTags) +Boolean value to enable or disabling loading tags. Default is \fITrue\fP. +.TP 8 .B enableBackups (\fPClass\fB EnableBackups) Specifies that, when edits made to an existing file are saved, .I xedit